실습(댓글)
- [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 += " <a class='remove " + reply.replyNumber + "' style='cursor: pointer;'>삭제</a>"
str += "<a class='modifyOk " + reply.replyNumber + "' style='display: none;cursor: pointer;'>수정완료</a>"
str += " <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><</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>></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 |