

Post
연관관계
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
public class Post extends Auditable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = " post_id")
private Long id;
@Column(name = "content", nullable = false)
private String content;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name ="member_id")
private Member member;
@OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Picture> pictures = new ArrayList<>();
@OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Likes> likes = new ArrayList<>();
@OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true)
private List<PostTag> postTags = new ArrayList<>();
@OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true)
private List<PostComment> postComments = new ArrayList<>();
Post Entity는 화면과 같이다수의 연관 관계가 설정된 자식 엔티티들이 존재하는데 , Picture, Like, Comment 와 OneToMany 연관관계를, Tag는 ManyToMany 연관관계를 형성하고있다.
- 부모인
Member Entity 와는 ManyToOne연관 관계를 맺고있다.
N + 1 문제
- 이번 Main Project에서는 Pre-Project에서 만났던 N+1과 같은 DB와 관련된 문제를 해결하고, 성능을 최적화 하기 위해 노력을 기울였다.
- 게시물 정보를 불러오기 위해 Post 엔티티를 조회할 때, 연관된 자식 엔티티도 함께 조회할 경우 N+1 문제가 발생한다. 그렇다면, 지금부터 N+1문제가 무엇인지, 그리고 어떻게 해결하는지 알아보자.
N+1 문제란?
- 쿼리 1번으로 N건의 부모 엔티티를 조회했을 때, 조회된 부모의 수 만큼 자식 테이블 쿼리가 N번 추가 발생하는 현상을 말한다.
Why?
Fetch Type
Eager
- JPQL과 함께 사용시 N+1 문제 발생
- JPA가 JPQL로 SQL을 생성할 때는 글로벌 Fetch 전략을 무시하고 JPQL만 가지고 생성하기 때문에 find() 메소드로 조회할 때와 달리 Join을 통해서 연관관계를 가져오지 않기 때문에 N+1 문제 발생
- 사용하지 않는 데이터도 매번 불러오게 된다
Lazy
- 전체 데이터 가져온 이후 연관 관계인 하위 엔티티를 사용할때 다시 조회할때 발생
How?
Fetch Join
- 특정 엔티티를 조회할 때 연관된 엔티티 혹은 컬렉션을 함께 한번에 조회하는 기능
Batch Size
- 지정한 수만큼 in절에 부모 Key를 사용하게 해주는 옵션

해결과정
문제 1. MulitpleBagFetchException
- Post 엔티티를 조회할 때, N +1 문제를 해결하기 위해
OneToMany 관계인 엔티티 여러곳에 Fetch Join을 적용 → MulitpleBagFetchException 에러 발생
원인
Fetch Join의 조건
- ToOne 관계는 몇개든 사용 가능
- ToMany 관계는 1개만 사용 가능
해결 1.
OneToMany 관계의 자식 엔티티들중 가장 데이터가 많은 자식쪽에 Fetch Join을 사용하고, ToOne 관계의 자식엔티티들은 모두 Fetch Join을 적용하여 한방 쿼리 수행
- 그렇다면 나머지
OneToMany관계의 자식엔티티들은 FetchJoin없이 어떻게 N+1문제를 해결할 수있을까?
BatchSize를 적용하여, 부모 엔티티의 Key가 1개씩 사용 되던 조건문을 in절로 묶어서 한번에 조회 함으로써 N+1 문제를 해결
- BatchSize 적용과정

- 부모 엔티티가 지연로딩된 ToMany 관계의 자식 엔티티(컬렉션)을 최초 조회 할때 이미 조회하여 영속성 컨텍스트에서 관리되고 있는 부모 엔티티들의 ID를 모아서 Where 조건문에
WHERE Y.X_ID IN (?, ?, ?...) 와 같은 SQL IN 구문에 담아 자식엔티티 조회 쿼리를 날려 필요한 자식엔티티 데이터들을 한번에 조회
- 위에서 설명한 방법을 적용하였더니,
MultipleBagFetchException에러는 발생하지 않았으나, 페이징할때 사용하던 기존의 SQL LIMIT 구문이 등장하지 않음
- 쿼리결과를 전부
메모리에 적재한 뒤 어플리케이션 단에서 Pagination 작업을 수행한다는 경고 로그가 발생
원인

OneToMany 관계를 Join하면 카타시안 곱 이 적용되어 DB Row수가 뻥튀기되어 조회되기 때문에 Limit 구문을 사용하는 쿼리로 페이지네이션을 적용하기 어려워 조회된 결과를 모두 메모리로 가져와 JPA가 페이지네이션 계산을 진행
OneToMany 관계의 컬렉션을 Fetch Join하면서, 동시에 Pagination API를 사용하면 OutOfMemoryError가 발생할 수 있어, 이 둘을 동시에 사용해서는 안된다.
- 반대로
ManyToOne 관계의 엔티티를 Fetch Join 할때는 DB Row의 수가 변경되지 않아 문제없이 Pagination이 적용가능
ToMany 관계의 자식엔티티들은 FetchJoin없이 어떻게 N+1문제를 해결할 수있을까?
해결 2.
- BatchSize를 적용
- Batch Size를 적용한 결과
where comments0_.post_id in (?, ?, ?)를 포함하는 Comment 조회 쿼리 1개로 줄어들었다.
- 최종적으로
ToOne 관계인 자식 엔티티는 FetchJoin을 , ToMany 관계인 자식엔티티는 BatchSize를 적용하여 N+1과 문제와 Pagination관련 문제를 해결