22.10.31 - Clone_Project - 2(게시물 목록 요청 ,동적 SQL, 댓글)
SQL
mySQL
<select id="findAll2" resultMap="boardMap">
SELECT * FROM tbl_board
<if test="type == 'title'">
WHERE title LIKE CONCAT('%'.#{keyword},'%')
</if>
<if test="type == 'writer'">
WHERE writer LIKE CONCAT('%'.#{keyword},'%')
</if>
<if test="type == 'content'">
WHERE content LIKE CONCAT('%'.#{keyword},'%')
</if>
<if test="type == 'content'">
WHERE title LIKE CONCAT('%'.#{keyword},'%')
OR content LIKE CONCAT('%'.#{keyword},'%')
</if>
ORDER BY board_no DESC
LIMIT #{start},#{amount};
</select>
Oracle
<select id="findAll2" resultMap="boardMap">
SELECT *
FROM (
SELECT ROWNUM rn, v_board.*
FROM (
SELECT *
FROM tbl_board
<if test="type == 'title'">WHERE title LIKE '%' || #{keyword} || '%'</if>
<if test="type == 'writer'">WHERE writer LIKE '%' || #{keyword} || '%'</if>
<if test="type == 'content'">WHERE content LIKE '%' || #{keyword} || '%'</if>
<if test="type == 'tc'">
WHERE title LIKE '%' || #{keyword} || '%'
OR content LIKE '%' || #{keyword} || '%'
</if>
ORDER BY board_no DESC
) v_board
)
WHERE rn BETWEEN (#{pageNum} - 1) * #{amount} + 1 AND (#{pageNum} * #{amount})
</select>
동적 SQL 사용(MySql)
<!-- 동적 SQL 코드 재사용하기 -->
<sql id="search">
<if test="type == 'title'">WHERE title LIKE CONCAT('%', #{keyword}, '%')</if>
<if test="type == 'writer'">WHERE writer LIKE CONCAT('%', #{keyword}, '%')</if>
<if test="type == 'content'">WHERE content LIKE CONCAT('%', #{keyword}, '%')</if>
<if test="type == 'tc'">
WHERE title LIKE CONCAT('%', #{keyword}, '%')
OR content LIKE CONCAT('%', #{keyword}, '%')
</if>
</sql>
MySQL
<select id="findAll2" resultMap="boardMap">
SELECT * FROM tbl_board
<include refid="search" />
ORDER BY board_no DESC
LIMIT #{start}, #{amount}
</select>
<select id="getTotalCount2" resultType="int">
SELECT COUNT(*)
FROM tbl_board
<include refid="search" />
</select>
resultType : 단일 컬럼 ⇒ 컬럼 한 줄만 조회될 때, 값이 여러개이면 list int와 같은 형태로 나옴, 형태를 지정 하고 싶을 때 지정할 수 있음
resultMap : 다중 컬럼 ⇒ 컬럼이 여러줄 나올 때
JSP
<!-- 검색창 영역 -->
<div class="search">
<form action="/board/list" method="get">
<select class="form-select" name="type" id="search-type">
<option value="title">제목</option>
<option value="content">내용</option>
<option value="writer">작성자</option>
<option value="tc">제목+내용</option>
</select>
<!-- 아래의 코드에서 value는 방금 검색했던 검색어가 들어가도록 받는 것-->
<input type="text" class="form-control" name="keyword" value="${s.keyword}">
<button class="btn btn-primary" type="submit">
<i class="fas fa-search"></i>
</button>
</form>
</div>
JS
// 옵션태그 고정
function fixSearchOption() {
const $select = document.getElementById('search-type');
for (let $opt of [...$select.children]) {
if ($opt.value === '${s.type}') {
$opt.setAttribute('selected', 'selected');
break;
}
}
}
Service
// 게시물 전체 조회 요청 중간 처리 with searching
public Map<String, Object> findAllService(Search search) {
log.info("findAll service start");
Map<String, Object> findDataMap = new HashMap<>();
List<Board> boardList = repository.findAll2(search);
// 목록 중간 데이터 처리
processConverting(boardList);
findDataMap.put("bList", boardList);
findDataMap.put("tc", repository.getTotalCount2(search));
return findDataMap;
}
Controller
// 게시물 목록 요청
@GetMapping("/list")
public String list(@ModelAttribute("s") Search search, Model model) {
log.info("controller request /board/list GET! - search: {}", search);
Map<String, Object> boardMap = boardService.findAllService(search);
log.debug("return data - {}", boardMap);
// 페이지 정보 생성
PageMaker pm = new PageMaker(
new Page(search.getPageNum(), search.getAmount())
, (Integer) boardMap.get("tc"));
model.addAttribute("bList", boardMap.get("bList"));
model.addAttribute("pm", pm);
return "board/board-list";
}
댓글
SQL
CREATE TABLE tbl_reply (
reply_no INT(10) AUTO_INCREMENT,
reply_text VARCHAR(1000) NOT NULL,
reply_writer VARCHAR(50) NOT NULL,
reply_date DATETIME default current_timestamp,
board_no INT(10),
CONSTRAINT pk_reply PRIMARY KEY (reply_no),
CONSTRAINT fk_reply_board
FOREIGN KEY (board_no)
REFERENCES tbl_board (board_no)
);
domain
package com.project.web_prj.reply.domain;
import lombok.*;
import java.util.Date;
@Setter @Getter @ToString
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Reply {
private Long replyNo; //댓글번호
private String replyText; //댓글내용
private String replyWriter; //댓글작성자
private Date replyDate; //작성일자
private Long boardNo; //원본 글번호
}
api
Rest개념
ReplyApiController
package com.project.web_prj.reply.api; import com.project.web_prj.common.paging.Page; import com.project.web_prj.reply.domain.Reply; import com.project.web_prj.reply.service.ReplyService; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; import org.springframework.web.bind.annotation.*; import java.util.Map; @RestController @RequiredArgsConstructor @Log4j2 @RequestMapping("/api/v1/replies") public class ReplyApiController { private final ReplyService replyService; /* - 댓글 목록 조회요청 : /api/v1/replies - GET - 댓글 개별 조회요청 : /api/v1/replies/72 - GET - 댓글 쓰기 요청 : /api/v1/replies - POST - 댓글 수정 요청 : /api/v1/replies/72 - PUT - 댓글 삭제 요청 : /api/v1/replies/72 - DELETE */ // 댓글 목록 요청 @GetMapping("") public Map<String, Object> list(Long boardNo, Page page) { log.info("/api/v1/replies GET! bno={}, page={}", boardNo, page); Map<String, Object> replies = replyService.getList(boardNo, page); return replies; } // 댓글 등록 요청 @PostMapping("") public String create(@RequestBody Reply reply) { log.info("/api/v1/replies POST! - {}", reply); boolean flag = replyService.write(reply); return flag ? "insert-success" : "insert-fail"; } // 댓글 수정 요청 @PutMapping("/{rno}") public String modify(@PathVariable Long rno, @RequestBody Reply reply) { reply.setReplyNo(rno); log.info("/api/v1/replies PUT! - {}", reply); boolean flag = replyService.modify(reply); return flag ? "mod-success" : "mod-fail"; } // 댓글 삭제 요청 @DeleteMapping("/{rno}") public String delete(@PathVariable Long rno) { log.info("/api/v1/replies DELETE! - {}", rno); boolean flag = replyService.remove(rno); return flag ? "del-success" : "del-fail"; } }RestBasicController
package com.project.web_prj.common.api; import com.project.web_prj.board.domain.Board; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.Setter; import lombok.ToString; import lombok.extern.log4j.Log4j2; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletResponse; import java.util.List; // jsp 뷰포워딩을 하지않고 클라이언트에게 JSON데이터를 전송함 @RestController //@Controller+@ResponseBody @Log4j2 public class RestBasicController { @Setter @Getter @ToString @AllArgsConstructor public static class BoardResponseDto { private Long bno; private String writer; private String content; private String title; public BoardResponseDto(Board b) { this.bno = b.getBoardNo(); this.writer = b.getWriter(); this.content = b.getContent(); this.title = b.getTitle(); } } @GetMapping("/api/hello") @ResponseBody public String hello() { return "hello!!!"; } @GetMapping("/api/board") @ResponseBody public BoardResponseDto board() { Board board = new Board(); board.setBoardNo(10L); board.setContent("할룽~"); board.setTitle("메룽~~"); board.setWriter("박영희"); return new BoardResponseDto(board); } @GetMapping("/api/arr") public String[] arr() { String[] foods = {"짜장면", "레몬에이드", "볶음밥"}; return foods; } // post 요청처리 @PostMapping("/api/join") public String join(@RequestBody List<String> info) { log.info("/api/join POST!! - {}", info); return "POST-OK"; } // put 요청처리 @PutMapping("/api/join") public String joinPut(@RequestBody Board board) { log.info("/api/join PUT!! - {}", board); return "PUT-OK"; } // delete 요청처리 @DeleteMapping("/api/join") public String joinDelete() { log.info("/api/join DELETE!!"); return "DEL-OK"; } // RestController에서 뷰포워딩하기 @GetMapping("/hoho") public ModelAndView hoho() { ModelAndView mv = new ModelAndView(); mv.setViewName("index"); return mv; } }
repository
package com.project.web_prj.reply.repository;
import com.project.web_prj.common.paging.Page;
import com.project.web_prj.reply.domain.Reply;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface ReplyMapper {
//댓글 입력
boolean save(Reply reply);
//댓글 수정
boolean modify(Reply reply);
//댓글 삭제
boolean remove(Long replyNo);
//댓글 전체 삭제
boolean removeAll(Long boardNo);
//댓글 개별 조회
Reply findOne(Long replyNo);
//댓글 목록 조회
List<Reply> findAll(@Param("boardNo") Long boardNo
, @Param("page") Page page);
// 댓글 수 조회
int getReplyCount(Long boardNo);
}
service
package com.project.web_prj.reply.service;
import com.project.web_prj.common.paging.Page;
import com.project.web_prj.common.paging.PageMaker;
import com.project.web_prj.reply.domain.Reply;
import com.project.web_prj.reply.repository.ReplyMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
@Service
@RequiredArgsConstructor
public class ReplyService {
private final ReplyMapper replyMapper;
//댓글 목록 조회
public Map<String, Object> getList(Long boardNo, Page page) {
PageMaker maker
= new PageMaker(page, getCount(boardNo));
Map<String, Object> replyMap = new HashMap<>();
replyMap.put("replyList", replyMapper.findAll(boardNo, page));
replyMap.put("maker", maker);
return replyMap;
}
//댓글 총 개수 조회
public int getCount(Long boardNo) {
return replyMapper.getReplyCount(boardNo);
}
//댓글 개별 조회
public Reply get(Long replyNo) {
return replyMapper.findOne(replyNo);
}
//댓글 쓰기 중간처리
public boolean write(Reply reply) {
return replyMapper.save(reply);
}
//댓글 수정 중간처리
public boolean modify(Reply reply) {
return replyMapper.modify(reply);
}
//댓글 삭제 중간처리
public boolean remove(Long replyNo) {
return replyMapper.remove(replyNo);
}
}
resources
<?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.project.web_prj.reply.repository.ReplyMapper">
<resultMap id="replyMap" type="com.project.web_prj.reply.domain.Reply">
<result property="replyNo" column="reply_no" />
<result property="replyText" column="reply_text" />
<result property="replyWriter" column="reply_writer" />
<result property="replyDate" column="reply_date" />
<result property="boardNo" column="board_no" />
</resultMap>
<insert id="save">
<!-- INSERT INTO tbl_reply-->
<!-- (reply_no, reply_text, reply_writer, board_no)-->
<!-- VALUES-->
<!-- (seq_tbl_reply.nextval, #{replyText}, #{replyWriter}, #{boardNo})-->
INSERT INTO tbl_reply
(reply_text, reply_writer, board_no)
VALUES
(#{replyText}, #{replyWriter}, #{boardNo})
</insert>
<!-- 댓글 수정 -->
<update id="modify">
UPDATE tbl_reply
SET reply_text = #{replyText}
WHERE reply_no = #{replyNo}
</update>
<!-- 댓글 삭제 -->
<delete id="remove">
DELETE FROM tbl_reply
WHERE reply_no = #{replyNo}
</delete>
<!-- 댓글 전체 삭제 -->
<delete id="removeAll">
DELETE FROM tbl_reply
WHERE board_no = #{boardNo}
</delete>
<!-- 댓글 개별조회 -->
<select id="findOne" resultMap="replyMap">
SELECT * FROM tbl_reply
WHERE reply_no = #{replyNo}
</select>
<!-- 댓글 목록 조회 -->
<select id="findAll" resultMap="replyMap">
<!-- SELECT *-->
<!-- FROM (-->
<!-- SELECT ROWNUM rn, v_reply.*-->
<!-- FROM (-->
<!-- SELECT *-->
<!-- FROM tbl_reply-->
<!-- WHERE board_no = #{boardNo}-->
<!-- ORDER BY board_no DESC-->
<!-- ) v_reply-->
<!-- )-->
<!-- WHERE rn BETWEEN (#{page.pageNum} - 1) * #{page.amount} + 1 AND (#{page.pageNum} * #{page.amount})-->
SELECT *
FROM tbl_reply
WHERE board_no = #{boardNo}
ORDER BY reply_no
LIMIT #{page.start}, #{page.amount}
</select>
<select id="getReplyCount" resultType="int">
SELECT COUNT(*)
FROM tbl_reply
WHERE board_no=#{boardNo}
</select>
</mapper>
## Client 댓글 처리
---
### board-detail.jsp
```html
<%@ page contentType="text/html; charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html lang="ko">
<head>
<%@ include file="../include/static-head.jsp" %>
<style>
.content-container {
width: 60%;
margin: 150px auto;
position: relative;
}
.content-container .main-title {
font-size: 24px;
font-weight: 700;
text-align: center;
border-bottom: 2px solid rgb(75, 73, 73);
padding: 0 20px 15px;
width: fit-content;
margin: 20px auto 30px;
}
.content-container .main-content {
border: 2px solid #ccc;
border-radius: 20px;
padding: 10px 25px;
font-size: 1.1em;
text-align: justify;
min-height: 400px;
}
.content-container .custom-btn-group {
position: absolute;
bottom: -10%;
left: 50%;
transform: translateX(-50%);
}
/* 페이지 액티브 기능 */
.pagination .page-item.p-active a {
background: #333 !important;
color: #fff !important;
cursor: default;
pointer-events: none;
}
.pagination .page-item:hover a {
background: #888 !important;
color: #fff !important;
}
</style>
</head>
<body>
<div class="wrap">
<%@ include file="../include/header.jsp" %>
<div class="content-container">
<h1 class="main-title">${b.boardNo}번 게시물</h1>
<div class="mb-3">
<label for="exampleFormControlInput1" class="form-label">작성자</label>
<input type="text" class="form-control" id="exampleFormControlInput1" placeholder="이름" name="writer"
value="${b.writer}" disabled>
</div>
<div class="mb-3">
<label for="exampleFormControlInput2" class="form-label">글제목</label>
<input type="text" class="form-control" id="exampleFormControlInput2" placeholder="제목" name="title"
value="${b.title}" disabled>
</div>
<div class="mb-3">
<label for="exampleFormControlTextarea1" class="form-label">내용</label>
<p class="main-content">
${b.content}
</p>
</div>
<div class="btn-group btn-group-lg custom-btn-group" role="group">
<button id="mod-btn" type="button" class="btn btn-warning">수정</button>
<button id="del-btn" type="button" class="btn btn-danger">삭제</button>
<button id="list-btn" type="button" class="btn btn-dark">목록</button>
</div>
<!-- 댓글 영역 -->
<div id="replies" class="row">
<div class="offset-md-1 col-md-10">
<!-- 댓글 쓰기 영역 -->
<div class="card">
<div class="card-body">
<div class="row">
<div class="col-md-9">
<div class="form-group">
<label for="newReplyText" hidden>댓글 내용</label>
<textarea rows="3" id="newReplyText" name="replyText" class="form-control"
placeholder="댓글을 입력해주세요."></textarea>
</div>
</div>
<div class="col-md-3">
<div class="form-group">
<label for="newReplyWriter" hidden>댓글 작성자</label>
<input id="newReplyWriter" name="replyWriter" type="text" class="form-control"
placeholder="작성자 이름" style="margin-bottom: 6px;">
<button id="replyAddBtn" type="button"
class="btn btn-dark form-control">등록</button>
</div>
</div>
</div>
</div>
</div> <!-- end reply write -->
<!--댓글 내용 영역-->
<div class="card">
<!-- 댓글 내용 헤더 -->
<div class="card-header text-white m-0" style="background: #343A40;">
<div class="float-left">댓글 (<span id="replyCnt">0</span>)</div>
</div>
<!-- 댓글 내용 바디 -->
<div id="replyCollapse" class="card">
<div id="replyData">
<!--
< JS로 댓글 정보 DIV삽입 >
-->
</div>
<!-- 댓글 페이징 영역 -->
<ul class="pagination justify-content-center">
<!--
< JS로 댓글 페이징 DIV삽입 >
-->
</ul>
</div>
</div> <!-- end reply content -->
</div>
</div> <!-- end replies row -->
<!-- 댓글 수정 모달 -->
<div class="modal fade bd-example-modal-lg" id="replyModifyModal">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<!-- Modal Header -->
<div class="modal-header" style="background: #343A40; color: white;">
<h4 class="modal-title">댓글 수정하기</h4>
<button type="button" class="close text-white" data-bs-dismiss="modal">X</button>
</div>
<!-- Modal body -->
<div class="modal-body">
<div class="form-group">
<input id="modReplyId" type="hidden">
<label for="modReplyText" hidden>댓글내용</label>
<textarea id="modReplyText" class="form-control" placeholder="수정할 댓글 내용을 입력하세요."
rows="3"></textarea>
</div>
</div>
<!-- Modal footer -->
<div class="modal-footer">
<button id="replyModBtn" type="button" class="btn btn-dark">수정</button>
<button id="modal-close" type="button" class="btn btn-danger"
data-bs-dismiss="modal">닫기</button>
</div>
</div>
</div>
</div>
<!-- end replyModifyModal -->
</div>
<%@ include file="../include/footer.jsp" %>
</div>
<!-- 게시글 상세보기 관련 script -->
<script>
const [$modBtn, $delBtn, $listBtn] = [...document.querySelector('div[role=group]').children];
// const $modBtn = document.getElementById('mod-btn');
//수정버튼
$modBtn.onclick = e => {
location.href = '/board/modify?boardNo=${b.boardNo}';
};
//삭제버튼
$delBtn.onclick = e => {
if (!confirm('정말 삭제하시겠습니까?')) {
return;
}
location.href = '/board/delete?boardNo=${b.boardNo}';
};
//목록버튼
$listBtn.onclick = e => {
location.href = '/board/list?pageNum=${p.pageNum}&amount=${p.amount}';
};
</script>
<!-- 댓글관련 script -->
<script>
//원본 글 번호
const bno = '${b.boardNo}';
// console.log('bno:', bno);
// 댓글 요청 URL
const URL = '/api/v1/replies';
//날짜 포맷 변환 함수
function formatDate(datetime) {
//문자열 날짜 데이터를 날짜객체로 변환
const dateObj = new Date(datetime);
// console.log(dateObj);
//날짜객체를 통해 각 날짜 정보 얻기
let year = dateObj.getFullYear();
//1월이 0으로 설정되어있음.
let month = dateObj.getMonth() + 1;
let day = dateObj.getDate();
let hour = dateObj.getHours();
let minute = dateObj.getMinutes();
//오전, 오후 시간체크
let ampm = '';
if (hour < 12 && hour >= 6) {
ampm = '오전';
} else if (hour >= 12 && hour < 21) {
ampm = '오후';
if (hour !== 12) {
hour -= 12;
}
} else if (hour >= 21 && hour <= 24) {
ampm = '밤';
hour -= 12;
} else {
ampm = '새벽';
}
//숫자가 1자리일 경우 2자리로 변환
(month < 10) ? month = '0' + month: month;
(day < 10) ? day = '0' + day: day;
(hour < 10) ? hour = '0' + hour: hour;
(minute < 10) ? minute = '0' + minute: minute;
return year + "-" + month + "-" + day + " " + ampm + " " + hour + ":" + minute;
}
// 댓글 페이지 태그 생성 렌더링 함수
function makePageDOM(pageInfo) {
let tag = "";
const begin = pageInfo.beginPage;
const end = pageInfo.endPage;
//이전 버튼 만들기
if (pageInfo.prev) {
tag += "<li class='page-item'><a class='page-link page-active' href='" + (begin - 1) +
"'>이전</a></li>";
}
//페이지 번호 리스트 만들기
for (let i = begin; i <= end; i++) {
let active = '';
if (pageInfo.page.pageNum === i) {
active = 'p-active';
}
tag += "<li class='page-item " + active + "'><a class='page-link page-custom' href='" + i +
"'>" + i + "</a></li>";
}
//다음 버튼 만들기
if (pageInfo.next) {
tag += "<li class='page-item'><a class='page-link page-active' href='" + (end + 1) +
"'>다음</a></li>";
}
// 페이지태그 렌더링
const $pageUl = document.querySelector('.pagination');
$pageUl.innerHTML = tag;
// ul에 마지막페이지 번호 저장.
$pageUl.dataset.fp = pageInfo.finalPage;
}
// 댓글 목록 DOM을 생성하는 함수
function makeReplyDOM({ //=> (replyMap)으로 하면 뒤에 계속 붙여야 하니까 이거를 풀어서 쓴거
replyList,
count,
maker
}) {
// 각 댓글 하나의 태그
let tag = '';
if (replyList === null || replyList.length === 0) {
tag += "<div id='replyContent' class='card-body'>댓글이 아직 없습니다! ㅠㅠ</div>";
} else {
for (let rep of replyList) {
tag += "<div id='replyContent' class='card-body' data-replyId='" + rep.replyNo + "'>" +
" <div class='row user-block'>" +
" <span class='col-md-3'>" +
" <b>" + rep.replyWriter + "</b>" +
" </span>" +
" <span class='offset-md-6 col-md-3 text-right'><b>" + formatDate(rep.replyDate) +
"</b></span>" +
" </div><br>" +
" <div class='row'>" +
" <div class='col-md-6'>" + rep.replyText + "</div>" +
" <div class='offset-md-2 col-md-4 text-right'>" +
" <a id='replyModBtn' class='btn btn-sm btn-outline-dark' data-bs-toggle='modal' data-bs-target='#replyModifyModal'>수정</a> " +
" <a id='replyDelBtn' class='btn btn-sm btn-outline-dark' href='#'>삭제</a>" +
" </div>" +
" </div>" +
" </div>";
}
}
// 댓글 목록에 생성된 DOM 추가
document.getElementById('replyData').innerHTML = tag;
// 댓글 수 배치
document.getElementById('replyCnt').textContent = count;
// 페이지 렌더링
makePageDOM(maker);
}
// 댓글 목록을 서버로부터 비동기요청으로 불러오는 함수
function showReplies(pageNum = 1) {
fetch(URL + '?boardNo=' + bno + '&pageNum=' + pageNum)
.then(res => res.json())
.then(replyMap => {
// console.log(replyMap.replyList);
makeReplyDOM(replyMap);
});
}
// 페이지 버튼 클릭이벤트 등록 함수
function makePageButtonClickEvent() {
// 페이지 버튼 클릭이벤트 처리
const $pageUl = document.querySelector('.pagination');
$pageUl.onclick = e => {
if (!e.target.matches('.page-item a')) return;
e.preventDefault();
// 누른 페이지 번호 가져오기
const pageNum = e.target.getAttribute('href');
// console.log(pageNum);
// 페이지 번호에 맞는 목록 비동기 요청
showReplies(pageNum);
};
}
// 댓글 등록 이벤트 처리 핸들러 등록 함수
function makeReplyRegisterClickEvent() {
document.getElementById('replyAddBtn').onclick = makeReplyRegisterClickHandler;
}
// 댓글 등록 이벤트 처리 핸들러 함수
function makeReplyRegisterClickHandler(e) {
const $writerInput = document.getElementById('newReplyWriter');
const $contentInput = document.getElementById('newReplyText');
// 서버로 전송할 데이터들
const replyData = {
replyWriter: $writerInput.value,
replyText: $contentInput.value,
boardNo: bno
};
// POST요청을 위한 요청 정보 객체
const reqInfo = {
method: 'POST',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify(replyData)
};
fetch(URL, reqInfo)
.then(res => res.text())
.then(msg => {
if (msg === 'insert-success') {
alert('댓글 등록 성공');
// 댓글 입력창 리셋
$writerInput.value = '';
$contentInput.value = '';
// 댓글 목록 재요청, 작성 후 마지막 댓글 페이지로 이동
showReplies(document.querySelector('.pagination').dataset.fp);
} else {
alert('댓글 등록 실패');
}
});
}
// 댓글 수정화면 열기 상세처리
function processModifyShow(e, rno) {
// console.log('수정버튼 클릭함!! after');
// 클릭한 버튼 근처에 있는 댓글 내용텍스트를 얻어온다.
const replyText = e.target.parentElement.parentElement.firstElementChild.textContent;
//console.log('댓글내용:', replyText);
// 모달에 해당 댓글내용을 배치한다.
document.getElementById('modReplyText').textContent = replyText;
// 모달을 띄울 때 다음 작업(수정완료처리)을 위해 댓글번호를 모달에 달아두자.
const $modal = document.querySelector('.modal');
$modal.dataset.rno = rno;
}
// 댓글 삭제 상세처리
function processRemove(rno) {
if (!confirm('진짜로 삭제합니까??')) return;
fetch(URL + '/' + rno, {
method: 'DELETE'
})
.then(res => res.text())
.then(msg => {
if (msg === 'del-success') {
alert('삭제 성공!!');
showReplies(); // 댓글 새로불러오기
} else {
alert('삭제 실패!!');
}
});
}
// 댓글 수정화면 열기, 삭제 처리 핸들러 정의
function makeReplyModAndDelHandler(e) {
const rno = e.target.parentElement.parentElement.parentElement.dataset.replyid;
e.preventDefault();
// console.log('수정버튼 클릭함!! before');
if (e.target.matches('#replyModBtn')) {
processModifyShow(e, rno);
} else if (e.target.matches('#replyDelBtn')) {
processRemove(rno);
}
}
// 댓글 수정 화면 열기, 삭제 이벤트 처리
function openModifyModalAndRemoveEvent() {
const $replyData = document.getElementById('replyData');
$replyData.onclick = makeReplyModAndDelHandler;
}
// 댓글 수정 비동기 처리 이벤트
function replyModifyEvent() {
const $modal = $('#replyModifyModal');
document.getElementById('replyModBtn').onclick =
e => {
// console.log('수정 완료 버튼 클릭!');
// 서버에 수정 비동기 요청 보내기
const rno = e.target.closest('.modal').dataset.rno;
// console.log(rno);
const reqInfo = {
method: 'PUT',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify({
replyText: $('#modReplyText').val(),
replyNo: rno
})
};
fetch(URL + '/' + rno, reqInfo)
.then(res => res.text())
.then(msg => {
if (msg === 'mod-success') {
alert('수정 성공!!');
$modal.modal('hide'); // 모달창 닫기
showReplies(); // 댓글 새로불러오기
} else {
alert('수정 실패!!');
}
});
};
}
// 메인 실행부
(function () {
// 초기 화면 렌더링시 댓글 1페이지 렌더링
showReplies();
// 댓글 페이지 버튼 클릭이벤트 처리
makePageButtonClickEvent();
// 댓글 등록 버튼 클릭이벤트 처리
makeReplyRegisterClickEvent();
// 댓글 수정 모달 오픈, 삭제 이벤트 처리
openModifyModalAndRemoveEvent();
// 댓글 수정 완료 버튼 이벤트 처리
replyModifyEvent();
})();
</script>
</body>
</html>'Program > Spring' 카테고리의 다른 글
| Clone_Project - 4(로그인 처리, 인터셉터(Interceptor), 사용자 권한) (0) | 2022.12.30 |
|---|---|
| Clone_Project - 3(파일 업로드, 비동기 파일 업로드) (0) | 2022.12.30 |
| Clone_Project -1 (Cookie, 페이징 처리, 글 상세 → 이전 글 목록 페이지) (0) | 2022.12.30 |
| VO/DTO, @ToString, 생성자/빌더, 단위 테스트, 생성자주입VS필드 주입, Controller VS Service, Log4j2, JSP 분리 (0) | 2022.12.30 |
| 객체 지향 설계 원칙(SOLID 원칙) ,OCP, MVC (0) | 2022.12.27 |