본문 바로가기
Spring Boot

스프링 부트(Spring Boot ) 게시판 - 게시글 상세정보 조회 기능 구현하기 [Thymeleaf, MariaDB, IntelliJ, Gradle, MyBatis]

by 도뎡 2023. 4. 5.
반응형

 


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

이전 글에서는 게시글 리스트 페이지를 구현해 보았습니다. 이번에는 특정 게시글의 상세정보를 보여주는 상세 페이지를 구현해 보도록 하겠습니다. 리스트 페이지와 마찬가지로, 컨트롤러와 HTML만 손봐주면 상세 페이지 구현이 완료됩니다.

 

1. 컨트롤러(Controller)에 메서드 추가하기

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

    // 게시글 상세 페이지
    @GetMapping("/post/view.do")
    public String openPostView(@RequestParam final Long id, Model model) {
        PostResponse post = postService.findPostById(id);
        model.addAttribute("post", post);
        return "post/view";
    }

 

1) id

PostMapper의 findById 쿼리의 WHERE 조건으로 사용되는 게시글 번호(PK)입니다.

 

2) post

PostService의 findPostById( )의 실행 결과(특정 게시글의 상세정보)를 담은 게시글 응답 객체입니다. 화면(HTML)에서는 "${ post.변수명 }"으로 응답 객체의 각 데이터에 접근합니다.

 

3) return 문

write.html, list.html과 마찬가지로 상세 화면(HTML)의 경로를 의미하며, openPostView( )의 "post/view"는 src/main/resources/templates/post/view.html을 의미합니다.

 

 

2. 화면(HTML) 생성하기

다음으로 openPostView( )의 리턴 문에 선언된 view.html을 추가해 줄 차례입니다. write.html, list.html과 동일한 경로에 view.html을 추가하고, 소스 코드를 작성해 주세요.

 

마찬가지로 타임리프에 대한 내용은 분량이 적지 않기 때문에 생략하도록 하겠습니다. 혹시라도 궁금하신 분들께서는 여기를 참고해 주세요!

<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="layout/basic">
    <th:block layout:fragment="title">
        <title>상세 페이지</title>
    </th:block>

    <th:block layout:fragment="content">
        <div class="page_tits">
            <h3>게시판 관리</h3>
            <p class="path"><strong>현재 위치 :</strong> <span>게시판 관리</span> <span>리스트형</span> <span>상세정보</span></p>
        </div>
    
        <div class="content">
            <section>
                <table class="tb tb_row">
                    <colgroup>
                        <col style="width:10%;" /><col style="width:23%;" /><col style="width:10%;" /><col style="width:23%;" />
                    </colgroup>
                    <tbody>
                        <tr>
                            <th scope="row">글 유형</th>
                            <td th:text="${post.noticeYn == false ? '일반' : '공지'}"></td>
    
                            <th scope="row">등록일</th>
                            <td th:text="${#temporals.format( post.createdDate, 'yyyy-MM-dd HH:mm' )}"></td>
                        </tr>
                        <tr>
                            <th scope="row">제목</th>
                            <td>[[ ${post.title} ]]</td>
    
                            <th scope="row">조회</th>
                            <td colspan="3">[[ ${post.viewCnt} ]]</td>
                        </tr>
                        <tr>
                            <th scope="row">이름</th>
                            <td colspan="3">[[ ${post.writer} ]]</td>
                        </tr>
                        <tr>
                            <th scope="row">내용</th>
                            <td colspan="3">[[ ${post.content} ]]</td>
                        </tr>
                    </tbody>
                </table>
                <p class="btn_set">
                    <a th:href="@{/post/write.do( id=${post.id} )}" class="btns btn_bdr4 btn_mid">수정</a>
                    <button type="button" class="btns btn_bdr1 btn_mid">삭제</button>
                    <a th:href="@{/post/list.do}" class="btns btn_bdr3 btn_mid">뒤로</a>
                </p>
            </section>
        </div> <!--/* .content */-->
    </th:block>
</html>

 

1) layout:fragment="content"

write.html, list.html과 마찬가지로 게시글 상세 페이지의 실제 콘텐츠가 들어갑니다. openPostView( )에서 화면(HTML)으로 전달한 post 객체를 이용해서 필요한 게시글 정보를 텍스트 형식으로 출력합니다.

 

2) 전체 버튼

버튼 요청 URI 파라미터 설명
수정 "/post/write.do" 게시글 번호(id) 게시글 작성 페이지로 이동합니다. 신규 글 작성과 달리, id를 파라미터로 전달해서 기존 게시글 정보를 화면에 출력합니다.
삭제 "/post/delete.do" 게시글 번호(id) 해당 버튼에 onclick 이벤트를 바인딩해서 게시글 삭제 URI를 호출합니다.

자세한 내용은 게시글 삭제 기능 구현에서 말씀드리도록 하겠습니다.
뒤로 "/post/list.do" X 게시글 리스트 페이지로 이동합니다.

 

 

3. 애플리케이션 실행하기

애플리케이션을 실행하고, 브라우저에서 게시글 리스트 페이지로 접속해 주세요.

(이미지 깨짐 방지를 위해 헤더, 푸터, 좌측 메뉴는 제외시켰습니다.)

게시글 리스트 페이지

 

 

이제, 특정 게시글의 제목을 클릭해 보면 게시글 상세 페이지로 이동하는데요. 저는 제일 상단에 있는 공지글의 제목을 클릭해 보았습니다.

공지글 상세 페이지

 

 

다음은 게시글 상세 페이지의 주소(URL)입니다.

게시글 상세 페이지 URL

 

쿼리 스트링 (Query String)

브라우저 상단의 주소창(URL)을 보시면, ("/post/view.do?id=1") 이와 같이 ' ? ' 뒤에 id(게시글 번호)가 파라미터로 연결되어 있는데요. 이렇게 파라미터의 정보가 "key=value" 형태(구조)의 문자열로 이루어진 것을 쿼리 스트링이라고 합니다.

 

쿼리 스트링의 첫 번째 파라미터는 ' ? '로 시작하고, 두 번째 파라미터부터는 ' & '로 구분합니다. 쿼리 스트링으로 연결된 URI는 HTTP 요청 메서드 중 GET 방식임을 의미합니다.

 

 

4. 게시글 작성 페이지 수정하기


4-1) 자바스크립트 함수 추가하기

다음은 PostController의 openPostWrite( ) 메서드의 구조입니다.

openPostWrite( ) 구조

 

게시글 상세 페이지에서 "수정" 버튼을 클릭하면 게시글 작성 페이지로 이동하는데요. 이동하는 시점에 파라미터로 id(게시글 번호)를 함께 전달하기 때문에 21번 라인의 조건은 true가 됩니다. 즉, 게시글 상세정보(post)를 화면(HTML)으로 전달한다는 것이죠.

 

하지만, write.html에는 게시글 상세정보를 화면에 렌더링 해주는 처리가 되어있지 않습니다. 우리는 자바스크립트를 이용해서, post 객체가 화면으로 넘어왔을 때 사용자에게 게시글 상세정보를 보여주도록 하겠습니다.

 

우선 write.html에 renderPostInfo( ) 함수를 추가하고, onload( )에서 renderPostInfo( )를 호출하도록 변경해 주세요. 변경된 후의 전체 자바스크립트 소스 코드는 다음과 같습니다.

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


    // 게시글 상세정보 렌더링
    function renderPostInfo() {
        const post = [[ ${post} ]];

        if ( !post ) {
            initCreatedDate();
            return false;
        }

        const form = document.getElementById('saveForm');
        const fields = ['id', 'title', 'content', 'writer', 'noticeYn'];
        form.isNotice.checked = post.noticeYn;
        form.createdDate.value = dayjs(post.createdDate).format('YYYY-MM-DD HH:mm');

        fields.forEach(field => {
            form[field].value = post[field];
        })
    }


    // 등록일 초기화
    function initCreatedDate() {
        document.getElementById('createdDate').value = dayjs().format('YYYY-MM-DD');
    }


    // 게시글 저장(수정)
    function savePost() {
        const form = document.getElementById('saveForm');
        const fields = [form.title, form.writer, form.content];
        const fieldNames = ['제목', '이름', '내용'];

        for (let i = 0, len = fields.length; i < len; i++) {
            isValid(fields[i], fieldNames[i]);
        }

        document.getElementById('saveBtn').disabled = true;
        form.noticeYn.value = form.isNotice.checked;
        form.action = [[ ${post == null} ]] ? '/post/save.do' : '/post/update.do';
        form.submit();
    }

 

1) window.onload( )

자바스크립트의 onload 이벤트로, 페이지가 로딩되는 시점에 단 한 번만 실행되는 코드입니다. 게시글 상세정보는 상세 페이지에 접속했을 때 한 번만 보여주면 되기 때문에 여러 번 호출할 필요가 없습니다.

 

2) renderPostInfo( )

게시글 상세정보를 화면에 렌더링 하는 역할을 하는 함수입니다. 신규 게시글 작성은 post 객체를 화면으로 전달하지 않기 때문에 오늘 날짜를 렌더링 한 후 로직을 종료하고, 기존 게시글을 수정하는 경우에만 메인 로직이 실행됩니다.

renderPostInfo( )의 메인 로직

 

메인 로직이 실행되면, saveForm에서 fields에 선언한 필드명에 해당되는 필드를 찾아, 기존 게시글 정보를 렌더링 합니다.

 

 

4-2) 게시글 수정 페이지로 이동하기

이제, 게시글 수정 페이지로 이동했을 때 기존 게시글 정보가 필드에 정상적으로 렌더링 됩니다.

renderPostInfo( ) 함수 실행 결과

 

 

5. 컨트롤러에 게시글 수정 메서드 추가하기

게시글 수정 기능 또한 미리 구현해 두었기 때문에 서비스의 메서드(비즈니스 로직)만 연결해 주면 됩니다. PostController에 다음의 메서드를 추가해 주세요.

    // 기존 게시글 수정
    @PostMapping("/post/update.do")
    public String updatePost(final PostRequest params) {
        postService.updatePost(params);
        return "redirect:/post/list.do";
    }

 

 

6. 게시글 수정해 보기

우선 기존 리스트 데이터는 다음과 같습니다. 여기서 공지글 상세 페이지로 이동해 보겠습니다.

게시글 리스트 페이지

 

 

상세 페이지로 이동했습니다. 이제 수정 버튼을 클릭해 보겠습니다.

공지글 상세 페이지

 

 

수정 페이지로 이동했습니다. 공지글 설정을 해제하고, 제목/이름/내용을 수정한 후 저장해 보겠습니다.

게시글 작성(수정) 페이지 - 변경할 데이터

 

 

updatePost( )가 실행된 후에 리스트 페이지로 리다이렉트 되었습니다. 제목과 작성자는 정상적으로 변경되었습니다. 기존에 공지글이었던 2번 글의 상세 페이지로 이동해 보겠습니다.

게시글 리스트 페이지 - 공지글 수정 후

 

 

UPDATE 쿼리가 정상적으로 실행된 듯합니다. 변경한 내용 그대로 상세 페이지에 출력되고 있습니다.

수정된 게시글 상세 페이지

 

 

마치며

이번에는 게시글 상세 페이지와 게시글 수정 기능을 구현해 보았습니다. 다음 글에서는 CRUD의 마지막 단계인 게시글 삭제 기능을 구현해 보도록 하겠습니다.

 

오늘도 방문해 주신 여러분께 진심으로 감사드립니다! 좋은 하루 보내세요 :)

 

Board.zip
0.65MB

반응형

댓글