이번 글은 데브코스 Spring Boot REST API 실습 10강 내용을 정리한 글이다.
10강에서는 게시글에 달린 댓글 다건 조회 / 단건 조회 API를 구현하며,
@OneToMany 연관관계와 LAZY 로딩이 실제로 언제 쿼리를 발생시키는지를 함께 학습한다.
10강의 목표는 다음과 같다.
@OneToMany)와 지연 로딩(LAZY) 동작 이해댓글은 항상 게시글에 종속되므로
API URL도 게시글 하위 리소스 구조로 설계한다.
GET /api/v1/posts/{postId}/comments
GET /api/v1/posts/{postId}/comments/{id}
이는 REST 관점에서도 자연스러운 구조다.
@RestController
@RequestMapping("/api/v1/posts/{postId}/comments")
@RequiredArgsConstructor
public class ApiV1PostCommentController {
private final PostService postService;
// 댓글 다건 조회
@GetMapping
public List<PostCommentDto> getItems(
@PathVariable int postId
) {
Post post = postService.findById(postId).get();
return post
.getComments()
.stream()
.map(PostCommentDto::new)
.toList();
}
// 댓글 단건 조회
@GetMapping("/{id}")
public PostCommentDto getItem(
@PathVariable int postId,
@PathVariable int id
) {
Post post = postService.findById(postId).get();
PostComment postComment = post.findCommentById(id).get();
return new PostCommentDto(postComment);
}
}
댓글 역시 엔티티를 그대로 노출하지 않고 DTO로 변환한다.
public record PostCommentDto(
int id,
LocalDateTime createDate,
LocalDateTime modifyDate,
String content
) {
public PostCommentDto(PostComment postComment) {
this(
postComment.getId(),
postComment.getCreateDate(),
postComment.getModifyDate(),
postComment.getContent()
);
}
}
DTO를 사용함으로써:
@OneToMany(
mappedBy = "post",
fetch = LAZY,
cascade = {PERSIST, REMOVE},
orphanRemoval = true
)
private List<PostComment> comments = new ArrayList<>();
이 한 줄에는 여러 JPA 개념이 동시에 들어 있다.
post_id)는 PostComment.post가 관리Post.comments는 읽기 전용(반대편) 역할즉, 댓글이 어떤 게시글에 속하는지는
PostComment 쪽에서 결정된다.
fetch = LAZY는
연관된 엔티티를 바로 가져오지 않고, 필요할 때 가져오는 방식이다.
Post post = postService.findById(postId).get();
이 시점에서는:
post만 조회post.getComments();
이 순간:
SELECT *
FROM post_comment
WHERE post_id = ?
👉 댓글 조회 쿼리는 getComments()를 처음 호출하는 순간 실행된다.
post.getComments()
.stream()
.map(PostCommentDto::new)
.toList();
getComments() 호출한 번 로딩된 댓글 컬렉션은
같은 트랜잭션 내에서 다시 쿼리가 나가지 않는다.
PostComment postComment = post.findCommentById(id).get();
이 코드의 실제 동작은 다음과 같다.
post.getComments() 호출id 필터링즉, 단건 조회처럼 보이지만 DB에서는 다건 조회가 먼저 수행된다.
이 방식은 학습 단계에서는 이해하기 좋지만,
댓글 수가 많아질 경우 성능 이슈로 이어질 수 있다.
comments 리스트에서 제거하면댓글 삭제 기능 구현 시 매우 유용한 설정이다.
fetch = LAZY의 실제 쿼리 실행 시점을 이해했다10강은 댓글 조회 API를 구현하면서
JPA 연관관계와 LAZY 로딩이 실제로 어떻게 동작하는지 체감하는 강의였다.