테스트 환경을 복잡하게 만드는 요소와 해소 - 대역을 구현하기

대역의 필요성 - 자원을 많이 사용하고 통제 불가능 외부 API의 경우 항상 접근 가능한지 알 수 없다. 접근 가능하더라도 모든 테스트마다 외부 API와 연결하는 것은 자원 낭비이다. 테스트를 위한 API가 존재하지 않을 수 있다. 외부 API는 롤백이 어렵다. 리포지토리의 경우, 테스트 DB에 데이터가 남아 있는 경우 정상적인 테스트를 수행하지 못할 수도 있다. (특히 멀티 스레드를 테스트할 때) 트랜잭션으로 롤백할 수 없는 테스트가 존재한다. 이 경우 insert가 되어 테스트 DB가 오염될 수 있다. DB와의 연결 자체가 자원을 소비한다. in memory db를 사용하더라도, create database -> create table -> insert into … 의 과정을 겪어야 한다.

java, 반복되는 로직의 흐름을 강제하기 위한 탬플릿 패턴 - Comparable과 함수형 인터페이스

특정 로직 흐름을 명시하고 강제할 필요가 있다. 이를 if문으로 해소할 경우 복잡해진다. 회원의 등급이 두 개가 있다. 슈퍼 어드민 - 일반 어드민 일반 어드민은 자신이 작성한 글을 수정할 수 있지만 남이 작성한 글을 수정할 수 없다. 슈퍼 어드민은 누가 쓴 글이든 수정할 수 있다. 이 경우 코드로 작성하면 어떻게 될까?

java, 비교와 정렬 - Comparable, Comparator, Arrays.sort 외

자바에서 비교를 위해 기본타입의 경우 단순하게 비교가 되지만 객체의 경우 비교하기가 어렵다. 자바에서는 Comparable 와 Comparator를 통해 비교를 위한 명확하고 확장 가능한 인터페이스를 제공한다.

spring interceptor에서 http body를 추출할 수 있을까?

spring interceptor는 body를 추출할 수 없다. 웹 어플리케이션을 다룰 때 보통 content-type은 application/x-www-form-urlencoded 혹은 application/json을 사용한다. 전자의 key와 value의 묶음인 query parameter는, http method인 get과 post에 관계 없이 컨트롤러와 스프링 인터셉터 등에서 손쉽게 활용 가능하다. 특히 인터셉터에서 파라미터에 대한 로깅을 할 때 효과적으로 사용 가능하다. 좀 더 욕심을 내자. 후자인 json을 로그로 남기고 싶었다. 여러 시도를 했지만 결과적으로 실패하였다. 실패한 이유와 해결책은 다음 블로그에 잘 정리되어 있다. https://stuffdrawers.tistory.com/9 해당 내용을 정리하면 대략 아래와 같다.

DDD Start! 도메인 주도 개발 시작하기 읽기

들어가며 TDD, 리팩터링 등 OOP를 위한 다양한 패러다임을 학습하고 익히면서 도메인 주도 개발에 대한 욕구가 생겼다. 최범균 개발자님의 “테스트 주도 개발 시작하기”를 잘 읽었었다. 이번 신규 서적인 “도메인 주도 개발 시작하기: DDD 핵심 개념 정리부터 구현까지” 를 읽었다. 아래 정리한 내용은 내가 이해하고 실무에 적용할만한 부분들에 대하여 간단하게 정리하였다.

java, thread-safe로 바라보는 LocalThread와 ConcurrentHashMap

thread safety 란? 병렬 처리를 학습하며 thread safety 혹은 thread-safe란 표현을 자주 본다. 병렬 처리 과정에서 안전하다는 의미이다. 한편, 나는 안전하고 효과적인 병렬 처리를 위해 자주 사용되는 LocalThread과 ConcurrentHashMap을 학습하며, thread safety가 무엇인지 혼란스러웠다. LocalThread은 스레드 마다 별도로 할당된 공간으로서, 다른 스레드의 ThreadLocal에는 접근할 수 없다. ConcurrentHashMap은 여러 스레드가 동시에 접근 가능하되, thread safety를 보장한다. 두 객체가 스레드에 제공하는 기능과 용도는 명확하게 달라 보인다. thread safety란 정확히 무엇인가?

sql, column is null과 column = null

‘is null’과 ‘= null’의 차이를 명확하게 확인할 필요가 있었다 sql을 사용하면서 column is null 과 column = null 의 차이가 궁금했다. 특히 아래와 같이 sql mapping를 활용하여 어플리케이션에서 쿼리를 날릴 때 기대하는 방향대로 동작하지 않을까 걱정했다.

레코드의 정합성 문제 해소를 위한 고민 - SERIALIZABLE, x 락(for update), update - affected 검토

들어가며 격리수준, 락, 데이터 정합성에 대하여 앞서 4개의 글로 정황하게 정리했던 이유는, 사실 지금의 문제를 해소하기 위해서였다. 문제가 발생한 환경은 mysql, mybatis, 격리수준은 Repeatable Read이다. 문제가 발생했던 코드는 대략 아래와 같다.

x lock은 정말로 select에 대한 배타적 락을 보장하는가?

x 락이 동일한 쿼리에 대한 배타적인 권한을 줄 것이라 기대하였다. x 락은 조회에 대하여 배타적인 권리를 가진다. 자바는 synchronized가 존재하며 이는 하나의 어플리케이션에서 하나의 스레드만 동작함을 보장한다. 나는 x락을 일종의 db의 synchronized로 이해하였다. 어떤 상황에서는 단 하나의 트랜잭션만 배타적인 권한을 행사함을 기대하였다. 대체로 DB는 하나이다. 하지만 어플리케이션은 여러 개이고 그 내부의 스레드는 여러 개이다. 수십개의 스레드와 커넥션이 있는 상황에서 DB 차원에서 동기적 처리를 보장하면 개발할 때 쉬울 것임은 분명하다. 동기적 처리를 DB에 미루고 싶었다. 이처럼 DB 차원에서의 동기적 처리를 보장하는 기능으로 접근하고 학습하였는데, 실제로 테스트한 결과 이는 제한적이었다.

DB의 데이터 정합성을 고려한 개발 팁

트랜잭션의 편리한 롤백 기능은 사실 제한적인 기능이다 트랜잭션은 예외 상황이 발생할 경우 롤백을 통해 해소한다. 수행했던 작업을 취소한다. all or nothing. 모든 작업이 트랜잭션과 같이 동작할 경우, 쉽게 데이터의 정합성을 보장할 수 있다. 하지만 롤백처럼 간단하게 취소되는 상황은 어플리케이션 개발 과정에서 일부분에 불과하다. 다른 트랜잭션, 다른 커넥션, 이미 커멧된 트랜잭션에 대해서 DB는 롤백을 보장하지 않는다. 외부 API와의 통신 역시 롤백이 불가능하다. 컨펌 메일을 보냈는데, 내부 사정으로 사업을 취소하고 싶다고 하여, 이미 보낸 메일을 되돌려 받을 수 없다. 각각의 상황에서 데이터 정합성을 어떻게 확보할지에 대하여 고민해야 한다. 대체로 catch 블럭을 활용하여 롤백을 위한 기능을 마련한다. 데이터 정합성에 문제가 생길 수 있는...

db의 lock에 대하여

Share lock, S lock 하나의 로우에 대한 락을 여러 트랜잭션이 소유할 수 있다. select만을 한다면 s lock은 특별한 제약이 없다. 다만, 두 개 이상의 트랜잭션이 s lock을 가진 상태에서 그 누구도 해당 레코드에 대해 x lock을 가질 수 없다. 그러니까 갱신을 할 수 없다. select ... lock in share mode, select ... for share 의 쿼리를 작성하거나, 격리수준을 SERIALIZABLE로 한다.

트랜잭션의 격리수준

격리수준? 하나의 트랜잭션이 DB를 조회(select) 함에 있어서 어느 수준으로 데이터의 일관성을 보장하는지에 대한 수준이다. 격리수준이 높을 수록 일관성이 확보되지만 성능이나 락 문제가 발생할 수 있다. 격리수준은 아래와 같은 종류가 있다. READ UNCOMMITTED READ COMMITTED REPEATABLE READ SERIALIZABLE

클린코더스, SOLID

SOLID The Source Code is the Design 엔지니어는 설계도를 만드는 사람이다. 소프트웨어 개발자에게 설계도는 무엇인가? 키보드 엔지니어는 키보드의 설계도를 만든다. 실제 키보드 생산은 공장이 한다. 소프트웨어 엔지니어는 소스코드를 생산한다. 실제 어플리케이션은 빌드가 된 바이너리 코드이다. 좋은 설계는 좋은 소스코드이며, 좋은 소스코드를 생산하는 것이 매우 중요하다.

클린코더스, Architecture

좋은 Architecture는 tool로부터 decouple해야 한다. 대체로 아키텍쳐라고 하면 보통 언어(java), 프레임워크(스프링), DB(Mysql), sql mapper(jpa hibernate) 등을 의미하며, 개발 이전의 설계나 기획이라고 생각하는 경향이 크다. 오히려 좋은 아키텍쳐는 위에 나열한 툴과 기획에 의존하지 않는 상태에서 만들어진다. tool에 한정되는 순간 툴에 종속적인 유연하지 않은 어플리케이션이 된다. tool로부터 decouple하고 최대한 그 선택을 늦춰야(Deferring decisions) 한다. 현대적 의미에서 아키텍쳐는 use case로 설명한다. 어플리케이션의 actor를 상정하고, 사용자가 어플리케이션을 사용하고 상호작용하는 것에 초점을 맞춘다.

클린코더스, tdd 시작하기

TDD의 원칙들 실패하는 테스트가 있을 때만 프로덕션 코드를 작성할 수 있다. 실패를 나타내는데 충분한 테스트 코드만 작성하라 실패하는 테스트가 성공할 수 있는 수준으로만 테스트 코드를 작성하라.

클린코더스, form, class와 data structure

class와 data structure란? class는 필드가 private 이며 method로 동작한다. data structure는 필드가 public 이며 method가 존재하지 않는다. 자바를 사용하고 class 파일을 만들어도 자바빈 패턴을 사용한다면 이는 class가 아닌 data structure를 사용하는 것과 다름 없다.

클린코더스, form 좋은 주석을 위하여

필요 없는 주석은 달지 말라 소스코드를 읽는 사람이 짜증나게 만드는 주석을 달지 말라. 만든이, 변경로그 등 커밋 로그를 통해 나타낼 수 있는 내용 메서드 및 인자에 대한 필요 없는 설명 -> 소스코드 자체가 주석이 되도록 코드를 작성한다!

클린코더스, 레거시 코드 리팩터링 시작하기

들어가며 클린코더스에서는 좋은 함수를 구현하기 위한 여러 기법을 소개하였다. 한편, 좋은 코드를 레거시 코드에 작성하기 위해서 리팩터링을 해야 한다. 이러한 리팩터링은 위험하고 어려워 선뜻 시도하기 어렵다. 특히 리팩터링 과정에서 기존에 동작하는 코드를 망가뜨린다는 두려움에 이러한 시도를 더욱 어렵게 만든다. 좋은 함수를 적용하기 위하여, 레거시 코드를 리팩터링 하기 위한 좋은 기법을 소개해줬으며 그 방법은 아래와 같다.

클린코더스, function structure 좋은 함수의 구현

function structure 좋은 함수를 구현하기 위한 방법 코드를 읽고 유지보수해야 하는 reader의 입장에서 코드는 수십번 읽혀진다. 코드 작성에 있어서 가장 존중받아야 할 사람은 reader이다. reader를 최우선으로 배려하는 방식으로 코드를 작성해야 한다. TDD를 잘한 코드는 자연스럽게 좋은 디자인으로 만들어진다. TDD를 기반으로 좋은 함수를 만들자.

SQL mapper을 객체지향적으로 동적 쿼리를 작성하는 방법과 예제 (mybatis와 함께)

들어가며 SQL mapper는 DB에 제출할 쿼리를 동적으로 생성시키는 기술이다. java-spring 진영에서는 Mybatis와 JDBCTemplate를 동적쿼리를 위한 기술로 주로 사용한다. ORM 기술인 JPA 또한 EntityManager#createQuery와 @Query의 형태로 동적 쿼리를 지원한다. JPA는 객치지향 기술이며 객체지향적인 방향으로 개발을 유도한다. 반대로 SQL Mapper의 경우 DB에서 파싱된 데이터는 단순한 데이터로서의 DTO로 반환하고 절차지향적인 방식으로 다룬다. 비지니스로직을 자바 코드로 구현되는 것이 아닌 쿼리로 구현되는 것을 자주 볼 수 있고, 이로 인하여 길게는 천 줄짜리의 복잡한 쿼리도 쉽게 찾아볼 수 있다. 다만, SQL Mapper를 사용해야 하는 상황에서 작성 방식을 잘 고려한다면 충분하게 좋은 방법으로 개발할 수 있다고 생각한다. 아래는 update 쿼리를 작성할 때 mybatis를 사용하며 작성해왔던 동적 쿼리를...

Remove Assignments to Parameters, 객체에서는 어떻게 적용하는가?

매개변수에 값을 대입해서는 안된다. Remove Assignments to Parameters 아래의 코드를 보면 매개변수에 값을 대입하여 변경함을 확인할 수 있다. 은 매개변수는 final을 사용하진 않지만 묵시적으로 불변한 상태를 유지해야 한다고 한다. 그래서 지역변수 result를 두고, result에 값을 대입함을 확인할 수 있다.

이펙티브자바, 49- 메서드

49 매개변수가 유효한지 검사하라 매개변수의 유효성은 메서드 몸체가 시작하기 전에 검사해야 한다. 오류는 가능한 빨리 잡아야 한다. 만약 그렇지 않으면 오류가 예상치 못한 곳에 발생하여 유지보수를 어렵게 할 수 있다. 객체가 변경되어 실패 원자성을 어기는 결과를 낳을 수 있다. 노출된 API에 대해서는 매개변수에 일반적으로 사용되는 예외(IllegalArgumentException, IndexOutOfBoundsException, NullPointerException)만 제공하더라도 클라이언트가 제약을 지키도록 유도할 수 있다. 물론 이에 대한 문서화를 해야 한다.

java, 안전한 연산을 위하여 - 오버플로우, 부동소수점, NaN, Infinity 문제

오버플로우 제한된 메모리를 가진 정수가 그 한계 이상을 초과하여 원치 않는 결과를 발생하는 경우가 있다. 이런 경우를 오버플로우라고 하며 그 예시는 아래 코드와 같다. Integer#toBinaryString는 정수를 이진법으로 나타낸 문자열을 리턴한다.

이펙티브자바, 57- 일반적인 프로그래밍 원칙

57 지역변수의 범위를 최소화하라 지역변수에 대한 이해와 오남용을 방지하기 위하여, 지역변수의 사용은 최소화 한다. 만약 사용한다면 선언을 먼저하지 않는다. 가능한 초기화할 때 선언한다. 지역변수를 최소화 할 수 있는 방법을 활용한다. 반복문에서 지역변수를 최소화하기 위하여 while -> for -> 항상된 for문 순으로 사용을 우선한다.

이펙티브자바 69-77 예외 Exception

69 예외는 진짜 예외 상황에만 사용하라 예외를 일종의 제어흐름으로 사용하면 안된다. 더 이상 수행이 불가능할 때, catch(SomeException e) 로 잡고, 다음 로직을 진행하는 경우가 있다.

java, mockito를 활용한 대역 구현

Mockito 저수준의 객체를 테스트하는 것은 어렵지 않다. 한 두개 정도의 대역이나 인자를 생성하고 주입하면 된다. 고수준의 객체를 유닛 테스트하는 것은 쉽지 않다. 고수준의 객체는 수많은 객체의 구성을 통해 완성된다. 객체의 갯수만큼 대역을 필요로 한다. 어느 순간 프로덕션 코드보다 더 많은 테스트 코드를 확인할지도 모른다. 인프라스트럭처를 사용하는 테스트 역시 어렵다. 외부 API나 DB와 커넥션을 연결하고 테스트를 진행해야 한다. 스프링 컨텍스트를 로딩하고 DB나 API와 연결하느라 오랜 시간이 필요하다. 복잡한 로딩을 위하여 많은 컴퓨터 리소스를 소모한다. Mockito는 복잡한 대역의 구현이나 외부 통신에 대한 부담감을 줄여준다.

헷갈리는 단어 간단히 정리!

들어가며 정말로 나를 위한 게시글이다. 헷갈리는 표현들을 간략하게 정리하고자 한다.

테스트 주도 개발 시작하기 - TDD를 활용한 비지니스 로직 구현 연습

들어가며 최범균 개발자님의 ‘테스트 주도 개발 시작하기’ 를 읽었다. 테스트 주도 개발이 왜 “테스트” 주도 개발인지 알 수 있었다. 책에서 이해한 내용을 정리하였다. 예제로 있던 숫자야구를 직접 구현해봤다. (https://github.com/infoqoch/openstudy/tree/master/tdd/baseball-game)

테스트 주도 개발 시작하기를 읽으며

절차지향적 개발에 대한 소박한 경험들 나는 현재 mybatis를 사용하며 절차지향적인 개발을 하고 있다. 그 과정에서 내가 sql에 의존하는 개발에 대하여 느낀 경험을 전달한다. 절차지향적 개발은 DB와 쿼리에 매우 의존적이다. 특히 select 쿼리를 짜는데 있어서 개발자가 의도가 다 들어간다. 그 데이터는 DTO로 생성한다. 해당 객체는 어떤 기능도 가지지 않고 그저 데이터를 전달하는 목표만을 부여받는다. 객체가 DTO 수준으로 역할을 부여받듯 MVC 패턴 역시 DTO로 전락한다. select으로 생성한 쿼리를 적당하게 가공하는 역할에 불과하며, MVC 자체는 일종의 규칙에 불과하다. 컨트롤러는 jsp나 api로 전달 받은 데이터를 검증하는 역할로 한정된다. 컨트롤러나 리포지토리 한 쪽에서만 처리하기 애매해거나 다수의 컨트롤러에서 사용할 만한 공통 메서드를 처리하는 수준으로 역할이 한정된다....

스프링 soap(wsdl) 다루기

soap 와 wsdl 을 만나다 최근 soap를 사용해야 하는 상황이 발생했다. 관련하여 학습 및 시스템에 적용하였는데 해당 내용을 정리하고자 한다. soap와 wsdl, 마셜링 등 무엇인지 알 수 있었다. 참고한 자료는 스프링 공식 문서이다. 이 글은 이에 대한 간단한 정리이므로 구체적인 내용은 아래 링크를 참고하자. https://spring.io/guides/gs/producing-web-service/ https://spring.io/guides/gs/consuming-web-service/