
저번주 목요일, 팀 프로젝트 리팩토링중에 갑자기 통과하던 테스트가 실패했다.
몇시간동안 디버깅해본 결과 우리가 내린 결론은 JPA @Modifying 옵션인 clearAutomatically = true 때문이었다. 해당 옵션을 지우니 테스트가 통과했다. 그런데 분명 로직 변화는 없었는데 갑자기 되던 테스트가 터진 이유가 궁금해졌다.
dev-be 브랜치와 리팩토링 중인 브랜치를 비교했다.
dev-be 브랜치


딱 하나 바뀐 부분이 있다면 타입이 long → Long으로 바뀐 것이었다.
리팩토링 중인 브랜치


프로젝트팀에서는 레이어마다 타입을 다르게 사용하는 것을 컨벤션으로 지정했었다. 그런데 프로젝트를 진행하면서 레이어마다 타입을 다르게 지정해주는게 쉽지 않았고 잘 지켜지지 않아 결국 모든 레이어에서 참조타입(Long)을 사용하는 것을 컨벤션으로 변경했다. 이번 리팩토링에서 해당 부분을 함께 변경하기로 한 것이다.
그런데 long → Long 타입으로 변경하면서 findById()의 파라미터 타입을 실수로 변경하지 않았다. (지금보니 인텔리제이에서도 no usages로 알려주고 있었다 🥲) 이는 findById를 호출했을 때 우리가 재정의한 함수가 아닌 SimpleJpaRepository의 findById()를 호출하게 했다.
재정의한 함수에는 join fetch로 lazy 옵션이 적용된 엔티티도 한 번에 가져올 수 있도록 했다. 그런데 SimpleJpaRepository의 findById은 proxy 형태로 lazy 옵션이 적용된 엔티티를 한 번에 가져온다. 이것이 문제가 되어 LazyInitializationException 이 발생했다.

여기서 잠깐, 서비스 메서드 updateChecklistById의 @Transactioanl이 붙어있는데 왜 문제가 발생하는 것일까? 서비스 메서드가 끝날 때까지 영속성 컨텍스트가 유지되어 lazy 엔티티도 가져올 수 있어야하는 것 아닌가?
이는 위에서 살짝 언급했던 clearAutomatically = true 때문이었다. 서비스 메서드를 실행하는 도중 어디선가 deleteAllByChecklistId()를 실행한다.
@Modifying(flushAutomatically = true, clearAutomatically = true)
@Transactional
@Query("UPDATE ChecklistOption co "
+ "SET co.deleted = true "
+ "WHERE co.checklist.id = :checklistId")
void deleteAllByChecklistId(@Param("checklistId") Long checklistId);
clearAutomatically=true는 해당 메서드가 실행된 후 영속성 컨텍스를 종료한다. 이는 DB에 수정된 값을 정상적으로 조회할 수 있게 하기 위해 사용한다.
@Transactional
public void updateChecklistById(User user, Long checklistId, ChecklistRequest checklistRequest) {
Checklist checklist = checklistRepository.getById(checklistId);
validateChecklistOwnership(user, checklist);
....(생략)
updateChecklistOptions(checklistRequest, checklist); // deleteById 메서드 실행
updateChecklistQuestions(checklistRequest, checklist);
updateChecklistIncludedMaintenances(checklistRequest, checklist);
}
이후 updateChecklistQuestions()에서 checklist.getQuestions() 를 사용하는데 이때 영속성 컨텍스트가 사라져버려 값을 조회할 수 없는 것이다.
이번 문제는 repository 메서드의 타입을 제대로 맞춰주면서 해결됐다. 하지만 clearAutomatically=true 옵션에 대한 의문이 든다. 영속성 컨텍스트를 종료해버리면서 서비스 메서드에서는 영속성 컨텍스트의 이점을 누릴 수 없게 되었기 때문이다. 더 공부해봐야겠다 🤔
대박 글까지 써버린 제제 최고다 🔥 알아내느라 고생했어요 ㅋㅋㅋㅋㅋ 다같이 머리붙잡고 쉽지 않았지,,