목적: 부모 댓글과 자식 댓글이 각각 다른 JSON으로 나오는게 마음에 들지 않는다.
childComments: [ 자식 댓글 JSON ]
처럼 중첩 JSON 형식으로 출력되도록 만들고 싶다.
{
"commentId": 3,
"userId": "ee5973c2-17c2-4f64-9350-a0fa6a7fe4df",
"educationContentId": 1,
"parentCommentId": null,
"body": "좀 되라",
"createdAt": "2024-05-20T10:30:30",
"modifiedAt": "2024-05-20T10:30:30",
"childCommentsId": [67]
},
{
"commentId": 67,
"userId": "ee5973c2-17c2-4f64-9350-a0fa6a7fe4df",
"educationContentId": 1,
"parentCommentId": 3,
"body": "Nested",
"createdAt": "2024-05-20T10:30:30",
"modifiedAt": "2024-05-20T10:30:30",
"childComments": [68]
},
{
"commentId": 68,
"userId": "ee5973c2-17c2-4f64-9350-a0fa6a7fe4df",
"educationContentId": 1,
"parentCommentId": 67,
"body": "Nested",
"createdAt": "2024-05-20T10:30:30",
"modifiedAt": "2024-05-20T10:30:30",
"childComments": []
}
}
따라서, CommentDTO와 CommentService를 뜯어고쳤다.
//CommentDTO
// ===== 수정전 ======
List<Long> childCommentsList = new ArrayList<>();
// =================
// ===== 수정후 ======
List<CommentDTO> childComments = new ArrayList<>();
// =================
// CommentService의 메소드
// EducationContentId 로 "부모 댓글이 없는" 댓글 리스트를 조회한다.
@Transactional
public ArrayList<CommentDTO> findOrphanCommentsByEducationContentId(Long contentId){
ArrayList<CommentDTO> dtos = new ArrayList<>();
Optional<EducationContent> byId = educationContentRepository.findById(contentId);
if(byId.get() == null){
log.error("해당되는 content 없음");
return null;
}
List<Comment> allComments = commentRepository.findAllByEducationContent(byId.get());
Map<Long, CommentDTO> commentMap = new HashMap<>();
for (Comment comment : allComments) {
Long parentCommentId = (comment.getParentComment() != null) ?
comment.getParentComment().getCommentId() : null;
CommentDTO commentDTO = CommentDTO.builder()
.commentId(comment.getCommentId())
.userId(comment.getUser().getId())
.educationContentId(comment.getEducationContent().getContentId())
.parentCommentId(parentCommentId)
.body(comment.getBody())
.createdAt(comment.getCreatedAt())
.modifiedAt(comment.getModifiedAt())
.childComments(new ArrayList<>()) // 일단 빈 리스트로 생성
.build();
dtos.add(commentDTO);
commentMap.put(comment.getCommentId(), commentDTO);
}
// 부모 댓글에 자식 댓글을 추가
for (CommentDTO dto : dtos) {
if (dto.getParentCommentId() != null) {
CommentDTO parentComment = commentMap.get(dto.getParentCommentId());
if (parentComment != null) {
parentComment.getChildComments().add(dto);
}
}
}
// 루트 댓글들 DTO만 반환한다.
// 대댓글들은 루트 댓글 DTO의 childComments 필드 내부에 존재하기 때문이다.
List<CommentDTO> rootComments = dtos.stream()
.filter(dto -> dto.getParentCommentId() == null)
.collect(Collectors.toList());
return new ArrayList<>(rootComments);
}
이렇게 수정한 뒤에, Controller에서 mapper.writeValueAsString(commentDtoList));
로 List를 JSON으로 변환하였더니 원하는 결과가 도출되었다.
[
{
"commentId": 3,
"userId": "ee5973c2-17c2-4f64-9350-a0fa6a7fe4df",
"educationContentId": 1,
"parentCommentId": null,
"body": "좀 되라",
"createdAt": "2024-05-20T10:30:30",
"modifiedAt": "2024-05-20T10:30:30",
"childComments": [
{
"commentId": 67,
"userId": "ee5973c2-17c2-4f64-9350-a0fa6a7fe4df",
"educationContentId": 1,
"parentCommentId": 3,
"body": "Nested",
"createdAt": "2024-05-20T10:30:30",
"modifiedAt": "2024-05-20T10:30:30",
"childComments": [
{
"commentId": 68,
"userId": "ee5973c2-17c2-4f64-9350-a0fa6a7fe4df",
"educationContentId": 1,
"parentCommentId": 67,
"body": "Nested",
"createdAt": "2024-05-20T10:30:30",
"modifiedAt": "2024-05-20T10:30:30",
"childComments": []
}
]
}
},
...
}
함수형 프로그래밍 관련 지식이 부족한 것 같아서 추가로 공부할 수 있었다.
이번 프로젝트에서 스트림과 람다식을 자주 사용하게 되었는데, 동작 원리를 모르니 답답했다.