본문 바로가기

JPA

[JPA] N+1 문제 해결하기

JPA를 사용하면 한번쯤 겪게 되는 N+1 문제와 해결방법에 대해 정리해보겠습니다.

 

1. N+1 문제란?

하나의 쿼리로 데이터 N개의 목록을 가져오려고 할 때, 각 데이터 별로 연관된 데이터를 조회하는 쿼리가 발생하여, 총 N개의 쿼리가 추가로 발생하는 문제를 N+1 문제라고 합니다.

 

한 회원이 여러 게시물을 작성할 수 있다고 가정하고, Board 에서만 N:1 단방향으로 회원을 참조하도록 설계해보겠습니다. 

게시물 엔티티
회원 엔티티

이제 BoardRepository와 MemberRepository를 작성해보겠습니다.

간단하게 삽입과 조회 기능만 추가해두겠습니다.

BoardRepository
MemberRepository

 

다음으로 회원과 게시물 데이터를 넣고, 게시물의 목록을 조회하는 findAllByViewDesc를 이용해 게시물 목록을 조회수 내림차순으로 조회해보겠습니다. 

 

테스트 코드는 위와 같습니다. 2명의 회원을 추가하고고, 3개의 게시물을 작성해두었습니다.

영속성 컨텍스트를 한번 비우고 난 후, 게시물 목록을 조회하면 어떻게 될까요?

 

조회하는 쿼리를 확인해보면, 다음과 같은 쿼리가 발생합니다. 

먼저 작성했던 쿼리 그대로, 게시물 목록을 조회수 내림차순으로 조회합니다. 

다음으로 회원을 조회하는 2번의 쿼리가 추가로 발생하는데, 이게 바로 N+1 문제입니다.

(3개의 게시물이긴 하지만 회원1은 첫 조회 후 영속성 컨텍스트에 저장되므로, 쿼리가 한번 생략되어 2번입니다!) 

 

쿼리가 발생하는 이유는 무엇일까요?

현재 Board가 Member를 EAGER로 fetch하고 있기 때문입니다. Board를 영속성 컨텍스트에 등록하면서 연관된 Member를 바로 fetch해오는 것이죠.

 

2. FetchType.LAZY?

그렇다면 FetchType을 LAZY로 변경하면 N+1 문제가 해결되는 것일까요?

결론부터 말하자면 그렇지 않습니다!

 

물론 위 테스트 코드를 그대로 돌려보면 쿼리가 발생하지 않습니다. FetcyType을 LAZY로 설정해두었으므로, Member를 참조하는 시점에서 쿼리가 발생합니다.

 

그러나 결국 조회한 결과를 이용하려면, 또 쿼리가 필요한 것입니다.

목표는 최초 1개 쿼리로 연관된 객체까지 불러오는 것이므로, 근본적인 해결책이 아닙니다.

 

3. Fetch Join

해결책은 fetch join을 이용하는 것입니다.

join fetch 추가

다음과 같이 변경하면, 게시물 목록을 읽어올 때, 회원을 join하여 불러오는 것을 확인할 수 있습니다. 

동일한 테스트 코드를 수행한 결과

이제 하나의 쿼리만으로 게시물의 목록과, 각 게시물 별로 작성자를 조회해올 수 있습니다. 

 

 

'JPA' 카테고리의 다른 글

[JPA] default_batch_fetch_size 동작  (0) 2023.04.12
[JPA] N+1 해결하기 (2) - OneToMany  (0) 2023.04.12
[JPA] 동적쿼리, QueryDSL  (0) 2023.04.07
[JPA] 즉시로딩, 지연로딩  (0) 2023.03.29