2024. 6. 8. 20:35ㆍProject/Naver Cafe
네이버 카페는 게시글에 달린 댓글에 '답글'이라는 것을 작성할 수 있다. 즉, 댓글에 댓글을 작성하는 형태인데 무한정 하위 댓글로 작성되는 것은 아니고 한 번 하위 댓글(=답글)로 작성됬다면 이후 달리는 댓글은 동위 댓글로서 작성된다. 단, 이렇게 될 경우 누구에게 답글을 작성한건지 사용자가 헷갈릴 수 있으므로 답글 대상의 카페 닉네임을 제공해 작성하는 사람도 작성된 답글을 보는 사람도 해당 답글의 대상이 누구인지 알 수 있도록 서비스를 제공한다.
결과적으로 답글과 댓글의 차이점은 본문에 본문 내용뿐만 아니라 '네임 태그'를 통해서 누구에게 작성한 내용인지 명확하게한다는 것 뿐이다. 하지만 이것은 사용자에게 보이는 부분이므로 구현부에서는 이러한 부분을 제공하기 위해서라도 댓글 엔티티(객체)가 어떤 댓글의 답글인지 식별할 수 있는 정보가 필요할 것이다. 또한 해당 부분은 사용자에게 댓글 정보 리스트를 조회해 댓글 목록을 제공할 때도 필요한 내용이다.
Comment
package CloneCoding.NaverCafe.domain.comment;
@Entity
@Table(name = "COMMENT")
@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class Comment {
public void setMainId(Long commentId) {
this.replyMain = commentId;
}
public void setTargetId(Long commentId) {
this.replyTarget = commentId;
}
}
- setMainId() : Comment 객체의 replyMain 값을 매개변수 값으로 수정하는 메서드
- setTargetId() : Comment 객체의 replyTarget 값을 매개변수 값으로 수정하는 메서드
ResponseReplyForm
package CloneCoding.NaverCafe.domain.comment.dto;
import static CloneCoding.NaverCafe.domain.comment.enums.BasicData.DEFAULT_BODY;
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ResponseReplyForm {
private String nickname;
private String targetNickname;
@Builder.Default
private String body = DEFAULT_BODY.getValue();
}
답글 작성양식에 필요한 정보를 전달하는 DTO 클래스이다.
CommentController
package CloneCoding.NaverCafe.domain.comment.controller;
@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/cafe/{cafe_url}")
public class CommentController {
private final CommentService commentService;
@GetMapping("/comment/{comment_id}")
public ResponseReplyForm getReplyForm(@PathVariable("cafe_url") String url,
@PathVariable("comment_id") Long commentId,
@RequestHeader("Authorization") String token) {
log.info("답글 양식 요청");
return commentService.createForm(url, commentId, token);
}
@PostMapping("/{normal_id}/comment/{comment_id}")
public String writeReply(@PathVariable("cafe_url") String url,
@PathVariable("normal_id") Long articleId,
@PathVariable("comment_id") Long commentId,
@RequestBody @Valid RequestWriteComment request,
@RequestHeader("Authorization") String token) {
log.info("답글 작성 요청");
return commentService.createReply(url, articleId, commentId, request, token);
}
}
- getReplyForm() : 카페 url, 댓글 id, 토큰 정보로 commentService의 createForm()을 호출하고 결과 값을 반환
- writeReply() : 카페 url, 게시글 id, 댓글 id, 요청 정보, 토큰 정보로 commentService의 writeReply()를 호출하고 결과 메시지를 반환
CommentServiceImpl
package CloneCoding.NaverCafe.domain.comment.service;
import static CloneCoding.NaverCafe.message.SystemMessage.WRITE_COMMENT_COMPLETE;
@Service
@RequiredArgsConstructor
public class CommentServiceImpl implements CommentService {
private final CommentRepository commentRepository;
private final CafeRepository cafeRepository;
private final CafeMemberRepository cafeMemberRepository;
private final NormalRepository normalRepository;
private final AesUtil aesUtil;
@Override
public ResponseReplyForm createForm(String url, Long commentId, String token) {
CafeMember user = checkCafeMember(url, token);
Comment replyTarget = commentRepository.findById(commentId)
.orElseThrow(() -> new NoSuchElementException("댓글 정보를 찾을 수 없습니다."));
return ResponseReplyForm.builder()
.nickname(user.getNickname())
.targetNickname(replyTarget.getNickname())
.build();
}
@Transactional
@Override
public String createReply(String url, Long normalId, Long commentId,
RequestWriteComment request, String token) {
CafeMember user = checkCafeMember(url, token);
Normal article = normalRepository.findByIdWithLock(normalId)
.orElseThrow(() -> new NoSuchElementException("게시글 정보를 찾을 수 없습니다."));
Comment reply = Comment.create(user, article.getId(), request);
Comment replyTarget = commentRepository.findById(commentId)
.orElseThrow(() -> new NoSuchElementException("댓글 정보를 찾을 수 없습니다."));
reply.setMainId(checkReplyMain(replyTarget));
reply.setTargetId(replyTarget.getId());
article.addCommentCount();
commentRepository.save(reply);
normalRepository.save(article);
return WRITE_COMMENT_COMPLETE.getMessage();
}
private CafeMember checkCafeMember(String url, String token) {
String accountId = aesUtil.aesDecode(token);
Cafe findCafe = cafeRepository.findByUrl(url);
return cafeMemberRepository.findByAccountId(findCafe, accountId);
}
private Long checkReplyMain(Comment replyTarget) {
Long replyMainId = 0L;
if (replyTarget.getReplyMain() == 0L) {
replyMainId = replyTarget.getId();
} else {
replyMainId = replyTarget.getReplyMain();
}
return replyMainId;
}
}
- createForm() : 답글 작성양식에 필요한 정보로 ResponseReplyForm 객체를 생성하고, 생성된 객체를 반환
- createReply() : Comment 객체를 생성해 엔티티로 저장하며, 더불어 해당하는 Normal 엔티티의 CommentCount 값을 수정한 후, 결과 메시지를 반환
- checkReplyMain() : 답글의 대상인 댓글 정보로 최상위 댓글을 찾아 id를 반환
API TEST
답글 작성양식에 필요한 정보를 요청한 테스트 결과이다. 답글 대상 댓글의 작성자 닉네임과, 답글 작성자의 닉네임, 그리고 본문에 작성을 알리는 기본 메시지가 잘 반환된 것을 확인할 수 있다.
우선 기존에 존재하던 댓글(id=1)에 답글을 작성하고, 작성한 답글(id=2)에 다시 새로운 답글을 작성하는 테스트를 진행했다. 두 API 요청 모두 정상적으로 완료되어 결과 메시지가 반환되었고, DB를 확인해 보면 첫 번째 답글(id=2)과 두 번째 답글(id=3)의 메인 댓글의 id는 '1'이며, 답글의 대상 댓글의 id는 각각 '1', '2'이다. 물론 댓글이 작성된 게시글 엔티티의 CommenCount 값은 '3'으로 증가하였다.
'Project > Naver Cafe' 카테고리의 다른 글
[클론 코딩] 네이버 카페 - 댓글 수정 (0) | 2024.06.10 |
---|---|
[클론 코딩] 네이버 카페 - 댓글 읽기 (0) | 2024.06.09 |
[클론 코딩] 네이버 카페 - 댓글 작성 (0) | 2024.06.08 |
[클론 코딩] 네이버 카페 - 게시글 '좋아요' 등록, 취소 (0) | 2024.06.06 |
[클론 코딩] 네이버 카페 - (통합게시판) 태그 등록, 수정(삭제) (0) | 2024.06.04 |