댓글 기능들을 구현하기에 앞서 MVN Repository에서 필요한 라이브러리를 찾아
Eclipse에서 WEB-INF > lib에 라이브러리들을 넣어둔다.
필요한 라이브러리는 json-simple-1.1.1 이며, MVN Repository의 링크는 다음과 같다.
라이브러리 추가를 완료했다면 댓글 생성을 위해서 DB에 comment table을 생성한다.
sql.sql
/* ... (기존 코드) */
-- 2023-12-12
CREATE TABLE comment (
cno INT AUTO_INCREMENT,
bno INT NOT NULL,
-- 회원만 쓰게 하려면 default 쓰지 말고 not null
writer VARCHAR(100) NOT NULL,
content VARCHAR(1000) NOT NULL,
regdate DATETIME DEFAULT NOW(),
PRIMARY KEY(cno)
);
comment table 구조를 참조하여 domian package에 CommentVO class를 생성한다.
CommentVO.java
package domain;
public class CommentVO {
private int cno;
private int bno;
private String writer;
private String content;
private String regdate;
public CommentVO() {}
// post : bno, writer, content
public CommentVO(int bno, String writer, String content) {
this.bno = bno;
this.writer = writer;
this.content = content;
}
// modify : cno, content
public CommentVO(int cno, String content) {
this.cno = cno;
this.content = content;
}
// 전부 다
public CommentVO(int cno, int bno, String writer, String content, String regdate) {
this.cno = cno;
this.bno = bno;
this.writer = writer;
this.content = content;
this.regdate = regdate;
}
public int getCno() {
return cno;
}
public void setCno(int cno) {
this.cno = cno;
}
public int getBno() {
return bno;
}
public void setBno(int bno) {
this.bno = bno;
}
public String getWriter() {
return writer;
}
public void setWriter(String writer) {
this.writer = writer;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getRegdate() {
return regdate;
}
public void setRegdate(String regdate) {
this.regdate = regdate;
}
@Override
public String toString() {
return "CommentVO [cno=" + cno + ", bno=" + bno + ", writer=" + writer + ", content=" + content + ", regdate="
+ regdate + "]";
}
}
CommentVO alias를 cvo로 설정하기 위해서 MybatisConfig.xml에 코드를 추가로 작성한다.
겸사겸사 commentMapper.xml까지 선언해주자.
MybatisConfig.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases> <!-- 여러개 생성 가능 / 없는 것을 만들 순 없음 -->
<typeAlias type="domain.MemberVO" alias="mvo"/>
<typeAlias type="domain.BoardVO" alias="bvo"/>
<typeAlias type="domain.PagingVO" alias="pgvo"/>
<typeAlias type="domain.CommentVO" alias="cvo"/>
</typeAliases>
<environments default="development">
<!-- ... (기존 코드) -->
<mappers>
<mapper resource="mapper/memberMapper.xml"/>
<mapper resource="mapper/boardMapper.xml"/>
<mapper resource="mapper/commentMapper.xml"/>
</mappers>
</configuration>
mapper package에 commentMapper.xml 파일도 미리 생성하여 기본 구조만 먼저 작성한다.
기본적인 구조는 https://mybatis.org/mybatis-3/ko/getting-started.html 를 참고해서 작성했다.
commentMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="CommentMapper">
</mapper>
controller package에 CommentController를 servlet으로 생성하되 URL Mappings은 /cmt/*로 설정하고
method stubs는 service에도 체크해준다.
그 후, 모든 Controller들이 공통적으로 사용했던 코드들을 작성한다.
Controller 뿐만 아니라 Server / ServerImpl / DAO / DAOImpl 모두 공통적으로 사용했던 코드들이
아직도... 아.직.도 이해가 되지 않는다면 https://rlog0918.tistory.com/177를 참고하여
member부터 제대로 이해하고 오는 것을 추천한다.
CommentController는 비동기식으로 작업이 진행되기 때문에 contentType는 JSP 화면으로 갈 때 설정한다.
기존에 있는 게시물에 댓글이 붙게 되는 형태이므로 rdp와 destPage도 필요하지 않다.
// CommentController에서는 설정안함
private RequestDispatcher rdp;
private String destPage;
response.setContentType("text/html; charset=UTF-8");
CommentController.java
package controller;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import service.CommentSerivceImpl;
import service.CommentService;
@WebServlet("/cmt/*")
public class CommentController extends HttpServlet {
private static final long serialVersionUID = 1L;
private static final Logger log = LoggerFactory.getLogger(CommentController.class);
// rdp와 destPage는 필요없다.
private CommentService csv;
private int isOk;
public CommentController() {
csv = new CommentSerivceImpl();
}
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
// response.setContentType("text/html; charset=UTF-8"); 는 JSP 화면으로 갈 때 설정
// 비동기식에서는 설정 안함
String uri = request.getRequestURI();
log.info(">>> 경로 >>> {}", uri); // cmt/list/370
String pathUri = uri.substring("/cmt/".length()); // post, list/370
String path = pathUri;
String pathVar = "";
if(pathUri.contains("/")) {
path = pathUri.substring(0, pathUri.lastIndexOf("/")); // list
pathVar = pathUri.substring(pathUri.lastIndexOf("/")+1);
}
log.info(">>> path >>> {}", path);
log.info(">>> pathVar >>> {}" , pathVar);
switch(path) {
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
service(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
service(request, response);
}
}
CommentController는 다른 Controller처럼 JSP 화면에서 데이터를 주고 받고 하는 것이 아니라
이미 존재하고 있는 JSP화면에서 JS를 이용해서 기능이 돌아가야한다.
그러므로 resources 폴더에 board_detail.js 파일을 생성하여
Controller와 주거니 받거니 할 수 있게 해주어야 한다.
JS가 제일 먼저 해야하는 일은 controller에서 list를 달라고 요청하기 위해서 await 키워드를 사용하여
비동기적으로 서버 응답을 기다린다.
서버에서 받아온 응답은 json형태로 변환하여 json객체를 return한다.
// Controller에서 list 달라고 요청
async function getCommentListFromServer(bno) {
try {
const resp = await fetch("/cmt/list/"+bno); // cmt / list /317
const result = await resp.json(); // '[{..}, {..}, {..}]'
return result;
} catch(error) {
console.log(error);
}
}
댓글 리스트들을 담은 result를 화면에 뿌리기 위해서는 댓글 객체 하나하나의 div가 필요하다.
앞서 말했다시피 comment는 별도의 JSP 화면에서 데이터를 주고 받는 것이 아니기 때문에
JS에서 HTML 구조를 직접 만들어야 한다.
function spreadComentList(result) { // result => 댓글 리스트
console.log("comment List >> " + result);
let div = document.getElementById("commentLine");
div.innerHTML = ""; // 원래 만들어뒀던 구조 지우기
for(let i = 0; i < result.length; i++) {
let html = `<div>`;
html += `<div>${result[i].cno}, ${result[i].bno}, ${result[i].writer}, ${result[i].regdate}</div>`;
html += `<div>`;
html += `<input type="text" class="cmtText" value="${result[i].content}">`;
if(result[i].writer == userId) {
html += `<button type ="button" data-cno="${result[i].cno}" class="cmtModBtn">수정</button>`;
html += `<button type ="button" data-cno="${result[i].cno}" class="cmtDelBtn">삭제</button><br>`;
}
html += `</div></div><br><hr>`;
div.innerHTML += html; // 각 댓글 객체를 누적해서 담기
}
}
then method를 사용해서 댓글 목록이 성공적으로 받아지면 실행할 코드 블록을 정의한다.
result라는 변수에 서버에서 받아온 댓글 목록을 저장하고
바로 앞전에 작성한 spreadComentList(result) 함수를 호출해서 댓글 목록을 화면에 뿌린다.
function printCommentList(bno) {
getCommentListFromServer(bno).then(result => { // cmtList
console.log(result);
if(result.length > 0) {
spreadComentList(result);
} else {
let div = document.getElementById("commentLine");
div.innerHTML = `<div>comment가 없습니다.</div>`;
}
});
}
postCommentToServer(comData) 함수는 서버에 전송할 URL을 /cmt/post로 설정한다.
그럼 이 설정된 값을 Comment Controller가 switch(path)에서 case post를 찾아
요청을 진행하게 되는 것이다.
클라이언트(브라우저)의 HTTP 요청에 필요한 구성은 config에 정의한다.
config의 header는 controller에서 선언하지 않았던 Content-Type를 여기서 선언하여
전송되는 데이터의 형식을 JSON 형식 데이터를 사용한다고 명시한다.
그러므로 요청의 부가적인 설정이나 정보를 담고 있다고 생각하면 된다.
body는 주로 post 요청과 함께 전송되는데 cmtData 객체를 JSON 형식의 문자열로 변환한 뒤, 클라이언트의 HTTP 요청에 필요한 데이터를 준다.
즉, 실제로 서버로 전송되는 데이터를 담고 있다.
fetch 함수는 서버에 요청을 보내고 그에 대한 응답을 비동기적으로 기다린다.
서버 응답에서 텍스트 데이터를 추출해서 resp.text()를 사용한다.
rest.text()는 HTTP 응답이 성공여부를 나타내는 값으로
우리가 사용해왔던 isOk와 하는 역할이 똑같다고 생각하면 된다.
async function postCommentToServer(cmtData) {
try {
const url = "/cmt/post";
const config = {
// method, headers, body (body가 없다면 headers도 없다 / get method는 config 안씀 / but 데이터를 보내야 해서 method, header, body 다 필요)
method : 'post',
headers : {
'Content-Type' : 'application/json; charset=utf-8'
},
body : JSON.stringify(cmtData)
};
const resp = await fetch(url, config);
const result = await resp.text(); // isOk 값을 리턴
return result;
} catch (error) {
console.log(error);
}
}
board_detail.js
console.log("comment js 시작합니다.");
console.log("[" + bnoVal + "]번 게시물");
// Controller에서 list 달라고 요청
async function getCommentListFromServer(bno) {
try {
const resp = await fetch("/cmt/list/"+bno); // cmt / list /317
const result = await resp.json(); // '[{..}, {..}, {..}]'
return result;
} catch(error) {
console.log(error);
}
}
// 댓글 객체 생성 & 누적해서 list로 => html 구조를 직접 생성
function spreadComentList(result) { // result => 댓글 리스트
console.log("comment List >> " + result);
let div = document.getElementById("commentLine");
div.innerHTML = ""; // 원래 만들어뒀던 구조 지우기
for(let i = 0; i < result.length; i++) {
let html = `<div>`;
html += `<div>${result[i].cno}, ${result[i].bno}, ${result[i].writer}, ${result[i].regdate}</div>`;
html += `<div>`;
html += `<input type="text" class="cmtText" value="${result[i].content}">`;
if(result[i].writer == userId) {
html += `<button type ="button" data-cno="${result[i].cno}" class="cmtModBtn">수정</button>`;
html += `<button type ="button" data-cno="${result[i].cno}" class="cmtDelBtn">삭제</button><br>`;
}
html += `</div></div><br><hr>`;
div.innerHTML += html; // 각 댓글 객체를 누적해서 담기
}
}
// 댓글 뿌리기
function printCommentList(bno) {
getCommentListFromServer(bno).then(result => { // cmtList
console.log(result);
if(result.length > 0) {
spreadComentList(result);
} else {
let div = document.getElementById("commentLine");
div.innerHTML = `<div>댓글이 없습니다.</div>`;
}
});
}
// 댓글 등록
async function postCommentToServer(cmtData) {
try {
const url = "/cmt/post";
const config = {
// method, headers, body (body가 없다면 headers도 없다 / get method는 config 안씀 / but 데이터를 보내야 해서 method, header, body 다 필요)
method : 'post',
headers : {
'Content-Type' : 'application/json; charset=utf-8'
},
body : JSON.stringify(cmtData)
};
const resp = await fetch(url, config);
const result = await resp.text(); // isOk 값을 리턴
return result;
} catch (error) {
console.log(error);
}
}
document.getElementById('cmtAddBtn').addEventListener('click',()=>{
const cmtText = document.getElementById('cmtText').value;
if(cmtText == null || cmtText == ''){
alert('댓글을 입력해주세요.');
return false;
} else {
// 댓글등록
let cmtData = {
bno : bnoVal,
writer : document.getElementById('cmtWriter').value,
content : cmtText
};
// 댓글 등록 비동기 통신 호출
postCommentToServer(cmtData).then(result => {
console.log(result);
if(result > 0) {
alert('댓글등록 성공~!!');
document.getElementById('cmtText').value = '';
}
// 댓글출력
printCommentList(bnoVal);
})
}
})
이제 CommentController 작성을 시작으로 mapper까지 순차적으로 진행하면 된다.
CommentController.java
package controller;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import domain.CommentVO;
import service.CommentSerivceImpl;
import service.CommentService;
@WebServlet("/cmt/*")
public class CommentController extends HttpServlet {
private static final long serialVersionUID = 1L;
private static final Logger log = LoggerFactory.getLogger(CommentController.class);
// rdp와 destPage는 필요없다.
private CommentService csv;
private int isOk;
public CommentController() {
csv = new CommentSerivceImpl();
}
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
// response.setContentType("text/html; charset=UTF-8"); 는 JSP 화면으로 갈 때 설정
// 비동기식에서는 설정 안함
String uri = request.getRequestURI();
log.info(">>> 경로 >>> {}", uri); // cmt/list/370
String pathUri = uri.substring("/cmt/".length()); // post, list/370
String path = pathUri;
String pathVar = "";
if(pathUri.contains("/")) {
path = pathUri.substring(0, pathUri.lastIndexOf("/")); // list
pathVar = pathUri.substring(pathUri.lastIndexOf("/")+1);
}
log.info(">>> path >>> {}", path);
log.info(">>> pathVar >>> {}" , pathVar);
switch(path) {
case "post":
try {
// js에서 보낸 데이터를 읽어들이는 작업
// js -> controller String
// 여러 줄이라서 String 형태로 못받아옴 그래서 String Buffer 사용
StringBuffer sb = new StringBuffer();
String line ="";
BufferedReader br = request.getReader(); // 댓글 객체
while((line = br.readLine()) != null) {
sb.append(line);
}
log.info(">>> sb >>> {}", sb.toString());
// 객체로 생성
// JSON은 전부 다 import org.json.simple 사용
JSONParser parser = new JSONParser();
JSONObject jsonObj = (JSONObject)parser.parse(sb.toString()); // key:value
// key를 이용하여 value를 추출
int bno = Integer.parseInt(jsonObj.get("bno").toString());
String writer = jsonObj.get("writer").toString();
String content = jsonObj.get("content").toString();
CommentVO cvo = new CommentVO(bno, writer, content);
log.info("commentVO >>> {} " + cvo);
isOk = csv.post(cvo);
log.info("isOk >>> " + ((isOk > 0) ? "OK" : "Fail"));
// 결과 데이터 전송 => 화면에 출력 (response 객체의 body에 기록)
PrintWriter out = response.getWriter();
out.print(isOk);
} catch (Exception e) {
log.info(">>> comment post error~!!");
e.printStackTrace();
}
break;
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
service(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
service(request, response);
}
}
Controller에서 사용된 JSON 데이터를 파싱하는 과정에 대해서 설명하자면,
문자열로 표현된 JSON 데이터를 JSONParser를 사용해서 파싱하고, 그 결과를 JSONObject 타입의 변수인
jsonObj에 저장하는 것이다.
파싱이란, 일반적으로 어떤 데이터를 읽고 해석하는 과정을 의미하는데
이 코드의 맥락에서는 JSON 형식의 문자열을 프로그래밍 언어에서 사용할 수 있는 데이터로 변환하는 과정을 의미한다.
JSONParser parser = new JSONParser();
JSONObject jsonObj = (JSONObject)parser.parse(sb.toString());
CommentService.interface
package service;
import domain.CommentVO;
public interface CommentService {
int post(CommentVO cvo);
}
CommentServiceImpl.java
package service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import controller.CommentController;
import domain.CommentVO;
import repository.CommentDAO;
import repository.CommentDAOImpl;
public class CommentSerivceImpl implements CommentService {
private static final Logger log = LoggerFactory.getLogger(CommentController.class);
private CommentDAO cdao;
public CommentSerivceImpl() {
cdao = new CommentDAOImpl();
}
@Override
public int post(CommentVO cvo) {
log.info("comment post check 2");
return cdao.insert(cvo);
}
}
CommentDAO.interface
package repository;
import domain.CommentVO;
public interface CommentDAO {
int insert(CommentVO cvo);
}
commentDAOImpl.java
package repository;
import org.apache.ibatis.session.SqlSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import controller.CommentController;
import domain.CommentVO;
import orm.DatabaseBuilder;
public class CommentDAOImpl implements CommentDAO {
private static final Logger log = LoggerFactory.getLogger(CommentController.class);
private SqlSession sql;
private int isOk;
public CommentDAOImpl() {
new DatabaseBuilder();
this.sql = DatabaseBuilder.getFactory().openSession();
}
@Override
public int insert(CommentVO cvo) {
log.info("comment post check 3");
isOk = sql.insert("CommentMapper.post", cvo);
if(isOk > 0) sql.commit();
return isOk;
}
}
commentMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="CommentMapper">
<insert id="post">
insert into comment (bno, writer, content)
values (#{bno}, #{writer}, #{content})
</insert>
</mapper>
border > detail.jsp에서 댓글을 작성하고 댓글 리스트를 뿌릴 위치를 잡아준다.
bvo.bno와 ses.id는 <c:out> 태그를 사용해 bnoVal, userId 이름으로 JavaScript 변수로 설정했다.
<script type="text/javascript">
const bnoVal = `<c:out value="${bvo.bno}" />`
console.log("게시번호 : " + bnoVal);
const userId = `<c:out value="${ses.id}" />`
console.log("작성자 : " + userId);
</script>
board_detail.js 파일을 포함시키기 위해 src를 지정한 script를 하나 더 작성했고
<script src="/resources/board_detail.js"></script>
마지막으로 printCommentList(bnoVal)를 호출하여 이전에 설정한 bnoVal 값을 이용하여
댓글 목록을 출력하는 함수를 썼다.
<script type="text/javascript">
printCommentList(bnoVal);
</script>
script는 순서대로 작성하지 않으면 값을 못불러 오는 경우도 있으므로 순서대로 작성하는 것이 좋다.
그러므로 printCommentList(bnoVal) 호출은 board_detail.js 파일을 먼저 불러놓고 수행하는 것이 좋다.
detail.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>자유 게시판</title>
</head>
<body>
<h1>상세 페이지</h1>
<img alt="" src="/_fileUpload/${bvo.boardFile }">
<table>
<tr>
<th>제목</th>
<td>${bvo.title }</td>
</tr>
<tr>
<th>작성자</th>
<td>${bvo.writer }</td>
</tr>
<tr>
<th>작성일</th>
<td>${bvo.regdate }</td>
</tr>
<tr>
<th>조회수</th>
<td>${bvo.readcount }</td>
</tr>
<tr>
<th>내용</th>
<td>${bvo.content }</td>
</tr>
<tr>
<th>수정일</th>
<td>${bvo.moddate }</td>
</tr>
</table>
<a href="/brd/register"><button>글쓰기</button></a>
<!-- 만약 로그인id와 작성자가 같다면... -->
<c:if test="${ses.id eq bvo.writer }">
<a href="/brd/modify?bno=${bvo.bno }"><button>수정</button></a>
<a href="/brd/remove?bno=${bvo.bno }"><button>삭제</button></a>
</c:if>
<a href="/brd/list"><button>목록</button></a>
<!-- comment line -->
<input type="hidden" id="cmtWriter" value="${ses.id }"><br>
<hr>
<div>
댓글 <br>
${ses.id }<br>
<input type="text" id="cmtText" placeholder="댓글을 작성해주세요.">
<button type="button" id="cmtAddBtn" >등록</button>
</div>
<hr>
<!-- 댓글 표시 라인 -->
<div id ="commentLine">
<div>cno, writer, regdate</div>
<div>
<button>수정</button> <button>삭제</button><br>
<input value="content">
</div>
</div>
<!-- script는 순서대로 써야한다. 값을 못불러 올 수도 있기 때문 -->
<script type="text/javascript">
const bnoVal = `<c:out value="${bvo.bno}" />`
console.log("게시번호 : " + bnoVal);
const userId = `<c:out value="${ses.id}" />`
console.log("작성자 : " + userId);
</script>
<script src="/resources/board_detail.js"></script>
<script type="text/javascript">
printCommentList(bnoVal);
</script>
</body>
</html>
여기까지 진행하면 댓글 등록만 진행할 수 있다.
등록한 댓글을 리스트에 뿌리기 위해서는 다시 Controller로 돌아가 case list에서 mapper까지
순차적으로 코드를 작성한다.
CommentController.java
package controller;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import domain.CommentVO;
import service.CommentSerivceImpl;
import service.CommentService;
@WebServlet("/cmt/*")
public class CommentController extends HttpServlet {
// ... (기존 코드)
switch(path) {
case "post":
// ... (기존 코드)
break;
case "list":
try {
int bno = Integer.parseInt(pathVar);
List<CommentVO> list = csv.getList(bno);
log.info("comment List >>> {} ", list);
// list => json으로 변환
// {..}, {..}, {..}
JSONObject[] jsonObjArr = new JSONObject[list.size()];
// [{..}, {..}, {..}]
JSONArray jsonObjList = new JSONArray();
for(int i = 0; i < list.size(); i++) {
jsonObjArr[i] = new JSONObject();
jsonObjArr[i].put("cno", list.get(i).getCno());
jsonObjArr[i].put("bno", list.get(i).getBno());
jsonObjArr[i].put("writer", list.get(i).getWriter());
jsonObjArr[i].put("content", list.get(i).getContent());
jsonObjArr[i].put("regdate", list.get(i).getRegdate());
jsonObjList.add(jsonObjArr[i]);
}
// '[{..}, {..}, {..}]'
String jsonData = jsonObjList.toJSONString();
/*
* JSONArray jsonArray = new JSONArray(); for (CommentVO comment : list) {
* JSONObject commentJson = new JSONObject(); commentJson.put("cno",
* comment.getCno()); commentJson.put("bno", comment.getBno());
* commentJson.put("writer", comment.getWriter()); commentJson.put("regdate",
* comment.getRegdate()); commentJson.put("content", comment.getContent());
* jsonArray.add(commentJson); }
*
* String jsonOutput = jsonArray.toJSONString();
*/
PrintWriter out = response.getWriter();
out.print(jsonData);
} catch (Exception e) {
log.info(">>> comment List error~!!");
e.printStackTrace();
}
break;
}
}
// ... (기존 코드)
}
case list에서 사용된 JSON에 관하여 설명하자면 다음과 같다.
list의 크기만큼 길이를 가진 JSONObject 배열을 생성하고, 이 배열에 각 댓글의 정보가 하나씩 담기면
jsonObjList에 댓글 하나하나가 모여 출력할 댓글 리스트를 형성하게 된다.
그러므로 최종적으로는 모든 댓글 정보가 담긴 jsonObjList를 JSON형식으로 형변환하여
jsonData라는 이름으로 클라이언트에게 전송되어 처리된다.
JSONObject[] jsonObjArr = new JSONObject[list.size()];
JSONArray jsonObjList = new JSONArray();
for(int i = 0; i < list.size(); i++) {
jsonObjArr[i] = new JSONObject();
jsonObjArr[i].put("cno", list.get(i).getCno());
jsonObjArr[i].put("bno", list.get(i).getBno());
jsonObjArr[i].put("writer", list.get(i).getWriter());
jsonObjArr[i].put("content", list.get(i).getContent());
jsonObjArr[i].put("regdate", list.get(i).getRegdate());
jsonObjList.add(jsonObjArr[i]);
}
String jsonData = jsonObjList.toJSONString();
CommentService.interface
package service;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import controller.CommentController;
import domain.CommentVO;
import repository.CommentDAO;
import repository.CommentDAOImpl;
public class CommentSerivceImpl implements CommentService {
// ... (기존 코드)
@Override
public List<CommentVO> getList(int bno) {
log.info("comment list check 2");
return cdao.getList(bno);
}
}
CommentServiceImpl.java
package service;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import controller.CommentController;
import domain.CommentVO;
import repository.CommentDAO;
import repository.CommentDAOImpl;
public class CommentSerivceImpl implements CommentService {
// ... (기존 코드)
@Override
public List<CommentVO> getList(int bno) {
log.info("comment list check 2");
return cdao.getList(bno);
}
}
CommentDAO.interface
package repository;
import java.util.List;
import domain.CommentVO;
public interface CommentDAO {
int insert(CommentVO cvo);
List<CommentVO> getList(int bno);
}
CommentDAOImpl.java
package repository;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import controller.CommentController;
import domain.CommentVO;
import orm.DatabaseBuilder;
public class CommentDAOImpl implements CommentDAO {
// ... (기존 코드)
@Override
public List<CommentVO> getList(int bno) {
log.info("comment list check 3");
return sql.selectList("CommentMapper.list", bno);
}
}
commentMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="CommentMapper">
<insert id="post">
insert into comment (bno, writer, content)
values (#{bno}, #{writer}, #{content})
</insert>
<select id="list" resultType="cvo">
select * from comment where bno = #{bno}
order by cno desc
</select>
</mapper>
상세 페이지 화면
상세 페이지 댓글 등록 후
[JSP/Servlet] 15. 댓글 - 등록과 리스트
(다음 게시물 예고편)
[JSP/Servlet] 16. 댓글 - 수정과 삭제
얼렁뚱땅 주니어 개발자
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!