find 후에도 변경감지가 동작하지 않는 문제

최인준·1일 전
0
post-thumbnail

문제

프로젝트를 진행하던 도중 레코드를 업데이트 해야하는 상황이 있었다.

ORM 기술로 JPA를 사용하고 있었기에 잘 알려진 변경감지를 통해 데이터를 수정하려 했었다.

하지만 구현한대로 동작하지 않았고 수정 쿼리가 나가지 않는 문제를 마주했다.

문제가 없다고 생각했었기 때문에 다시 천천히 생각해보았다.

  • Transaction 내에서 이루어지는 로직인가? → O
  • find를 통해 객체를 영속성 컨텍스트로 가져온 상태인가? → O
  • 해당 Transaction 내에서 다른 쿼리들은 잘 수행되는가? → O

이전에 영속성 컨텍스트에 객체를 가져오지 않은 채 객체를 변경하여 원하는 대로 변경감지가 적용되지 않는 문제가 있었는데 이 경우는 다른 문제였다.

그렇기에 객체를 수정하는 로직이 아닌 해당 함수 내의 다른 쿼리들로 인한 문제일 가능성이 높다고 생각했다.

그 해결과정을 담아보았다.

해결 과정

이전 코드

간단히 설명하자면 회원 탈퇴 비즈니스 로직으로 회원을 soft delete하고 추가적으로 해당 회원이 생성한 컨텐츠들을 전부 삭제처리하는 로직이다.

회원 탈퇴 함수

@Transactional
override fun withDraw(user: User, request: RevokeRequest) {
    if (user.authPlatform != request.authPlatform) {
        throw ClientValidationException(AuthErrorCode.INCORRECT_PLATFORM)
    }
	userPort.delete(user)
    contentPort.deleteByUserId(user.id)
}

Port를 통해 delete하는 코드만 보면된다.

content를 삭제하는 쿼리는 다음과 같이 구현되어 있다.

컨텐츠 삭제 쿼리

@Modifying(clearAutomatically = true)
@Query("""
    update ContentEntity c set c.deleted = true
    where c.categoryId in
    (select ct.id from CategoryEntity ct where ct.userId = :userId)
""")
fun deleteByUserId(@Param("userId") userId: Long)

결국 문제는 이 벌크연산 쿼리였다.

벌크연산은 영속성 컨텍스트를 무시하고 DB에 직접 쿼리를 날린다.(트랜잭션 커밋 상관없이)!

그렇기 때문에 수정 이후에 데이터를 조회하면 DB와 영속성 컨텍스트의 데이터 불일치가 발생할 수 있어서 clearAutomatically 옵션을 통해 영속성 컨텍스트를 clear 해준다.

여기서 문제가 발생한다‼️

  • user를 영속성 컨텍스트로 가져와 객체 수정을 했지만 다음 라인의 벌크 연산으로 인해 영속성 컨텍스트가 비워진다.
  • 결국 트랜잭션 커밋 시점엔 영속성 컨텍스트에 아무것도 남아있지 않았기에 스냅샷 비교를 할 필요도 없이 변경감지가 적용되지 않는다.

그래서 코드의 순서를 바꾸어 해결했다.

수정 후 코드

@Transactional
override fun withDraw(user: User, request: RevokeRequest) {
    if (user.authPlatform != request.authPlatform) {
        throw ClientValidationException(AuthErrorCode.INCORRECT_PLATFORM)
    }
    contentPort.deleteByUserId(user.id)
    userPort.delete(user)
}
  • 이렇게 수정하면 벌크 연산 후 영속성 컨텍스트를 비운 후에
  • user를 영속성 컨텍스트로 가져와 수정을 하면 영속성 컨텍스트에 변경된 객체가 남아있고
  • 트랜잭션 커밋 시점에 update 쿼리가 정상적으로 수행된다!

숲을 봐야하는데 나무만 봐서 간단한 문제로 시간을 꽤 쏟을 것 같다..

결론

평소 JPA를 잘 이해하고 있다고 생각했고 그렇기에 이번 트러블도 당황스러웠다.

내가 아는 범주 안에선 문제가 없는데 의도한대로 동작하지 않아 더 당황한 것 같다.

각각의 개념은 잘 알더라도 실제 프로젝트를 구현하면서 이들을 모두 연관지어 잘 생각하는 것은 부족했다.

그래도 또 하나의 문제상황을 경험해서 좋고 해결해서 더 좋다!

앞으로는 생각을 좀 더 유연하게 하는 것에 익숙해져야겠다.

0개의 댓글