상황
- CommentController 클래스의 getComments() API를 호출할 때 N+1 문제가 발생하고 있어요. N+1 문제란, 데이터베이스 쿼리 성능 저하를 일으키는 대표적인 문제 중 하나로, 특히 연관된 엔티티를 조회할 때 발생해요.
- 해당 문제가 발생하지 않도록 코드를 수정해주세요.
- N+1 로그

원인 분석
우선 Comment와 User와의 관계를 알아보기 위해 Comment 엔티티를 확인해보자.
Comment 엔티티의 정의를 보니 Comment와 User는 N:1 관계, 즉 한 명의 user가 여러 comment를 가질 수 있는 구조이다.

그리고 CommentRepository에서 N+1을 발생시키는 메서드를 확인해보자.
getComments() 메서드 실행 시 날아가는 JPQL은 일반 JOIN을 사용하고 있다. 이는 Comment 조회 시 User를 함께 가져오지 않기 때문에 User는 프록시 객체로 채워지게 된다.

이후 getComments()에서 user의 정보를 new UserResponse(user.getId(), user.getEmail())을 통해 불러올 때, 추가 쿼리가 발생하게 된다.

해결 방법
할일의 아이디값을 넣어 댓글 목록을 조회하면 해당 할일에 달린 댓글들의 목록을 가져오는 단순한 쿼리다. 각 댓글은 1명의 유저에 의해 작성되어 있고, 엔티티들 간의 관계가 복잡하지 않다.
한편, 유저 객체로부터는 아이디와 이메일이라는 두 정보만 가져온다. 가져오지 않은 나머지 정보는 password, userRole, nickname이다.
유저의 모든 정보를 가져오는 @EntityGraph나, Fetch Join보다는 필요한 컬럼만 가져올 수 있는 Projection 방법이 적절하다고 생각된다. 그리고 이후 댓글이 많아 페이지로 가져와야 할 경우 Projection 방법이 페이징을 구현하기에 적절하다.
우선 댓글을 추가해 보자. todoId = 6 앞으로 저장된 댓글이 4개, 5로 저장된 댓글이 4개다.

댓글에서 id와 contents, 사용자에서 id와 email만 필요하므로 이 정보만 받아올 dto 클래스를 새롭게 생성한다.

CommentRepository에 JPQL로 직접 매핑해준다.user 정보는 join을 통해 가져오고, 필요한 정보만 꺼내서 CommentWithUserResponse 생성자의 인수로 넣어준다.

CommentService도 다음과 같이 수정해 준다. 매우 깔끔해졌다.
수정 전

수정 후

마지막으로 CommentController도 ResponseEntity<List<CommentResponse>> 대신 ResponseEntity<List<CommentWithUserResponse>>를 반환하도록 수정한다.

결과
GET /todos/6/comments를 호출한 결과
쿼리가 1번 발생했고, comment는 4개가 모두 조회된다.

comment 및 user 정보도 새롭게 생성한 CommentWithUserResponse라는 dto 형태로 잘 반환된 것을 확인할 수 있다.

인사이트
연관관계 구조가 단순하다고 해서 구현이 쉽고 빠른 @EntityGraph나 Fetch Join을 생각했었는데,
이후 연관관계가 복잡해지고 페이징 적용 가능성 등을 고려하며 공부해 보니 DTO Projection이 가장 적합하다고 결론을 내렸다.
개발 초기부터 서비스의 확장성과 복잡성의 증가를 고려한 설계를 하는 것이 매우 중요하다는 것을 깨달았다.
앞으로도 익숙하고 간편한 기술에 맹목적으로 의지하지 않고, 지금 이 설계가 엔티티 간 복잡성이 증가하고 구현 형태가 달라지는 상황에서도 유지보수하기 편한 코드인지를 고려하는 습관을 가져야겠다.
'Projects > [Spring] Code Refactoring Project' 카테고리의 다른 글
| [Spring Plus] JPQL을 Querydsl로 변환하기 (0) | 2026.03.04 |
|---|---|
| [Spring Plus] JPA Cascade 옵션 지정하기 (0) | 2026.02.28 |
| [Spring Plus] AOP의 동작 흐름 제어하기 (0) | 2026.02.28 |
| [Spring Plus] 컨트롤러 테스트 성공시키기 (0) | 2026.02.28 |
| [Spring Plus] JPQL로 쿼리 옵션 추가하기 (0) | 2026.02.27 |