본문 바로가기
코틀린

코틀린 jpa 지연로딩

by 근즈리얼 2024. 4. 1.
728x90

코틀린으로 개인 프로젝트를 진행하던 중 흥미로운(미칠뻔한) 상황을 포스팅해보려고 합니다.

 

jpa를 사용하면서 중요하게 신경써야하는 부분이 n+1 쿼리라고 생각합니다.

그 이유는 당장의 슬로우 쿼리는 아니지만.. 개발자도 모르는 사이에 n+1 쿼리가 쌓여가고 사용자 수가 많아지면 언젠가 데이터베이스 서버에 부하를 줄 수 있기 때문입니다! 메모리 안차면 좋고~

 

따라서, 저는 jpa를 사용하면 fetchJoin을 이용해서 최대한 n+1쿼리가 안나게 만드려고 하는데요!

코틀린으로 jpa를 사용하면서 무슨 짓을 해도 발생하는 n+1 쿼리 때문에 매우 흥미로운(정신 나갈뻔..) 상황이 있었습니다 ㅎㅎ

 

상황을 만들어보겠습니다.

  1. team과 member가 있고 다대일 관계입니다.
  2. member에서 team을 fetchType을 lazy로 설정하고 member를 조회한다

위의 상황으로는 절대!! member를 조회할 때 team을 조회하는 쿼리는 안나갈것이라고 생각합니다.

하지만 ㅎㅎ n+1 쿼리는 계속해서 발생하고 있었습니다.

 

예시 코드입니다.

아래 사진처럼 Member에는 Team이 선언되어 있고 fetch = FetchType.LAZY로 설정되어 있습니다.

 

서비스 코드입니다.

fun getMember(id: Long) {
    val findById = memberRepository.findById(id)
}

 

코드는 짧지만.. 위의 코드상으로는 member만 조회될 것 같습니다.

하지만..

아래의 사진처럼 select 문이 member와 team이 n+1로 나가는 것을 확인할 수 있습니다.

 

처음에는 제가 뭔가 설정을 잘못했나 싶었습니다. jpa lazy 설정이 잘못됐나하고 열심히 찾아봤었는데요! 문제는 jpa가 아닌 코틀린에 있었습니다!

 

우선, hibernate에서는 지연로딩을 하기 위해서 객체를 프록시형태로 가져옵니다.

따라서, 위에 코드에서는 Member를 조회할 때, Team은 프록시 형태로 가져오고 Team을 실제로 필요로 하는 곳이 있을 때 실제 쿼리를 발생시키는 겁니다.

 

그렇다면 클래스가 final일 경우 어떻게 될까요?

엔티티 클래스로 fianl을 사용할 수는 있다고 합니다. 하지만 final클래스인 만큼 수정이 불가능하고 프록시로 우선 가져왔다가 쿼리를 발생시키고 가져온 데이터로 수정하는 것은 불가능하게 됩니다.

따라서, final이 선언된 엔티티는 프록시로 지연로딩 하는 것이 아닌 즉시 로딩으로 값을 채우게 됩니다.

 

코틀린에서는 클래스와 프로퍼티, 함수는 기본적으로 final로 되어 있다고 합니다. 이로 인해 아무리 fetch = FetchType.Lazy를 하더라도 n+1 쿼리가 발생했던 것입니다.

 

그렇다면 코틀린과 jpa를 사용할 때는 n+1 쿼리를 보고만 있어야 하는 걸까요?

이를 해결할 방법이 클래스 앞에 open을 붙여주면 됩니다.

하지만 모든 클래스 앞에 open을 붙일 수 없기 때문에 build.gradle 파일에 appOpen으로 특정 어노테이션이 붙은 클래스는 자동으로 open을 붙이도록 할 수 있습니다.

allOpen { // 추가적으로 열어줄 allOpen
    annotation("jakarta.persistence.Entity")
    annotation("javax.persistence.MappedSuperclass")
    annotation("javax.persistence.Embeddable")
}

 

아래의 사진처럼 하나의 select 쿼리가 발생한 것을 확인할 수 있습니다.

 

저는 처음에는 정말 미칠정도로 어려운 상황이었습니다. 하지만 공부를 하면서 코틀린과 jpa에 대해서 더 알게 되었습니다.

사실 lazy, proxy 라는 단어는 알고 있었고 final 일때는 수정할 수없는 개념도 알고 있었습니다.

즉, 지금 발생한 상황에 대해서 뜻을 알고 있었고 다른 사람이 적어놓은 글을 읽으며 아 그럴수 있겠구나라는 생각을 하게 되었습니다.

 

하지만, 원인이 되는 것들을 다 알고 있었음에도 스스로 해결하지 못했고 이 부분이 매우 아쉬웠습니다.

좀 더 기초를 정확히 이해하며 공부할 필요가 있음을 깨달으며 이번 포스팅 마치겠습니다.

감사합니다!

 

728x90

댓글