본문 바로가기
Spring/Spring boot

[SpringBoot] 스프링부트 JPA 단점과 Query DSL

by s_hoonee 2023. 11. 23.
반응형

이번 시간엔 연관 관계가 A -(One to Many)→ B -(One to Many)→ C로 설정된 경우JPA의 단점에 대해 포스팅하겠습니다. 

출처 https://m.blog.naver.com/PostView.naver?blogId=innogrid&logNo=222725730056&categoryNo=12&proxyReferer=

 

JPA사용시 단점 : A를통해 연관관계가 없는 C객체를 찾을 때의 이슈 (디폴트 Lazy): 

 

A->B->C 의 관계를 가진 엔티티에서의 A엔티티 값을 조회할 때, B와 C는 로딩되지 않습니다. 하지만 레파지토리A에서 A와 연관된 C에 접근하려 할 때 A->C로의 직접적인 연관된 매핑이 없어 B 리스트에 대한 쿼리가 발생하며, 만약 B와 C의 개수가 많다면 이는 성능상의 이슈를 발생시킬 수 있습니다.

Lazy 로딩:
A를 조회할 때, B와 C는 로딩되지 않습니다. 필요한 시점에 데이터베이스에서 실제로 가져옵니다.A에서 C를 조회하려 할 때, 추가적인 쿼리가 실행되어 B와 C를 가져오게 됩니다. 이때 N+1 쿼리 문제가 발생할 수 있습니다.성능 측면에서는 필요한 시점에만 데이터를 가져오기 때문에 메모리 부담이 적습니다.

Eager 로딩:
A를 조회할 때, B와 C도 함께 로딩됩니다. 즉, 한 번의 조회 쿼리로 A, B, C가 모두 가져와집니다.필요하지 않은 데이터까지 모두 가져오기 때문에 메모리 부담이 있을 수 있습니다.한 객체가 다른 객체를 참조하고, 그 다른 객체가 다시 첫 번째 객체를 참조할 수 있는데, 이 경우 무한 루프에 빠질 수 있습니다.

성능상 이슈 내용 :

 Lazy 로딩의 특성 상 필요한 시점에만 데이터를 가져오기 위해 쿼리가 여러 번 실행되는 것이기 때문에, N+1 쿼리 문제가 발생할 수 있습니다. N+1 쿼리 문제란 한 번의 조회 쿼리로 가져온 엔티티와 그에 연관된 엔티티들을 가져오기 위해 추가적인 N개의 쿼리가 발생하는 상황을 말합니다. 이는 데이터베이스 부하와 성능 저하를 초래합니다

이러한 문제를 해결하기 위해서는 FetchType을 Eager로 설정하거나, 별도의 쿼리를 작성하여 필요한 데이터를 한 번에 가져오는 방식 등을 고려해볼 수 있습니다. 하지만 두 방법 모두 정답은 아닙니다.  FetchType을 Eager로 설정하는 경우, 필요하지 않은 데이터까지 모두 가져오게 되어 메모리 부담이 발생할 수 있으므로 주의가 필요하고 양방향 매핑에서는 한 객체가 다른 객체를 참조하고, 그 다른 객체가 다시 첫 번째 객체를 참조할 수 있는데, 이 경우 무한 루프에 빠질 수 있으며, 이는 스택 오버플로우나 무한 루프로 이어질 수 있습니다. 또한 설계에 따라서 불필요한 Join 쿼리가 남발할 가능성이 있습니다. 결론적으로 메모리와 성능 측면에서 비효율적일 수 있습니다.


그렇다면 필요한 연관 관계에 대한 커스텀한 쿼리를 작성하거나, FetchType을 Lazy로 유지하면서 연관된 Entity를 특정 상황에서 로딩하는 방법을 고민할 수 있습니다. 또한 해당 애플리케이션의 특성과 성능 요건에 맞게

@Query, Query DSL 등을 사용하여 개발자 역량에 맞게 해결할 수 있습니다. 

다음시간엔 Query DSL에 대해 포스팅하겠습니다.