- 해당 문서는 일반 게시판을 기준으로 작성되었습니다.
- 해당 문서는 일반 게시판의 상세보기 페이지에 댓글창이 위치한다는 가정 하에 작성되었습니다.
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<%-- <jsp:include page="../jsp/weblib.jsp"/> --%>
<title>BoardView</title>
<script type="text/javascript">
let no=${vo.no};
console.log("no: "+no);
let repage=1; //replyProcess.js에서 사용할 page 값 설정
</script>
<script src="/js/datetime.js"></script>
<!-- BoardReplyService를 대신하는 brsvc 객체를 선언하는 js file -->
<script src="/js/boardreply.js"></script>
<!-- brsvc를 호출하여 board reply 관련 기능을 실행하는 js file
+ event 처리 -->
<script src="/js/replyProcess.js"></script>
<!-- 생략 -->
</head>
<body>
<!-- 생략 -->
<div id="reply">
<jsp:include page="boardreply.jsp"/>
</div>
<!-- 생략 -->
</body>
< script type="text/javascript" >
let no=${vo.no};
console.log("no: "+no);
let repage=1;
< /script >
해당 코드는 글 번호에 해당하는 no와 페이지 정보에 해당하는 page를 전역변수로 선언하는 모습이다. 이렇게 선언해두면 하단에 위치하는 js 파일과 boardreply.jsp에서 자유롭게 접근이 가능하다.
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<div class="row" style="margin:50px 0 0 0">
<div class="col-lg-12">
<div class="card">
<div class="card-header">
<strong>reply</strong>
<span class="btn btn-primary float-right" id="replywritebtn" data-toggle="modal" data-target="#replyModal"> 등록</span>
</div>
<div class="card-body">
<ul class="reply">
<li class="left clearfix">
<div>
<div class="header">
<strong class="primary-font"></strong>
<small class="float-right text-muted"></small>
</div>
<p></p>
</div>
</li>
</ul>
</div>
<div class="card-footer">
<ul class="pagination pagination-sm">
<li class="page-item"><a class="page-link" href="#">Previous</a></li>
</ul>
</div>
</div>
</div>
</div>
<!-- The Modal -->
<div class="modal fade" id="replyModal">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<%-- <input type="hidden" name="no" value="${vo.no }"> --%>
<!-- Modal Header -->
<div class="modal-header">
<h4 class="modal-title">Insert Reply</h4>
<button type="button" class="close" data-dismiss="modal">×</button>
</div>
<!-- Modal body -->
<div class="modal-body">
<div class="form-group">
<input type="hidden" id="replyrno">
<!-- 삭제 처리를 위한 span -->
<span id="warningspan"></span>
<!-- 댓글 내용 표출을 위한 textarea -->
<textarea class="form-control" id="replycontent" name="replycontent"></textarea>
</div>
</div>
<!-- Modal footer -->
<div class="modal-footer">
<button class="btn btn-primary" id="okbtn">등록</button>
<button class="btn btn-success" id="reupdatebtn">수정</button>
<button class="btn btn-danger" id="redeletebtn">삭제</button>
</div>
</div>
</div>
</div>
view.jsp가 실행될 때, < div id="reply" >부분에 page 디렉티브와 taglib 디렉티브를 제외한 코드가 페이지에 그대로 삽입된다.
let brsvc={
//댓글 리스트: brsvc.list(page)
"list":function(page, callback, error){
console.log("*** 댓글 리스트 ***");
//page가 넘어오지 않은 경우, 1로 세팅
if(!page) page=1;
console.log("page: "+page);
console.log("no: "+no);
//ajax by json -> getJSON()
$.getJSON(
//boardreplycontroller.list는 pageObject를 파라미터로 받는데,
//po에 선언된 변수라면 uri의 파라미터로 넘겨도 된다. -> page, key가 그 예시이다.
"/boardreply/list.do?page="+page+"&no="+no,
function(data){ //데이터 로딩 성공시 실행하는 함수
console.log(data);
console.log(JSON.stringify(data));
//html tag를 만들어 표시하는 함수
if(callback) callback(data);
}
).fail(function(xhr, status, err){
console.log("*** 댓글 리스트 로딩 중 오류 발생 ***")
console.log("fail()-xhr: "+xhr);
console.log("fail()-status: "+status);
console.log("fail()-error: "+err);
if(error) error();
else alert("! ! ! 댓글 리스트 로딩 중 오류 발생 ! ! !");
});
}
};
자바 프로젝트에서 사용하던 BoardReplyService를 대신하는 javascript file이다. 댓글 처리 서비스를 담당하는 객체를 JSON 형태로 선언한다.
JSON의 key에 메서드명이 위치하고, 실질적인 코드는 value에 function으로 위치한다.
/**
* replyService 객체를 이용한 댓글 처리 코드
*/
//글 보기, 댓글 등록, 댓글 수정, 댓글 삭제시마다 showList가 실행되어야 함
function showList(page){
brsvc.list(page,
//데이터 로딩 성공시 실행되는 함수: html tag 만들어 표시하기
function(data){
//data: map을 JSON 형태로 전달함
let list=data.list;
let str="";
if(list==null||list.length==0){
$(".reply").html("<li>데이터가 존재하지 않습니다.</li>");
$(".pagination").html("");
return;
}
//댓글이 있는 경우, 댓글을 하나씩 li로 만들기
for(let i=0;i<list.length;i++){
str+="<li class=\"left clearfix\" data-rno=\""+list[i].rno+"\">";
str+="<div>";
str+="<div class=\"header\">";
str+="<strong class=\"primary-font\">"+list[i].name+"("+list[i].id+")"+"</strong>";
str+="<small class=\"float-right text-muted\">"+datetime(list[i].writedate)+"</small>";
str+="</div>";
str+="<p class=\"recontent\">"+list[i].content+"</p>";
if(id==list[i].id){
str+="<div class=\"float-right\">";
str+="<small type=\"button\" class=\"reudtbtn\">수정</small>";
str+="  "
str+="<small type=\"button\" class=\"redltbtn\">삭제</small>";
str+="</div>";
}
str+="</div>";
str+="</li>";
str+="<hr>";
}//end of for
$(".reply").html(str);
//pagination
$(".pagination").html(replyPagination(data.po));
}// end of callback
);
};
//일반게시판 글 보기 실행시 실행되는 댓글 리스트
showList(repage);
boardreply.js와 상호작용하는 js 파일이다. boardreply.js에서 JSON 객체로 선언된 brsvc를 호출하고 brsvc의 callback 함수를 선언한다.
두 파일 외의 js 파일은 댓글 기능에 부수적인 역할을 하므로 설명을 생략한다.
//view.jsp
<div id="reply">
<jsp:include page="boardreply.jsp"/>
</div>
//replyProcess.js
function showList(page){
brsvc.list(page,
//데이터 로딩 성공시 실행되는 함수: html tag 만들어 표시하기
function(data){
//data: map을 JSON 형태로 전달함
let list=data.list;
let str="";
if(list==null||list.length==0){
$(".reply").html("<li>데이터가 존재하지 않습니다.</li>");
$(".pagination").html("");
return;
}
//댓글이 있는 경우, 댓글을 하나씩 li로 만들기
for(let i=0;i<list.length;i++){
str+="<li class=\"left clearfix\" data-rno=\""+list[i].rno+"\">";
str+="<div>";
str+="<div class=\"header\">";
str+="<strong class=\"primary-font\">"+list[i].name+"("+list[i].id+")"+"</strong>";
str+="<small class=\"float-right text-muted\">"+datetime(list[i].writedate)+"</small>";
str+="</div>";
str+="<p class=\"recontent\">"+list[i].content+"</p>";
if(id==list[i].id){
str+="<div class=\"float-right\">";
str+="<small type=\"button\" class=\"reudtbtn\">수정</small>";
str+="  "
str+="<small type=\"button\" class=\"redltbtn\">삭제</small>";
str+="</div>";
}
str+="</div>";
str+="</li>";
str+="<hr>";
}//end of for
$(".reply").html(str);
//pagination
$(".pagination").html(replyPagination(data.po));
}// end of callback
);
};
//일반게시판 글 보기 실행시 실행되는 댓글 리스트
showList(repage);
개요에서 설명한 바와 같이 view.jsp 페이지에 접근하면 브라우저는 js 파일을 로드하여 실행한다. replyProcess.js의 showList(repage);는 페이지 로드와 동시에 boardreply.jsp에 목록을 로딩하라는 의미이다.
replyProcess.js의 function showList(page){}는 boardreply.js에서 선언한 JSON 형태의 brsvc 객체의 list 키에 대응되는 function()을 실행하라는 의미이다.
//boardreply.js
let brsvc={
//댓글 리스트: brsvc.list(page)
"list":function(page, callback, error){
//ajax by json -> getJSON()
$.getJSON(
//boardreplycontroller.list는 pageObject를 파라미터로 받는데,
//po에 선언된 변수라면 uri의 파라미터로 넘겨도 된다. -> page, key가 그 예시이다.
"/boardreply/list.do?page="+page+"&no="+no,
function(data){ //데이터 로딩 성공시 실행하는 함수
console.log(data);
console.log(JSON.stringify(data));
//html tag를 만들어 표시하는 함수
if(callback) callback(data);
}
).fail(function(xhr, status, err){
console.log("*** 댓글 리스트 로딩 중 오류 발생 ***")
console.log("fail()-xhr: "+xhr);
console.log("fail()-status: "+status);
console.log("fail()-error: "+err);
if(error) error();
else alert("! ! ! 댓글 리스트 로딩 중 오류 발생 ! ! !");
});
}
boardreply.js에서 list key에 대응되는 function은 위와 같다. $.getJSON은 주로 ajax 통신을 하고, 그 결과 데이터를 JSON 형식으로 받아오고자 할 때 사용된다. 기본적인 형식은 다음과 같다.
$.getJSON(uri, (data), (success))
여기서 data와 success는 선택사항인데 boardreply.js는 data를 생략하고 success만 포함하고 있다. success는 문자 그대로 성공했을 때, 다시 말해 비동기 통신의 요청이 성공했을 때 실행되는 함수를 말한다. 이러한 함수를 콜백 함수라고 한다.
콜백 함수는 결과로 넘어온 데이터를 처리할 때 사용되는 것이 특징이다.
다시 본론으로 돌아오면, $.getJSON은 지정된 uri에서 비동기 통신을 시도하고, 그 결과에 따라 function(data){}를 실행하거나 .fail()이 실행된다.
그럼 uri인 /boardreply/list.do의 동작을 이해하기 위해 아래 코드를 살펴보자
//상단 생략
@Autowired
@Qualifier("BoardReplyServiceImpl")
private BoardReplyService service;
@GetMapping(value="/boardreply/list.do", produces= {
MediaType.APPLICATION_XML_VALUE,
MediaType.APPLICATION_JSON_UTF8_VALUE
})
public ResponseEntity<Map<String, Object>> list(PageObject po, Long no) {
log.info("list()------------");
List<BoardReplyVO> list=service.list(po, no);
log.info("page: "+po.getPage()+", no: "+no);
/*ajax로 댓글리스트를 호출한 곳으로 list와 po(for pagination)를 넘겨야 한다.
그런데, javascript 파일로는 하나의 변수만 넘길 수 있으므로 하나로 묶어야 한다.
따라서 map으로 list와 po를 묶어준다. */
Map<String, Object> map=new HashMap<String, Object>();
map.put("list", list);
map.put("po", po);
log.info("map: "+map);
//responseEntity: ajax를 호출하는 js 파일은 통신의 성공 여부를 판단하지 못한다.
//따라서 임의로 통신 성공 여부를 판단하고 그 상태를 전달하여야 한다.
ResponseEntity<Map<String, Object>> re=new ResponseEntity<Map<String,Object>>(map, HttpStatus.OK);
return re;
}
//하단 생략
/boardreply/list.do?page=page&no=no는 위의 코드와 연결된다. page와 no은 각각을 객체로 두는 po와 no로 파라미터 할당이 이루어진다. 그 값을 바탕으로 BoardService의 list(po, no)가 실행되고 그 결과가 list에 저장된다. 페이징처리가 필요 없다면 list를 전달해도 되지만, 페이징 처리를 위해 PageObjec와 list를 map으로 묶고, 통신코드를 보다 세밀히 제어하기 위해 ResponseEntity에 map과 통신코드를 묶어 return 하는 코드이다.
ajax 통신의 결과로 return된 데이터인 re는 function(data)의 매개변수로 사용되어 callback함수 호출에 사용된다.
여기서 말하는 callback 함수는 replyProcess.js의 아래 부분에 해당한다.
function(data){
//data: map을 JSON 형태로 전달함
let list=data.list;
let str="";
if(list==null||list.length==0){
$(".reply").html("<li>데이터가 존재하지 않습니다.</li>");
return;
}
//댓글이 있는 경우, 댓글을 하나씩 li로 만들기
for(let i=0;i<list.length;i++){
str+="<li class=\"left clearfix\" data-rno=\""+list[i].rno+"\">";
str+="<div>";
str+="<div class=\"header\">";
str+="<strong class=\"primary-font\">"+list[i].name+"("+list[i].id+")"+"</strong>";
str+="<small class=\"float-right text-muted\">"+datetime(list[i].writedate)+"</small>";
str+="</div>";
str+="<p class=\"recontent\">"+list[i].content+"</p>";
if(id==list[i].id){
str+="<div class=\"float-right\">";
str+="<small type=\"button\" class=\"reudtbtn\">수정</small>";
str+="  "
str+="<small type=\"button\" class=\"redltbtn\">삭제</small>";
str+="</div>";
}
str+="</div>";
str+="</li>";
str+="<hr>";
}//end of for
$(".reply").html(str);
}// end of callback
ajax 통신 결과로 전달받은 데이터는 위 함수 실행에 사용된다. html 태그 명령어를 저장할 변수 str을 선언하고, map<>에서 list를 추출하여 list 변수에 저장한다.
선언된 list 변수의 null 여부를 판단하고, null이면 데이터가 존재하지 않는다는 html 태그를 boardreply.jsp의 < ul class="reply" >에 위치시키고 콜백함수에서 벗어난다.
list 변수가 null이 아니면, list의 구성요소인 BoardReplyVO의 수만큼 for문을 돌리며 그 댓글 내용을 띄우는 태그이다.