Spring Boot/구현해보기

게시글에 댓글 달기 구현

xowoony 2022. 11. 27. 18:21

CommentVo.java

먼저 CommentVo를 만들어준다

CommentVo는 CommentEntity를 상속받으며 상속 받은 것들에 추가적으로 userNickname을 추가한다.

게시글 읽기 + DB에 인서트 하기 에서 ArticleReadVo를 만들어 줬던 것과 같은 맥락임.

그리고 getter & setter 추가

package dev.xowoony.studymemberbbs.vos.bbs;

import dev.xowoony.studymemberbbs.entities.bbs.CommentEntity;

public class CommentVo extends CommentEntity {
    private String userNickname;
    private boolean isSigned;
    private boolean isMine;
    private boolean isLiked;

    

    public boolean isLiked() {
        return isLiked;
    }

    public void setLiked(boolean liked) {
        isLiked = liked;
    }


    public boolean isMine() {
        return isMine;
    }

    public void setMine(boolean mine) {
        isMine = mine;
    }


    public boolean isSigned() {
        return isSigned;
    }

    public void setSigned(boolean signed) {
        isSigned = signed;
    }


    public String getUserNickname() {
        return userNickname;
    }
    public void setUserNickname(String userNickname) {
        this.userNickname = userNickname;
    }

}

 

BbsMapper.xml

resultType 은 CommentVo를,

 

select 쿼리를 이용하여 `study_bbs`.`comments` 테이블에 있는 정보를 가져오고

LEFT JOIN을 이용해 옆동네 애들 중 `user`.`nickname`을 데려온다

그저 사용자의 이메일 대신 닉네임을 표시하기 위함임.

<select id="selectCommentsByArticleIndex"
        resultType="dev.xowoony.studymemberbbs.vos.bbs.CommentVo">
    SELECT `index`           AS `index`,
           `comment_index`   AS `commentIndex`,
           `user_email`      AS `userEmail`,
           `article_index`   AS `articleIndex`,
           `content`         AS `content`,
           `written_on`      AS `writtenOn`,
           `user`.`nickname` AS `userNickname`
    FROM `study_bbs`.`comments` AS `comment`
             LEFT JOIN `study_member`.`users` AS `user` ON `comment`.`user_email` = `user`.`email`
    WHERE `comment`.`article_index` = #{articleIndex}
    ORDER BY `index`
</select>

 

 

IBbsMapper.java (인터페이스)

@Param을 이용하여 articleIndex를 배열로 가져오도록 한다.

배열일 경우에 이름지을 때 아래와 같이 selectCommentsByArticleIndex 이런식으로 복수형으로 지어주도록 한다.

CommentVo[] selectCommentsByArticleIndex(@Param(value="articleIndex") int articleIndex);
//배열일 경우 복수형으로 이름지어야 한다. (CommentsBy~뭐시기)

참고로 CommentVo를 만들기 전은 이런 모양이었음.

CommentEntity[] selectCommentsByArticleIndex(@Param(value = "articleIndex") int articleIndex);

 

 

 

BbsController.java

 commentObject 를 생성하고 

index~userNickname 등등을 집어넣고

또 그것을 responseArray에 집어 넣는다.

마지막으로 배열을 반환한다.(string)

 

향상된 for문을 사용하며

한바퀴씩 돌면서 댓글 하나가 생성된다.

@RequestMapping(value = "comment",
        method = RequestMethod.GET,
        produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public String getComment(@SessionAttribute(value = "user", required = false) UserEntity user,
                         @RequestParam(value = "aid") int articleIndex) {
    JSONArray responseArray = new JSONArray();
    CommentVo[] comments = this.bbsService.getComments(articleIndex);
    for (CommentVo comment : comments) {
        JSONObject commentObject = new JSONObject();
        commentObject.put("index", comment.getIndex());
        commentObject.put("commentIndex", comment.getCommentIndex());
        commentObject.put("userEmail", comment.getUserEmail());
        commentObject.put("articleIndex", comment.getArticleIndex());
        commentObject.put("content", comment.getContent());
        commentObject.put("writtenOn", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(comment.getWrittenOn()));
        commentObject.put("userNickname", comment.getUserNickname());
        commentObject.put("isSigned", user != null);
        commentObject.put("isMine", user != null && user.getEmail().equals(comment.getUserEmail()));
        commentObject.put("isLiked", comment.isLiked());
        responseArray.put(commentObject);
    }
    return responseArray.toString();
}

 

 

bbsService.java

사용자가 어느 게시글에 댓글을 달았는지 알아야 하기 때문에 (article_index) 를 리턴한다.

// 댓글 쓰기
public CommentVo[] getComments(int articleIndex) {
    return this.bbsMapper.selectCommentsByArticleIndex(articleIndex);
}

 

 

read.html

<tr class="comment-row">
    <td colspan="6">
        <form class="comment-form" id="commentForm">
            <label class="label">
                <span hidden>댓글작성</span>
                <input class="--object-input" id="objectInput" maxlength="100" name="content"
                       placeholder="댓글을 입력해 주세요" type="text">
            </label>
            <input type="hidden" th:value="${article.getIndex()}" name="aid">
            <input class="--object-button" type="submit" value="작성">
        </form>
        <div class="comment-container" id="commentContainer">
            <div class="comment liked mine">
                <div class="head">
                    <span class="writer">관리자</span>
                    <span class="dt">2022-01-01 00:00:00</span>
                    <span class="spring"></span>
                    <span class="action-container">
                        <a class="action reply" href="#" rel="actionReply">답글 달기</a>
                        <a class="action modify" href="#" rel="actionModify">수정</a>
                        <a class="action delete" href="#" rel="actionDelete">삭제</a>
                        <a class="action cancel" href="#" rel="actionCancel">취소</a>
                    </span>
                </div>
                <div class="body">
                    <div class="content">
                        <span class="text">댓글 내용임!</span>
                        <div class="like">
                            <a href="#" class="toggle">
                                <i class="icon fa-solid fa-heart"></i>
                            </a>
                            <span class="count">9,999</span>
                        </div>
                    </div>
                    <form class="modify-form">
                        <label class="label">
                            <span hidden>댓글 수정</span>
                            <input class="--object-input" maxlength="100" name="content"
                                   placeholder="댓글을 입력해 주세요" type="text">
                        </label>
                        <input class="--object-button" type="submit" value="수정">
                    </form>
                </div>
            </div>

            <form class="reply-form" id="replyForm">
                <div class="head">
                    <span class="to">@관리자</span>
                    <a href="#" class="cancel">취소</a>
                </div>
                <div class="body">
                    <label class="label">
                        <span hidden>답글작성</span>
                        <input class="--object-input" maxlength="100" name="content"
                               placeholder="답글을 입력해 주세요" type="text">
                    </label>
                    <input class="--object-button" type="submit" value="작성">
                </div>
            </form>

            <div class="comment sub">
                <div class="head">
                    <span class="writer">누구</span>
                    <span class="dt">2022-01-01 00:00:00</span>
                    <span class="spring"></span>
                    <span class="action-container">
                        <a class="action reply" href="#">답글달기</a>
                        <a class="action delete" href="#">삭제</a>
                </span>
                </div>
                <div class="body">zz</div>
            </div>


            <div class="comment sub mine">
                <div class="head">
                    <span class="writer">누구</span>
                    <span class="dt">2022-01-01 00:00:00</span>
                    <span class="spring"></span>
                    <span class="action-container">
                    <a class="action reply" href="#">답글달기</a>
                    <a class="action delete" href="#">삭제</a>
                    </span>
                </div>
                <div class="body">ss</div>
            </div>

        </div>
    </td>
</tr>

read.css

@charset "UTF-8";

body > .main > .content {
    margin: 5rem 0;

    align-items: stretch;
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
}

body > .main > .content > .title {
    font-size: 2rem;
    font-weight: 500;
    margin-bottom: 2.5rem;
}

body > .main > .content > .table > thead td,
body > .main > .content > .table > thead th {
    padding: 0.5rem 0.75rem;
    vertical-align: middle;
}

body > .main > .content > .table > thead td {
    padding: 0.5rem;
}

body > .main > .content > .table th {
    width: 0;
    background-color: rgb(255, 255, 255);
    border-radius: 0.5rem;
    color: rgb(128, 139, 150);
    font-weight: 400;
    text-align: center;
    white-space: nowrap;
}

body > .main > .content > .table .content-container {
    background-color: rgb(255, 255, 255);
    border-radius: 0.5rem;
    margin: 0.75rem 0;
    padding: 1rem 2rem;
}

body > .main > .content > .table .button-container {
    align-items: stretch;
    display: flex;
    flex-direction: row;
    justify-content: flex-start;
}

body > .main > .content > .table .button-container > * + * {
    margin-left: 0.5rem;
}

body > .main > .content > .table .button-container > .spring {
    flex: 1;
}

body > .main > .content > .table .button-container > a[href] {
    text-decoration: none;
}

body > .main > .content > .table .button-container > a[href].modify {
    background-color: rgb(40, 117, 225);
}

body > .main > .content > .table .button-container > a[href].delete {
    background-color: rgb(231, 76, 60);
}

#commentForm {
    margin-top: 0.5rem;

    align-items: stretch;
    display: flex;
    flex-direction: row;
    justify-content: flex-start;
}

#commentForm > .label {
    flex: 1;
    margin-right: 0.5rem;
}

/*댓글 전체 간격 margin-top*/
#commentContainer {
    margin-top: 0.75rem;

    align-items: stretch;
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
}

/*댓글들 사이사이 간격 margin-top*/
#commentContainer > * + * {
    margin-top: 0.5rem;
}

/*댓글창 각각의 모양*/
#commentContainer .comment {
    border-radius: 0.375rem;
    box-shadow: 0 0 0.5rem 0.0625rem rgba(0, 0, 0, 10%);
    overflow: hidden;

    align-items: stretch;
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
}

/*대댓글 오른쪽으로 들어가게 하기*/
#commentContainer .comment.sub {
    margin-left: 5rem;
}

/*댓글의 타이틀 모양*/
#commentContainer .comment > .head {
    background-color: rgb(128, 139, 150);
    color: rgb(255, 255, 255);
    font-size: 0.9rem;
    padding: 0.375rem 1rem;

    align-items: center;
    display: flex;
    flex-direction: row;
    justify-content: flex-start;
}

#commentContainer .comment.mine > .head {
    background-color: rgb(40, 117, 225);
}

/*작성일시*/
#commentContainer .comment > .head > .dt {
    color: rgb(213, 216, 220);
    margin-left: 0.5rem;
}

/*답글달기, 삭제 오른쪽으로 보내기*/
#commentContainer .comment > .head > .spring {
    flex: 1;
}

/*답글달기, 삭제*/
#commentContainer .comment > .head > .action-container {
    align-items: center;
    display: flex;
    flex-direction: row;
    justify-content: flex-start;
}

#commentContainer .comment > .head > .action-container > .action {
    color: inherit;
    text-decoration: none;
}

#commentContainer .comment > .head > .action-container > .action.cancel {
    disply: none;
}


#commentContainer .comment.modifying > .head > .action-container > .action.cancel {
    display: inline-block;
}

/*답글달기, 삭제 글자 사이 간격*/
#commentContainer .comment > .head > .action-container > .action + .action {
    margin-left: 0.5rem;
}


#commentContainer .comment.modifying > .head > .action-container > .action.reply,
#commentContainer .comment.modifying > .head > .action-container > .action.modify,
#commentContainer .comment.modifying > .head > .action-container > .action.delete {
    display: none;
}

/*답글달기, 삭제 글자 hover시 */
#commentContainer .comment > .head > .action-container > .action:hover {
    text-decoration: underline;
}

/*댓글입력칸 공간 크기 조절*/
#commentContainer .comment > .body {
    background-color: rgb(255, 255, 255);
    font-size: 1rem;
    padding: 0.75rem 1rem;

    align-items: stretch;
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
}

#commentContainer .comment > .body > .content {
    align-items: center;
    display: flex;
    flex-direction: row;
    justify-content: flex-start;
}

#commentContainer .comment.modifying > .body > .content {
    display: none;
}

#commentContainer .comment > .body > .content > .text {
    flex: 1;
}

#commentContainer .comment > .body > .content > .like {
    align-items: center;
    display: flex;
    flex-direction: column;
    justify-content: center;
}

#commentContainer .comment > .body > .content > .like > .count {
    color: rgb(128, 139, 150);
    font-size: 0.8rem;
    line-height: 100%;

}

#commentContainer .comment > .body > .modify-form {
    align-items: stretch;
    display: none;
    flex-direction: row;
    justify-content: flex-start;
}

#commentContainer .comment.modifying > .body > .modify-form {
    display: flex;
}

#commentContainer .comment > .body > .modify-form > .label {
    flex: 1;
    margin-right: 0.5rem;
}


#commentContainer .comment > .body > .content > .like > .toggle {
    width: 1.75rem;
    height: 1.75rem;
    border: 0.125rem solid rgb(171, 178, 185);
    border-radius: 0.375rem;
    color: rgb(171, 178, 185);
    margin-bottom: 0.5rem;
    text-decoration: none;

    align-items: center;
    display: flex;
    flex-direction: row;
    justify-content: center;
}

#commentContainer .comment > .body > .content > .like > .toggle:hover {
    border: 0.125rem solid rgb(128, 139, 150);
    color: rgb(128, 139, 150);
}

#commentContainer .comment.liked > .body > .content > .like > .toggle {
    background-color: rgb(231, 76, 60);
    border: 0.125rem solid rgb(241, 62, 22);
    color: rgb(220, 213, 215);
}

#commentContainer .comment.liked > .body > .content > .like > .toggle:hover {
    background-color: rgb(203, 67, 53);
    border: 0.125rem solid rgb(203, 67, 53);
}


/*대댓글창 크기*/
#commentContainer .reply-form {
    border-radius: 0.375rem;
    box-shadow: 0 0 0.5rem 0.0625rem rgba(0, 0, 0, 10%);
    overflow: hidden;
    margin-left: 5rem;


    align-items: stretch;
    display: none;
    flex-direction: row;
    justify-content: flex-start;
}

#commentContainer .reply-form.visible {
    display: inline-block;
}

/*대댓글창 머리부분*/
#commentContainer .reply-form > .head {
    background-color: rgb(13, 77, 168);
    color: rgb(255, 255, 255);
    font-size: 0.9rem;
    padding: 0.375rem 1rem;

    align-items: center;
    display: flex;
    flex-direction: row;
    justify-content: space-between;
}

#commentContainer .reply-form > .body {
    background-color: rgb(255, 255, 255);
    padding: 0.75rem 1rem;

    align-items: stretch;
    display: flex;
    flex-direction: row;
    justify-content: flex-start;
}

#commentContainer .reply-form > .body > .label {
    flex: 1;
    margin-right: 0.5rem;
}

#commentContainer .reply-form > .head > .cancel {
    color: inherit;
    text-decoration: none;
}

#commentContainer .reply-form > .head > .cancel:hover {
    text-decoration: underline;
}

#commentContainer .reply-form > .label {
    flex: 1;
    margin-right: 0.5rem;
}

read.js

따로 빼려면 복잡할 것 같아서

댓글달기, 수정, 삭제 로직이 함께 들어가 있다. (참고)

 

xhr을 open해주고 GET방식으로 /comment?aid=? 에 해당하는 게시글을 불러온다.

aid 값을 불러오는 이유는 사용자가 어느 게시글에 댓글을 달았는지 알아야 하기 때문이다. (article_index)

const commentForm = window.document.getElementById('commentForm');
const commentContainer = window.document.getElementById('commentContainer');

const loadComments = () => {
    commentContainer.innerHTML = '';
    const url = new URL(window.location.href);
    const searchParams = url.searchParams;
    const aid = searchParams.get('aid');
    const xhr = new XMLHttpRequest();
    xhr.open('GET', `./comment?aid=${aid}`);
    xhr.onreadystatechange = () => {
        if (xhr.readyState === XMLHttpRequest.DONE) {
            if (xhr.status >= 200 && xhr.status < 300) {
                const responseArray = JSON.parse(xhr.responseText);
                const appendComment = (commentObject, isSub) => {
                    const commentHtmlText = `
                    <div class="comment ${isSub ? 'sub' : ''} ${commentObject['isMine'] === true ? 'mine' : ''} ${commentObject['isLiked']}" rel="comment">
                            <div class="head">
                                <span class="writer">${commentObject['userNickname']}</span>
                                <span class="dt">${commentObject['writtenOn']}</span>
                                <span class="spring"></span>
                                <span class="action-container">
                                    ${commentObject['isSigned'] === true ? '<a class="action reply" href="#" rel="actionReply">답글 달기</a>' : ''}
                                    ${commentObject['isMine'] === true ? '<a class="action modify" href="#" rel="actionModify">수정</a>' : ''}
                                    ${commentObject['isMine'] === true ? '<a class="action delete" href="#" rel="actionDelete">삭제</a>' : ''}
                                    <a class="action cancel" href="#" rel="actionCancel">취소</a>
                                </span>
                            </div>
                            <div class="body">
                                <div class="content">
                                    <span class="text">${commentObject['content']}</span>
                                    <div class="like">
                                        <a href="#" class="toggle">
                                            <i class="icon fa-solid fa-heart"></i>
                                        </a>
                                        <span class="count">9,999</span>
                                    </div>
                                </div>
                                <form class="modify-form" rel="modifyForm">
                                    <label class="label">
                                        <span hidden>댓글 수정</span>
                                        <input class="--object-input" maxlength="100" name="content"
                                               placeholder="댓글을 입력해 주세요" type="text">
                                    </label>
                                    <input class="--object-button" type="submit" value="수정">
                                </form>
                            </div>
                        </div>
                        <form class="reply-form" rel="replyForm">
                            <div class="head">
                                <span class="to">@${commentObject['userNickname']}</span>
                                <a href="#" class="cancel" rel="replyCancel">취소</a>
                            </div>
                            <div class="body">
                                <label class="label">
                                    <span hidden>답글작성</span>
                                    <input class="--object-input" maxlength="100" name="content"
                                           placeholder="답글을 입력해 주세요" type="text">
                                </label>
                                <input class="--object-button" type="submit" value="작성">
                            </div>
                        </form>`;

                    // 위에서 가져온 의미 없는 문자열을 html 태그로 만들어버리기
                    const domParser = new DOMParser();
                    const dom = domParser.parseFromString(commentHtmlText, 'text/html'); // html 로 변환후 dom에 넣는다
                    const commentElement = dom.querySelector('[rel="comment"]');    // rel이 comment인 것
                    const replyFormElement = dom.querySelector('[rel="replyForm"]');
                    const modifyFormElement = dom.querySelector('[rel="modifyForm"]');


                    // 답글 달기 글자 클릭했을 때 visible 되면서 답글창이 나타남
                    dom.querySelector('[rel="actionReply"]')?.addEventListener('click', e => {
                        e.preventDefault();
                        replyFormElement.classList.add('visible');
                        replyFormElement['content'].focus();
                    });

                    // 답글창에서 취소 눌렀을 때 답글창이 사라지게됨
                    dom.querySelector('[rel="replyCancel"]')?.addEventListener('click', e => {
                        e.preventDefault();
                        replyFormElement.classList.remove('visible');
                    });


                    modifyFormElement.onsubmit = e => {
                        e.preventDefault();
                        // 댓글 수정 후 수정 버튼 눌렀을 때 알림창 띄우기
                        if (!confirm('댓글을 수정할까요?')) {
                            return;
                        }
                        // 댓글 수정 작업 요청
                        Cover.show('댓글 수정 중');
                        const xhr = new XMLHttpRequest();
                        const formData = new FormData();
                        formData.append('index', commentObject['index']);
                        formData.append('content', modifyFormElement['content'].value);
                        xhr.open('PATCH', './comment');
                        xhr.onreadystatechange = () => {
                            if (xhr.readyState === XMLHttpRequest.DONE) {
                                Cover.hide();
                                if (xhr.status >= 200 && xhr.status < 300) {
                                    const responseObject = JSON.parse(xhr.responseText);
                                    switch (responseObject['result']) {
                                        case 'no_such_allowed':
                                            alert('수정하려는 댓글이 더이상 존재하지 않습니다.');
                                            break;
                                        case 'not_allowed':
                                            alert('댓글을 수정할 권한이 존재하지 않습니다.');
                                            break;
                                        case 'success':
                                            loadComments();
                                            break;
                                        default:
                                            alert('알수 없는 이유로 수정 못함');
                                    }
                                } else {
                                    alert('서버와 통신하지 못하였습니다');
                                }
                            }
                        };
                        xhr.send(formData);
                    }


                    // 댓글 삭제하기 눌렀을 때 댓글 삭제되게
                    dom.querySelector('[rel="actionDelete"]')?.addEventListener('click', e => {
                        e.preventDefault();
                        if (!confirm('정말로 댓글을 삭제 할건가요?')) {
                            return;
                        }
                        const xhr = new XMLHttpRequest();
                        const formData = new FormData();
                        formData.append('index', commentObject['index']);

                        xhr.open('DELETE', './comment');
                        xhr.onreadystatechange = () => {
                            if (xhr.readyState === XMLHttpRequest.DONE) {
                                Cover.hide();
                                if (xhr.status >= 200 && xhr.status < 300) {
                                    const responseObject = JSON.parse(xhr.responseText);
                                    switch (responseObject['result']) {
                                        case 'success':
                                            loadComments();
                                            break;
                                        case 'no_such_comment':
                                            alert('삭제하려는 댓글이 더이상 존재하지 않습니다.\n이미 삭제되었을 수도 있어요.');
                                            break;
                                        case 'not_allowed':
                                            alert('삭제할 권한이 없어요.');
                                            break;
                                        default:
                                            alert('알 수 없는 이유로 삭제하지 못하였습니다. 다시 시도해주세요.');
                                    }
                                } else {
                                    alert('서버와 통신하지 못하였습니다. 잠시후 다시 시도해주세요');
                                }
                            }
                        };
                        xhr.send(formData);
                    });


                    // 수정하기 글자 눌렀을 때 밑에 댓글이 수정될 수 있게 바뀌고, 오른쪽에 답글달기, 수정, 삭제 글자 없어지게
                    dom.querySelector('[rel="actionModify"]')?.addEventListener('click', e => {
                        e.preventDefault();
                        commentElement.classList.add('modifying');
                        modifyFormElement['content'].value = commentObject['content'];
                        modifyFormElement['content'].focus();
                    });


                    // 취소 글씨를 눌렀을 때
                    dom.querySelector('[rel="actionCancel"]')?.addEventListener('click', e => {
                        e.preventDefault();
                        commentElement.classList.remove('modifying');
                    });

                    // 댓글 visible 된 상태에서 답글 내용 적고 작성버튼 눌렀을 때
                    // 댓글 작성을 위해 articleIndex, content, commentIndex 데이터 3개를 실어준다
                    replyFormElement.onsubmit = e => {
                        e.preventDefault();
                        if (replyFormElement['content'].value === '') {
                            replyFormElement['content'].focus();
                            return false;
                        }
                        Cover.show('댓글을 작성하고 있어요. 잠시만 기다려주세여.');
                        const xhr = new XMLHttpRequest();
                        const formData = new FormData();
                        formData.append('articleIndex', commentForm['aid'].value);
                        formData.append('content', replyFormElement['content'].value);
                        formData.append('commentIndex', commentObject['index']);

                        xhr.open('POST', './read')
                        xhr.onreadystatechange = () => {
                            Cover.hide();
                            if (xhr.readyState === XMLHttpRequest.DONE) {
                                if (xhr.status >= 200 && xhr.status < 300) {
                                    const responseObject = JSON.parse(xhr.responseText);
                                    switch (responseObject['result']) {
                                        case 'success':
                                            loadComments();
                                            break;
                                        default:
                                            alert('알 수 없는 이유로 게시글을 작성하지 못하였습니다. 다시 시도해주세요');
                                    }
                                } else {
                                    alert('서버와 통신하지 못하였습니다. 잠시 후 다시 시도해 주세요');
                                }
                            }
                        }
                        xhr.send(formData);
                    };
                    commentContainer.append(commentElement, replyFormElement);
                };
                // for (let commentObject of responseArray.filter(x => !x['commentIndex'])) {
                //     const replyArray = responseArray.filter(x => x['commentIndex'] === commentObject['index']);
                //     appendComment(commentObject, false);
                //     for (let replyObject of replyArray) {           // 대댓글 생성됨
                //         appendComment(replyObject, true);
                //     }
                // }

                //재귀호출
                const appendReplyOf = parentComment => {
                    const replyArray = responseArray.filter(x => x['commentIndex'] === parentComment['index']);
                    // 만족하는 조건의 요소가 없을 경우 빈 배열이 나온다.
                    // => for문이 더이상 돌지 않고 재귀호출이 중단된다.
                    for (let replyObject of replyArray) {
                        appendComment(replyObject, true);
                        appendReplyOf(replyObject);
                        // 대댓글이 다시 부모댓글이 됨 (다시 함수 호출)
                    }
                };
                // filter로 commentIndex가 아닌 애를 걸러낸다
                for (let commentObject of responseArray.filter(x => !x['commentIndex'])) {
                    appendComment(commentObject, false); // 원댓글 생성
                    appendReplyOf(commentObject);
                }

            }
        } else {

        }
    }
    xhr.send();
}


loadComments();


if (commentForm !== null) {
    commentForm.onsubmit = e => {
        e.preventDefault();
        // 댓글 값이 비었다면 alert 창 띄움
        if (commentForm['content'].value === '') {
            alert('댓글을 입력해주세요.');
            commentForm['content'].focus();
            return false;
        }
        // 댓글 작성중
        const xhr = new XMLHttpRequest();
        const formData = new FormData();
        formData.append('articleIndex', commentForm['aid'].value);  // 게시글 번호 (14, 16)
        formData.append('content', commentForm['content'].value);   // 글 내용

        xhr.open('POST', './read');
        xhr.onreadystatechange = () => {
            if (xhr.readyState === XMLHttpRequest.DONE) {
                Cover.hide();
                if (xhr.status >= 200 && xhr.status < 300) {
                    const responseObject = JSON.parse(xhr.responseText);
                    switch (responseObject['result']) {
                        case 'success':
                            loadComments();
                            break;
                        default:
                            alert('알 수 없는 이유로 댓글 작성을 못하였습니다.\n다시 시도해주세요.');
                    }
                } else {
                    alert('서버와 통신하지 못했습니다.\n잠시 후 다시 시도해주세요');
                }
            }
        };
        xhr.send(formData);
    }
}

 

 

개발자 도구

를 켜고 console창에 loadComments(); 입력을 하게 되면

insert된 댓글의 모든 정보가 Preview에 표시되어 나오게 된다.

 

실행결과