본문 바로가기

Server Programming/Spring Boot Backend Programming

[Spring 부트 - 댓글 프로젝트] 3-2. 게시물과 댓글, 컨트롤러와 화면 처리 [자바스크립트]

반응형

요구사항

1. N:1 연관관계를 이용해, 게시글, 댓글, 회원 엔티티 작성

2. CRUD를 이용해 게시글, 댓글, 회원의 추가, 수정, 삭제  메서드 생성

3. RESTful을 이용해, JSON으로, 댓글은 Ajax를 이용해 비동기 처리

 

필수 과제

1. @ManyToOne 다대일 연관관계를 설정

2. 연관관계가 없는 상황에서 left (outer) join 처리 방법

3. 즉시 로딩과 지연 로딩 차이와 효율적인 처리 방법

 


1. 컨트롤러 만들기

->게시물 등록의 경우, 작성자를 현재 존재하는 사용자의 이메일 주소로 지정

목록 : GET

등록 : GET/POST

조회 : GET

수정/삭제 : GET (read멤서드 이용) /POST (modify() / remove())

 

package com.board.boot3.controller;


import com.board.boot3.dto.BoardDTO;
import com.board.boot3.dto.PageRequestDTO;
import com.board.boot3.service.BoardService;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

@Controller
@RequestMapping("/board/")
@Log4j2
@RequiredArgsConstructor
public class BoardController {

    private final BoardService boardService;

    @GetMapping("/list")
    public void list(PageRequestDTO pageRequestDTO, Model model){

        log.info("list............." + pageRequestDTO);

        model.addAttribute("result", boardService.getList(pageRequestDTO));

    }

    @GetMapping("/register")
    public void register(){
        log.info("regiser get...");
    }

    @PostMapping("/register")
    public String registerPost(BoardDTO dto, RedirectAttributes redirectAttributes){

        log.info("dto..." + dto);
        //새로 추가된 엔티티의 번호
        Long bno = boardService.register(dto);

        log.info("BNO: " + bno);

        redirectAttributes.addFlashAttribute("msg", bno);

        return "redirect:/board/list";
    }

    @GetMapping({"/read", "/modify" })
    public void read(@ModelAttribute("requestDTO") PageRequestDTO pageRequestDTO, Long bno, Model model){

        log.info("bno: " + bno);

        BoardDTO boardDTO = boardService.get(bno);

        log.info(boardDTO);

        model.addAttribute("dto", boardDTO);

    }


    @PostMapping("/remove")
    public String remove(long bno, RedirectAttributes redirectAttributes){


        log.info("bno: " + bno);

        boardService.removeWithReplies(bno);

        redirectAttributes.addFlashAttribute("msg", bno);

        return "redirect:/board/list";

    }

    @PostMapping("/modify")
    public String modify(BoardDTO dto,
                         @ModelAttribute("requestDTO") PageRequestDTO requestDTO,
                         RedirectAttributes redirectAttributes){


        log.info("post modify.........................................");
        log.info("dto: " + dto);

        boardService.modify(dto);

        redirectAttributes.addAttribute("page",requestDTO.getPage());
//        redirectAttributes.addAttribute("type",requestDTO.getType());
//        redirectAttributes.addAttribute("keyword",requestDTO.getKeyword());

        redirectAttributes.addAttribute("bno",dto.getBno());

        return "redirect:/board/read";

    }




}

 

2. 화면 처리

#게시물 목록 페이지

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">

<th:block th:replace="~{/layout/basic :: setContent(~{this::content} )}">

    <th:block th:fragment="content">

        <h1 class="mt-4">Board List Page
            <span>
                <a th:href="@{/board/register}">
                    <button type="button" class="btn btn-outline-primary">REGISTER
                    </button>
                </a>
            </span>
        </h1>


<!--        <form action="/board/list" method="get" id="searchForm">-->
<!--            <div class="input-group">-->
<!--                <input type="hidden" name="page" value = "1">-->
<!--                <div class="input-group-prepend">-->
<!--                    <select class="custom-select" name="type">-->
<!--                        <option th:selected="${pageRequestDTO.type == null}">-&#45;&#45;&#45;&#45;&#45;&#45;</option>-->
<!--                        <option value="t" th:selected="${pageRequestDTO.type =='t'}" >제목</option>-->
<!--                        <option value="t" th:selected="${pageRequestDTO.type =='c'}"  >내용</option>-->
<!--                        <option value="t"  th:selected="${pageRequestDTO.type =='w'}" >작성자</option>-->
<!--                        <option value="tc"  th:selected="${pageRequestDTO.type =='tc'}" >제목 + 내용</option>-->
<!--                        <option value="tcw"  th:selected="${pageRequestDTO.type =='tcw'}" >제목 + 내용 + 작성자</option>-->
<!--                    </select>-->
<!--                </div>-->
<!--                <input class="form-control" name="keyword" th:value="${pageRequestDTO.keyword}">-->
<!--                <div class="input-group-append" id="button-addon4">-->
<!--                    <button class="btn btn-outline-secondary btn-search" type="button">Search</button>-->
<!--                    <button class="btn btn-outline-secondary btn-clear" type="button">Clear</button>-->
<!--                </div>-->
<!--            </div>-->
<!--        </form>-->



        <table class="table table-striped">
            <thead>
            <tr>
                <th scope="col">#</th>
                <th scope="col">Title</th>
                <th scope="col">Writer</th>
                <th scope="col">Regdate</th>
            </tr>
            </thead>
            <tbody>

            <tr th:each="dto : ${result.dtoList}" >
                <th scope="row">
                    <a th:href="@{/board/read(bno = ${dto.bno},
                    page= ${result.page})}">
<!--                    ,type=${pageRequestDTO.type} ,-->
<!--                    keyword = ${pageRequestDTO.keyword})}">-->
                        [[${dto.bno}]]
                    </a>
                </th>
                <td>[[${dto.title}]] ----------------  [<b th:text="${dto.replyCount}"></b>]</td>
                <td>[[${dto.writerName}]] <small>[[${dto.writerEmail}]]</small> </td>
                <td>[[${#temporals.format(dto.regDate, 'yyyy/MM/dd')}]]</td>
            </tr>



            </tbody>
        </table>

        <ul class="pagination h-100 justify-content-center align-items-center">

            <li class="page-item " th:if="${result.prev}">
                <a class="page-link" th:href="@{/board/list(page= ${result.start -1},
                    type=${pageRequestDTO.type} ,
                    keyword = ${pageRequestDTO.keyword} ) }" tabindex="-1">Previous</a>
            </li>

            <li th:class=" 'page-item ' + ${result.page == page?'active':''} " th:each="page: ${result.pageList}">
                <a class="page-link" th:href="@{/board/list(page = ${page})}">
<!--                   ,type=${pageRequestDTO.type} ,-->
<!--                   keyword = ${pageRequestDTO.keyword}  )}">-->
                    [[${page}]]
                </a>
            </li>

            <li class="page-item" th:if="${result.next}">
                <a class="page-link" th:href="@{/board/list(page= ${result.end + 1})}">Next</a>
<!--                    ,type=${pageRequestDTO.type} ,-->
<!--                    keyword = ${pageRequestDTO.keyword} )}">Next</a>-->
            </li>

        </ul>


        <div class="modal" tabindex="-1" role="dialog">
            <div class="modal-dialog" role="document">
                <div class="modal-content">
                    <div class="modal-header">
                        <h5 class="modal-title">Modal title</h5>
                        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                            <span aria-hidden="true">&times;</span>
                        </button>
                    </div>
                    <div class="modal-body">
                        <p>Modal body text goes here.</p>
                    </div>
                    <div class="modal-footer">
                        <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
                        <button type="button" class="btn btn-primary">Save changes</button>
                    </div>
                </div>
            </div>
        </div>

        <script th:inline="javascript">

            var msg = [[${msg}]];

            console.log(msg);

            if(msg){
                $(".modal").modal();
            }
            var searchForm = $("#searchForm");

            $('.btn-search').click(function(e){

                searchForm.submit();

            });

            $('.btn-clear').click(function(e){

                searchForm.empty().submit();

            });


        </script>



    </th:block>

</th:block>

#게시물 등록 페이지 [GET 방식 / POST 방식

: <form action="@{/board/register}" th:method="post">

: 작성자를 writerEmail로 변경

-> member 테이블의 이메일 주소로 지정

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">

<th:block th:replace="~{/layout/basic :: setContent(~{this::content} )}">

    <th:block th:fragment="content">

        <h1 class="mt-4">Board Register Page</h1>

        <form th:action="@{/board/register}" th:method="post">
            <div class="form-group">
                <label >Title</label>
                <input type="text" class="form-control" name="title" placeholder="Enter Title">
            </div>
            <div class="form-group">
                <label >Content</label>
                <textarea class="form-control" rows="5" name="content"></textarea>
            </div>
            <div class="form-group">
                <label >Writer Email</label>
                <input type="email" class="form-control" name="writerEmail" placeholder="Writer Email ">
            </div>

            <button type="submit" class="btn btn-primary">Submit</button>
        </form>

    </th:block>

</th:block>

#게시물 조회 페이지

: 특정 번호 클릭시 조회 화면으로 이동

-> 댓글 작업은 조회 페이지에서 댓글의 숫자를 보여주거나, 댓글 입력하는 화면 추가

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">

<th:block th:replace="~{/layout/basic :: setContent(~{this::content} )}">

    <th:block th:fragment="content">

        <h1 class="mt-4">Board Read Page</h1>

        <div class="form-group">
            <label >Bno</label>
            <input type="text" class="form-control" name="gno" th:value="${dto.bno}" readonly >
        </div>

        <div class="form-group">
            <label >Title</label>
            <input type="text" class="form-control" name="title" th:value="${dto.title}" readonly >
        </div>
        <div class="form-group">
            <label >Content</label>
            <textarea class="form-control" rows="5" name="content" readonly>[[${dto.content}]]</textarea>
        </div>
        <div class="form-group">
            <label >Writer</label>
            <input type="text" class="form-control" name="writer" th:value="${dto.writerName}" readonly>
        </div>
        <div class="form-group">
            <label >RegDate</label>
            <input type="text" class="form-control" name="regDate" th:value="${#temporals.format(dto.regDate, 'yyyy/MM/dd HH:mm:ss')}" readonly>
        </div>
        <div class="form-group">
            <label >ModDate</label>
            <input type="text" class="form-control" name="modDate" th:value="${#temporals.format(dto.modDate, 'yyyy/MM/dd HH:mm:ss')}" readonly>
        </div>

        <a th:href="@{/board/modify(bno = ${dto.bno}, page=${requestDTO.page})}">
<!--            , type=${requestDTO.type}, keyword =${requestDTO.keyword}-->
            <button type="button" class="btn btn-primary">Modify</button>
        </a>

        <a th:href="@{/board/list(page=${requestDTO.page})}">
<!--            , type=${requestDTO.type}, keyword =${requestDTO.keyword}-->
            <button type="button" class="btn btn-info">List</button>
        </a>


    </th:block>

</th:block>


#게시물 수정 페이지

: GET/POST 방식

-> 버튼의 이벤트 처리 [자바스크립트]

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">

<th:block th:replace="~{/layout/basic :: setContent(~{this::content} )}">

    <th:block th:fragment="content">

        <h1 class="mt-4">Board Modify Page</h1>

        <form action="/board/modify" method="post">

            <!--페이지 번호  -->
            <input type="hidden" name="page" th:value="${requestDTO.page}">
<!--            <input type="hidden" name="type" th:value="${requestDTO.type}" >-->
<!--            <input type="hidden" name="keyword" th:value="${requestDTO.keyword}" >-->


            <div class="form-group">
                <label >Bno</label>
                <input type="text" class="form-control" name="bno" th:value="${dto.bno}" readonly >
            </div>

            <div class="form-group">
                <label>Title</label>
                <input type="text" class="form-control" name="title" th:value="${dto.title}" >
            </div>
            <div class="form-group">
                <label >Content</label>
                <textarea class="form-control" rows="5" name="content">[[${dto.content}]]</textarea>
            </div>
            <div class="form-group">
                <label >Writer</label>
                <input type="text" class="form-control" name="writer" th:value="${dto.writerEmail}" readonly>
            </div>
            <div class="form-group">
                <label >RegDate</label>
                <input type="text" class="form-control" th:value="${#temporals.format(dto.regDate, 'yyyy/MM/dd HH:mm:ss')}" readonly>
            </div>
            <div class="form-group">
                <label >ModDate</label>
                <input type="text" class="form-control" th:value="${#temporals.format(dto.modDate, 'yyyy/MM/dd HH:mm:ss')}" readonly>
            </div>

        </form>

        <button type="button" class="btn btn-primary modifyBtn">Modify</button>

        <button type="button" class="btn btn-info listBtn">List</button>

        <button type="button" class="btn btn-danger removeBtn">Remove</button>

        <script th:inline="javascript">

            var actionForm = $("form"); //form 태그 객체

            $(".removeBtn").click(function(){

                actionForm
                    .attr("action", "/board/remove")
                    .attr("method","post");

                actionForm.submit();

            });

            $(".modifyBtn").click(function() {

                if(!confirm("수정하시겠습니까?")){
                    return ;
                }

                actionForm
                    .attr("action", "/board/modify")
                    .attr("method","post")
                    .submit();
            });

            $(".listBtn").click(function() {

                //var pageInfo = $("input[name='page']");
                var page = $("input[name='page']");
                // var type = $("input[name='type']");
                // var keyword = $("input[name='keyword']");

                actionForm.empty(); //form 태그의 모든 내용을 지우고

                actionForm.append(page);
                // actionForm.append(type);
                // actionForm.append(keyword);


                actionForm
                    .attr("action", "/board/list")
                    .attr("method","get");

                actionForm.submit();

            })

        </script>


    </th:block>

</th:block>

버튼 이벤트 처리를 위한 자바스크립트 이벤트 메서드 

 

기본 메서드 : 문서가 준비되면 매개변수로 넣은 콜백 함수를 실행하라

$(document).ready(function(){

});
<script th:inline="javascript">

$(document).ready(function(){
	alert('First READY');
});

$(document).ready(function(){
	alert('Second READY');
});

$(document).ready(function(){
	alert('Thired READY');
});

<!-- 함수에 함수에 함수가 들어간 형태 -->

$(document).ready(function() {

    <!-- 자바스크립트 변수에는 인라인 표현식,엔티티의 변수 모두 가능-->
    var bno = [[${dto.bno}]];

    var listGroup = $(".replyList");

    $(".replyCount").click(function () {

        $.getJSON('/replies/board/' + bno, function (arr) {
               console.log(arr);
               <!-- 자바스크립트의 출력 함수 console-->


        })//end getJSON

    })//end click

<!-- 간단한 형식으로 전환-->
<!-- (document).ready 생략 -->
$(function(){

});

<!-- 기본형-->

<!-- 엔티티의 변수에 접근 가능 dto.replyCount 변수를 클릭할 경우-->


$(".replyCount").click(function(){
	$.getJSON(){
    })
})

</script>

 

jQuery 메서드의 기본 형태

${'h1').css('color','red');

$ : 선택자, 식별자

.메서드 : .뒤에는 메서드 부분 ('여기에', '이걸') 넣는다.

-> 속성에 값을 넣는 방식

 

'.클래스 속성'

$('.replyCount').
$('input[name="replyText"]')
 $('input[name="replyer"]')

".엔티티 변수"

$(".replyList");
$(".replyCount")
$(".addReply")
$(".modal-footer .btn")
$(".replySave, .replyClose")

 

 기본 형태

.html 함수

let value = $('h1').html();
//h1 의 내용을 변수 value 에 저장합니다.
 
$('div').html('<a>hello</a>');
//이전 내용을 지우고 새로운 내용을 넣습니다.

 

   <script th:inline="javascript">
            $(document).ready(function() {
            // 문서 로딩 완료 후, 이러한 것들이 수행 된다.
            
            
            // 댓글 숫자 클릭시
            $(".replyCount").click(function () {
            		$.getJSON('/replies/board/' + bno, function (arr) {

                    console.log(arr);

                })//end getJSON

            })//end click
            
            <!--댓글 처리 함수에서 사용할 날짜 처리하는 함수 만들어 두기-->
            //날짜 처리를 위한 함수
                function formatTime(str) {
                    var date = new Date(str);

                    return date.getFullYear() + '/' +
                        (date.getMonth() + 1) + '/' +
                        date.getDate() + ' ' +
                        date.getHours() + ':' +
                        date.getMinutes();
                }
                
                <!--특정한 게시글 댓글 처리 함수 -->
            	function loadJSONData() {
                    $.getJSON('/replies/board/' + bno, function (arr) {

                        console.log(arr);

                        var str = "";

                        $('.replyCount').html(" Reply Count  " + arr.length);

                        $.each(arr, function (idx, reply) {
                            console.log(reply);
                            str += '    <div class="card-body" data-rno="' + reply.rno + '"><b>' + reply.rno + '</b>';
                            str += '    <h5 class="card-title">' + reply.text + '</h5>';
                            str += '    <h6 class="card-subtitle mb-2 text-muted">' + reply.replyer + '</h6>';
                            str += '    <p class="card-text">' + formatTime(reply.regDate) + '</p>';
                            str += '    </div>';
                        })

                        listGroup.html(str);
                        //html() 함수는 선택한 요소 안의 내용을 가져오거나, 다른 내용으로 바꿉니다. 사용방식. h1 의 내용을 변수 value 에 저장합니다

                    });
                }

            
            
            
            });
</script>

 

.attr(attributeName)

선택된 요소 집합에서 첫번째 요소의 attributeName에 해당하는 속성값을 반환한다.


.attr(attributeName, value)

선택자에 의해 선택된 요소에 하나 이상의 속성을 부여할 수 있다.
여러 속성을 부여할 때, 속성명에 따옴표는 선택사항이다.
(class속성을 세팅할 때는 따옴표를 사용)

 

 

: 삭제 버튼

-> form 태그 객체를 저장한 변수 actionForm

-> 변수 actionForm의 action요소에 삭제URL 속성 부여

-> 변수 actionForm의 method요소에 POST방식 속성 부여

 

: 수정 버튼

-> form 태그 객체를 저장한 변수 actionForm

-> !수정 할지 말지 If문 처리 -> true, return

-> false이면

-> 변수 actionForm의 action요소에 수정URL 속성 부여

-> 변수 actionForm의 method요소에 POST방식 속성 부여

 

: 목록 버튼

-> page 변수 생성하는데, input태그에서 이름 클래스의 page 변수 전달 :  $(input[name='page']");

-> actionFom에 있는 모든 내용을 지우고, 목록 페이지로 이동하는데

-> actionFrom에  페이지 정보를 넣어서 전달한다.

-> 변수 actionForm의 action요소에 목록URL 속성 부여

-> 변수 actionForm의 method요소에 GET방식 속성 부여

        <script th:inline="javascript">

            var actionForm = $("form"); //form 태그 객체

            $(".removeBtn").click(function(){

                actionForm
                    .attr("action", "/board/remove")
                    .attr("method","post");

                actionForm.submit();

            });

            $(".modifyBtn").click(function() {

                if(!confirm("수정하시겠습니까?")){
                    return ;
                }

                actionForm
                    .attr("action", "/board/modify")
                    .attr("method","post")
                    .submit();
            });

            $(".listBtn").click(function() {

                //var pageInfo = $("input[name='page']");
                var page = $("input[name='page']");
                // var type = $("input[name='type']");
                // var keyword = $("input[name='keyword']");

                actionForm.empty(); //form 태그의 모든 내용을 지우고

                actionForm.append(page);
                // actionForm.append(type);
                // actionForm.append(keyword);


                actionForm
                    .attr("action", "/board/list")
                    .attr("method","get");

                actionForm.submit();

            })

        </script>
반응형