본문 바로가기

웹 개발/Spring

[Web_Spring] 19

실습(댓글)

- [Web_Spring] 18 이어서

1. src/main/java/com.example.board/domain/vo/ReplyPageDTO.java​

package com.example.board.domain.vo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.stereotype.Component;

import java.util.List;


@Component
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ReplyPageDTO {
    private List<ReplyVO> list;
    private int total; 
}

 

 

 

2. src/main/resource/static/js/reply.js

let replyService = (function(){

    function add(reply, callback){
        console.log("add reply.........");
        console.log(reply);
        $.ajax({
            url: "/reply/new",
            type: "post",
            data: JSON.stringify(reply),
            contentType: "application/json",
            success: function(result){
                if(callback){
                    callback(result);
                } 
            }
        });
    }

    function getList(param, callback){
        let boardNumber = param.boardNumber;
        let page = param.page || 1;

        $.getJSON("/reply/list/" + boardNumber + "/" + page, function(replyPageDTO){
            if(callback){
                callback(replyPageDTO.list, replyPageDTO.total);
            }
        });
    }

    function read(replyNumber, callback){
        $.get("/reply/" + replyNumber, function(reply){
            if(callback){
                callback(reply);
            }
        });
    }

    function remove(replyNumber, callback){
        $.ajax({
            url: "/reply/" + replyNumber,
            type: "delete",
            success: function(result){
                if(callback){
                    callback(result);
                }
            }
        });
    }

    function modify(reply, callback){
        $.ajax({
            url: "/reply/" + reply.replyNumber,
            type: "PUT",
            data: JSON.stringify(reply),
            contentType: "application/json; charset=utf-8",
            success: function(result){
                if(callback){
                    callback(result);
                }
            }
        });
    }

    function getReplyDate(replyDate){
        let today = new Date();
        let rDate = new Date(replyDate);
        let gap = today.getTime() - rDate.getTime();

        if(gap < 1000 * 60 * 60 * 24){
            let h = rDate.getHours();
            let m = rDate.getMinutes();
            let s = rDate.getSeconds();

            return [(h < 10 ? '0' : '') + h, (m < 10 ? '0' : '') + m, (s < 10 ? '0' : '') + s].join(":");
        }else{
            let y = rDate.getFullYear();
            let m = rDate.getMonth() + 1;
            let d = rDate.getDate();

            return [y, (m < 10 ? '0' : '') + m, (d < 10 ? '0' : '') + d].join("-")
        }
    }

    return {add: add, getList: getList, read: read, remove: remove, modify: modify, getReplyDate: getReplyDate};
})();

 

 

 

3. src/main/java/com.example.board/controller/ReplyController.java​

package com.example.board.controller;

/*
 * REST(Representational State Transfer)
 *   하나의 URI는 하나의 고유한 리소스(데이터)를 대표하도록 설계된다.
 *   예)/board/123 : 게시글 중 123번 게시글의 전체 정보
 * */

import com.example.board.domain.vo.Criteria;
import com.example.board.domain.vo.ReplyPageDTO;
import com.example.board.domain.vo.ReplyVO;
import com.example.board.service.ReplyService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus; 
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.io.UnsupportedEncodingException;
import java.util.List;

@RestController
@Slf4j
@RequestMapping("/reply/*")
@RequiredArgsConstructor
public class ReplyController {
    private final ReplyService replyService;

    //    댓글 등록
//    브라우저에서 JSON타입으로 데이터를 전송하고 서버에서는 댓글의 처리 결과에 따라 문자열로 결과를 리턴한다.
//    consumes : 전달받은 데이터의 타입
//    produces : 콜백함수로 결과를 전달할 때의 타입
//    @RequestBody : 전달받은 데이터를 알맞는 매개변수로 주입할 때 사용
//    ResponseEntity : 서버의 상태코드, 응답 메세지 등을 담을 수 있는 타입
    @PostMapping(value="/new", consumes = "application/json", produces = "text/plain; charset=utf-8")
    public ResponseEntity<String> create(@RequestBody ReplyVO replyVO) throws UnsupportedEncodingException {
        log.info("replyVO : " + replyVO);
        replyService.register(replyVO);
        return new ResponseEntity<>(new String("댓글 등록 성공".getBytes(), "UTF-8"), HttpStatus.OK);
    }

    @GetMapping("/{replyNumber}")
    public ReplyVO read(@PathVariable("replyNumber") Long replyNumber){
        log.info("read......... : " + replyNumber);
        return replyService.read(replyNumber);
    }

    @GetMapping("/list/{boardNumber}/{page}")
    public ReplyPageDTO getList(@PathVariable("page") int pageNum, @PathVariable("boardNumber") Long boardNumber){
        log.info("getList........... : " + pageNum + ", " + boardNumber);
        return new ReplyPageDTO(replyService.getList(new Criteria(pageNum, 10), boardNumber), replyService.getTotal(boardNumber));
    }

    //    댓글 삭제
    @DeleteMapping("/{replyNumber}")
    public String remove(@PathVariable("replyNumber") Long replyNumber){
        log.info("remove............... : " + replyNumber);
        replyService.remove(replyNumber);
        return "댓글 삭제 성공";
    }

    //    댓글 수정
//    PUT : 자원의 전체 수정
//    PATCH : 자원의 일부 수정
    @PutMapping("/{replyNumber}")
    public String modify(@PathVariable("replyNumber") Long replyNumber, @RequestBody ReplyVO replyVO){
        log.info("modify............... : " + replyNumber);
        log.info(replyVO.toString());

        replyVO.setReplyNumber(replyNumber);
        replyService.modify(replyVO);

        return replyService.read(replyNumber) == null ? "댓글 수정 실패" : "댓글 수정 성공";
    }


    //5개
    //1번 매개변수 없고 리턴은 문자열
    //2번 매개변수 1개 있고 리턴은 문자열
    //3번 매개변수 없고 리턴은 JSON Object
    //4번 매개변수 여러 개 있고 리턴은 JSON Object
    //5번 매개변수 여러 개 있고 리턴은 JSON Array

    //4개(DB 테이블 생성)
    //1번 매개변수 1개 있고 리턴은 문자열
    //2번 매개변수 없고 리턴은 JSON Object
    //3번 매개변수 여러 개 있고 리턴은 JSON Object
    //4번 매개변수 여러 개 있고 리턴은 JSON Array

    /*
     * $.ajax({
     *   url: "/user/3"
     *   data: JSON.stringfy(JS객체-{a: "a", b: "b", ...})
     *   contentType: "application/json"
     *   dataType: "json"
     *   success: function(result){}
     * });
     *
     * */

    //Git 개인 레포지토리 push

}

 

 

 

4. src/main/resource/templates/board/read.html

<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Board</title>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />
    <meta name="description" content="" />
    <meta name="keywords" content="" />
    <link rel="stylesheet" href="/css/main.css" />
    <style>
        .line {
            border-bottom: 1px solid #ff8b77;
        }

        .replies li {
            list-style: none;
        }

        h4 {
            margin-bottom: 0;
            margin-top: 25px;
        }
    </style>
</head>
<body class="is-preload">
<!-- Main -->
<div id="main">
    <div class="wrapper">
        <div class="inner">

            <!-- Elements -->
            <header class="major">
                <h1>Board</h1>
                <p>게시글 상세보기</p>
            </header>
            <!-- Table -->
            <h3><a id="goList" class="button small">목록 보기</a></h3>
            <div class="content">
                <div class="form">
                    <form action="/board/remove" method="post">
                        <div class="fields" th:object="${board}">
                            <div class="field">
                                <h4>번호</h4>
                                <input type="text" th:field="*{boardNumber}" readonly/>
                            </div>
                            <div class="field">
                                <h4>제목</h4>
                                <input type="text" th:field="*{boardTitle}" readonly/>
                            </div>
                            <div class="field">
                                <h4>내용</h4>
                                <textarea rows="6" style="resize:none" th:field="*{boardContent}" readonly></textarea>
                            </div>
                            <div class="field">
                                <h4>작성자</h4>
                                <input type="text" th:field="*{boardWriter}" readonly/>
                            </div>
                        </div>
                        <ul class="actions special">
                            <li>
                                <input type="button" class="button" value="수정" onclick="goUpdate()"/>
                                <input type="submit" class="button" value="삭제"/>
                            </li>
                        </ul>
                        <a href="javascript:void(0)" class="register button primary small" style="width: 100%">댓글 등록</a>
                        <div style="display: none" class="register-form">
                            <div>
                                <h4>작성자</h4>
                                <input type="text" name="replyWriter" placeholder="Replier">
                            </div>
                            <div>
                                <h4>댓글</h4>
                                <textarea rows="6" name="replyContent" placeholder="Reply" style="resize: none"></textarea>
                            </div>
                            <div style="text-align: right">
                                <a href="javascript:void(0)" class="finish button primary small">등록</a>
                                <a href="javascript:void(0)" class="cancel button primary small">취소</a>
                            </div>
                        </div>
                        <ul class="replies"></ul>
                    </form>
                    <div class="paging" style="text-align: center"></div>
                </div>
            </div>
        </div>
    </div>
</div>
</body>
<!-- Scripts -->
<script src="/js/jquery.min.js"></script>
<script src="/js/jquery.dropotron.min.js"></script>
<script src="/js/browser.min.js"></script>
<script src="/js/breakpoints.min.js"></script>
<script src="/js/util.js"></script>
<script src="/js/main.js"></script>
<!--<script th:inline="javascript">let boardNumber = [[${board.boardNumber}]];</script>-->
<script src="/js/reply.js"></script>
<script th:inline="javascript">
    let boardNumber = [[${board.boardNumber}]];
    let params = [[${criteria.listLink}]];
    const $replyUL = $(".replies");

    let pageNum = 1;

    showList(pageNum);

    function showList(page){
        replyService.getList({
            boardNumber: boardNumber,
            page: page
        }, function(list, total){
            let str = "";
            if(list == null || list.length == 0){
                $replyUL.html("<p>등록된 댓글이 없습니다.</p>");
                return;
            }

            $.each(list, function(i, reply){
                str += "<li>";
                str += "<div style='display: flex; justify-content: space-between;'>";
                str += "<strong style='display: block;'>" + reply.replyWriter + "</strong>";
                str += "<div class='btns'>";
                str += "<a class='modify " + reply.replyNumber + "' style='cursor: pointer;'>수정</a>"
                str += "&nbsp;&nbsp;<a class='remove " + reply.replyNumber + "' style='cursor: pointer;'>삭제</a>"
                str += "<a class='modifyOk " + reply.replyNumber + "' style='display: none;cursor: pointer;'>수정완료</a>"
                str += "&nbsp;&nbsp;<a class='cancel " + reply.replyNumber + "' style='display: none;cursor: pointer'>취소</a>"
                str += "</div>";
                str += "</div>";
                str += "<div style='display: flex; justify-content: space-between;'>";
                str += "<p class='" + reply.replyNumber + "'>" + reply.replyContent + "</p>";
                str += "<p><strong>";
                str += replyService.getReplyDate(reply.replyRegisterDate);
                str += "</strong></p>";
                str += "</div>";
                str += "<div class='line'></div>";
                str += "</li>";
            });

            $replyUL.html(str);
            showReplyPage(total);
        });
    }

    function showReplyPage(total){
        const $paging = $("div.paging");
        let str = "";
        let endPage = Math.ceil(pageNum / 10.0) * 10;
        let startPage = endPage - 9;
        let realEnd = Math.ceil(total / 10.0);

        if(endPage > realEnd){endPage = realEnd;}

        let next = endPage * 10 < total;
        let prev = startPage > 1;

        if(prev){
            str += "<a class='changePage' href='" + (startPage - 1) +"'><code>&lt;</code></a>";
        }
        for(let i=startPage; i<=endPage; i++){
            str += pageNum == i ? "<code>" + i + "</code>" : "<a class='changePage' href='" + i + "'><code>" + i + "</code></a>";
        }

        if(next){
            str += "<a class='changePage' href='" + (endPage + 1) +"'><code>&gt;</code></a>";

        }
        $paging.html(str);
    }

    $(".paging").on("click", "a.changePage", function(e){
        e.preventDefault();
        pageNum = $(this).attr("href");
        showList(pageNum);
    });

    $(".finish").on("click", function(e){
        e.preventDefault();

        const replyWriter = $("input[name='replyWriter']").val();
        const replyContent = $("textarea[name='replyContent']").val();

        if(!replyWriter || !replyContent){
            alert("내용을 입력해주세요.");
            return;
        }

        replyService.add({
            boardNumber: boardNumber,
            replyContent: replyContent,
            replyWriter: replyWriter

        }, function(result){
            $("input[name='replyWriter']").val("");
            $("textarea[name='replyContent']").val("");
            pageNum = 1;
            showList(pageNum);
            $(".register").show();
            $(".register-form").hide();
        });
    });

    let contentOriginal = "";
    let modifyCheck = false;
    //이벤트 위임
    //이미 작성되어 있는 HTML 요소에 이벤트를 먼저 걸어준 뒤
    //DOM으로 추가될 자식 요소에 이벤트를 전달해주는 방식이다.
    //DOM으로 추가된 태그는 이벤트 보다 늦게 실행되기 때문이다.
    $("ul.replies").on("click", "div.btns a.modify", function(e){
        e.preventDefault();
        if(modifyCheck){
            alert("이미 수정중인 댓글이 있습니다.");
            return;
        }
        modifyCheck = true;
        let str = "";
        let replyNumber = this.classList[1];
        contentOriginal = $("p." + replyNumber).text();

        console.log($("a." + replyNumber));
        $("a." + replyNumber).show();
        $(this).hide();
        $(this).next().hide();

        str += "<textarea class='" + replyNumber + "' style='resize: none'>";
        str += $("p." + replyNumber).text();
        str += "</textarea>";

        $("p." + replyNumber).replaceWith(str);
        $("textarea." + replyNumber).css("width", "70%");
    });

    $("ul.replies").on("click", "div.btns a.cancel", function(e){
        e.preventDefault();
        modifyCheck = false;
        let str = "";
        let replyNumber = this.classList[1];

        $("a." + replyNumber).show();
        $(this).hide();
        $(this).prev().hide();

        str += "<p class='" + replyNumber + "'>";
        str += contentOriginal;
        str += "</p>";

        $("textarea." + replyNumber).replaceWith(str);
    });

    $("ul.replies").on("click", "div.btns a.remove", function(e){
        replyService.remove(this.classList[1], function(result){
            showList(pageNum);
        });
    });

    $("ul.replies").on("click", "div.btns a.modifyOk", function(e){
        let replyNumber = this.classList[1];
        replyService.modify({
            replyNumber: replyNumber,
            replyContent: $("textarea." + replyNumber).val()
        }, function(result){
            showList(pageNum);
            modifyCheck = false;
        });
    });

    $(".register").on("click", function(e){
        e.preventDefault();
        $(this).hide();
        $(".register-form").show();
    });

    $(".cancel").on("click", function(e){
        e.preventDefault();
        $(".register").show();
        $(".register-form").hide();
    });


    // replyService.add({
    //     boardNumber: boardNumber,
    //     replyContent: "JS TEST",
    //     replyWriter: "tester"
    // }, function(result){alert(result);})

    // replyService.getList({
    //     boardNumber: boardNumber
    // }, function(list){
    //     console.log(list);
    // });

    // replyService.read(26, function(reply){console.log(reply);})

    // replyService.remove(24, function(result){alert(result);});

    // replyService.modify({
    //     replyNumber: 26,
    //     replyContent: "JS MODIFIED",
    // }, function(result){alert(result);});

    function goUpdate(){
        location.href = "/board/modify" + params + "&boardNumber=" + boardNumber;
    }

    $("a#goList").click(function(e){
        e.preventDefault();
        location.href = "/board/list" + params;
    })
</script>
</html>

 

 

 

 

'웹 개발 > Spring' 카테고리의 다른 글

[Web_Spring] 20  (0) 2022.07.02
[Web_Spring] 23  (0) 2022.07.01
[Web_Spring] 18  (0) 2022.06.30
[Web_Spring] 17  (0) 2022.06.29
[Web_Spring] 16  (0) 2022.06.28