본문 바로가기
Spring Boot

스프링 부트(Spring Boot) 게시판 - REST API 방식으로 댓글 리스트(목록) 기능 구현하기 [Thymeleaf, MariaDB, IntelliJ, Gradle, MyBatis]

by 도뎡 2023. 4. 20.
반응형
  • 본 게시판 프로젝트는 단계별(step by step)로 진행되니, 이전 단계를 진행하시는 것을 권장드립니다.
  • DBMS 툴은 DBeaver를 이용하며, DB는 MariaDB를 이용합니다. (MariaDB 설치하기)
  • 화면 처리는 HTML5 기반의 자바 템플릿 엔진인 타임리프(Thymeleaf)를 사용합니다.

이전 글에서는 @RestController와 jQuery의 Ajax를 이용해서 화면의 움직임(이동 또는 새로고침) 없이 댓글을 등록하는 기능을 구현해 보았습니다.

이번에는 특정 게시글에 등록된 댓글을 출력하는 기능을 구현해 볼 건데요. 게시글의 경우, 게시글이 생성/수정/삭제되는 시점에 리스트 페이지로 리다이렉트 하도록 처리했었습니다.

댓글은 등록/수정/삭제된 시점에 페이지를 이동하거나 새로고침 하지 않고, 테이블에서 SELECT 한 결과 데이터를 댓글 영역에 다시 렌더링 합니다.

말로는 이해가 쉽지 않으실 수 있으니, 차근차근 기능을 구현해 보겠습니다.

 

1. 댓글 API 컨트롤러(Controller) - 메서드 추가하기

CommentApiController에 다음의 메서드를 추가해 주세요.

    // 댓글 리스트 조회
    @GetMapping("/posts/{postId}/comments")
    public List<CommentResponse> findAllComment(@PathVariable final Long postId) {
        return commentService.findAllComment(postId);
    }

 

로직 해석

해당 메서드는 이전 글에서 설명드린 REST API 설계 규칙에서 컬렉션(Collection)에 해당되는 기능으로, 특정 게시글(postId)에 등록된 모든 댓글을 조회합니다.

REST API - URI 설계 규칙

 

2. 상세 페이지 - 댓글 렌더링 영역 추가하기

view.html의 콘텐츠(content) 영역에서 댓글 작성 영역(<div class="cm_write"></div>) 뒤에 다음의 코드를 추가해 주세요.

    <!--/* 댓글 렌더링 영역 */-->
    <div class="cm_list">

    </div>

 

3. 상세 페이지 - JS 함수 작성하기

view.html의 JS(script) 영역에 onload( )와 findAllComment( ) 함수를 추가해 주세요.

    window.onload = () => {
        findAllComment();
    }


    // 전체 댓글 조회
    function findAllComment() {

        const postId = [[ ${post.id} ]];

        $.ajax({
            url : `/posts/${postId}/comments`,
            type : 'get',
            dataType : 'json',
            async : false,
            success : function (response) {
                console.log(response);
            },
            error : function (request, status, error) {
                console.log(error)
            }
        })
    }

 

함수 설명
onload( ) 페이지가 로딩되는 시점에 단 한 번만 실행되는 함수입니다. 상세 페이지에 접속하면 댓글 목록을 출력하는 findAllComment( ) 함수가 실행됩니다.
findAllComment( ) 게시글 번호(postId)를 기준으로 특정 게시글에 등록된 전체 댓글을 조회합니다. 댓글을 등록하는 saveComment( )는 저장할 댓글 정보를 서버로 전송하기 위해 data와 contentType 옵션이 사용되었지만, 해당 함수는 URI와 요청 메서드만으로 자원(Resource)을 조회할 수 있습니다.

 

4. 댓글 조회 테스트 해보기

이제, 상세 페이지에 접속한 후 F12를 눌러 브라우저 개발자 도구를 열어보면, 콘솔(Console)에 댓글(response)이 출력되는 걸 확인하실 수 있습니다.

댓글(response) 출력 결과

 

5. findAllComment( ) 함수 수정하기

success( ) 함수의 response는 CommentApiController의 findAllComment( )가 리턴하는 List 타입의 객체배열입니다. 이제 response를 이용해서 댓글 HTML을 그린 후 화면에 렌더링 해주면 됩니다.

    // 전체 댓글 조회
    function findAllComment() {

        const postId = [[ ${post.id} ]];

        $.ajax({
            url : `/posts/${postId}/comments`,
            type : 'get',
            dataType : 'json',
            async : false,
            success : function (response) {

                // 1. 조회된 데이터가 없는 경우
                if ( !response.length ) {
                    document.querySelector('.cm_list').innerHTML = '<div class="cm_none"><p>등록된 댓글이 없습니다.</p></div>';
                    return false;
                }

                // 2. 렌더링 할 HTML을 저장할 변수
                let commentHtml = '';

                // 3. 댓글 HTML 추가
                response.forEach(row => {
                    commentHtml += `
                        <div>
                            <span class="writer_img"><img src="/images/default_profile.png" width="30" height="30" alt="기본 프로필 이미지"/></span>
                            <p class="writer">
                                <em>${row.writer}</em>
                                <span class="date">${dayjs(row.createdDate).format('YYYY-MM-DD HH:mm')}</span>
                            </p>
                            <div class="cont"><div class="txt_con">${row.content}</div></div>
                            <p class="func_btns">
                                <button type="button" class="btns"><span class="icons icon_modify">수정</span></button>
                                <button type="button" class="btns"><span class="icons icon_del">삭제</span></button>
                            </p>
                        </div>
                    `;
                })

                // 4. class가 "cm_list"인 요소를 찾아 HTML을 렌더링
                document.querySelector('.cm_list').innerHTML = commentHtml;
            },
            error : function (request, status, error) {
                console.log(error)
            }
        })
    }

 

로직 해석

PostController - openPostList( )

게시글 리스트와 같은 동기 처리 방식은 컨트롤러에서 화면(HTML)으로 데이터를 전달해서 처리하지만, 비동기 처리 방식은 화면 내에서 Ajax를 이용해 실시간으로 서버와 데이터를 주고받을 수 있습니다.

findAllComment( )의 response.forEach( )에서 row는 response에 담긴 각각의 댓글 객체를 의미하는데요. response를 순환하며 commentHtml에 댓글 HTML을 추가한 후 div.cm_list에 렌더링 합니다.

 

6. saveComment( ) 함수 수정하기

이제, 게시글 상세 페이지로 접속해 보면 등록된 댓글이 출력되는 걸 확인하실 수 있습니다.

게시글 상세 페이지 - (댓글이 있는 경우)

 

다음은 등록된 댓글이 없는 경우입니다.

게시글 상세 페이지 - (댓글이 없는 경우)

 

마지막으로 댓글 저장 함수의 로직을 조금만 손봐주면 됩니다. view.html의 saveComment( )를 다음과 같이 변경해 주세요.

    // 댓글 저장
    function saveComment() {

        const content = document.getElementById('content');
        isValid(content, '댓글');

        const postId = [[ ${post.id} ]];
        const params = {
            postId : postId,
            content : content.value,
            writer : '홍길동'
        }

        $.ajax({
            url : `/posts/${postId}/comments`,
            type : 'post',
            contentType : 'application/json; charset=utf-8',
            dataType : 'json',
            data : JSON.stringify(params),
            async : false,
            success : function (response) {
                alert('저장되었습니다.');
                content.value = '';
                document.getElementById('counter').innerText = '0/300자';
                findAllComment();
            },
            error : function (request, status, error) {
                console.log(error)
            }
        })
    }

 

로직 해석

Ajax의 success( ) 함수의 내부 로직만 변경되었는데요. 댓글 저장이 완료되면 사용자에게 저장 완료 메시지를 보여주고, 입력했던 댓글 내용과 입력된 자릿수를 초기화한 후 findAllComment( )를 호출해서 화면에 댓글을 다시 렌더링 합니다.

 

7. 댓글 등록해 보기

이번에도 리스트 1페이지의 최상단 게시글에 댓글을 등록해 보겠습니다.

게시글 상세 페이지 - (댓글 입력 및 등록)

 

저장 완료 메시지를 닫으면 댓글 내용과 자릿수가 초기화되고, 새로 등록한 댓글도 정상적으로 조회됩니다.

게시글 상세 페이지 - (댓글 저장 완료)

 

마치며

비동기 처리 방식에 대해 어느 정도 감이 잡히셨을 거라고 생각합니다. 쉽게 생각하면 동기 처리는 컨트롤러에서 데이터와 화면 두 가지를 동시에 처리하는 반면, 비동기 처리는 화면 내에서 데이터의 처리(CRUD 등)를 실시간으로 서버에 요청합니다.

동기 방식은 화면의 움직임(페이지 이동 또는 새로고침)이 발생합니다. 화면의 움직임은 HTML, CSS, JS 코드가 처음부터 끝까지 다시 렌더링 됨을 의미합니다. 즉, 페이지를 처음부터 다시 그리는 작업이다 보니 리소스 낭비가 심합니다.

반대로 비동기 방식의 장점 중 하나는 페이지를 다시 로딩하지 않아도 된다는 점입니다. 댓글 조회의 경우, 서버에 필요한 데이터를 요청하고, 자바스크립트로 HTML 코드를 직접 그린 후에 DOM에 코드를 렌더링(끼워 넣는) 하는 방식입니다. 즉, 페이지를 처음부터 로딩하지 않고 최소한의 영역만 변경하기 때문에 리소스 낭비가 적고, 속도적인 측면에서도 유리하다고 볼 수 있습니다.

이제, 댓글에서 추가적으로 구현해야 할 기능은 댓글 수정/삭제와 페이징 처리 정도가 남아있는데요. 다음 글에서는 댓글 수정 기능을 구현해 보겠습니다.

오늘도 방문해 주신 여러분께 감사의 말씀을 드립니다. 좋은 하루 보내세요 :)

Board.zip
0.87MB

반응형

댓글