프로젝트/Ku:room

N+1 문제 해결하기 (@EntityGraph 사용)

개발하는 민우 2025. 2. 27. 01:33

@EntityGraph란?

연관관계가 지연 로딩으로 되어있을 경우 fetch 조인을 사용하여 여러 번의 쿼리를 한 번에 해결할 수 있습니다.

@EntityGraph는 Data JPA에서 fetch 조인을 어노테이션으로 사용할 수 있도록 만들어 준 기능입니다.

 

 

N+1 발생 이유

기본적으로 @OneToMany, @ManyToMany 관계는 Lazy Loading으로 되어 있는 경우

JPA에서 @OneToMany와 @ManyToOne 관계 등을 지연 로딩(Lazy)으로 설정한 경우,  연관관계에서 종속된 엔티티는 쿼리 실행 시 select 되지 않고 proxy 객체를 만들어 엔티티가 적용시킵니다. 그 후 해당 proxy 객체를 호출할 때마다 그때 그때 select 쿼리가 실행됩니다.

 

EnttiyGraph 를 사용한 fetch-join

attributePaths 인자에 fetch-join 을 할 대상(필드명)을 작성하여 해당 엔티티들을 fetch-join 할 수 있습니다.

@EntityGraph(attributePaths = "Department")
List<User> findAll();

Repository 클래스 내에, attributePaths 인자에 fetch-join 을 할 대상(필드명)을 작성하여 해당 엔티티들을 fetch-join 할 수 있습니다.

User는 Department와 연관관계를 가지므로,  @EntityGraph를 통해 Lazy 로딩을 Eager 로딩으로 변경 가능합니다.

 

프로젝트 적용기

@Getter
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Friend extends BaseEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "fromUser_id", nullable = false)
    private User fromUser;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "toUser_id", nullable = false)
    private User toUser;

    @Enumerated(EnumType.STRING)
    private FriendStatus status;

    public void setStatus(FriendStatus status) {
        this.status = status;
    }

    @Builder
    private Friend(User fromUser, User toUser, FriendStatus status) {
        this.fromUser = fromUser;
        this.toUser = toUser;
        this.status = status;
    }

    public static Friend of(User fromUser, User toUser, FriendStatus status) {
        return Friend.builder()
                .fromUser(fromUser)
                .toUser(toUser)
                .status(status)
                .build();
    }
}

User 도메인은 Lazy 전략으로 되어 있어서, proxy 객체로 임시 저장을 하고, 필요할때마다 Select 문이 나갈 수 있다.

 

@Repository
public interface FriendRepository extends JpaRepository<Friend, Long>, FriendRepositoryCustom {
    @EntityGraph(attributePaths = {"fromUser", "toUser"})
    Optional<Friend> findFirstByFromUserAndToUserAndStatus(User fromUser, User toUser, FriendStatus status);
}

따라서 @EntityGraph에서 연관관계를 가지는 fromUser, toUser에 있어, EntityGraph를 적용했다.