728x90
글수정 및 글삭제
게시글 상세보기 화면에서 버튼을 클릭하면 해당 글을 수정 또는 삭제할 수 있는 화면으로 진입할 수 있도록 구현했다.
1. updateForm.jsp 작성
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ include file="../layout/header.jsp"%>
<c:if test="${signIn == null}"><c:redirect url="http://staffriends.duckdns.org/user/needLogin"/></c:if>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>board</title>
<style>
.ck.ck-editor {max-width: 90%; height: auto; margin: 0 auto;}
.textarea {width: 100%; border: none; resize: none; height: 3.1rem;}
</style>
</head>
<body class="center-div">
<div style="text-align:center">
<div class="container">
<div class="container justify-content-center">
<h2 style="text-align: center; margin-top: 30px; margin-bottom: 20px; font-family: KakaoBold;">게시글 수정</h2>
<form id="frm" method="post">
<table style="margin-left:auto;margin-right:auto; margin: 0 auto">
<tbody>
<tr>
<td>
<textarea class="textarea" id="title" name="title" placeholder="제목을 입력해주세요." style="font-size: 30px">${boardVo.title}</textarea>
<hr style="margin-top: 0;"/>
</td>
</tr>
<tr>
<td colspan="4" class="view_text" style="max-width: 30%; height: auto">
<textarea title="내용" id="contents" name="contents" style="text-align: center; ">${boardVo.contents}</textarea>
<script>
writeEditor();
</script>
</td>
</tr>
</tbody>
</table>
<input type="hidden" id="boardIdx" name="boardIdx" value="${boardVo.boardIdx}">
</form>
<div class="btn-group" role="group" aria-label="Basic mixed styles example">
<button type="button" class="btn btn-warning" id="list" style="margin-top: 20px; margin-bottom: 20px;">되돌아가기</button>
<button type="button" class="btn btn-success" id="successEdit" style="margin-top: 20px; margin-bottom: 20px;">수정완료</button>
</div>
<script>
modifyBoard();
</script>
</div>
</div>
</div>
</body>
<%@ include file="../layout/footer.jsp"%>
게시글 수정 페이지에서는 제목 및 내용을 가져와 수정할 수 있는 상태로 출력해주고, 이전페이지로 되돌아가는 버튼과 수정완료 버튼을 추가하였다.
게시글 삭제 버튼을 클릭하면 따로 페이지 이동 없이 게시글 삭제가 완료되었다는 알림창을 띄우고 목록 화면으로 돌아가도록 구현했다.
2. BoardController 작성
package board.controller;
import board.vo.BoardVo;
import board.service.BoardService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Map;
@Controller
public class BoardController {
@Autowired
private BoardService boardService;
@RequestMapping({"/", ""})
public String index() throws Exception {
return "/index";
}
@RequestMapping("/boardList") // 게시글 리스트 출력 및 페이징
public ModelAndView boardList(HttpServletRequest request) throws Exception {
System.out.println("page:"+request.getParameter("page"));
ModelAndView mv = new ModelAndView();
Map<String, Integer> map = boardService.paging(request.getParameter("page")); // 쿼리 스트링의 페이지 번호를 전달
List<BoardVo> list = boardService.selectBoardList(map); // 게시글 정보(BoardVo)
int startPage = map.get("startPage"); // 시작 페이지 번호
int endPage = map.get("endPage"); // 끝 페이지 번호
int totalPages = map.get("totalPages"); // 전체 페이지 갯수
int cPage = map.get("cPage"); // 현재 페이지 번호
mv.addObject("list", list);
mv.addObject("startPage", startPage);
mv.addObject("endPage", endPage);
mv.addObject("totalPages", totalPages);
mv.addObject("cPage", cPage);
mv.setViewName("/board/boardList");
return mv;
}
@RequestMapping("/board/boardWrite") // 글 작성 페이지
public String boardWrite() {
return "/board/boardWrite";
}
@RequestMapping("/board/insertBoard") // 글 작성 로직
public String insertBoard(BoardVo boardVo, @RequestParam String username, @RequestParam String nickname) throws Exception {
boardVo.setUsername(username);
boardVo.setNickname(nickname);
boardService.insertBoard(boardVo);
return "redirect:/board";
}
@RequestMapping("/board/boardDetail") // 글 상세 보기
public String boardDetail(@RequestParam int boardIdx, Model model) throws Exception {
BoardVo boardVo = boardService.selectBoardDetail(boardIdx);
model.addAttribute("board", boardVo);
return "/board/boardDetail";
}
@RequestMapping("/board/updateForm") // 글 수정 폼 요청
public String updateForm(@RequestParam int boardIdx, Model model) throws Exception {
BoardVo boardVo = boardService.selectBoardDetail(boardIdx);
model.addAttribute("boardVo", boardVo);
return "/board/updateForm";
}
@RequestMapping("/board/modifyBoard") // 글 수정 요청
public String modifyBoard(Model model, BoardVo boardVo) throws Exception {
int boardIdx = boardVo.getBoardIdx();
boardService.updateBoard(boardVo);
model.addAttribute("boardVo", boardVo);
return "redirect:/board/boardDetail?boardIdx="+boardIdx;
}
@RequestMapping("/board/deleteBoard") // 글 삭제
public String deleteBoard(BoardVo boardVo) throws Exception {
boardService.deleteBoard(boardVo);
return "redirect:/board";
}
}
updateForm, modifyBoard, deleteBoard 메서드가 추가되었다.
- updateForm 메서드는 boardIdx에 해당하는 게시글의 정보를 가져와 글 수정 페이지로 이동시킨다.
- modifyForm 메서드는 전달받은 boardVo의 boardIdx를 구한 뒤 해당하는 게시글 내용을 업데이트 시킨 뒤 업데이트 된 게시글 상세보기 화면으로 이동한다.
- deleteBoard 메서드는 단순히 해당 게시글의 deleted_yn의 값을 "Y"로 업데이트 시켜 게시글 목록에서 더이상 출력되지 않도록 한다.
3. BoardService, BoardServiceImpl, BoardMapper 작성
BoardService.java
package board.service;
import board.vo.BoardVo;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
@Service
public interface BoardService {
List<BoardVo> selectBoardList(Map<String, Integer> map) throws Exception;
void insertBoard(BoardVo boardVo) throws Exception;
BoardVo selectBoardDetail(int boardIdx) throws Exception;
void updateBoard(BoardVo boardVo) throws Exception;
void deleteBoard(BoardVo boardVo) throws Exception;
int getTotalRows();
Map<String, Integer> paging(String tempPage) throws Exception;
}
BoardServiceImpl.java
package board.service;
import board.vo.BoardVo;
import board.mapper.BoardMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Service
public class BoardServiceImpl implements BoardService {
@Autowired
private BoardMapper boardMapper;
@Override
public List<BoardVo> selectBoardList(Map<String, Integer> map) throws Exception { // 게시판 목록 출력
return boardMapper.selectBoardList(map);
}
@Override
public void insertBoard(BoardVo boardVo) throws Exception { // 글쓰기
boardMapper.insertBoard(boardVo);
}
@Override
public BoardVo selectBoardDetail(int boardIdx) throws Exception { // 글 상세보기(조회수 증가)
boardMapper.updateHitCount(boardIdx);
return boardMapper.selectBoardDetail(boardIdx);
}
@Override
public void updateBoard(BoardVo boardVo) throws Exception { // 글 수정
boardMapper.updateBoard(boardVo);
}
@Override
public void deleteBoard(BoardVo boardVo) throws Exception{ // 글 삭제
boardMapper.deleteBoard(boardVo);
}
@Override
public int getTotalRows() { // 전체 행 조회
return boardMapper.getTotalRows();
}
@Override
public Map<String, Integer> paging(String tempPage) throws Exception {
Map<String, Integer> map = new HashMap<>();
int cPage; // 현재 위치한 페이지의 번호
int currentBlock; // 페이지 블록의 갯수
int pageLength = 10; // 보여줄 게시글의 갯수
int startPage; // 페이지 블록의 시작 번호
int endPage; // 페이지 블록의 마지막 번호
int totalPages; // 전체 페이지의 갯수
int start; // SQL 쿼리의 LIMIT에 들어갈 변수
// // cPage(현재 위치한 페이지의 번호 정하기)
// if (tempPage == null || tempPage.length() == 0 || tempPage.equals("0")) { // 파라미터로 전달받은 페이지 정보가 없거나 0이면 1페이지로 이동
// cPage = 1;
// }
try {
cPage = Integer.parseInt(tempPage); // 파라미터로 전달받은 페이지 번호를 현재 페이지에 변환하여 담아줌
} catch (NumberFormatException e) { // 에러 발생 시(숫자가 아닌 문자가 들어오는 경우) 1로 설정
cPage = 1;
}
int totalRows = getTotalRows(); // 전체 게시글의 갯수
totalPages = totalRows % pageLength == 0 ? totalRows / pageLength : (totalRows / pageLength) + 1; // 필요한 총 페이지의 개수는 데이터의 개수를 보여줄 게시글의 개수로 나누어 딱맞으면 0, 초과하면 1개의 페이지를 더 생성
if (totalPages == 0) { // 현재 게시글 데이터의 수가 페이지 최소 단위보다 적은 경우 최소 1개의 페이지를 생성
totalPages = 1;
}
if (cPage > totalPages || cPage <= 0) { // 페이지가 최소 또는 최대 페이지 범위를 벗어난 경우 1로 설정
cPage = 1;
}
// currentBlock: 현재 위치한 페이지 블럭
// cPage: 현재 페이지
// pageLength: 전체 페이지 길이
// startPage: 페이지 블럭의 가장 첫번째 번호
// endPage: 페이지 블럭의 가장 마지막 번호
// 페이지 처음과 끝을 지정하는 부분
currentBlock = cPage % pageLength == 0 ? cPage / pageLength : (cPage / pageLength) + 1; // 현재 위치한 페이지 블록 구하기(ex. cPage=1, pageLength=10 -> 1)
startPage = (currentBlock - 1) * pageLength + 1; // 페이지 블럭의 시작 번호 구하기(ex. currentBlock=1, pageLength=10 -> 1)
endPage = startPage + pageLength - 1; // 페이지 블럭의 마지막 번호 구하기(ex. startPage=1, pageLength=10 -> 10)
// 마지막 페이지 묶음에서 총 페이지수를 넘어가면 끝 페이지를 마지막 페이지 숫자로 지정
if (endPage > totalPages) {
endPage = totalPages;
}
start = (cPage - 1) * pageLength; // 각 페이지의 첫 번째 게시글의 번호를 구하기 : (현재 페이지 - 1) * 보여줄 게시글의 갯수(ex. 1페이지=0~9, 2페이지=10~19, 3페이지=20~29)
// 첫 번째 게시글의 번호와 보여줄 게시글의 갯수를 가지고 게시글 목록을 조회(총 pageLength개의 데이터를 가져오며, 현재 페이지 - 1 * pageLength에 해당하는 번호의 게시글들이 조회됨)
map.put("start", start);
map.put("pageLength", pageLength);
map.put("startPage", startPage);
map.put("endPage", endPage);
map.put("totalPages", totalPages);
map.put("cPage", cPage);
return map;
}
}
BoardMapper.java
package board.mapper;
import board.vo.BoardVo;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
import java.util.Map;
@Mapper
public interface BoardMapper {
List<BoardVo> selectBoardList(Map<String, Integer> map) throws Exception;
void insertBoard(BoardVo boardVo) throws Exception;
void updateHitCount(int boardIdx) throws Exception;
BoardVo selectBoardDetail(int boardIdx) throws Exception;
void updateBoard(BoardVo boardVo) throws Exception;
void deleteBoard(BoardVo boardVo) throws Exception;
int getTotalRows();
}
4. js/board.js 작성
function editAndDelete() {
let frm = document.getElementById("frm");
let list = document.getElementById("list");
let editButton = document.createElement("button"); // 수정하기 버튼 생성
editButton.type = 'button';
editButton.id = 'edit';
editButton.innerHTML = '수정';
editButton.className = 'btn btn-warning';
editButton.style = 'margin-top: 20px; margin-bottom: 20px;';
list.insertAdjacentElement("afterend", editButton);
let deleteButton = document.createElement("button"); // 삭제하기 버튼 생성
deleteButton.type = 'button';
deleteButton.id = 'delete';
deleteButton.innerHTML = '삭제';
deleteButton.className = 'btn btn-danger';
deleteButton.style = 'margin-top: 20px; margin-bottom: 20px;';
editButton.insertAdjacentElement("afterend", deleteButton);
document.getElementById("edit").onclick = function(event) {
frm.action = "/board/updateForm";
frm.submit();
}
document.getElementById("delete").onclick = function(event) {
frm.action = "/board/deleteBoard";
frm.submit();
alert('게시글 삭제가 완료되었습니다.');
}
}
function showBoardDetail() {
ClassicEditor.create( document.querySelector( '#editor' ) ) // 읽기 전용 모드
.then(editor => {
editor.enableReadOnlyMode('true');
})
document.getElementById("list").onclick = function(event) {
history.back();
}
}
function insertBoard() {
document.getElementById("save").onclick = function(event) {
let frm = document.getElementById("frm");
frm.action = "/board/insertBoard";
frm.submit();
alert('글 작성이 완료되었습니다.');
}
}
function writeEditor() {
CKEDITOR.replace('contents', {
filebrowserUploadUrl: '/fileUpload',
height: '500px'
});
}
function modifyBoard() {
let frm = document.getElementById("frm");
document.getElementById("successEdit").onclick = function(event) {
let boardIdx = document.getElementById('boardIdx');
frm.action = "/board/modifyBoard";
frm.submit();
alert('수정이 완료되었습니다.');
}
document.getElementById("list").onclick = function(event) {
history.back();
// event.preventDefault(); // 클릭 시 결과 실행 후 대기
}
}
5. sql-board.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="board.mapper.BoardMapper">
<select id="selectBoardList" resultType="board.vo.BoardVo" parameterType="Map">
<![CDATA[
SELECT
board_idx,
title,
hit_cnt,
DATE_FORMAT(created_datetime, '%Y.%m.%d %H:%i:%s') AS created_datetime,
reply_count,
username,
nickname
FROM
board
WHERE
deleted_yn = 'N'
ORDER BY board_idx DESC
LIMIT #{start}, #{pageLength}
]]>
</select>
<select id="getTotalRows" resultType="int">
<![CDATA[
SELECT
count(*)
FROM
board
WHERE
deleted_yn = 'N'
]]>
</select>
<insert id="insertBoard" parameterType="board.vo.BoardVo">
<![CDATA[
INSERT INTO board
(
title,
contents,
created_datetime,
username,
nickname
)
VALUES
(
#{title},
#{contents},
now(),
#{username},
#{nickname}
)
]]>
</insert>
<update id="updateHitCount" parameterType="int">
<![CDATA[
UPDATE
board
SET
hit_cnt = hit_cnt + 1
WHERE
board_idx = #{boardIdx}
]]>
</update>
<select id="selectBoardDetail" parameterType="int" resultType="board.vo.BoardVo">
<![CDATA[
SELECT
board_idx, title, contents, hit_cnt,
DATE_FORMAT(created_datetime, '%Y.%m.%d %H:%i:%s') AS created_datetime,
username, nickname
FROM
board
WHERE
board_idx = #{boardIdx} AND deleted_yn = 'N'
]]>
</select>
<update id="updateBoard" parameterType="board.vo.BoardVo">
<![CDATA[
UPDATE
board
SET
title = #{title},
contents = #{contents},
updated_datetime = now(),
updater_id = #{username}
WHERE
board_idx = #{boardIdx}
]]>
</update>
<update id="deleteBoard" parameterType="int"> -- 글 삭제
<![CDATA[
UPDATE
board
SET
deleted_yn = 'Y',
updated_datetime = now(),
updater_id = #{username}
WHERE
board_idx = #{boardIdx}
]]>
</update>
</mapper>
프로젝트 전체 코드 Github 주소
https://github.com/sungwoo-jo/Staffriends-Project
728x90