게시판 댓글, 대댓글(무한댓글) 로직

DragonTiger·2022년 6월 5일
5

간단한 게시판 프로젝트를 만들면서 가장 힘들게 만들었던 로직 입니다. (부족한 부분 지적해주시면 감사하겠습니다.)

댓글, 대댓글

테이블

댓글, 대댓글에 대해 정보를 찾다가 찾은 컬럼정보를 토대로 비즈니스 로직을 만들어봤습니다.
우선 댓글과 대댓글은 하나의 테이블로 처리하였습니다.

아래의 댓글 테이블에서 로직을 위해 중요하게 봐야하는 컬럼은 5개입니다.

REF, REFORDER, STEP, PARENTNUM, ANSWERNUM
REF 컬럼으로 댓글을 그룹으로 묶습니다.
REFORDER는 댓글 그룹들의 순서,
STEP은 댓글의 계층을 나타냅니다.
PARENTNUM는 부모댓글의 ID값을 나타내고,
ANSWERNUM는 해당댓글의 자식댓글의 수를 나타냅니다.

비즈니스 로직(서비스)

if문으로 댓글과 대댓글의 저장로직을 구분해줍니다.

댓글

댓글이 저장될때마다 ref의 최댓값을 찾아서 ref컬럼에 +1 을 하여 저장해줍니다.
(하나의 댓글에는 하나의 그룹을 지정해줍니다.)

대댓글

step 0 == 댓글
step 1 == 0의댓글
step 2 == 1의댓글
step 3 == 2의댓글

대댓글일때에는 부모댓글을 찾아줍니다.
찾아온 부모댓글의 step컬럼에 +1 더해준값을 변수에 저장합니다.
(저장될 대댓글step 값은 찾아온 부모댓글에 step +1 이기때문이다.)
부모댓글의 그룹내의 자식댓글의 총갯수를 더한값을 찾아줍니다.
부모댓글의 그룹내의 step컬럼의 최댓값을 찾아줍니다.

step의 조건비교로 refOrder(그룹내의 순서)를 정합니다.

저장할 대댓글의 step이 댓글의 그룹내에서 최대 step보다 작다면
이 그룹내의 합산한 자식수의 댓글에 +1 을하여 refOrder가 정해집니다.
refOrder는 answerNumSum + 1L;

저장할 대댓글의 step이 댓글의 그룹내에서 최대 step 같다면
부모댓글의 그룹내의 순서와 자식댓글을 더한값 보다 큰 refOrder는 모두 +1 컬럼을 업데이트 해줍니다.
refOrder는 refOrder + answerNum + 1L;

저장할 대댓글의 step이 댓글의 그룹내에서 최대 step보다 크다면
부모댓글의 그룹내의 순서보다 큰 refOrder는 모두 +1 더해줍니다.
refOrder는 refOrder + 1L;

이해를 돕기위해 제가 참고한 유튜브 영상입니다.
계층형 답변형 게시판 구현 로직 설명

 public Board_Comments commentsSave(Long commentId, Board_CommentsForm board_commentsForm, PrincipalDetails principalDetails) {

        Board board = boardRepository.findById(board_commentsForm.getBoard_id()).orElseThrow(() -> new IllegalArgumentException("작성할 게시글이 없습니다."));
        
        //댓글 그룹번호 NVL 함수 NULL이면 0  NULL이아니면 최대값
        Long commentsRef = board_commentsRepository.findByNvlRef(board_commentsForm.getBoard_id());
        //SELECT NVL(MAX(ref),0) FROM BOARD_COMMENTS b WHERE b.board_id = ?1
        
        //commentId가 null 이면 댓글, null이 아니면 대댓글 저장
        if (commentId == null) { //댓글 저장

            Board_Comments savedComment = board_commentsRepository.save(Board_Comments.builder()
                    .content(board_commentsForm.getContent())
                    .regdate(LocalDateTime.now())
                    .member(principalDetails.getMember())
                    .board(board)
                    .ref(commentsRef + 1l)
                    .step(0l)
                    .refOrder(0l)
                    .answerNum(0l)
                    .parentNum(0l)
                    .build());

            return savedComment;

        } else {
            //대댓글 저장
            //부모 댓글 데이터
            Board_Comments board_comments = board_commentsRepository.findById(commentId).orElseThrow(() -> new IllegalArgumentException("작성할 답글이 없습니다."));
            //refOrderAndUpdate 메소드 실행후 값 가져오기
            Long refOrderResult = refOrderAndUpdate(board_comments);
            // null이면 대댓글 작성 오류
            if (refOrderResult == null) {
                return null;
            }

            //대댓글 저장
            Board_Comments savedReply = board_commentsRepository.save(Board_Comments.builder()
                    .content(board_commentsForm.getContent())
                    .regdate(LocalDateTime.now())
                    .member(principalDetails.getMember())
                    .board(board)
                    .ref(board_comments.getRef())
                    .step(board_comments.getStep() + 1l)
                    .refOrder(refOrderResult)
                    .answerNum(0l)
                    .parentNum(commentId)
                    .build());

 			//부모댓글의 자식컬럼수 + 1 업데이트
            board_commentsRepository.updateAnswerNum(board_comments.getId(), board_comments.getAnswerNum());

            return savedReply;
        }
         private Long refOrderAndUpdate(Board_Comments board_comments) {

        Long saveStep = board_comments.getStep() + 1l;
        Long refOrder = board_comments.getRefOrder();
        Long answerNum = board_comments.getAnswerNum();
        Long ref = board_comments.getRef();

        //부모 댓글그룹의 answerNum(자식수)
        Long answerNumSum = board_commentsRepository.findBySumAnswerNum(ref); 
        //SELECT SUM(answerNum) FROM BOARD_COMMENTS WHERE ref = ?1
        //부모 댓글그룹의 최댓값 step
        Long maxStep = board_commentsRepository.findByNvlMaxStep(ref);
        //SELECT MAX(step) FROM BOARD_COMMENTS WHERE ref = ?1
      
        //저장할 대댓글 step과 그룹내의최댓값 step의 조건 비교
        /*
        step + 1 < 그룹리스트에서 max step값  AnswerNum sum + 1 * NO UPDATE
        step + 1 = 그룹리스트에서 max step값  refOrder + AnswerNum + 1 * UPDATE
        step + 1 > 그룹리스트에서 max step값  refOrder + 1 * UPDATE
        */
        if (saveStep < maxStep) {
            return answerNumSum + 1l;
        } else if (saveStep == maxStep) {
            board_commentsRepository.updateRefOrderPlus(ref, refOrder + answerNum);
            //UPDATE BOARD_COMMENTS SET refOrder = refOrder + 1 WHERE ref = ?1 AND refOrder > ?2
            return refOrder + answerNum + 1l;
        } else if (saveStep > maxStep) {
            board_commentsRepository.updateRefOrderPlus(ref, refOrder);
            //UPDATE BOARD_COMMENTS SET refOrder = refOrder + 1 WHERE ref = ?1 AND refOrder > ?2
            return refOrder + 1l;
        }

        return null;
    }

쿼리문 (QueryDSL)

아래 쿼리문에서처럼 ref 그룹을 역순정렬 후 refOrder 그룹내 순서를 정렬하면

    @Override
    public Page<Board_Comments> findAllByBoardOrderByCommentsOrRegdate(Long board_id, Pageable pageable) {

        QueryResults<Board_Comments> board_comments = jpaQueryFactory
                .select(qBoard_comments)
                .from(qBoard_comments)
                .innerJoin(qBoard_comments.board,qBoard)
                .innerJoin(qBoard_comments.member,qMember)
                .where(qBoard_comments.board.id.eq(board_id))
                .offset(pageable.getOffset())
                .limit(pageable.getPageSize())
                .orderBy(qBoard_comments.ref.desc(),
                        qBoard_comments.refOrder.asc())
                .fetchResults();

        List<Board_Comments> content = board_comments.getResults();
        long total = board_comments.getTotal();

        return new PageImpl<>(content,pageable,total);
    }

예시



profile
take the bull by the horns

3개의 댓글

comment-user-thumbnail
2022년 11월 9일

저장할 대댓글 step과 그룹내의최댓값 step의 조건 비교 에서

step + 1 < 그룹리스트에서 max step값 AnswerNum sum + 1 NO UPDATE
step + 1 = 그룹리스트에서 max step값 refOrder + AnswerNum + 1
UPDATE
step + 1 > 그룹리스트에서 max step값 refOrder + 1 * UPDATE

저기서 UPDATE가 의미하는게 뭔가요?

1개의 답글
comment-user-thumbnail
2023년 5월 2일

감사합니다

답글 달기