[클론 코딩] 네이버 카페 - 댓글 읽기

2024. 6. 9. 23:19Project/Naver Cafe

  네이버 카페는 사용자에게 댓글을 보여줄 때, '댓글'과 '답글' 2가지 종류로 보여준다. 댓글의 경우 작성된 순서대로 노출된다. 하지만 답글의 경우 대상 댓글의 하위에 노출되며 이후 생성되는 답글은 작성순으로 노출된다. 즉 답글은 댓글에 끼어들어 노출된다는 것이다.

 

  그러니 단순하게 말하자면 하나의 댓글과 해당 댓글의 하위 답글(들)을 하나의 그룹으로 봤을 때, 이런 그룹의 집합이 댓글 목록이 될 것이다. 이 목록은 기본적으로 작성순(내림차순)으로 정렬되며, 가장 최근 댓글을 가진 그룹부터 노출 된다는 이야기다.

 

  이렇기에 기존에 댓글 생성시 댓글과 답글로 나누어 생성되게 하였고, 게시글에서도 카운트를 따로 한 뒤 정보 전달시 두 카운트를 더해 댓글수로 반환하도록 수정하였다. 아마 삭제의 경우도 생성과 같이 2종류로 나누어 구현하게 될 것이다.


ResponseReadComment

package CloneCoding.NaverCafe.domain.comment.dto;

@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ResponseReadComment {

    private String profileImage;

    private String nickname;

    private String accountId;

    private String targetAccountId;

    private String targetNickname;

    private String body;

    private LocalDateTime updateAt;

}

 

  사용자가 댓글을 읽을 때 필요한 정보를 전달하는 DTO 클래스로, 답글 대상자의 정보를 전달하기 위해(targetNickname, targetAccountId)를 전달하며 나머지 정보는 지극히 댓글 작성자의 정보와 댓글 정보이다.


ResponseReadComments

package CloneCoding.NaverCafe.domain.comment.dto;

@Getter
@NoArgsConstructor
public class ResponseReadComments {

    public ResponseReadComments(List<ResponseReadComment> comments) {
        this.comments = comments;
    }

    private List<ResponseReadComment> comments = new ArrayList<>();

}

 

  댓글 목록을 전달하기 위한 DTO이다. ResponseReadComment 리스트를 전달한다.


CommentController

package CloneCoding.NaverCafe.domain.comment.controller;

@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/cafe/{cafe_url}")
public class CommentController {

    private final CommentService commentService;

    @GetMapping("{normal_id}/comments")
    public ResponseReadComments readComments(@PathVariable("cafe_url") String cafeUrl,
                                             @PathVariable("normal_id") Long articleId,
                                             @RequestHeader("Authorization") String token) {
        log.info("댓글 목록 요청");
        return commentService.createCommentList(cafeUrl, articleId, token);
    }

}

 

  • readComments() : 카페 url, 게시글 id, 토큰 정보로 NormalService의 createCommentList()를 호출하고 결과 값을 반환

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 ResponseReadComments createCommentList(String cafeUrl, Long articleId, String token) {

        checkCafeMember(cafeUrl, token);
        List<Comment> comments = commentRepository.findByArticleId(articleId);

        List<ResponseReadComment> result = new ArrayList<>();

        for (Comment c : comments) {
            result.add(ResponseReadComment.builder()
                    .profileImage(c.getProfileImage())
                    .nickname(c.getNickname())
                    .accountId(c.getAccountId())
                    .targetAccountId(c.getTargetAccountId())
                    .targetNickname(c.getTargetNickname())
                    .body(c.getBody())
                    .updateAt(c.getUpdateAt())
                    .build());
        }

        return new ResponseReadComments(result);
    }

    private CafeMember checkCafeMember(String url, String token) {

        String accountId = aesUtil.aesDecode(token);
        Cafe findCafe = cafeRepository.findByUrl(url);

        return cafeMemberRepository.findByAccountId(findCafe, accountId);
    }

}

 

  • createCommentList() : 조회한 댓글(들)의 정보로 ResponseReadComment 객체(들)을 생성해 ResponseReadComments 객체에 담아 반환

QueryCommentRespositoryImpl

package CloneCoding.NaverCafe.domain.comment.repository;

import static CloneCoding.NaverCafe.domain.comment.QComment.*;

@Repository
@RequiredArgsConstructor
public class QueryCommentRepositoryImpl implements QueryCommentRepository {

    private final JPAQueryFactory query;

    @Override
    public List<Comment> findByArticleId(Long articleId) {
        return Optional.ofNullable(query
                        .selectFrom(comment)
                        .where(comment.menuId.eq(articleId))
                        .orderBy(comment.createAt.asc(), comment.commentGroup.asc())
                        .fetch())
                .orElseThrow(() -> new NoSuchElementException("댓글 정보를 찾을 수 없습니다."));
    }

}

 

  • findByArticleId() : 전달 받은 게시판 id 정보로 특정 게시판의 Comment 리스트를 조회 후, List<Comment> 반환한다. 조회한 리스트는 생성일자로 내림차순 정렬한 후 댓글 그룹(commentGroup)으로 한 번더 내림차순 정렬한 상태로 반환된다.

API TEST

DB - 테스트 전 COMMENT 테이블 상태

 

  테스트 요청 전 COMMENT 테이블에 저장된 댓글 정보이다. 댓글과 답글 작성시 다음 댓글 내용에는 숫자를 '1'씩 증가시켜 작성했다. 해당 정보들은 아래와 같이 목록을 구상해 입력해두었다.

  • 테스트 댓글 - 답글3 - 답글6
  • 테스트 댓글2 - 답글 - 답글2 - 답글5
  • 테스트 댓글3 - 답글4

API TEST - 댓글(목록) 읽기

 

  게시글(id=1)이 가지는 모든 댓글(+답글) 목록 정보를 요청하는 API 테스트 결과이다. 게시글에 작성된 댓글만 추린 후, 작성일을 기준으로 내림차순 정렬을 한 뒤, 댓글그룹으로 한 번더 내림차순으로 정렬해 Comment 리스트로 반환한 정보들이다. 덕분에 기존 네이버 카페 게시글 댓글과 같이 댓글 정보를 노출하는 것을 볼 수 있다.