IT/개발로그

    Spring Data JPA @Query 사용시 주의점(JPA 버그)

    이전 회사에서 새벽에 돌아야할 배치가 안돌아서 난리가 났던 적이 한번 있었는데 알고보니 쿼리 하나가 몇 시간째 돌고 있었다. 어쨌든 원인을 빨리 찾아서 고치긴 했는데 분석을 해보니 Spring Data JPA(2.6.2 기준)에 문제가 있었다. @Query annotaion을 쓰면서 Paging 처리를 할 때 발생할 수 있는 이슈인데, 다음 번에 깊게 파볼 생각으로 남겨두었던게 갑자기 생각이 나서 좀 들여다보았다. 상황을 재현해보면 아래와 같다. public interface ProductJpaRepository extends JpaRepository { @Query(value = "select id, name from product where name = :productName", nativeQuery..

    [Java] Jackson 사용시 Getter를 주의하자

    짧고 담백하게 결론부터 말하자면 자바에서 Jackson 라이브러리를 사용하여 json 문자열로 Serialize 할때는 getter를 주의해야한다. 예를 들어 아래와 같은 클래스가 있다고 하자. @Builder @NoArgsConstructor @AllArgsConstructor @Getter public class Product { private long id; private String name; private long price; public String getProductInfo(){ return name + price; } } 멤버 변수로 정의한 것은 id, name, price이고 편의상 getProductInfo()라는 메서드를 만들어 상품의 이름과 가격을 함께 리턴하도록 했다. 이 상태로 ..

    단위 테스트는 왜 해야할까

    회사를 옮기고 나서 처음 놀랐던 순간은 테스트 코드를 봤을때였다. 복잡한 비즈니스 로직을 다루면서도 테스트 디렉터리가 말끔히 비워져 있었다. 코드 레벨의 자동화된 단위 테스트보다는 e2e 테스트에 의존하고 있었으며 그래서 초래되는 문제도 적지 않아 보였다. 당연하게도(?) 팀원들은 테스트 코드의 필요성에 대해서 깊이 공감하지는 못하고 있었다. 우선 그들을 설득할 필요가 있어보였는데, 만약 누군가 나에게 왜 꼭 단위 테스트를 해야하는지를 묻는다면 뭐라고 대답할지를 생각해보았다. 내가 그동안 느낀바로는 다음과 같은 이유들이 있다. 개발자 버전의 요구사항 문서 테스트 코드를 작성하면 서비스가 충족해야하는 요구사항을 명문화해 볼수 있다. 개발자라면 요구 사항을 잘못 이해하여 일어난 대참사를 직간접적으로 경험해본..

    스프링 배치 대용량데이터 처리 성능 개선기 [1편]

    요구사항 혜택 고객 선정 모델을 통해 혜택 대상 고객들을 선정하고, 해당 고객들에게 쿠폰을 발급해주는 시스템을 구축해야 했다. 여기서 나는 일배치를 통해 집계된 대상 고객들에게 쿠폰을 발급하는 시스템을 설계하고 개발하기로 했다. 그런데 문제는 일별로 발급대상 데이터의 건수가 수십-수백만까지 예상되며 이를 3시간 내에 처리해야한다는 것이었다. 알던 이전에 구축해놓은 스프링 배치 서버가 있어 이를 활용해보기로 했고 전반적인 시스템 구조부터 그려봤다. Overall System Architecture 스프링 배치 관리/모니터링 도구로는 젠킨스를 활용하고 있다. 배치가 실행되면 대상고객이 집계되어있는 DB에서 타겟을 가져와서 쿠폰발급 서버에 1건 단위로 발급을 요청한다 발급 결과(쿠폰번호)를 받으면 데이터 집계..

    Spring Data JPA의 saveAll() 사용시 주의점

    현재 개발 중인 배치 프로그램이 있는데 처리 과정이 모두 끝나고 마지막에 몇 만건의 데이터를 DB에 insert하는 과정이 있다. 대량의 데이터를 한꺼번에 처리해야되니 Spring Data에서 제공하는 saveAll()을 사용할 생각이었는데.. 얼마전 DBA께서 ERD를 보며 하신 말씀이 떠올랐다. "이 테이블은 commit 단위를 만 건 이하로 끊어주세요." saveAll을 호출하게 되면 몇 만건의 데이터가 한꺼번에 bulk insert 되기 때문에 데이터를 적절한 chunk로 나누어줄 필요가 있다는 것인데, 그 전에 saveAll의 내부 구현을 확인해보았다. saveAll 내부에서는 save를 반복 호출하는데, 두 메서드에 모두 @Transactional이 걸려있다. 이 경우 우선순위는 어디에 있을까..