서비스단에서 아래와 같은 코드가 있다고 해봅시다.
public List<CommentResponse> getComments(long todoId) {
List<Comment> commentList = commentRepository.findByTodoIdWithUser(todoId);
List<CommentResponse> dtoList = new ArrayList<>();
for (Comment comment : commentList) {
User user = comment.getUser();
CommentResponse dto = new CommentResponse(
comment.getId(),
comment.getContents(),
new UserResponse(user.getId(), user.getEmail())
);
dtoList.add(dto);
}
return dtoList;
}
for 문을 돌면서 ``User user = comment.getUser();`` comment에 있는 User을 하나 하나 꺼내오고 있습니다.
이때 N+1 문제가 발생할 수 있습니다.
N+1 문제는 특정 객체를 대상으로 수행한 쿼리 -
**List<Comment> commentList = commentRepository.findByTodoIdWithUser(todoId);** -
가 해당 객체가 가지고 있는 연관관계 또한 조회하게 되면서 N번의 추가적인 쿼리가 발생하는 것을 말합니다.
이를 방지 또는 해결하기 위해선 어떻게 해야할까요 ??
JPQL
// Repository
@Query("SELECT c FROM Comment c JOIN c.user WHERE c.todo.id = :todoId")
List<Comment> findByTodoIdWithUser(@Param("todoId") Long todoId);
// 위를 아래처럼 바꿉니다.
@Query("SELECT c FROM Comment c JOIN FETCH c.user WHERE c.todo.id = :todoId")
List<Comment> findByTodoIdWithUser(@Param("todoId") long todoId);
QueryDsl
// CommentRepositoryQueryImpl
@Override
public List<Comment> findByTodoIdWithUser(Long todoId) {
QComment comment = QComment.comment;
QUser user = QUser.user;
return queryFactory
.selectFrom(comment)
.join(comment.user, user).fetchJoin()
.where(comment.todo.id.eq(todoId))
.fetch();
}
>> 즉시 로딩(Fetch Join) 을 적용해 Comment와 User를 한 번에 가져오도록 처리할 수 있습니다.
즉시 로딩과 지연 로딩이란 무엇일까 ??
- 즉시 로딩: 즉시 로딩은 엔티티를 조회할 때 해당 엔티티와 연관된 모든 엔티티를 함께 조회하는 방식입니다. 연관 데이터를 캐시에 미리 올려놓기 때문에 추가적인 쿼리가 발생하지 않습니다.
- 지연 로딩: 지연 로딩은 엔티티를 조회할 때, 연관된 엔티티는 조회하지 않고 필요할 때 별도의 쿼리를 통해 로딩하는 방식입니다. 필요한 시점에만 연관된 엔티티를 로딩하므로 메모리 효율이 좋습니다.
`1 : N = Todo : Comment`
1 : N 연관관계에서 'N'의 엔티티를 조회할 때 지연 로딩이 기본값입니다. 왜냐하면 필요로 하지 않는 데이터를 찾으며 메모리를 사용하지 않고, 필요로 할 때 쿼리를 날리면 효율적이기 때문입니다. 위의 예시에서 작성자가 누군지 궁금하지 않고 Comment의 내용만 알고 싶을 수도 있습니다.
public List<CommentResponse> getComments(long todoId) {
List<Comment> commentList = commentRepository.findByTodoIdWithUser(todoId);
List<CommentResponse> dtoList = new ArrayList<>();
for (Comment comment : commentList) {
CommentResponse dto = new CommentResponse(
comment.getId(),
comment.getContents())
);
dtoList.add(dto);
}
return dtoList;
}
'Spring > Spring 문법' 카테고리의 다른 글
Pageable 과 PagedModel (1) | 2024.12.09 |
---|---|
Mock 을 이용한 테스트 코드 - argument matcher (0) | 2024.11.26 |
AWS S3 버킷 사용하기 (0) | 2024.11.25 |
Transaction Propagation (0) | 2024.11.25 |
QueryDsl - Projections 의 4가지 방식 (0) | 2024.11.21 |