스프링부트

[스프링] 프로젝트 N+1 해결하기 (fetch join

컴공코딩러 2023. 9. 1. 18:32

 

나의 팀 프로젝트 미간지!

https://miganzi.vercel.app/

 

미간지

나만 알고 있는 우리 동네의 미지의 공간을 특별한 큐레이팅을 통해 소개

miganzi.vercel.app

 

 

 

사이트는 이렇게 되어있고

 

메인에 들어가면 2개의 요청이 날아간다 reissue는 토큰이 종료되었을때 날아감

posts는 게시물 (무한스크롤)

popular-post는 말그대로 인기 포스트이다 (페이지 맨위에 나오는 게시물)

 

 

 

하지만 posts 쿼리의 상태가?? 맛있게 N+1 나가는 모습!! 

 

1
2
3
4
5
6
7
    @Operation(summary = "전체 게시물 조회 API")
    @GetMapping("/posts")
    public Slice<PostsDto> getBoardList(
            @PageableDefault (size = 6,sort = "createdDate",direction = Sort.Direction.DESC) 
@Parameter(hidden = true) Pageable pageable) {
        List<UserPost> userPosts = userPostService.getUserPosts();
        return getUsersPostsDto(pageable,userPosts);
    }
cs
1
2
3
4
5


public
 class UserPostService {
    private final BoardRepository boardRepository;
    public List<UserPost> getUserPosts(){
        return boardRepository.findAll();
    }


cs

 

컨트롤러와 서비스 모습 당연 findAll로 쏘니 그럴수밖에..

1
2
@Query(value = "select u from UserPost u left join fetch u.user" )
List<UserPost> findAllPostFetchJoin();
cs

 

그래서 left fetch join 을 추가해줬다

이제 쿼리가 짧게 나간다 문제 해결!!

 

 

그리고 인기 게시물 5개 받아오는 쿼리도 수정해줬다.

1
2
@Query(value = "select u from UserPost u left join fetch u.user order by u.viewCount desc limit 5")
List<UserPost> findPolarPost();
cs

 

문제 해결

 

fetch join은 뭐길래 해결해줄까..

 

Fetch Join의 개념

Fetch Join은 데이터베이스에서 연관된 엔티티 또는 컬렉션을 함께 로딩하는 방법 중 하나입니다. 이를 통해 N+1 쿼리 문제를 해결할 수 있습니다. 예를 들어, 주문(Order) 엔티티와 그에 속한 상품(Product) 엔티티가 있다면, Fetch Join을 사용하면 주문과 관련된 모든 상품을 한 번의 쿼리로 가져올 수 있습니다.

 

나는 이미 영속성 컨텍스트와 연관이 있다는걸 알기때문에 영속성 컨텍스트와 fetch join에 대해서 검색해봤다.

 

영속성 컨텍스트(Persistence Context)란?

영속성 컨텍스트는 JPA에서 관리하는 엔티티(Entity) 객체의 상태를 추적하고 관리하는 논리적인 작업 영역입니다. 

이 컨텍스트는 엔티티의 생명 주기를 관리하며, 데이터베이스와의 데이터 동기화를 담당합니다.
영속성 컨텍스트는 엔티티를 캐싱하고, 데이터베이스로의 변경 사항을 추적하며, 지연 로딩(Lazy Loading) 및 쓰기 지연(Write-Behind) 기능을 제공합니다.
영속성 컨텍스트를 통해 엔티티를 데이터베이스에서 가져오고, 업데이트하고, 저장할 수 있으며, 이를 통해 객체 지향적인 데이터 접근이 가능해집니다.


Fetch Join과 영속성 컨텍스트

Fetch Join은 영속성 컨텍스트와 밀접한 관련이 있습니다. Fetch Join을 사용하면 엔티티와 그와 연관된 엔티티 또는 컬렉션을 함께 데이터베이스에서 가져올 수 있습니다.
예를 들어, 주문(Order) 엔티티와 주문에 속한 상품(Product) 엔티티가 있다고 가정해봅시다. Fetch Join을 사용하면 주문과 관련된 모든 상품을 한 번의 쿼리로 가져올 수 있습니다. 이때, 가져온 상품들은 영속성 컨텍스트에 캐싱됩니다.
영속성 컨텍스트에 캐싱된 엔티티는 다시 쿼리할 필요 없이 메모리에서 조회할 수 있으며, 이는 성능 향상을 가져옵니다.


주의사항

FetchType을 EAGER로 설정하면 Fetch Join을 사용할 때 주의해야 합니다. 모든 관련 엔티티가 항상 함께 로딩되므로 성능 문제가 발생할 수 있습니다. 따라서 FetchType을 신중하게 선택하고, Fetch Join을 필요한 경우에만 사용해야 합니다.
영속성 컨텍스트의 관리는 세션(Session) 또는 트랜잭션(Transaction) 범위 내에서 이루어집니다. 따라서 영속성 컨텍스트를 너무 오랫동안 유지하거나, 데이터베이스 트랜잭션을 적절하게 관리하지 않으면 문제가 발생할 수 있습니다.