스프링(Spring) - Ajax 댓글 처리 모듈화
스프링(Spring) - Ajax 댓글 처리 모듈화
reply.js
replyService
/**
Javascript reply ajax Module
*/
// replyService 는 JSON 타입이다.
var replyService = (function(){
// 댓글 추가
function add(reply, callback, error){
console.log("add reply.........");
// 자바에서의 메소드는 저장공간이라는 것
$.ajax({
type: "post",
url: "/replies/new",
data: JSON.stringify(reply), // data는 내가 전달할 데이터이다.
// 내가 전달할 데이터의 타입이 무엇인가. 안에 내장된 내용의 타입을 보내는 것.
contentType: "application/json; charset=utf-8",
success: function(result){
if(callback){
callback(result);
}
},
error: function(xhr, status, er){
if(error){
error(er);
}
}
});
}
return {add: add};
})();
a라는 결과로 어떻게 할 지 정하지 않았기 때문에, 이 함수를 사용한 쪽에서 활용하는 것으로 목적을 둔다.
그리하여 callback 함수를 넣고 리턴한다. 오류가 날 수 있으므로 error를 전달한다.
필요에 따라 사용하되, 순서를 지켜야 한다.
댓글 목록
// 댓글 목록
// 받을 때는 json으로 받는다.
function getList(param, callback, error){
console.log("get List........");
var bno = param.bno;
var page = param.page || 1;
// $.getJSON("", function(){}.fail(function(){}) // 구조
// xml이므로 .json 확장자로 바꾼다.
$.getJSON("/replies/pages/"+ bno + "/"+ page +".json",
function(data){
if(callback){callback(data);}
})
.fail(function(xhr, status, err){
if(error){
error(err);
}
})
}
return {add: add, getList: getList};
})();
댓글 삭제
// 댓글 삭제
function remove(rno, callback, error){
console.log("remove...........");
$.ajax({
type: "delete",
url: "/replies/" + rno,
success: function(result){
if(callback){callback(result);}
},
error: function(xhr, status, err){
if(error){error(err);}
}
});
}
return {add: add, getList: getList, remove: remove};
})();
댓글 수정
// 댓글 수정
function modify(reply, callback, error){
console.log("modify : "+reply.rno);
$.ajax({
type: "PUT",
url: "/replies/" + reply.rno,
data: JSON.stringify(reply),
contentType: "application/json; charset=utf-8",
success: function(result, status, xhr){
if(callback){callback(result);}
},
error: function(xhr, status, err){
if(error){error(err);}
}
});
}
return {add: add, getList: getList, remove: remove, modify: modify};
})();
댓글 조회
// 댓글 조회
function get(rno, callback, error){
$.get("/replies/"+ rno + ".json", function(result){if(callback){callback(result);}})
.fail(function(xhr, status, err){if(error){error(err);}
})
}
return {add: add, getList: getList, remove: remove, modify: modify, get: get};
})();
view 페이지에서 댓글을 뿌려줄 뷰 부분을 수정한다.
get.jsp 수정
<ul class="icons">
<li>
<span class="icon solid fa-envelope"></span>
<strong>댓글</strong>
</li>
</ul>
<a style="width:100%" href="javascript:void(0)" class="button primary small register">댓글 등록</a>
<div class="fields register-form" style="display:none">
<div class="field">
<h4>작성자</h4>
<input type="text" name="replyer" placeholder="Replyer">
</div>
<div class="field">
<h4>댓글</h4>
<textarea rows="6" name="reply" placeholder="Reply" style="resize:none;"></textarea>
</div>
<div class="field regBtn">
<a href="javascript:void(0)" class="button primary small finish">등록</a>
<a href="javascript:void(0)" class="button primary small cancel">취소</a>
</div>
</div>
<ul class="replies">
</ul>
<div class="paging" style="text-align:center;">
</div>
get.jsp에 script 부분
등록과 취소 이벤트 추가
<script>
$(document).ready(function(){
$(".register").on("click", function(e){
e.preventDefault();
$(".register-form").show();
$(this).hide();
});
$(".cancel").on("click", function(e){
e.preventDefault();
$(".register-form").hide();
$(".register").show();
});
});
</script>
script에 댓글 등록 추가
var bno = "${board.bno}";
// 등록버튼을 눌렀을 때
$(".finish").on("click", function(e){
e.preventDefault();
// 필요한 데이터: 댓글 작성자, 댓글
var replyer = $("input[name='replyer']").val();
var reply = $("textarea[name='reply']").val();
if(replyer == "" || reply == "" ) {return;}
replyService.add({bno: bno, reply:reply, replyer:replyer}, function(result){
alert(result);
$("input[name='replyer']").val("Replyer");
$("textarea[name='reply']").val("Reply");
$(".register-form").hide();
$(".register").show();
pageNum = 1;
showList(pageNum);
});
});
추가 확인
showList() 와 showReplyPage() 설계
function showReplyPage(){
var startNum
var endNum
var realEnd
}
// 현재 페이지가 무엇인지 알아야하므로 페이지 번호를 받아와야 한다.
function showList(){
replyService.getList();
}
showList와 showReplyPage
var bno = "${board.bno}";
var replyUL = $(".replies");
var pageNum = 1;
showList(1);
function showReplyPage(replyCnt){
var str = "";
var endNum = Math.ceil(pageNum / 10.0 ) * 10;
var startNum = endNum - 9;
var realEnd = Math.ceil(replyCnt / 10.0);
if(endNum > realEnd ){
endNum = realEnd;
}
var prev = startNum != 1;
var next = endNum * 10 < replyCnt;
if(matchMedia("screen and (max-width:918px)").matches){
if(pageNum != 1 ) {
str += "<a class='changePage' href='"+ (pageNum - 1) +"'><code><</code></a>"
}
str += "<code>"+ pageNum + "</code>";
if(pageNum != realEnd) {
str += "<a class='changePage' href='"+ (pageNum + 1) +"'><code>></code></a>"
}
} else {
if(prev){
str += "<a class='changePage' href='"+ (startNum - 1) +"'><code><</code>"
}
for(let i = startNum; i <= endNum; i++ ){
if(pageNum == i){
str += "<code>" + i +"</code>";
continue;
}
str += "<a class='changePage' href='"+ i +"'><code>" + i + "</code></a>"
}
if(next){
str += "<a class='changePage' href='"+ (endNum + 1) +"'><code>></code></a>"
}
}
replyPaging.html(str); // DOM
}
// 위임
$(".paging").on("click", "a.changePage", function(e){
e.preventDefault();
pageNum = parseInt($(this).attr("href"));
console.log(pageNum)
showList(pageNum);
});
// 현재 페이지가 무엇인지 알아야하므로 페이지 번호를 받아와야 한다.
function showList(page){
replyService.getList({bno:bno, page: page||1}, function(replyCnt, list){
console.log("replyCnt : " + replyCnt);
console.log("list : " + list);
var str = "";
if(list == null || list.length == 0 ){
if(pageNum > 1 ) { // 2페이지에서 하나 남은 댓글을 삭제하면 1페이지로 가야되는데 2페이지로 유지되면서 등록된 댓글이 없다고 나온다.
pageNum -= 1; // 내 페이지를 1개 감소 시키고
showList(pageNum); // 다시 그리기.
}
replyUL.html("등록된 댓글이 없습니다.");
return;
}
for(let i = 0, len = list.length || 0; i < len; i++){
str += "<li data-rno='"+ list[i].rno +"'>";
// data-rno 라는 옵션을 통해서 파일첨부 때 이용할 수 있다.
str += "<strong>" + list[i].replyer + "</strong>";
str += "<p class='reply"+ list[i].rno +"'>" + list[i].reply + "</p>";
// 모든 p태그의 reply라는 클래스 반복문을 돌리기 번거로우니, RNO를 붙여 연결을 시킨다.
str += "<div style='text-align:right;'>";
str += "<a class='modify' href='"+ list[i].rno +"'>수정</a>";
str += "<a class='finish' href='"+ list[i].rno +"' style='display:none;'>수정완료</a>";
str += " ";
str += "<a class='remove' href='"+ list[i].rno +"'>삭제</a>";
str += "</div><div class='line'></div></li>";
}
replyUL.html(str);
showReplyPage(replyCnt);
});
}
- matchMedia는 반응형 함수이다.
내가 데이터베이스에 추가해야할 데이터를 모듈로 보낸다.
모듈로 가서 해당 함수로 실행될 때 전달을 하고 컨트롤러에서 db조회를 하고, 결과가
모듈 ajax success로 콜백하게 된다.
이것을 가지고 get.jsp에서 콜백 함수를 매개변수로 받는 것이다.
ReplyMapper.xml
<select id="getTotal" resultType="_int">
SELECT COUNT(RNO) FROM TBL_REPLY WHERE BNO = #{bno}
</select>
ReplyMapper.java
public List<ReplyVO> getListWithPaging(@Param("cri") Criteria cri, @Param("bno") Long bno);
public int getTotal(Long bno);
ReplyServiceImple.java
@Override
public ReplyPageDTO getListWithPaging(Criteria cri, Long bno) {
log.info("get Reply List of a Board" + bno);
//댓글을 구현하기 위해 필요한 두 개의 요소를 ReplyPageDTO에 전달한다.
return new ReplyPageDTO(mapper.getTotal(bno), mapper.getListWithPaging(cri, bno));
}
ReplyPageDTO.java 추가
package com.koreait.domain;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
/*
* 댓글 목록을 구현할 때 페이징처리도 해야 한다.
* 페이징 처리를 하기 위해서는 해당 게시글의 전체 개수가 필요하므로
* ReplyPageDTO객체를 선언하여 두 가지 데이터를 필드로 구현해 놓는다.
* */
public class ReplyPageDTO {
private int replyCnt;//해당 게시글의 총 댓글 수
private List<ReplyVO> list;//해당 게시글에서 페이지에 맞는 댓글 목록(amount는 10이다)
}
ReplyService.Interface 수정
public ReplyPageDTO getListWithPaging(Criteria cri, Long bno);
ReplyController 수정
//게시글 댓글 전체 조회
@GetMapping(value="/pages/{bno}/{page}", produces= {MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_UTF8_VALUE})
//서비스에 있는 getListWithPaging은 댓글 목록과 댓글 전체 개수 두 개를 갖고 있는 ReplyPageDTO 타입이다.
public ResponseEntity<ReplyPageDTO> getList(@PathVariable("bno") Long bno, @PathVariable("page") int page){
log.info("getList..........");
Criteria cri = new Criteria(page, 10);
log.info(cri);
return new ResponseEntity<ReplyPageDTO>(service.getListWithPaging(cri, bno), HttpStatus.OK);
}
reply.js 수정
function getList(param, callback, error){
console.log("get List........");
var bno = param.bno;
var page = param.page || 1;
// $.getJSON("", function(){}.fail(function(){}) // 구조
// xml이므로 .json 확장자로 바꾼다.
$.getJSON("/replies/pages/"+ bno + "/"+ page +".json",
function(data){
if(callback){callback(data.replyCnt, data.list);}
})
.fail(function(xhr, status, err){
if(error){
error(err);
}
})
}
테스트
페이징 처리까지 잘 나오는것을 확인할 수 있다.
댓글 수정과 삭제
javascript
// 댓글 삭제
// 삭제 버튼 클릭 시 해당 댓글 번호를 가져와 삭제하기
$(".replies").on("click", "a.remove", function(e){ // 이벤트 위임
e.preventDefault();
var rnoValue = $(this).attr("href");
replyService.remove(rnoValue, function(result){
alert(result);
showList(pageNum); // 삭제를 했으면 다시 보여주도록 하기.(새로고침 없이)
});
});
var check = false; // 다른 댓글 수정 못하게 (동시에) 플래그를 만들어야 한다.
// 댓글 수정
$(".replies").on("click", "a.modify", function(e){ // 이벤트 위임
// 1. 수정완료 버튼
// 2. p태그를 textarea로 변경 (기존 p태그의 내용을 textarea로 옮겨야 한다.)
e.preventDefault();
if(check){alert("수정중인 댓글이 있습니다."); return;}
var rnoValue = $(this).attr("href");
var reply = $(".reply" + rnoValue); // p태그
reply.html("<textarea class='"+ rnoValue +"'>"+ reply.text() + "</textarea>"); // textarea로 변경
$(this).hide(); // 수정 버튼 숨김
var finishs = $(".finish");
for(let i = 0; i < finishs.length; i++) {
if($(finishs[i]).attr("href") == rnoValue ){
$(finishs[i]).show(); // 수정완료 버튼
check = true;
break;
}
}
});
$(".replies").on("click", "a.finish", function(e){
e.preventDefault();
var rnoValue = $(this).attr("href");
var newReply = $("." + rnoValue).val(); //사용자가 랜더링(브라우저 해석) 다 되고나서 가져와야하기 때문이다.
replyService.modify({rno:rnoValue, reply:newReply}, function(result){
alert(result);
check = false;
showList(pageNum);
});
});
전체 코드
get.jsp
get.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<script>
//테스트용
// $(document).ready(function(){
//console.log(replyService.name);
/* console.log("==========");
console.log("JS TEST"); */
/* replyService.modify({rno : 21, reply : "Modified Reply"}, function(result){
alert("MODIFY : " + result);
});
*/
/* replyService.remove(41, function(result){
if(result == "success"){
alert("REMOVED");
}
},function(err){
alert("ERROR...");
}) */
/* replyService.getList({bno:"${board.bno}", page: 1},
function(data){
console.log(data);
}); */
/* replyService.add({reply:"JS TEST", replyer:"tester", bno:"${board.bno}"},
function(result){
alert("RESULT : " + result);
}); */
/* replyService.get(21, function(result){
console.log(result);
}) */
// })
</script>
<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="/resources/assets/css/main.css" />
<style>
.line{border-bottom: 1px solid #ff8b77;}
</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>
<h3><a href="/board/list${cri.getListLink()}" class="button small">목록 보기</a></h3>
<div class="content">
<div class="form">
<form action="/board/remove">
<input type="hidden" name="pageNum" value="${cri.pageNum}">
<input type="hidden" name="amount" value="${cri.amount}">
<input type="hidden" name="type" value="${cri.type}">
<input type="hidden" name="keyword" value="${cri.keyword}">
<div class="fields">
<div class="field">
<h4>번호</h4>
<input name="bno" type="text" value="${board.bno}" readonly/>
</div>
<div class="field">
<h4>제목</h4>
<input name="title" type="text" value="${board.title}" readonly/>
</div>
<div class="field">
<h4>내용</h4>
<textarea name="content" rows="6" style="resize:none" readonly>${board.content}</textarea>
</div>
<div class="field">
<h4>작성자</h4>
<input name="writer" type="text" value="${board.writer}" readonly/>
</div>
</div>
<ul class="actions special">
<li>
<input type="button" class="button" value="수정" onclick="location.href='/board/modify${cri.getListLink()}&bno=${board.bno}'"/>
<input type="submit" class="button" value="삭제"/>
</li>
</ul>
<ul class="icons">
<li>
<span class="icon solid fa-envelope"></span>
<strong>댓글</strong>
</li>
</ul>
<a style="width:100%" href="javascript:void(0)" class="button primary small register">댓글 등록</a>
<div class="fields register-form" style="display:none">
<div class="field">
<h4>작성자</h4>
<input type="text" name="replyer" placeholder="Replyer">
</div>
<div class="field">
<h4>댓글</h4>
<textarea rows="6" name="reply" placeholder="Reply" style="resize:none;"></textarea>
</div>
<div class="field regBtn">
<a href="javascript:void(0)" class="button primary small finish">등록</a>
<a href="javascript:void(0)" class="button primary small cancel">취소</a>
</div>
</div>
<ul class="replies">
</ul>
<div class="paging" style="text-align:center;">
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<!-- Scripts -->
<script src="/resources/assets/js/jquery.min.js"></script>
<script src="/resources/assets/js/jquery.dropotron.min.js"></script>
<script src="/resources/assets/js/browser.min.js"></script>
<script src="/resources/assets/js/breakpoints.min.js"></script>
<script src="/resources/assets/js/util.js"></script>
<script src="/resources/assets/js/main.js"></script>
<script src="/resources/assets/js/reply.js"></script>
</body>
<script>
$(document).ready(function(){
var bno = "${board.bno}";
var replyUL = $(".replies");
var replyPaging = $(".paging");
var pageNum = 1;
showList(pageNum);
$(".register").on("click", function(e){
e.preventDefault();
$(".register-form").show();
$(this).hide();
});
$(".cancel").on("click", function(e){
e.preventDefault();
$(".register-form").hide();
$(".register").show();
});
// 등록버튼을 눌렀을 때
$(".finish").on("click", function(e){
e.preventDefault();
// 필요한 데이터: 댓글 작성자, 댓글
var replyer = $("input[name='replyer']").val();
var reply = $("textarea[name='reply']").val();
if(replyer == "" || reply == "" ) {return;}
replyService.add({bno: bno, reply:reply, replyer:replyer}, function(result){
alert(result);
$("input[name='replyer']").val("Replyer");
$("textarea[name='reply']").val("Reply");
$(".register-form").hide();
$(".register").show();
pageNum = 1;
showList(pageNum);
});
});
function showReplyPage(replyCnt){
var str = "";
var endNum = Math.ceil(pageNum / 10.0 ) * 10;
var startNum = endNum - 9;
var realEnd = Math.ceil(replyCnt / 10.0);
if(endNum > realEnd ){
endNum = realEnd;
}
var prev = startNum != 1;
var next = endNum * 10 < replyCnt;
if(matchMedia("screen and (max-width:918px)").matches){
if(pageNum != 1 ) {
str += "<a class='changePage' href='"+ (pageNum - 1) +"'><code><</code></a>"
}
str += "<code>"+ pageNum + "</code>";
if(pageNum != realEnd) {
str += "<a class='changePage' href='"+ (pageNum + 1) +"'><code>></code></a>"
}
} else {
if(prev){
str += "<a class='changePage' href='"+ (startNum - 1) +"'><code><</code>"
}
for(let i = startNum; i <= endNum; i++ ){
if(pageNum == i){
str += "<code>" + i +"</code>";
continue;
}
str += "<a class='changePage' href='"+ i +"'><code>" + i + "</code></a>"
}
if(next){
str += "<a class='changePage' href='"+ (endNum + 1) +"'><code>></code></a>"
}
}
replyPaging.html(str); // DOM
}
// 위임
$(".paging").on("click", "a.changePage", function(e){
e.preventDefault();
pageNum = parseInt($(this).attr("href"));
console.log(pageNum)
showList(pageNum);
});
// 현재 페이지가 무엇인지 알아야하므로 페이지 번호를 받아와야 한다.
function showList(page){
replyService.getList({bno:bno, page: page||1}, function(replyCnt, list){
console.log("replyCnt : " + replyCnt);
console.log("list : " + list);
var str = "";
if(list == null || list.length == 0 ){
if(pageNum > 1 ) { // 2페이지에서 하나 남은 댓글을 삭제하면 1페이지로 가야되는데 2페이지로 유지되면서 등록된 댓글이 없다고 나온다.
pageNum -= 1; // 내 페이지를 1개 감소 시키고
showList(pageNum); // 다시 그리기.
}
replyUL.html("등록된 댓글이 없습니다.");
return;
}
for(let i = 0, len = list.length || 0; i < len; i++){
str += "<li data-rno='"+ list[i].rno +"'>";
// data-rno 라는 옵션을 통해서 파일첨부 때 이용할 수 있다.
str += "<strong>" + list[i].replyer + "</strong>";
str += "<p class='reply"+ list[i].rno +"'>" + list[i].reply + "</p>";
// 모든 p태그의 reply라는 클래스 반복문을 돌리기 번거로우니, RNO를 붙여 연결을 시킨다.
str += "<div style='text-align:right;'>";
str += "<a class='modify' href='"+ list[i].rno +"'>수정</a>";
str += "<a class='finish' href='"+ list[i].rno +"' style='display:none;'>수정완료</a>";
str += " ";
str += "<a class='remove' href='"+ list[i].rno +"'>삭제</a>";
str += "</div><div class='line'></div></li>";
}
replyUL.html(str);
showReplyPage(replyCnt);
});
}
// 댓글 삭제
// 삭제 버튼 클릭 시 해당 댓글 번호를 가져와 삭제하기
$(".replies").on("click", "a.remove", function(e){ // 이벤트 위임
e.preventDefault();
var rnoValue = $(this).attr("href");
replyService.remove(rnoValue, function(result){
alert(result);
showList(pageNum); // 삭제를 했으면 다시 보여주도록 하기.(새로고침 없이)
});
});
var check = false; // 다른 댓글 수정 못하게 (동시에) 플래그를 만들어야 한다.
// 댓글 수정
$(".replies").on("click", "a.modify", function(e){ // 이벤트 위임
// 1. 수정완료 버튼
// 2. p태그를 textarea로 변경 (기존 p태그의 내용을 textarea로 옮겨야 한다.)
e.preventDefault();
if(check){alert("수정중인 댓글이 있습니다."); return;}
var rnoValue = $(this).attr("href");
var reply = $(".reply" + rnoValue); // p태그
reply.html("<textarea class='"+ rnoValue +"'>"+ reply.text() + "</textarea>"); // textarea로 변경
$(this).hide(); // 수정 버튼 숨김
var finishs = $(".finish");
for(let i = 0; i < finishs.length; i++) {
if($(finishs[i]).attr("href") == rnoValue ){
$(finishs[i]).show(); // 수정완료 버튼
check = true;
break;
}
}
});
// 수정완료
$(".replies").on("click", "a.finish", function(e){
e.preventDefault();
var rnoValue = $(this).attr("href");
var newReply = $("." + rnoValue).val(); //사용자가 랜더링(브라우저 해석) 다 되고나서 가져와야하기 때문이다.
if(newReply == "" ) {return;}
replyService.modify({rno:rnoValue, reply:newReply}, function(result){
alert(result);
check = false;
showList(pageNum);
});
});
});
</script>
</html>
reply.js모듈
reply.js 모듈
/**
Javascript reply ajax Module
*/
// replyService 는 JSON 타입이다.
var replyService = (function(){
// a라는 결과로 어떻게 할지 모르기 때문에, 이 함수를 사용한 쪽에서 활용하는 것으로 목적을 둔다.
// 그리하여 callback 함수를 넣고 리턴한다. 오류가 날 수 있으므로 error를 전달한다.
// 필요에 따라 사용하되, 순서는 지키면 된다.
// 댓글 추가
function add(reply, callback, error){
console.log("add reply.........");
// 자바에서의 메소드는 저장공간이라는 것
$.ajax({
type: "post",
url: "/replies/new",
data: JSON.stringify(reply), // data는 내가 전달할 데이터이다.
// 내가 전달할 데이터의 타입이 무엇인가. 안에 내장된 내용의 타입을 보내는 것.
contentType: "application/json; charset=utf-8",
success: function(result){
if(callback){
callback(result);
}
},
error: function(xhr, status, er){
if(error){
error(er);
}
}
});
}
// 댓글 목록
// 받을 때는 json으로 받는다.
function getList(param, callback, error){
console.log("get List........");
var bno = param.bno;
var page = param.page || 1;
// $.getJSON("", function(){}.fail(function(){}) // 구조
// xml이므로 .json 확장자로 바꾼다.
$.getJSON("/replies/pages/"+ bno + "/"+ page +".json",
function(data){
if(callback){callback(data.replyCnt, data.list);}
})
.fail(function(xhr, status, err){
if(error){
error(err);
}
})
}
// 댓글 삭제
function remove(rno, callback, error){
console.log("remove...........");
$.ajax({
type: "delete",
url: "/replies/" + rno,
success: function(result){
if(callback){callback(result);}
},
error: function(xhr, status, err){
if(error){error(err);}
}
});
}
// 댓글 수정
function modify(reply, callback, error){
console.log("modify : "+reply.rno);
$.ajax({
type: "PUT",
url: "/replies/" + reply.rno,
data: JSON.stringify(reply),
contentType: "application/json; charset=utf-8",
success: function(result, status, xhr){
if(callback){callback(result);}
},
error: function(xhr, status, err){
if(error){error(err);}
}
});
}
// 댓글 조회
function get(rno, callback, error){
$.get("/replies/"+ rno + ".json", function(result){if(callback){callback(result);}})
.fail(function(xhr, status, err){if(error){error(err);}
})
}
return {add: add, getList: getList, remove: remove, modify: modify, get: get};
})();
ReplyController.java
package com.koreait.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.koreait.domain.Criteria;
import com.koreait.domain.ReplyPageDTO;
import com.koreait.domain.ReplyVO;
import com.koreait.service.ReplyService;
import lombok.Setter;
import lombok.extern.log4j.Log4j;
@RequestMapping("/replies/")
@RestController // 각 메소드의 리턴은 자동으로 view resolver로 가지 않는다.
@Log4j
//@AllArgsConstructor
public class ReplyController {
@Setter(onMethod_ = @Autowired)
private ReplyService service;
// 댓글 등록
/*
* 브라우저에서 JSON 타입으로 데이터를 전송하고 서버에서는 댓글의 처리 결과에 따라 문자열로 결과를 리턴한다.
* consumes : Ajax를 통해 전달받은 데이터의 타입 ( 외부에서 전달한 타입 )
* produces : Ajax의 success: function(result)에 있는 result로 전달할 데이터의 타입 ( 외부로 전달할 타입 )
*/
// @ResponseBody : @Controller에서 Body를 응답하기 위해서는 (viewResolver를 가지 않게 하기 위해서) 사용된다.
// 한글이 안깨지기 하기 위해서는 text/pain; charset=utf-8를 작성한다.
@PostMapping(value = "/new", consumes = "application/json", produces ="text/plain; charset=utf-8")
// json 방식 데이터만 처리하도록 하고 문자열을 반환하도록 설계,
// ResponseEntity : 서버의 상태 코드, 응답 메시지등을 담을 수 있는 타입.
// create의 파라미터 @RequestBody를 적용해 JSON 데이터를 ReplyVO 타입으로 반환하도록 지정.
// @RequestBody : 외부에서 전달받은 데이터를 parse (파싱) 해주는 속성이다.
public ResponseEntity<String> create(@RequestBody ReplyVO reply) {
log.info("ReplyVO: " + reply);
int insertCount = service.register(reply);
log.info("Reply INSERT COUNT : " + insertCount);
return insertCount == 1 ? new ResponseEntity<> ("댓글 추가 성공", HttpStatus.OK)
: new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
// create는 내부적으로 ReplyServiceImple을 호출해 register를 부르고 댓글이 추가된 숫자를 확인해서 브라우저에
// 200 = OK 혹은 500 = ERROR를 반환하도록 한다.
}
// 게시글 댓글 전체 조회
@GetMapping(value="/pages/{bno}/{page}", produces = {MediaType.APPLICATION_XML_VALUE,
MediaType.APPLICATION_JSON_UTF8_VALUE
})
// 서비스에 있는 getListWithPaging은 댓글 목록과 댓글 전체 개수 두 개를 갖고있는 ReplyPageDTO 타입이다.
// uri에다 {} 중괄호가 들어가면 변수 선언이다.
public ResponseEntity<ReplyPageDTO> getList(
// uri로 넘겨 매핑하기 위해 어노테이션 @PathVariable을 사용한다.
@PathVariable("page") int page,
@PathVariable("bno") Long bno) {
log.info("getList..........");
Criteria cri = new Criteria(page, 10);
// new를 한다고 하여 의존성에 문제가 생기는것이 아니다. 클래스와 클래스간이 의존성이며, 전역변수 사용에 주입을 하는것이다.
// 메소드 안에서는 의존성 주입(DI)가 되지않기 때문이다.
// 그렇기 때문에 전역변수에만 주입하는 것이다.
// 메소드 안에서는 new로 할당한다.
log.info(cri);
return new ResponseEntity<ReplyPageDTO>(service.getListWithPaging(cri, bno), HttpStatus.OK);
}
// 댓글 조회
@GetMapping(value="/{rno}", produces = {
MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_UTF8_VALUE
})
public ResponseEntity<ReplyVO> get(@PathVariable("rno") Long rno) {
log.info("get....... : "+rno);
return new ResponseEntity<>(service.get(rno), HttpStatus.OK);
}
// 댓글 수정
// PUT :자원 전체 수정, 자원 내 모든 필드를 전달해야 함, 일부만 전달할 경우 전달되지 않은 필드는 모두 초기화 처리가 된다.
// PATCH : 자원 일부 수정, 수정할 필드만 전송
// @RequestMapping을 사용하는 이유는 여러개의 메소드를 받기 위해.
@RequestMapping(method = {RequestMethod.PUT, RequestMethod.PATCH},
value = "/{rno}",
consumes = "application/json",
produces = {MediaType.TEXT_PLAIN_VALUE})
public ResponseEntity<String> modify(@RequestBody ReplyVO reply, @PathVariable("rno") Long rno){
reply.setRno(rno);
log.info("rno : " + rno);
log.info("modify : " + reply);
return service.modify(reply) == 1
? new ResponseEntity<String> ("success", HttpStatus.OK)
: new ResponseEntity<String>(HttpStatus.INTERNAL_SERVER_ERROR);
}
// 굳이 DeleteMapping과 PATCH를 하는 이유는 사용할 수 있는 메소드가 여러개 생기고,
// 외부에서 사용자가 전달할 데이터가 무엇인지 검사할 수 있다.
// 목적에 맞는 어노테이션을 쓰는 방법도 하나의 좋은 방법이다.
@DeleteMapping(value="/{rno}", produces = {MediaType.TEXT_PLAIN_VALUE})
public ResponseEntity<String> remove(@PathVariable("rno") Long rno) {
log.info("remove ... : " +rno);
return service.remove(rno) == 1
? new ResponseEntity<String>("success", HttpStatus.OK)
: new ResponseEntity<String>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
ReplyMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.koreait.mapper.ReplyMapper">
<insert id="insert">
INSERT INTO TBL_REPLY (RNO, BNO, REPLY, REPLYER)
VALUES(SEQ_REPLY.NEXTVAL, #{bno}, #{reply}, #{replyer})
</insert>
<select id="read" resultType="com.koreait.domain.ReplyVO">
SELECT RNO, BNO, REPLY, REPLYER, REPLYDATE, UPDATEDATE FROM TBL_REPLY
WHERE RNO = #{rno}
</select>
<select id="getListWithPaging" resultType="com.koreait.domain.ReplyVO">
<![CDATA[
SELECT RNO, BNO, REPLY, REPLYER, REPLYDATE, UPDATEDATE
FROM
(
SELECT /*+ INDEX_DESC(TBL_REPLY PK_REPLY)*/
ROWNUM RN, RNO, BNO, REPLY, REPLYER, REPLYDATE, UPDATEDATE
FROM TBL_REPLY
WHERE BNO = #{bno}
AND ROWNUM <= #{cri.pageNum} * #{cri.amount}
)
WHERE RN > (#{cri.pageNum} - 1) * #{cri.amount}
]]>
</select>
<delete id="delete">
DELETE FROM TBL_REPLY WHERE RNO = #{rno}
</delete>
<update id="update">
UPDATE TBL_REPLY
SET REPLY = #{reply}, UPDATEDATE = SYSDATE
WHERE RNO = #{rno}
</update>
<select id="getTotal" resultType="_int">
SELECT COUNT(RNO) FROM TBL_REPLY WHERE BNO = #{bno}
</select>
</mapper>
'⚙️ Backend > 스프링(Spring) Framework' 카테고리의 다른 글
[03] Spring 프레임워크의 이론적인 설명 - 스프링 웹 프로젝트 (0) | 2021.05.31 |
---|---|
스프링 - 코드로 배우는 스프링 웹 프로젝트-개정판.pptx (0) | 2021.05.26 |
스프링(Spring) - 컨트롤러 result 보낼 때 한글 깨짐 현상 (0) | 2021.05.24 |
스프링 - Ajax 댓글 처리 구현 (0) | 2021.05.17 |
스프링 - 검색 페이징 처리 (0) | 2021.05.14 |
댓글