웹 프로젝트를 진행하던 중에 양방향 매핑 관계에서 발생한 N+1 문제를 겪었다.
N+1 문제의 이유와 해결 방법에 앞서 즉시로딩과 지연로딩에 대해 먼저 이해해야 한다고 생각한다.
즉시로딩과 지연로딩
즉시로딩과 지연로딩은 양방향 매핑이 되어 있는 두 Entity가 있고 한 Entity를 조회할 때 매핑된 Entity를 같이 조회할지, 실제로 매핑된 Entity의 조회가 필요한 시점에 조회할지를 정하는 방법이다.
비즈니스 로직에서 단순히 특정 Entity와 관련된 로직만 사용하는데 매핑된 Entity까지 조회하면 비효율적일 것이다. JPA는 이 문제를 지연로딩을 사용해서 프록시로 조회하는 방법으로 해결한다.
다음 예시를 통해 자세히 알아보자.
지연로딩(Lazy)
@Entity
@AllArgsConstructor
@Getter
@Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
private String password;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "user")
private List<Post> postList;
}
@Entity
@AllArgsConstructor
@Getter
@Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Post {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private String content;
private String writer;
@ManyToOne(fetch = FetchType.LAZY)
private User user;
}
즉시로딩(Eager)
@Entity
@AllArgsConstructor
@Getter
@Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
private String password;
@OneToMany(fetch = FetchType.EAGER, mappedBy = "user")
private List<Post> postList;
}
@Entity
@AllArgsConstructor
@Getter
@Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Post {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private String content;
private String writer;
@ManyToOne(fetch = FetchType.EAGER)
private User user;
}