관리 메뉴

거니의 velog

231121_SPRING 1 (5-1) 본문

대덕인재개발원_웹기반 애플리케이션

231121_SPRING 1 (5-1)

Unlimited00 2023. 11. 21. 08:37

[view.jsp]

<%@ page contentType="text/html; charset=utf-8"%>
<html>
<head>
<link href="${pageContext.request.contextPath }/resources/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="${pageContext.request.contextPath }/resources/css/headers.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<title>일반게시판 등록/수정</title>
</head>
<style>
.bi {
	vertical-align: -.125em;
	fill: currentColor;
}
</style>
<body>
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
  <symbol id="bootstrap" viewBox="0 0 118 94">
    <title>Bootstrap</title>
    <path fill-rule="evenodd" clip-rule="evenodd" d="M24.509 0c-6.733 0-11.715 5.893-11.492 12.284.214 6.14-.064 14.092-2.066 20.577C8.943 39.365 5.547 43.485 0 44.014v5.972c5.547.529 8.943 4.649 10.951 11.153 2.002 6.485 2.28 14.437 2.066 20.577C12.794 88.106 17.776 94 24.51 94H93.5c6.733 0 11.714-5.893 11.491-12.284-.214-6.14.064-14.092 2.066-20.577 2.009-6.504 5.396-10.624 10.943-11.153v-5.972c-5.547-.529-8.934-4.649-10.943-11.153-2.002-6.484-2.28-14.437-2.066-20.577C105.214 5.894 100.233 0 93.5 0H24.508zM80 57.863C80 66.663 73.436 72 62.543 72H44a2 2 0 01-2-2V24a2 2 0 012-2h18.437c9.083 0 15.044 4.92 15.044 12.474 0 5.302-4.01 10.049-9.119 10.88v.277C75.317 46.394 80 51.21 80 57.863zM60.521 28.34H49.948v14.934h8.905c6.884 0 10.68-2.772 10.68-7.727 0-4.643-3.264-7.207-9.012-7.207zM49.948 49.2v16.458H60.91c7.167 0 10.964-2.876 10.964-8.281 0-5.406-3.903-8.178-11.425-8.178H49.948z"></path>
  </symbol>
  <symbol id="home" viewBox="0 0 16 16">
    <path d="M8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4.5a.5.5 0 0 0 .5-.5v-4h2v4a.5.5 0 0 0 .5.5H14a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146zM2.5 14V7.707l5.5-5.5 5.5 5.5V14H10v-4a.5.5 0 0 0-.5-.5h-3a.5.5 0 0 0-.5.5v4H2.5z"/>
  </symbol>
  <symbol id="grid" viewBox="0 0 16 16">
    <path d="M1 2.5A1.5 1.5 0 0 1 2.5 1h3A1.5 1.5 0 0 1 7 2.5v3A1.5 1.5 0 0 1 5.5 7h-3A1.5 1.5 0 0 1 1 5.5v-3zM2.5 2a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5h-3zm6.5.5A1.5 1.5 0 0 1 10.5 1h3A1.5 1.5 0 0 1 15 2.5v3A1.5 1.5 0 0 1 13.5 7h-3A1.5 1.5 0 0 1 9 5.5v-3zm1.5-.5a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5h-3zM1 10.5A1.5 1.5 0 0 1 2.5 9h3A1.5 1.5 0 0 1 7 10.5v3A1.5 1.5 0 0 1 5.5 15h-3A1.5 1.5 0 0 1 1 13.5v-3zm1.5-.5a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5h-3zm6.5.5A1.5 1.5 0 0 1 10.5 9h3a1.5 1.5 0 0 1 1.5 1.5v3a1.5 1.5 0 0 1-1.5 1.5h-3A1.5 1.5 0 0 1 9 13.5v-3zm1.5-.5a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5h-3z"/>
  </symbol>
</svg>
<main>
	<header>
		<div class="px-3 py-2 text-bg-dark">
			<div class="container">
				<div class="d-flex flex-wrap align-items-center justify-content-center justify-content-lg-start">
					<a href="/" class="d-flex align-items-center my-2 my-lg-0 me-lg-auto text-white text-decoration-none">
						<svg class="bi me-2" width="40" height="32" role="img" aria-label="Bootstrap">
							<use xlink:href="#bootstrap" />
						</svg>
					</a>
					<ul class="nav col-12 col-lg-auto my-2 justify-content-center my-md-0 text-small">
						<li>
							<a href="/main.do" class="nav-link text-white"> 
								<svg class="bi d-block mx-auto mb-1" width="24" height="24">
									<use xlink:href="#home" />
								</svg> Home
							</a>
						</li>
						<li>
							<a href="/board/list.do" class="nav-link text-white"> 
								<svg class="bi d-block mx-auto mb-1" width="24" height="24">
									<use xlink:href="#grid" />
								</svg> 일반게시판
							</a>
						</li>
						<li>
							<a href="/notice/list.do" class="nav-link text-white"> 
								<svg class="bi d-block mx-auto mb-1" width="24" height="24">
									<use xlink:href="#grid" />
								</svg> 공지사항게시판
							</a>
						</li>
						<li>
							<a href="/free/list.do" class="nav-link text-white"> 
								<svg class="bi d-block mx-auto mb-1" width="24" height="24">
									<use xlink:href="#grid" />
								</svg> 자유게시판
							</a>
						</li>
					</ul>
				</div>
			</div>
		</div>
	</header>

	<div class="position-relative overflow-hidden p-3 p-md-8 m-md-8 text-center bg-white">
		<div class="col-md-8 p-lg-8 mx-auto my-5">
			<h4 class="display-4 fw-normal">일반게시판</h4>
			<p class="lead fw-normal">일반게시판 CRUD를 작성해주세요.</p>
		</div>
		<div class="product-device shadow-sm d-none d-md-block"></div>
		<div
			class="product-device product-device-2 shadow-sm d-none d-md-block"></div>
	</div>

	<div class="container">
		<div class="row">
			<div class="col-md-12">
				<div class="">
					<div class="card-header">
						<h3 class="card-title">${board.boTitle }</h3>
						<div class="card-tools mt-1">
							${board.boWriter } ${board.boDate } ${board.boHit }
						</div>
					</div>
					<div class="card-body">
						<div class="form-group row mt-1">
							<div class="col-sm-12">${board.boContent }</div>
						</div>
					</div>
					<div class="card-footer">
						<button type="button" class="btn btn-primary" id="listBtn">목록</button>
						<button type="button" class="btn btn-info" id="modifyBtn">수정</button>
						<button type="button" class="btn btn-danger" id="delBtn">삭제</button>
					</div>
					<form action="/board/update.do" method="get" id="boardForm" name="boardForm">
						<input type="hidden" name="boNo" value="${board.boNo }" />
					</form>
				</div>
			</div>
			<div class="col-md-12"><br/><br/><br/></div>
		</div>
	</div>
</main>
</body>
<script type="text/javascript">
	$(function(){
		var boardForm = $("#boardForm");
		var listBtn = $("#listBtn");
		var modifyBtn = $("#modifyBtn");
		var delBtn = $("#delBtn");
		
		// 목록 버튼 이벤트
		listBtn.on("click", function(){
			location.href = "/board/list.do"
		});
		
		// 수정 버튼 이벤트
		modifyBtn.on("click", function(){
			boardForm.submit();
		});
		
		// 삭제 버튼 이벤트
		delBtn.on("click", function(){
			if(confirm("정말로 삭제하시겠습니까?")){
				boardForm.attr("action", "/board/delete.do");
				boardForm.attr("method", "post");
				boardForm.submit();
			}
		});
	});
</script>
</html>

[BoardModifyController.java]

package kr.or.ddit.board.web;

import javax.inject.Inject;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import kr.or.ddit.ServiceResult;
import kr.or.ddit.board.service.IBoardService;
import kr.or.ddit.vo.BoardVO;

@Controller
@RequestMapping("/board")
public class BoardModifyController {

	@Inject
	private IBoardService boardService;
	
	@RequestMapping(value = "/update.do", method = RequestMethod.GET)
	public String boardUpdateForm(int boNo, Model model) {
		BoardVO boardVO = boardService.selectBoard(boNo);
		model.addAttribute("board", boardVO);
		model.addAttribute("status", "u"); // '현재 기능은 수정입니다'를 알리기 위한 flag
		return "board/form";
	}
	
	@RequestMapping(value = "/update.do", method = RequestMethod.POST)
	public String boardUpdate(BoardVO boardVO, Model model) {
		String goPage = "";
		
		ServiceResult result = boardService.updateBoard(boardVO);
		if(result.equals(ServiceResult.OK)) { // 수정 성공
			goPage = "redirect:/board/detail.do?boNo=" + boardVO.getBoNo();
		}else { // 수정 실패
			model.addAttribute("board", boardVO);
			model.addAttribute("status", "u");
			goPage = "board/form";
		}
		
		return goPage;
	}
	
}

[form.jsp]

<%@ page contentType="text/html; charset=utf-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>

<head>
    <link href="${pageContext.request.contextPath }/resources/css/bootstrap.min.css" rel="stylesheet">
    <link rel="stylesheet" href="${pageContext.request.contextPath }/resources/css/headers.css">
	<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
    <script type="text/javascript" src="${pageContext.request.contextPath }/resources/ckeditor/ckeditor.js"></script>
    <title>일반게시판 등록/수정</title>
</head>
<style>
    .bi {
        vertical-align: -.125em;
        fill: currentColor;
    }
</style>
<body>

	<c:set value="등록" var="name" />
	<c:if test="${status eq 'u' }">
		<c:set value="수정" var="name" />
	</c:if>
	
    <svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
        <symbol id="bootstrap" viewBox="0 0 118 94">
            <title>Bootstrap</title>
            <path fill-rule="evenodd" clip-rule="evenodd" d="M24.509 0c-6.733 0-11.715 5.893-11.492 12.284.214 6.14-.064 14.092-2.066 20.577C8.943 39.365 5.547 43.485 0 44.014v5.972c5.547.529 8.943 4.649 10.951 11.153 2.002 6.485 2.28 14.437 2.066 20.577C12.794 88.106 17.776 94 24.51 94H93.5c6.733 0 11.714-5.893 11.491-12.284-.214-6.14.064-14.092 2.066-20.577 2.009-6.504 5.396-10.624 10.943-11.153v-5.972c-5.547-.529-8.934-4.649-10.943-11.153-2.002-6.484-2.28-14.437-2.066-20.577C105.214 5.894 100.233 0 93.5 0H24.508zM80 57.863C80 66.663 73.436 72 62.543 72H44a2 2 0 01-2-2V24a2 2 0 012-2h18.437c9.083 0 15.044 4.92 15.044 12.474 0 5.302-4.01 10.049-9.119 10.88v.277C75.317 46.394 80 51.21 80 57.863zM60.521 28.34H49.948v14.934h8.905c6.884 0 10.68-2.772 10.68-7.727 0-4.643-3.264-7.207-9.012-7.207zM49.948 49.2v16.458H60.91c7.167 0 10.964-2.876 10.964-8.281 0-5.406-3.903-8.178-11.425-8.178H49.948z"></path>
        </symbol>
        <symbol id="home" viewBox="0 0 16 16">
            <path d="M8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4.5a.5.5 0 0 0 .5-.5v-4h2v4a.5.5 0 0 0 .5.5H14a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146zM2.5 14V7.707l5.5-5.5 5.5 5.5V14H10v-4a.5.5 0 0 0-.5-.5h-3a.5.5 0 0 0-.5.5v4H2.5z" />
        </symbol>
        <symbol id="grid" viewBox="0 0 16 16">
            <path d="M1 2.5A1.5 1.5 0 0 1 2.5 1h3A1.5 1.5 0 0 1 7 2.5v3A1.5 1.5 0 0 1 5.5 7h-3A1.5 1.5 0 0 1 1 5.5v-3zM2.5 2a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5h-3zm6.5.5A1.5 1.5 0 0 1 10.5 1h3A1.5 1.5 0 0 1 15 2.5v3A1.5 1.5 0 0 1 13.5 7h-3A1.5 1.5 0 0 1 9 5.5v-3zm1.5-.5a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5h-3zM1 10.5A1.5 1.5 0 0 1 2.5 9h3A1.5 1.5 0 0 1 7 10.5v3A1.5 1.5 0 0 1 5.5 15h-3A1.5 1.5 0 0 1 1 13.5v-3zm1.5-.5a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5h-3zm6.5.5A1.5 1.5 0 0 1 10.5 9h3a1.5 1.5 0 0 1 1.5 1.5v3a1.5 1.5 0 0 1-1.5 1.5h-3A1.5 1.5 0 0 1 9 13.5v-3zm1.5-.5a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5h-3z" />
        </symbol>
    </svg>
    <main>
        <header>
            <div class="px-3 py-2 text-bg-dark">
                <div class="container">
                    <div class="d-flex flex-wrap align-items-center justify-content-center justify-content-lg-start">
                        <a href="/" class="d-flex align-items-center my-2 my-lg-0 me-lg-auto text-white text-decoration-none">
                            <svg class="bi me-2" width="40" height="32" role="img" aria-label="Bootstrap">
                                <use xlink:href="#bootstrap" />
                            </svg>
                        </a>
                        <ul class="nav col-12 col-lg-auto my-2 justify-content-center my-md-0 text-small">
                            <li>
                                <a href="/main.do" class="nav-link text-white">
                                    <svg class="bi d-block mx-auto mb-1" width="24" height="24">
                                        <use xlink:href="#home" />
                                    </svg> Home
                                </a>
                            </li>
                            <li>
                                <a href="/board/list.do" class="nav-link text-white">
                                    <svg class="bi d-block mx-auto mb-1" width="24" height="24">
                                        <use xlink:href="#grid" />
                                    </svg> 일반게시판
                                </a>
                            </li>
                            <li>
                                <a href="/notice/list.do" class="nav-link text-white">
                                    <svg class="bi d-block mx-auto mb-1" width="24" height="24">
                                        <use xlink:href="#grid" />
                                    </svg> 공지사항게시판
                                </a>
                            </li>
                            <li>
                                <a href="/free/list.do" class="nav-link text-white">
                                    <svg class="bi d-block mx-auto mb-1" width="24" height="24">
                                        <use xlink:href="#grid" />
                                    </svg> 자유게시판
                                </a>
                            </li>
                        </ul>
                    </div>
                </div>
            </div>
        </header>

        <div class="position-relative overflow-hidden p-3 p-md-8 m-md-8 text-center bg-white">
            <div class="col-md-8 p-lg-8 mx-auto my-5">
                <h4 class="display-4 fw-normal">일반게시판</h4>
                <p class="lead fw-normal">일반게시판 CRUD를 작성해주세요.</p>
            </div>
            <div class="product-device shadow-sm d-none d-md-block"></div>
            <div class="product-device product-device-2 shadow-sm d-none d-md-block"></div>
        </div>

        <div class="container">
            <div class="row">
                <div class="col-md-12">
                    <div class="">
                        <div class="card-body">
                            <form method="post" action="/board/insert.do" id="boardForm" name="boardForm" class="form-horizontal">
                            	<c:if test="${status eq 'u' }">
	                            	<input type="hidden" name="boNo" value="${board.boNo }" />
                            	</c:if>
                                <div class="form-group row">
                                    <label class="col-sm-2 control-label">제목</label>
                                    <div class="col-sm-10">
                                        <input id="boTitle" name="boTitle" type="text" class="form-control" value="${boardVO.boTitle }" placeholder="subject">
                                    </div>
                                    <font color="red" style="font-size: 12px;">${errors.boTitle }</font>
                                </div>
                                <div class="form-group row mt-4">
                                    <label class="col-sm-2 control-label">내용</label>
                                    <div class="col-sm-10">
                                        <textarea id="boContent" name="boContent" cols="50" rows="5" class="form-control" placeholder="content">${boardVO.boContent }</textarea>
                                    </div>
                                    <font color="red" style="font-size: 12px;">${errors.boContent }</font>
                                </div>
                                <div class="form-group row mt-4">
                                    <div class="col-sm-offset-2 col-sm-12 ">
                                        <input type="button" value="${name }" class="btn btn-primary float-right" id="addBtn">
                                        <c:if test="${status eq 'u' }">
	                                        <input type="button" value="취소" class="btn btn-danger float-right" id="cancelBtn">
                                        </c:if>
                                        <c:if test="${status ne 'u' }">
	                                        <input type="button" value="목록" class="btn btn-success float-right" id="listBtn">
                                        </c:if>
                                    </div>
                                </div>
                            </form>
                        </div>
                    </div>
                </div>
                <div class="col-md-12"><br /><br /><br /></div>
            </div>
        </div>
    </main>
</body>
<script type="text/javascript">
	$(function(){
		CKEDITOR.replace("boContent"); // CKEDITOR를 textarea에 등록
		
		var addBtn = $("#addBtn"); // 등록 버튼
		var cancelBtn = $("#cancelBtn"); // 취소 버튼
		var listBtn = $("#listBtn"); // 목록 버튼
		var boTitle = $("#boTitle");
		var boContent = $("#boContent");
		var boardForm = $("#boardForm"); // 등록 form
		
		// 등록 버튼 이벤트
		addBtn.on("click", function(){
			var title = boTitle.val(); // 제목 데이터
			
			// 일반적인 데이터를 가져올 때 사용하는 방법(textarea 사용시)
			//var content = boContent.val(); // 내용 데이터
			
			// CKEDITOR를 사용해서 데이터를 가져와야 할 때 사용하는 방법(editor 사용 시)
			var content = CKEDITOR.instances.boContent.getData(); // 내용 데이터
			
			// 제목을 입력하지 않았을 때 발생할 이벤트
			if(!title) {
				alert("제목을 입력해주세요!");
				boTitle.focus();
				return false;
			}
			
			// 내용을 입력하지 않았을 때 발생할 이벤트
			if(!content) {
				alert("내용을 입력해주세요!");
				boContent.focus();
				return false;
			}
			
			// 수정일 때, 경로를 변경한다. (기존은 등록 경로로 설정되어 있음)
			if($(this).val() == "수정"){
				boardForm.attr("action", "/board/update.do");
			}
			
			boardForm.submit();
		});
		
		// 취소 버튼 이벤트
		cancelBtn.on("click", function(){
			
		});
		
		// 목록 버튼 이벤트
		listBtn.on("click", function(){
			location.href = "/board/list.do";
		});
	});
</script>
</html>

[IBoardService.java]

package kr.or.ddit.board.service;

import kr.or.ddit.ServiceResult;
import kr.or.ddit.vo.BoardVO;

public interface IBoardService {

	public ServiceResult insertBoard(BoardVO boardVO);
	public BoardVO selectBoard(int boNo);
	public ServiceResult updateBoard(BoardVO boardVO);
	
}

[BoardServiceImpl.java]

package kr.or.ddit.board.service;

import javax.inject.Inject;

import org.springframework.stereotype.Service;

import kr.or.ddit.ServiceResult;
import kr.or.ddit.board.dao.IBoardDAO;
import kr.or.ddit.vo.BoardVO;

@Service
public class BoardServiceImpl implements IBoardService {

	@Inject
	private IBoardDAO boardDao;
	
	@Override
	public ServiceResult insertBoard(BoardVO boardVO) {
		ServiceResult result = null;
		int status = boardDao.insertBoard(boardVO);
		if(status > 0) { // 성공
			result = ServiceResult.OK;
		}else { // 실패
			result = ServiceResult.FAILED;
		}
		return result;
	}
	
	@Override
	public BoardVO selectBoard(int boNo) {
		// 조회수 증가
		boardDao.incrementHit(boNo);
		return boardDao.selectBoard(boNo);
	}
	
	@Override
	public ServiceResult updateBoard(BoardVO boardVO) {
		ServiceResult result = null;
		int status = boardDao.updateBoard(boardVO);
		if(status > 0) { // 수정 성공
			result = ServiceResult.OK;
		}else { // 수정 실패
			result = ServiceResult.FAILED;
		}
		return result;
	}

}

[IBoardDAO.java]

package kr.or.ddit.board.dao;

import kr.or.ddit.vo.BoardVO;

public interface IBoardDAO {

	public int insertBoard(BoardVO boardVO);
	public BoardVO selectBoard(int boNo);
	public void incrementHit(int boNo);
	public int updateBoard(BoardVO boardVO);
	
}

[BoardDAOImpl.java]

package kr.or.ddit.board.dao;

import javax.inject.Inject;

import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.stereotype.Repository;

import kr.or.ddit.vo.BoardVO;

@Repository
public class BoardDAOImpl implements IBoardDAO {

	@Inject
	private SqlSessionTemplate sqlSession;
	
	@Override
	public int insertBoard(BoardVO boardVO) {
		return sqlSession.insert("Board.insertBoard", boardVO);
	}
	
	@Override
	public BoardVO selectBoard(int boNo) {
		return sqlSession.selectOne("Board.selectBoard", boNo);
	}
	
	@Override
	public void incrementHit(int boNo) {
		sqlSession.update("Board.incrementHit", boNo);
	}
	
	@Override
	public int updateBoard(BoardVO boardVO) {
		return sqlSession.update("Board.updateBoard", boardVO);
	}

}

[board_SQL.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="Board">

	<insert id="insertBoard" parameterType="boardVO" useGeneratedKeys="true">
		<selectKey keyProperty="boNo" resultType="int" order="BEFORE">
			select seq_board.nextval from dual
		</selectKey>
		insert into board(
			bono, botitle, bocontent, bowriter, bodate
		)values(
			#{boNo}, #{boTitle}, #{boContent}, #{boWriter}, sysdate
		)
	</insert>
	
	<update id="incrementHit" parameterType="int">
		update board 
		set 
			bohit = bohit + 1 
		where bono = #{boNo}
	</update>
	
	<select id="selectBoard" parameterType="int" resultType="boardVO">
		select 
			bono, botitle, bocontent, bowriter, bodate, bohit
		from board 
		where bono = #{boNo}
	</select>
	
	<update id="updateBoard" parameterType="boardVO">
		update board 
		set 
			botitle = #{boTitle}, 
			bocontent = #{boContent}, 
			bodate = sysdate 
		where bono = #{boNo}
	</update>

</mapper>

- http://localhost/board/detail.do?boNo=6

이 시점의 문제는 boardVO로 설정한 파라미터 값이 수정의 board 로 설정한 파라미터 명과 통일되지 않아서 값이 세팅되지 않는 것이 문제였음. 이를 통일해 주는 작업을 밑에서 한 것.

- model.addAttribute("board", boardVO);

- value="${board.boTitle }" 를 form.jsp에서도 통일해 줘야 함.

package kr.or.ddit.board.web;

import java.util.HashMap;
import java.util.Map;

import javax.inject.Inject;

import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import kr.or.ddit.ServiceResult;
import kr.or.ddit.board.service.IBoardService;
import kr.or.ddit.vo.BoardVO;

@Controller
@RequestMapping("/board")
public class BoardInsertController {
	
	@Inject
	private IBoardService boardService;

	@RequestMapping(value = "/form.do", method = RequestMethod.GET)
	public String boardForm() {
		return "board/form";
	}
	
	@RequestMapping(value = "/insert.do", method = RequestMethod.POST)
	public String boardInsert(BoardVO boardVO, Model model) {
		String goPage = ""; // 페이지 정보를 담을 변수
		
		// 클라이언트에서 넘어온 제목, 내용에 대한 데이터가 공백 또는 null로 들어오진 않는다.
		// 하지만, 데이터가 혹시나 검증되지 않고 서버로 넘어올 시 에러가 발생할 확률이 가장 높기 때문에
		// 서버에서도 데이터에 대한 입력값 검증에 대한 필터를 걸어준다.
		// 클라이언트에서 1번 거르고, 서버에서 한 번 더 거른다.
		
		Map<String, String> errors = new HashMap<String, String>();
		//if(boardVO.getBoTitle().equals("") && boardVO.getBoTitle() == null) {
		if(StringUtils.isBlank(boardVO.getBoTitle())) {
			errors.put("boTitle", "제목을 입력해 주세요!");
		}
		
		if(StringUtils.isBlank(boardVO.getBoContent())) {
			errors.put("boContent", "내용을 입력해 주세요!");
		}
		
		if(errors.size() > 0) { // 에러가 있음
			// 다시 클라이언트로 내가 입력했던 제목, 내용을 돌려준다.
			// 에러 정보도 전달
			// 이 때, 페이지는 어디로?
			model.addAttribute("board", boardVO);
			model.addAttribute("errors", errors);
			goPage = "board/form"; // 포워딩
		}else { // 에러가 없는 정상적인 데이터가 입력되었다
			boardVO.setBoWriter("a001");
			ServiceResult result = boardService.insertBoard(boardVO);
			if(result.equals(ServiceResult.OK)) {
				goPage = "redirect:/board/detail.do?boNo=" + boardVO.getBoNo();
			}else {
				model.addAttribute("board", boardVO);
				model.addAttribute("message", "서버 에러, 다시 시도해주세요!");
				goPage = "board/form"; // 포워딩
			}
		}
		
		return goPage;
	}
	
}
<%@ page contentType="text/html; charset=utf-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>

<head>
    <link href="${pageContext.request.contextPath }/resources/css/bootstrap.min.css" rel="stylesheet">
    <link rel="stylesheet" href="${pageContext.request.contextPath }/resources/css/headers.css">
	<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
    <script type="text/javascript" src="${pageContext.request.contextPath }/resources/ckeditor/ckeditor.js"></script>
    <title>일반게시판 등록/수정</title>
</head>
<style>
    .bi {
        vertical-align: -.125em;
        fill: currentColor;
    }
</style>
<body>

	<c:set value="등록" var="name" />
	<c:if test="${status eq 'u' }">
		<c:set value="수정" var="name" />
	</c:if>
	
    <svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
        <symbol id="bootstrap" viewBox="0 0 118 94">
            <title>Bootstrap</title>
            <path fill-rule="evenodd" clip-rule="evenodd" d="M24.509 0c-6.733 0-11.715 5.893-11.492 12.284.214 6.14-.064 14.092-2.066 20.577C8.943 39.365 5.547 43.485 0 44.014v5.972c5.547.529 8.943 4.649 10.951 11.153 2.002 6.485 2.28 14.437 2.066 20.577C12.794 88.106 17.776 94 24.51 94H93.5c6.733 0 11.714-5.893 11.491-12.284-.214-6.14.064-14.092 2.066-20.577 2.009-6.504 5.396-10.624 10.943-11.153v-5.972c-5.547-.529-8.934-4.649-10.943-11.153-2.002-6.484-2.28-14.437-2.066-20.577C105.214 5.894 100.233 0 93.5 0H24.508zM80 57.863C80 66.663 73.436 72 62.543 72H44a2 2 0 01-2-2V24a2 2 0 012-2h18.437c9.083 0 15.044 4.92 15.044 12.474 0 5.302-4.01 10.049-9.119 10.88v.277C75.317 46.394 80 51.21 80 57.863zM60.521 28.34H49.948v14.934h8.905c6.884 0 10.68-2.772 10.68-7.727 0-4.643-3.264-7.207-9.012-7.207zM49.948 49.2v16.458H60.91c7.167 0 10.964-2.876 10.964-8.281 0-5.406-3.903-8.178-11.425-8.178H49.948z"></path>
        </symbol>
        <symbol id="home" viewBox="0 0 16 16">
            <path d="M8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4.5a.5.5 0 0 0 .5-.5v-4h2v4a.5.5 0 0 0 .5.5H14a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146zM2.5 14V7.707l5.5-5.5 5.5 5.5V14H10v-4a.5.5 0 0 0-.5-.5h-3a.5.5 0 0 0-.5.5v4H2.5z" />
        </symbol>
        <symbol id="grid" viewBox="0 0 16 16">
            <path d="M1 2.5A1.5 1.5 0 0 1 2.5 1h3A1.5 1.5 0 0 1 7 2.5v3A1.5 1.5 0 0 1 5.5 7h-3A1.5 1.5 0 0 1 1 5.5v-3zM2.5 2a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5h-3zm6.5.5A1.5 1.5 0 0 1 10.5 1h3A1.5 1.5 0 0 1 15 2.5v3A1.5 1.5 0 0 1 13.5 7h-3A1.5 1.5 0 0 1 9 5.5v-3zm1.5-.5a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5h-3zM1 10.5A1.5 1.5 0 0 1 2.5 9h3A1.5 1.5 0 0 1 7 10.5v3A1.5 1.5 0 0 1 5.5 15h-3A1.5 1.5 0 0 1 1 13.5v-3zm1.5-.5a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5h-3zm6.5.5A1.5 1.5 0 0 1 10.5 9h3a1.5 1.5 0 0 1 1.5 1.5v3a1.5 1.5 0 0 1-1.5 1.5h-3A1.5 1.5 0 0 1 9 13.5v-3zm1.5-.5a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5h-3z" />
        </symbol>
    </svg>
    <main>
        <header>
            <div class="px-3 py-2 text-bg-dark">
                <div class="container">
                    <div class="d-flex flex-wrap align-items-center justify-content-center justify-content-lg-start">
                        <a href="/" class="d-flex align-items-center my-2 my-lg-0 me-lg-auto text-white text-decoration-none">
                            <svg class="bi me-2" width="40" height="32" role="img" aria-label="Bootstrap">
                                <use xlink:href="#bootstrap" />
                            </svg>
                        </a>
                        <ul class="nav col-12 col-lg-auto my-2 justify-content-center my-md-0 text-small">
                            <li>
                                <a href="/main.do" class="nav-link text-white">
                                    <svg class="bi d-block mx-auto mb-1" width="24" height="24">
                                        <use xlink:href="#home" />
                                    </svg> Home
                                </a>
                            </li>
                            <li>
                                <a href="/board/list.do" class="nav-link text-white">
                                    <svg class="bi d-block mx-auto mb-1" width="24" height="24">
                                        <use xlink:href="#grid" />
                                    </svg> 일반게시판
                                </a>
                            </li>
                            <li>
                                <a href="/notice/list.do" class="nav-link text-white">
                                    <svg class="bi d-block mx-auto mb-1" width="24" height="24">
                                        <use xlink:href="#grid" />
                                    </svg> 공지사항게시판
                                </a>
                            </li>
                            <li>
                                <a href="/free/list.do" class="nav-link text-white">
                                    <svg class="bi d-block mx-auto mb-1" width="24" height="24">
                                        <use xlink:href="#grid" />
                                    </svg> 자유게시판
                                </a>
                            </li>
                        </ul>
                    </div>
                </div>
            </div>
        </header>

        <div class="position-relative overflow-hidden p-3 p-md-8 m-md-8 text-center bg-white">
            <div class="col-md-8 p-lg-8 mx-auto my-5">
                <h4 class="display-4 fw-normal">일반게시판</h4>
                <p class="lead fw-normal">일반게시판 CRUD를 작성해주세요.</p>
            </div>
            <div class="product-device shadow-sm d-none d-md-block"></div>
            <div class="product-device product-device-2 shadow-sm d-none d-md-block"></div>
        </div>

        <div class="container">
            <div class="row">
                <div class="col-md-12">
                    <div class="">
                        <div class="card-body">
                            <form method="post" action="/board/insert.do" id="boardForm" name="boardForm" class="form-horizontal">
                            	<c:if test="${status eq 'u' }">
	                            	<input type="hidden" name="boNo" value="${board.boNo }" />
                            	</c:if>
                                <div class="form-group row">
                                    <label class="col-sm-2 control-label">제목</label>
                                    <div class="col-sm-10">
                                        <input id="boTitle" name="boTitle" type="text" class="form-control" value="${board.boTitle }" placeholder="subject">
                                    </div>
                                    <font color="red" style="font-size: 12px;">${errors.boTitle }</font>
                                </div>
                                <div class="form-group row mt-4">
                                    <label class="col-sm-2 control-label">내용</label>
                                    <div class="col-sm-10">
                                        <textarea id="boContent" name="boContent" cols="50" rows="5" class="form-control" placeholder="content">${board.boContent }</textarea>
                                    </div>
                                    <font color="red" style="font-size: 12px;">${errors.boContent }</font>
                                </div>
                                <div class="form-group row mt-4">
                                    <div class="col-sm-offset-2 col-sm-12 ">
                                        <input type="button" value="${name }" class="btn btn-primary float-right" id="addBtn">
                                        <c:if test="${status eq 'u' }">
	                                        <input type="button" value="취소" class="btn btn-danger float-right" id="cancelBtn">
                                        </c:if>
                                        <c:if test="${status ne 'u' }">
	                                        <input type="button" value="목록" class="btn btn-success float-right" id="listBtn">
                                        </c:if>
                                    </div>
                                </div>
                            </form>
                        </div>
                    </div>
                </div>
                <div class="col-md-12"><br /><br /><br /></div>
            </div>
        </div>
    </main>
</body>
<script type="text/javascript">
	$(function(){
		CKEDITOR.replace("boContent"); // CKEDITOR를 textarea에 등록
		
		var addBtn = $("#addBtn"); // 등록 버튼
		var cancelBtn = $("#cancelBtn"); // 취소 버튼
		var listBtn = $("#listBtn"); // 목록 버튼
		var boTitle = $("#boTitle");
		var boContent = $("#boContent");
		var boardForm = $("#boardForm"); // 등록 form
		
		// 등록 버튼 이벤트
		addBtn.on("click", function(){
			var title = boTitle.val(); // 제목 데이터
			
			// 일반적인 데이터를 가져올 때 사용하는 방법(textarea 사용시)
			//var content = boContent.val(); // 내용 데이터
			
			// CKEDITOR를 사용해서 데이터를 가져와야 할 때 사용하는 방법(editor 사용 시)
			var content = CKEDITOR.instances.boContent.getData(); // 내용 데이터
			
			// 제목을 입력하지 않았을 때 발생할 이벤트
			if(!title) {
				alert("제목을 입력해주세요!");
				boTitle.focus();
				return false;
			}
			
			// 내용을 입력하지 않았을 때 발생할 이벤트
			if(!content) {
				alert("내용을 입력해주세요!");
				boContent.focus();
				return false;
			}
			
			// 수정일 때, 경로를 변경한다. (기존은 등록 경로로 설정되어 있음)
			if($(this).val() == "수정"){
				boardForm.attr("action", "/board/update.do");
			}
			
			boardForm.submit();
		});
		
		// 취소 버튼 이벤트
		cancelBtn.on("click", function(){
			
		});
		
		// 목록 버튼 이벤트
		listBtn.on("click", function(){
			location.href = "/board/list.do";
		});
	});
</script>
</html>


[BoardModifyController.java]

package kr.or.ddit.board.web;

import javax.inject.Inject;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import kr.or.ddit.ServiceResult;
import kr.or.ddit.board.service.IBoardService;
import kr.or.ddit.vo.BoardVO;

@Controller
@RequestMapping("/board")
public class BoardModifyController {

	@Inject
	private IBoardService boardService;
	
	@RequestMapping(value = "/update.do", method = RequestMethod.GET)
	public String boardUpdateForm(int boNo, Model model) {
		BoardVO boardVO = boardService.selectBoard(boNo);
		model.addAttribute("board", boardVO);
		model.addAttribute("status", "u"); // '현재 기능은 수정입니다'를 알리기 위한 flag
		return "board/form";
	}
	
	@RequestMapping(value = "/update.do", method = RequestMethod.POST)
	public String boardUpdate(BoardVO boardVO, Model model) {
		String goPage = "";
		
		ServiceResult result = boardService.updateBoard(boardVO);
		if(result.equals(ServiceResult.OK)) { // 수정 성공
			goPage = "redirect:/board/detail.do?boNo=" + boardVO.getBoNo();
		}else { // 수정 실패
			model.addAttribute("board", boardVO);
			model.addAttribute("status", "u");
			goPage = "board/form";
		}
		
		return goPage;
	}
	
	@RequestMapping(value = "/delete.do", method = RequestMethod.POST)
	public String deleteBoard(int boNo, Model model) {
		String goPage = "";
		
		ServiceResult result = boardService.deleteBoard(boNo);
		if(result.equals(ServiceResult.OK)) { // 삭제 성공
			goPage = "redirect:/board/list.do";
		}else { // 삭제 실패
			goPage = "redirect:/board/detail.do?boNo=" + boNo;
		}
		
		return goPage;
	}
	
}

[IBoardService.java]

package kr.or.ddit.board.service;

import kr.or.ddit.ServiceResult;
import kr.or.ddit.vo.BoardVO;

public interface IBoardService {

	public ServiceResult insertBoard(BoardVO boardVO);
	public BoardVO selectBoard(int boNo);
	public ServiceResult updateBoard(BoardVO boardVO);
	public ServiceResult deleteBoard(int boNo);
	
}

[BoardServiceImpl.java]

package kr.or.ddit.board.service;

import javax.inject.Inject;

import org.springframework.stereotype.Service;

import kr.or.ddit.ServiceResult;
import kr.or.ddit.board.dao.IBoardDAO;
import kr.or.ddit.vo.BoardVO;

@Service
public class BoardServiceImpl implements IBoardService {

	@Inject
	private IBoardDAO boardDao;
	
	@Override
	public ServiceResult insertBoard(BoardVO boardVO) {
		ServiceResult result = null;
		int status = boardDao.insertBoard(boardVO);
		if(status > 0) { // 성공
			result = ServiceResult.OK;
		}else { // 실패
			result = ServiceResult.FAILED;
		}
		return result;
	}
	
	@Override
	public BoardVO selectBoard(int boNo) {
		// 조회수 증가
		boardDao.incrementHit(boNo);
		return boardDao.selectBoard(boNo);
	}
	
	@Override
	public ServiceResult updateBoard(BoardVO boardVO) {
		ServiceResult result = null;
		int status = boardDao.updateBoard(boardVO);
		if(status > 0) { // 수정 성공
			result = ServiceResult.OK;
		}else { // 수정 실패
			result = ServiceResult.FAILED;
		}
		return result;
	}
			
	@Override
	public ServiceResult deleteBoard(int boNo) {
		ServiceResult result = null;
		int status = boardDao.deleteBoard(boNo);
		if(status > 0) { // 삭제 성공
			result = ServiceResult.OK;
		}else { // 삭제 실패
			result = ServiceResult.FAILED;
		}
		return result;
	}

}

[IBoardDAO.java]

package kr.or.ddit.board.dao;

import kr.or.ddit.vo.BoardVO;

public interface IBoardDAO {

	public int insertBoard(BoardVO boardVO);
	public BoardVO selectBoard(int boNo);
	public void incrementHit(int boNo);
	public int updateBoard(BoardVO boardVO);
	public int deleteBoard(int boNo);
	
}

[BoardDAOImpl.java]

package kr.or.ddit.board.dao;

import javax.inject.Inject;

import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.stereotype.Repository;

import kr.or.ddit.vo.BoardVO;

@Repository
public class BoardDAOImpl implements IBoardDAO {

	@Inject
	private SqlSessionTemplate sqlSession;
	
	@Override
	public int insertBoard(BoardVO boardVO) {
		return sqlSession.insert("Board.insertBoard", boardVO);
	}
	
	@Override
	public BoardVO selectBoard(int boNo) {
		return sqlSession.selectOne("Board.selectBoard", boNo);
	}
	
	@Override
	public void incrementHit(int boNo) {
		sqlSession.update("Board.incrementHit", boNo);
	}
	
	@Override
	public int updateBoard(BoardVO boardVO) {
		return sqlSession.update("Board.updateBoard", boardVO);
	}
	
	@Override
	public int deleteBoard(int boNo) {
		return sqlSession.delete("Board.deleteBoard", boNo);
	}

}

[board_SQL.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="Board">

	<insert id="insertBoard" parameterType="boardVO" useGeneratedKeys="true">
		<selectKey keyProperty="boNo" resultType="int" order="BEFORE">
			select seq_board.nextval from dual
		</selectKey>
		insert into board(
			bono, botitle, bocontent, bowriter, bodate
		)values(
			#{boNo}, #{boTitle}, #{boContent}, #{boWriter}, sysdate
		)
	</insert>
	
	<update id="incrementHit" parameterType="int">
		update board 
		set 
			bohit = bohit + 1 
		where bono = #{boNo}
	</update>
	
	<select id="selectBoard" parameterType="int" resultType="boardVO">
		select 
			bono, botitle, bocontent, bowriter, bodate, bohit
		from board 
		where bono = #{boNo}
	</select>
	
	<update id="updateBoard" parameterType="boardVO">
		update board 
		set 
			botitle = #{boTitle}, 
			bocontent = #{boContent}, 
			bodate = sysdate 
		where bono = #{boNo}
	</update>
	
	<delete id="deleteBoard" parameterType="int">
		delete from board 
		where bono = #{boNo}
	</delete>

</mapper>

- http://localhost/board/detail.do?boNo=7

7번 게시판 삭제 완료


[BoardRetreiveController.java]

package kr.or.ddit.board.web;

import java.util.List;

import javax.inject.Inject;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import kr.or.ddit.board.service.IBoardService;
import kr.or.ddit.vo.BoardVO;

@Controller
@RequestMapping("/board")
public class BoardRetreiveController {

	@Inject
	private IBoardService boardService;
	
	@RequestMapping(value = "/list.do", method = RequestMethod.GET)
	public String boardList(Model model) {
		List<BoardVO> boardList = boardService.selectBoardList();
		model.addAttribute("boardList", boardList);
		return "board/list";
	}
	
	@RequestMapping(value = "detail.do", method = RequestMethod.GET)
	public String boardDetail(int boNo, Model model) {
		BoardVO boardVO = boardService.selectBoard(boNo);
		model.addAttribute("board", boardVO);
		return "board/view";
	}
	
}

[IBoardService.java]

package kr.or.ddit.board.service;

import java.util.List;

import kr.or.ddit.ServiceResult;
import kr.or.ddit.vo.BoardVO;

public interface IBoardService {

	public ServiceResult insertBoard(BoardVO boardVO);
	public BoardVO selectBoard(int boNo);
	public ServiceResult updateBoard(BoardVO boardVO);
	public ServiceResult deleteBoard(int boNo);
	public List<BoardVO> selectBoardList();
	
}

[BoardServiceImpl.java]

package kr.or.ddit.board.service;

import java.util.List;

import javax.inject.Inject;

import org.springframework.stereotype.Service;

import kr.or.ddit.ServiceResult;
import kr.or.ddit.board.dao.IBoardDAO;
import kr.or.ddit.vo.BoardVO;

@Service
public class BoardServiceImpl implements IBoardService {

	@Inject
	private IBoardDAO boardDao;
	
	@Override
	public ServiceResult insertBoard(BoardVO boardVO) {
		ServiceResult result = null;
		int status = boardDao.insertBoard(boardVO);
		if(status > 0) { // 성공
			result = ServiceResult.OK;
		}else { // 실패
			result = ServiceResult.FAILED;
		}
		return result;
	}
	
	@Override
	public BoardVO selectBoard(int boNo) {
		// 조회수 증가
		boardDao.incrementHit(boNo);
		return boardDao.selectBoard(boNo);
	}
	
	@Override
	public ServiceResult updateBoard(BoardVO boardVO) {
		ServiceResult result = null;
		int status = boardDao.updateBoard(boardVO);
		if(status > 0) { // 수정 성공
			result = ServiceResult.OK;
		}else { // 수정 실패
			result = ServiceResult.FAILED;
		}
		return result;
	}
			
	@Override
	public ServiceResult deleteBoard(int boNo) {
		ServiceResult result = null;
		int status = boardDao.deleteBoard(boNo);
		if(status > 0) { // 삭제 성공
			result = ServiceResult.OK;
		}else { // 삭제 실패
			result = ServiceResult.FAILED;
		}
		return result;
	}
	
	@Override
	public List<BoardVO> selectBoardList() {
		return boardDao.selectBoardList();
	}

}

[IBoardDAO.java]

package kr.or.ddit.board.dao;

import java.util.List;

import kr.or.ddit.vo.BoardVO;

public interface IBoardDAO {

	public int insertBoard(BoardVO boardVO);
	public BoardVO selectBoard(int boNo);
	public void incrementHit(int boNo);
	public int updateBoard(BoardVO boardVO);
	public int deleteBoard(int boNo);
	public List<BoardVO> selectBoardList();
	
}

[BoardDAOImpl.java]

package kr.or.ddit.board.dao;

import java.util.List;

import javax.inject.Inject;

import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.stereotype.Repository;

import kr.or.ddit.vo.BoardVO;

@Repository
public class BoardDAOImpl implements IBoardDAO {

	@Inject
	private SqlSessionTemplate sqlSession;
	
	@Override
	public int insertBoard(BoardVO boardVO) {
		return sqlSession.insert("Board.insertBoard", boardVO);
	}
	
	@Override
	public BoardVO selectBoard(int boNo) {
		return sqlSession.selectOne("Board.selectBoard", boNo);
	}
	
	@Override
	public void incrementHit(int boNo) {
		sqlSession.update("Board.incrementHit", boNo);
	}
	
	@Override
	public int updateBoard(BoardVO boardVO) {
		return sqlSession.update("Board.updateBoard", boardVO);
	}
	
	@Override
	public int deleteBoard(int boNo) {
		return sqlSession.delete("Board.deleteBoard", boNo);
	}
	
	@Override
	public List<BoardVO> selectBoardList() {
		return sqlSession.selectList("Board.selectBoardList");
	}

}

[board_SQL.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="Board">

	<insert id="insertBoard" parameterType="boardVO" useGeneratedKeys="true">
		<selectKey keyProperty="boNo" resultType="int" order="BEFORE">
			select seq_board.nextval from dual
		</selectKey>
		insert into board(
			bono, botitle, bocontent, bowriter, bodate
		)values(
			#{boNo}, #{boTitle}, #{boContent}, #{boWriter}, sysdate
		)
	</insert>
	
	<update id="incrementHit" parameterType="int">
		update board 
		set 
			bohit = bohit + 1 
		where bono = #{boNo}
	</update>
	
	<select id="selectBoard" parameterType="int" resultType="boardVO">
		select 
			bono, botitle, bocontent, bowriter, bodate, bohit
		from board 
		where bono = #{boNo}
	</select>
	
	<update id="updateBoard" parameterType="boardVO">
		update board 
		set 
			botitle = #{boTitle}, 
			bocontent = #{boContent}, 
			bodate = sysdate 
		where bono = #{boNo}
	</update>
	
	<delete id="deleteBoard" parameterType="int">
		delete from board 
		where bono = #{boNo}
	</delete>
	
	<select id="selectBoardList" resultType="boardVO">
		select 
			bono, botitle, bocontent, bowriter, bodate, bohit 
		from board 
	</select>

</mapper>

[list.jsp]

<%@ page contentType="text/html; charset=utf-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>

<head>
    <link href="${pageContext.request.contextPath }/resources/css/bootstrap.min.css" rel="stylesheet">
    <link rel="stylesheet" href="${pageContext.request.contextPath }/resources/css/headers.css">
    <script src="${pageContext.request.contextPath}/resources/plugins/jquery/jquery.min.js"></script>
    <title>메인화면</title>
</head>
<style>
    .bi {
        vertical-align: -.125em;
        fill: currentColor;
    }
</style>

<body>
    <svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
        <symbol id="bootstrap" viewBox="0 0 118 94">
            <title>Bootstrap</title>
            <path fill-rule="evenodd" clip-rule="evenodd" d="M24.509 0c-6.733 0-11.715 5.893-11.492 12.284.214 6.14-.064 14.092-2.066 20.577C8.943 39.365 5.547 43.485 0 44.014v5.972c5.547.529 8.943 4.649 10.951 11.153 2.002 6.485 2.28 14.437 2.066 20.577C12.794 88.106 17.776 94 24.51 94H93.5c6.733 0 11.714-5.893 11.491-12.284-.214-6.14.064-14.092 2.066-20.577 2.009-6.504 5.396-10.624 10.943-11.153v-5.972c-5.547-.529-8.934-4.649-10.943-11.153-2.002-6.484-2.28-14.437-2.066-20.577C105.214 5.894 100.233 0 93.5 0H24.508zM80 57.863C80 66.663 73.436 72 62.543 72H44a2 2 0 01-2-2V24a2 2 0 012-2h18.437c9.083 0 15.044 4.92 15.044 12.474 0 5.302-4.01 10.049-9.119 10.88v.277C75.317 46.394 80 51.21 80 57.863zM60.521 28.34H49.948v14.934h8.905c6.884 0 10.68-2.772 10.68-7.727 0-4.643-3.264-7.207-9.012-7.207zM49.948 49.2v16.458H60.91c7.167 0 10.964-2.876 10.964-8.281 0-5.406-3.903-8.178-11.425-8.178H49.948z"></path>
        </symbol>
        <symbol id="home" viewBox="0 0 16 16">
            <path d="M8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4.5a.5.5 0 0 0 .5-.5v-4h2v4a.5.5 0 0 0 .5.5H14a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146zM2.5 14V7.707l5.5-5.5 5.5 5.5V14H10v-4a.5.5 0 0 0-.5-.5h-3a.5.5 0 0 0-.5.5v4H2.5z" />
        </symbol>
        <symbol id="grid" viewBox="0 0 16 16">
            <path d="M1 2.5A1.5 1.5 0 0 1 2.5 1h3A1.5 1.5 0 0 1 7 2.5v3A1.5 1.5 0 0 1 5.5 7h-3A1.5 1.5 0 0 1 1 5.5v-3zM2.5 2a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5h-3zm6.5.5A1.5 1.5 0 0 1 10.5 1h3A1.5 1.5 0 0 1 15 2.5v3A1.5 1.5 0 0 1 13.5 7h-3A1.5 1.5 0 0 1 9 5.5v-3zm1.5-.5a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5h-3zM1 10.5A1.5 1.5 0 0 1 2.5 9h3A1.5 1.5 0 0 1 7 10.5v3A1.5 1.5 0 0 1 5.5 15h-3A1.5 1.5 0 0 1 1 13.5v-3zm1.5-.5a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5h-3zm6.5.5A1.5 1.5 0 0 1 10.5 9h3a1.5 1.5 0 0 1 1.5 1.5v3a1.5 1.5 0 0 1-1.5 1.5h-3A1.5 1.5 0 0 1 9 13.5v-3zm1.5-.5a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5h-3z" />
        </symbol>
    </svg>
    <main>
        <header>
            <div class="px-3 py-2 text-bg-dark">
                <div class="container">
                    <div class="d-flex flex-wrap align-items-center justify-content-center justify-content-lg-start">
                        <a href="/" class="d-flex align-items-center my-2 my-lg-0 me-lg-auto text-white text-decoration-none">
                            <svg class="bi me-2" width="40" height="32" role="img" aria-label="Bootstrap">
                                <use xlink:href="#bootstrap" />
                            </svg>
                        </a>
                        <ul class="nav col-12 col-lg-auto my-2 justify-content-center my-md-0 text-small">
                            <li>
                                <a href="/main.do" class="nav-link text-white">
                                    <svg class="bi d-block mx-auto mb-1" width="24" height="24">
                                        <use xlink:href="#home" />
                                    </svg> Home
                                </a>
                            </li>
                            <li>
                                <a href="/board/list.do" class="nav-link text-white">
                                    <svg class="bi d-block mx-auto mb-1" width="24" height="24">
                                        <use xlink:href="#grid" />
                                    </svg> 일반게시판
                                </a>
                            </li>
                            <li>
                                <a href="/notice/list.do" class="nav-link text-white">
                                    <svg class="bi d-block mx-auto mb-1" width="24" height="24">
                                        <use xlink:href="#grid" />
                                    </svg> 공지사항게시판
                                </a>
                            </li>
                            <li>
                                <a href="/free/list.do" class="nav-link text-white">
                                    <svg class="bi d-block mx-auto mb-1" width="24" height="24">
                                        <use xlink:href="#grid" />
                                    </svg> 자유게시판
                                </a>
                            </li>
                        </ul>
                    </div>
                </div>
            </div>
        </header>

        <div class="position-relative overflow-hidden p-3 p-md-8 m-md-8 text-center bg-white">
            <div class="col-md-8 p-lg-8 mx-auto my-5">
                <h4 class="display-4 fw-normal">일반게시판</h4>
                <p class="lead fw-normal">일반게시판 CRUD를 작성해주세요.</p>
            </div>
            <div class="product-device shadow-sm d-none d-md-block"></div>
            <div class="product-device product-device-2 shadow-sm d-none d-md-block"></div>
        </div>

        <div class="container">
            <div class="row">
                <div class="col-md-12">
                    <div class="">
                        <div class="card-header">
                            <div class="card-tools">
                            </div>
                            <h3 class="card-title">일반게시판</h3>
                            <div align="right">
                                <span class="badge bg-dark-subtle border border-dark-subtle text-dark-emphasis rounded-pill">전체 0건</span>
                            </div>
                        </div>
                        <div class="card-body mt-3">
                            <div align="right">
                                <form class="input-group input-group-sm" method="post" id="searchForm" style="width: 440px;">
                                    <select class="form-control" name="searchType">
                                        <option value="title">제목</option>
                                        <option value="writer">작성자</option>
                                    </select>
                                    <input type="text" name="searchWord" class="form-control float-right" value="" placeholder="Search">
                                    <div class="input-group-append">
                                        <button type="submit" class="btn btn-default">
                                            <i class="fas fa-search"></i>검색
                                        </button>
                                    </div>
                                </form>
                            </div>
                            <div class="mt-2">
                                <table class="table">
                                    <thead class="table-dark">
                                        <tr>
                                            <th style="width: 6%">#</th>
                                            <th style="width: px">제목</th>
                                            <th style="width: 12%">작성자</th>
                                            <th style="width: 12%">작성일</th>
                                            <th style="width: 10%">조회수</th>
                                        </tr>
                                    </thead>
                                    <tbody>
                                    	<c:choose>
                                    		<c:when test="${empty boardList }">
		                                        <tr>
		                                            <td colspan="5">조회하신 게시글이 존재하지 않습니다.</td>
		                                        </tr>
                                    		</c:when>
                                    		<c:otherwise>
                                    			<c:forEach items="${boardList }" var="board">
			                                        <tr>
			                                            <td>${board.boNo }</td>
			                                            <td>
			                                            	<a href="/board/detail.do?boNo=${board.boNo }">${board.boTitle }</a>
			                                            </td>
			                                            <td>${board.boWriter }</td>
			                                            <td>${board.boDate }</td>
			                                            <td>${board.boHit }</td>
			                                        </tr>
                                    			</c:forEach>
                                    		</c:otherwise>
                                    	</c:choose>
                                    </tbody>
                                </table>
                            </div>
                            <div align="left">
                                <a href="/board/form.do" class="btn btn-primary">등록</a>
                            </div>
                        </div>
                        <div class="card-footer clearfix mt-4" id="pagingArea"></div>
                    </div>
                </div>
                <div class="col-md-12"><br /><br /><br /></div>
            </div>
        </div>
    </main>
</body>

</html>

- http://localhost/board/list.do


[페이징 구조 살펴보기]

* 목록에 대한 데이터를 1페이지 당 (Screen Size) 10개 씩 출력해 준다고 가정할 떄, 번호는 총 5개의 페이지 정보(Block Size)를 나타낼 것. 

* StartRow, EndRow / StartPage, EndPage

* 총 게시글이 121개라 가정하면

  screenSize = 10개, blockSize = 5개라 하면 나머지가 1이므로 한 페이지를 더 구성하는 것.

  즉, 하나의 공식화가 가능.

* TotalPage는 TotalRecode를 이용

  - currentPage를 defalut로 1로 줄 것.

  - currentPage가 변경됨에 따라 페이지 이동이 일어나고, 1,2,3... 으로 바뀌고, 그 바뀐 정보는 StartRow, EndRow에 영향을 미침.

  - 데이터 순번을 RNUM으로 구분해서 StartRow, EndRow를 계산할 것임.

* 이 모든 전처리를 PaginationInfoVO에서 처리할 것.


[PaginationInfoVO.java]

package kr.or.ddit.vo;

import java.util.List;

import lombok.Data;

@Data
public class PaginationInfoVO<T> {

	private int totalRecord;	// 총 게시글 수
	private int totalPage;		// 총 페이지 수
	private int currentPage;	// 현재 페이지
	private int screenSize;		// 페이지 당 게시글 수
	private int blockSize;		// 페이지 블록 수
	private int startRow;		// 시작 row
	private int endRow;			// 끝 row
	private int startPage;		// 시작 페이지
	private int endPage;		// 끝 페이지
	private List<T> dataList;	// 결과를 넣을 데이터 리스트
	private String searchType;	// 검색 타입(제목, 작성자, 제목+작성자 등등)
	private String searchWord;	// 검색 단어(키워드)
	
	public PaginationInfoVO() {}
	public PaginationInfoVO(int screenSize, int blockSize) {
		super();
		this.screenSize = screenSize;
		this.blockSize = blockSize;
	}
	
	public void setTotalRecord(int totalRecord) {
		this.totalRecord = totalRecord;
		// ceil은 올림에 해당한다. 즉, 나머지가 올림으로 페이지를 1 증가시킴
		// 총 게시글 수가 121개 일 떄, screenSize로 나누게 되면 몫은 12이지만 나머지가 1 존재한다.
		// 이 때, 올림을 하게 되면 나머지에 대한 부분이 몫으로 올림이 되므로 총 페이지는 13이 된다.
		totalPage = (int)Math.ceil(totalRecord / (double)screenSize);
	}
	
	public void setCurrentPage(int currentPage) {
		this.currentPage = currentPage; // 현재 페이지
		endRow = currentPage * screenSize;		// 끝 row = 현재 페이지 * 한 페이지당 게시글 수
		startRow = endRow - (screenSize - 1);	// 시작 row = 끝 row - (한 페이지당 게시글 수 - 1)
		// 마지막 페이지 = (현재 페이지 + (페이지 블록 사이즈 - 1)) / 페이지 블록 사이즈 * 페이지 블록 사이즈
		// blockSize * blockSize는 1, 2, 3, 4 ... 페이지마다 실수 계산이 아닌 정수 계산을 이용해 endPage를 구한다.
		endPage = (currentPage + (blockSize - 1)) / blockSize * blockSize;
		startPage = endPage - (blockSize - 1);
	}
	
	public String getPagingHTML() {
		StringBuffer html = new StringBuffer();
		
		html.append("<ul class='pagination pagination-sm m-0 float-right'>");
		
		if(startPage > 1) {
			html.append("<li class='page-item'><a href='' class='page-link' data-page='"+(startPage - blockSize)+"'>Prev</a></li>");
		}
		
		// 현재 페이지부터 blockSize에 해당하는 페이지 정보를 출력할 반복문
		for(int i = startPage; i<= (endPage < totalPage ? endPage : totalPage); i++) {
			if(i == currentPage) {
				html.append("<li class='page-item active'><span class='page-link'>"+i+"</span></li>");
			}else {
				html.append("<li class='page-item'><a href='' class='page-link' data-page='"+i+"'>"+i+"</a></li>");
			}
		}
		
		if(endPage < totalPage) {
			html.append("<li class='page-item'><a href='' class='page-link' data-page='"+(endPage+1)+"'>Next</a></li>");
		}
		
		html.append("</ul>");
		
		return html.toString();
	}
	
}

[BoardRetreiveController.java]

package kr.or.ddit.board.web;

import java.util.List;

import javax.inject.Inject;

import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import kr.or.ddit.board.service.IBoardService;
import kr.or.ddit.vo.BoardVO;
import kr.or.ddit.vo.PaginationInfoVO;

@Controller
@RequestMapping("/board")
public class BoardRetreiveController {

	@Inject
	private IBoardService boardService;
	
	@RequestMapping(value = "/list.do", method = RequestMethod.GET)
	public String boardList(
			@RequestParam(name="page", required = false, defaultValue = "1") int currentPage, 
			@RequestParam(required = false, defaultValue = "title") String searchType, 
			@RequestParam(required = false) String searchWord,
			Model model) {
		// 방법 1 - 기본적인 목록 조회 시 사용
//		List<BoardVO> boardList = boardService.selectBoardList();
//		model.addAttribute("boardList", boardList);
		
		// 방법 2 - PaginationInfoVO 클래스를 활용한 목록 조회 시 사용
		PaginationInfoVO<BoardVO> pagingVO = new PaginationInfoVO<BoardVO>();
		
		// 브라우저에서 검색한 검색 유형, 검색 키워드를 이용하여 검색 처리
		// 검색 키워드가 있으면 검색을 한거고, 키워드가 없으면 검색을 하지 않음
		if(StringUtils.isNotBlank(searchWord)) {
			pagingVO.setSearchType(searchType);
			pagingVO.setSearchWord(searchWord);
			model.addAttribute("searchType", searchType);
			model.addAttribute("searchWord", searchWord);
		}
		
		pagingVO.setCurrentPage(currentPage);
		int totalRecord = boardService.selectBoardCount(pagingVO);
		pagingVO.setTotalRecord(totalRecord);
		List<BoardVO> dataList = boardService.selectBoardList(pagingVO);
		pagingVO.setDataList(dataList);
		
		model.addAttribute("pagingVO", pagingVO);
		
		return "board/list";
	}
	
	@RequestMapping(value = "detail.do", method = RequestMethod.GET)
	public String boardDetail(int boNo, Model model) {
		BoardVO boardVO = boardService.selectBoard(boNo);
		model.addAttribute("board", boardVO);
		return "board/view";
	}
	
}

[IBoardService.java]

package kr.or.ddit.board.service;

import java.util.List;

import kr.or.ddit.ServiceResult;
import kr.or.ddit.vo.BoardVO;
import kr.or.ddit.vo.PaginationInfoVO;

public interface IBoardService {

	public ServiceResult insertBoard(BoardVO boardVO);
	public BoardVO selectBoard(int boNo);
	public ServiceResult updateBoard(BoardVO boardVO);
	public ServiceResult deleteBoard(int boNo);
	public List<BoardVO> selectBoardList_();
	public int selectBoardCount(PaginationInfoVO<BoardVO> pagingVO);
	public List<BoardVO> selectBoardList(PaginationInfoVO<BoardVO> pagingVO);
	
}

[BoardServiceImpl.java]

package kr.or.ddit.board.service;

import java.util.List;

import javax.inject.Inject;

import org.springframework.stereotype.Service;

import kr.or.ddit.ServiceResult;
import kr.or.ddit.board.dao.IBoardDAO;
import kr.or.ddit.vo.BoardVO;
import kr.or.ddit.vo.PaginationInfoVO;

@Service
public class BoardServiceImpl implements IBoardService {

	@Inject
	private IBoardDAO boardDao;
	
	@Override
	public ServiceResult insertBoard(BoardVO boardVO) {
		ServiceResult result = null;
		int status = boardDao.insertBoard(boardVO);
		if(status > 0) { // 성공
			result = ServiceResult.OK;
		}else { // 실패
			result = ServiceResult.FAILED;
		}
		return result;
	}
	
	@Override
	public BoardVO selectBoard(int boNo) {
		// 조회수 증가
		boardDao.incrementHit(boNo);
		return boardDao.selectBoard(boNo);
	}
	
	@Override
	public ServiceResult updateBoard(BoardVO boardVO) {
		ServiceResult result = null;
		int status = boardDao.updateBoard(boardVO);
		if(status > 0) { // 수정 성공
			result = ServiceResult.OK;
		}else { // 수정 실패
			result = ServiceResult.FAILED;
		}
		return result;
	}
			
	@Override
	public ServiceResult deleteBoard(int boNo) {
		ServiceResult result = null;
		int status = boardDao.deleteBoard(boNo);
		if(status > 0) { // 삭제 성공
			result = ServiceResult.OK;
		}else { // 삭제 실패
			result = ServiceResult.FAILED;
		}
		return result;
	}
	
	@Override
	public List<BoardVO> selectBoardList_() {
		return boardDao.selectBoardList_();
	}
	
	@Override
	public int selectBoardCount(PaginationInfoVO<BoardVO> pagingVO) {
		return boardDao.selectBoardCount(pagingVO);
	}
	
	@Override
	public List<BoardVO> selectBoardList(PaginationInfoVO<BoardVO> pagingVO) {
		return boardDao.selectBoardList(pagingVO);
	}

}

[IBoardDAO.java]

package kr.or.ddit.board.dao;

import java.util.List;

import kr.or.ddit.vo.BoardVO;
import kr.or.ddit.vo.PaginationInfoVO;

public interface IBoardDAO {

	public int insertBoard(BoardVO boardVO);
	public BoardVO selectBoard(int boNo);
	public void incrementHit(int boNo);
	public int updateBoard(BoardVO boardVO);
	public int deleteBoard(int boNo);
	public List<BoardVO> selectBoardList_();
	public int selectBoardCount(PaginationInfoVO<BoardVO> pagingVO);
	public List<BoardVO> selectBoardList(PaginationInfoVO<BoardVO> pagingVO);
	
}

[BoardDAOImpl.java]

package kr.or.ddit.board.dao;

import java.util.List;

import javax.inject.Inject;

import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.stereotype.Repository;

import kr.or.ddit.vo.BoardVO;
import kr.or.ddit.vo.PaginationInfoVO;

@Repository
public class BoardDAOImpl implements IBoardDAO {

	@Inject
	private SqlSessionTemplate sqlSession;
	
	@Override
	public int insertBoard(BoardVO boardVO) {
		return sqlSession.insert("Board.insertBoard", boardVO);
	}
	
	@Override
	public BoardVO selectBoard(int boNo) {
		return sqlSession.selectOne("Board.selectBoard", boNo);
	}
	
	@Override
	public void incrementHit(int boNo) {
		sqlSession.update("Board.incrementHit", boNo);
	}
	
	@Override
	public int updateBoard(BoardVO boardVO) {
		return sqlSession.update("Board.updateBoard", boardVO);
	}
	
	@Override
	public int deleteBoard(int boNo) {
		return sqlSession.delete("Board.deleteBoard", boNo);
	}
	
	@Override
	public List<BoardVO> selectBoardList_() {
		return sqlSession.selectList("Board.selectBoardList_");
	}
	
	@Override
	public int selectBoardCount(PaginationInfoVO<BoardVO> pagingVO) {
		return sqlSession.selectOne("Board.selectBoardCount", pagingVO);
	}
	
	@Override
	public List<BoardVO> selectBoardList(PaginationInfoVO<BoardVO> pagingVO) {
		return sqlSession.selectList("Board.selectBoardList", pagingVO);
	}

}

[board_SQL.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="Board">

	<sql id="boardSearch">
		<if test="searchType != null and searchType == 'title'">
			and (botitle like '%' || #{searchWord} || '%')
		</if>
		<if test="searchType != null and searchType == 'writer'">
			and (bowriter like '%' || #{searchWord} || '%')
		</if>
	</sql>

	<insert id="insertBoard" parameterType="boardVO" useGeneratedKeys="true">
		<selectKey keyProperty="boNo" resultType="int" order="BEFORE">
			select seq_board.nextval from dual
		</selectKey>
		insert into board(
			bono, botitle, bocontent, bowriter, bodate
		)values(
			#{boNo}, #{boTitle}, #{boContent}, #{boWriter}, sysdate
		)
	</insert>
	
	<update id="incrementHit" parameterType="int">
		update board 
		set 
			bohit = bohit + 1 
		where bono = #{boNo}
	</update>
	
	<select id="selectBoard" parameterType="int" resultType="boardVO">
		select 
			bono, botitle, bocontent, bowriter, bodate, bohit
		from board 
		where bono = #{boNo}
	</select>
	
	<update id="updateBoard" parameterType="boardVO">
		update board 
		set 
			botitle = #{boTitle}, 
			bocontent = #{boContent}, 
			bodate = sysdate 
		where bono = #{boNo}
	</update>
	
	<delete id="deleteBoard" parameterType="int">
		delete from board 
		where bono = #{boNo}
	</delete>
	
	<select id="selectBoardList_" resultType="boardVO">
		select 
			bono, botitle, bocontent, bowriter, bodate, bohit 
		from board 
	</select>
	
	<select id="selectBoardCount" parameterType="pagingVO" resultType="int">
		select count(bono) from board 
		where 1=1 
		<include refid="boardSearch" /> 
	</select>
		
	<select id="selectBoardList" parameterType="pagingVO" resultType="boardVO">
		select b.*
		from (
			select
				a.*, row_number() over (order by a.bono desc) rnum
			from (
				select 
					bono, botitle, bocontent, bowriter, bodate, bohit 
				from board 
				where 1=1 
				<include refid="boardSearch" /> 
				order by bono desc
			) a
		) b
		<![CDATA[
			where b.rnum >= #{startRow} and b.rnum <= #{endRow}
		]]>
	</select>

</mapper>
insert into board values (seq_board.nextval, '제목11', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목12', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목13', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목14', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목15', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목16', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목17', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목18', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목19', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목21', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목22', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목23', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목24', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목25', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목26', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목27', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목28', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목29', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목30', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목31', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목32', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목33', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목34', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목35', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목36', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목37', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목38', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목39', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목40', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목41', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목42', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목43', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목44', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목45', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목46', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목47', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목48', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목49', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목50', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목51', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목52', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목53', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목54', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목55', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목56', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목57', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목58', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목59', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목60', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목61', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목62', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목63', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목64', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목65', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목66', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목67', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목68', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목69', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목70', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목71', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목72', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목73', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목74', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목75', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목76', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목77', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목78', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목79', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목80', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목11', '내용', 'a002', sysdate, 0);
	insert into board values (seq_board.nextval, '제목12', '내용', 'a002', sysdate, 0);
	insert into board values (seq_board.nextval, '제목13', '내용', 'a002', sysdate, 0);
	insert into board values (seq_board.nextval, '제목14', '내용', 'a002', sysdate, 0);
	insert into board values (seq_board.nextval, '제목15', '내용', 'a002', sysdate, 0);
	insert into board values (seq_board.nextval, '제목16', '내용', 'a002', sysdate, 0);
	insert into board values (seq_board.nextval, '제목17', '내용', 'a002', sysdate, 0);
	insert into board values (seq_board.nextval, '제목18', '내용', 'a002', sysdate, 0);
	insert into board values (seq_board.nextval, '제목19', '내용', 'a002', sysdate, 0);
	insert into board values (seq_board.nextval, '제목21', '내용', 'a002', sysdate, 0);
	insert into board values (seq_board.nextval, '제목22', '내용', 'a002', sysdate, 0);
	insert into board values (seq_board.nextval, '제목23', '내용', 'a002', sysdate, 0);
	insert into board values (seq_board.nextval, '제목24', '내용', 'a002', sysdate, 0);
	insert into board values (seq_board.nextval, '제목25', '내용', 'a002', sysdate, 0);
	insert into board values (seq_board.nextval, '제목26', '내용', 'a002', sysdate, 0);
	insert into board values (seq_board.nextval, '제목27', '내용', 'a002', sysdate, 0);
	insert into board values (seq_board.nextval, '제목28', '내용', 'a002', sysdate, 0);
	insert into board values (seq_board.nextval, '제목29', '내용', 'a002', sysdate, 0);
	insert into board values (seq_board.nextval, '제목30', '내용', 'a002', sysdate, 0);
	insert into board values (seq_board.nextval, '제목31', '내용', 'a002', sysdate, 0);
	insert into board values (seq_board.nextval, '제목32', '내용', 'a002', sysdate, 0);
	insert into board values (seq_board.nextval, '제목33', '내용', 'a002', sysdate, 0);
	insert into board values (seq_board.nextval, '제목34', '내용', 'a002', sysdate, 0);
	insert into board values (seq_board.nextval, '제목35', '내용', 'a002', sysdate, 0);
	insert into board values (seq_board.nextval, '제목36', '내용', 'a002', sysdate, 0);
	insert into board values (seq_board.nextval, '제목37', '내용', 'a002', sysdate, 0);
	insert into board values (seq_board.nextval, '제목38', '내용', 'a002', sysdate, 0);
	insert into board values (seq_board.nextval, '제목39', '내용', 'a002', sysdate, 0);
	insert into board values (seq_board.nextval, '제목40', '내용', 'a002', sysdate, 0);
	insert into board values (seq_board.nextval, '제목41', '내용', 'a003', sysdate, 0);
	insert into board values (seq_board.nextval, '제목42', '내용', 'a003', sysdate, 0);
	insert into board values (seq_board.nextval, '제목43', '내용', 'a003', sysdate, 0);
	insert into board values (seq_board.nextval, '제목44', '내용', 'a003', sysdate, 0);
	insert into board values (seq_board.nextval, '제목45', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목46', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목47', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목48', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목49', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목50', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목51', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목52', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목53', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목54', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목55', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목56', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목57', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목58', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목59', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목60', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목61', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목62', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목63', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목64', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목65', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목66', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목67', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목68', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목69', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목70', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목71', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목72', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목73', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목74', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목75', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목76', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목77', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목78', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목79', '내용', 'a001', sysdate, 0);
	insert into board values (seq_board.nextval, '제목80', '내용', 'a001', sysdate, 0);

[PaginationInfoVO.java]

@Data
public class PaginationInfoVO<T> {

	private int totalRecord;	// 총 게시글 수
	private int totalPage;		// 총 페이지 수
	private int currentPage;	// 현재 페이지
	private int screenSize = 10;// 페이지 당 게시글 수
	private int blockSize = 5;	// 페이지 블록 수
	private int startRow;		// 시작 row
	private int endRow;			// 끝 row
	private int startPage;		// 시작 페이지
	private int endPage;		// 끝 페이지
	private List<T> dataList;	// 결과를 넣을 데이터 리스트
	private String searchType;	// 검색 타입(제목, 작성자, 제목+작성자 등등)
	private String searchWord;	// 검색 단어(키워드)

[mybatisAlias.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>

	<settings>
		<setting name="mapUnderscoreToCamelCase" value="true" />
	</settings>
	
	<typeAliases>
		<typeAlias type="kr.or.ddit.vo.BoardVO" alias="boardVO" />
		<typeAlias type="kr.or.ddit.vo.PaginationInfoVO" alias="pagingVO" />
	</typeAliases>

</configuration>

[list.jsp]

<%@ page contentType="text/html; charset=utf-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>

<head>
    <link href="${pageContext.request.contextPath }/resources/css/bootstrap.min.css" rel="stylesheet">
    <link rel="stylesheet" href="${pageContext.request.contextPath }/resources/css/headers.css">
    <script src="${pageContext.request.contextPath}/resources/plugins/jquery/jquery.min.js"></script>
    <title>메인화면</title>
</head>
<style>
    .bi {
        vertical-align: -.125em;
        fill: currentColor;
    }
</style>

<body>
    <svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
        <symbol id="bootstrap" viewBox="0 0 118 94">
            <title>Bootstrap</title>
            <path fill-rule="evenodd" clip-rule="evenodd" d="M24.509 0c-6.733 0-11.715 5.893-11.492 12.284.214 6.14-.064 14.092-2.066 20.577C8.943 39.365 5.547 43.485 0 44.014v5.972c5.547.529 8.943 4.649 10.951 11.153 2.002 6.485 2.28 14.437 2.066 20.577C12.794 88.106 17.776 94 24.51 94H93.5c6.733 0 11.714-5.893 11.491-12.284-.214-6.14.064-14.092 2.066-20.577 2.009-6.504 5.396-10.624 10.943-11.153v-5.972c-5.547-.529-8.934-4.649-10.943-11.153-2.002-6.484-2.28-14.437-2.066-20.577C105.214 5.894 100.233 0 93.5 0H24.508zM80 57.863C80 66.663 73.436 72 62.543 72H44a2 2 0 01-2-2V24a2 2 0 012-2h18.437c9.083 0 15.044 4.92 15.044 12.474 0 5.302-4.01 10.049-9.119 10.88v.277C75.317 46.394 80 51.21 80 57.863zM60.521 28.34H49.948v14.934h8.905c6.884 0 10.68-2.772 10.68-7.727 0-4.643-3.264-7.207-9.012-7.207zM49.948 49.2v16.458H60.91c7.167 0 10.964-2.876 10.964-8.281 0-5.406-3.903-8.178-11.425-8.178H49.948z"></path>
        </symbol>
        <symbol id="home" viewBox="0 0 16 16">
            <path d="M8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4.5a.5.5 0 0 0 .5-.5v-4h2v4a.5.5 0 0 0 .5.5H14a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146zM2.5 14V7.707l5.5-5.5 5.5 5.5V14H10v-4a.5.5 0 0 0-.5-.5h-3a.5.5 0 0 0-.5.5v4H2.5z" />
        </symbol>
        <symbol id="grid" viewBox="0 0 16 16">
            <path d="M1 2.5A1.5 1.5 0 0 1 2.5 1h3A1.5 1.5 0 0 1 7 2.5v3A1.5 1.5 0 0 1 5.5 7h-3A1.5 1.5 0 0 1 1 5.5v-3zM2.5 2a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5h-3zm6.5.5A1.5 1.5 0 0 1 10.5 1h3A1.5 1.5 0 0 1 15 2.5v3A1.5 1.5 0 0 1 13.5 7h-3A1.5 1.5 0 0 1 9 5.5v-3zm1.5-.5a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5h-3zM1 10.5A1.5 1.5 0 0 1 2.5 9h3A1.5 1.5 0 0 1 7 10.5v3A1.5 1.5 0 0 1 5.5 15h-3A1.5 1.5 0 0 1 1 13.5v-3zm1.5-.5a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5h-3zm6.5.5A1.5 1.5 0 0 1 10.5 9h3a1.5 1.5 0 0 1 1.5 1.5v3a1.5 1.5 0 0 1-1.5 1.5h-3A1.5 1.5 0 0 1 9 13.5v-3zm1.5-.5a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5h-3z" />
        </symbol>
    </svg>
    <main>
        <header>
            <div class="px-3 py-2 text-bg-dark">
                <div class="container">
                    <div class="d-flex flex-wrap align-items-center justify-content-center justify-content-lg-start">
                        <a href="/" class="d-flex align-items-center my-2 my-lg-0 me-lg-auto text-white text-decoration-none">
                            <svg class="bi me-2" width="40" height="32" role="img" aria-label="Bootstrap">
                                <use xlink:href="#bootstrap" />
                            </svg>
                        </a>
                        <ul class="nav col-12 col-lg-auto my-2 justify-content-center my-md-0 text-small">
                            <li>
                                <a href="/main.do" class="nav-link text-white">
                                    <svg class="bi d-block mx-auto mb-1" width="24" height="24">
                                        <use xlink:href="#home" />
                                    </svg> Home
                                </a>
                            </li>
                            <li>
                                <a href="/board/list.do" class="nav-link text-white">
                                    <svg class="bi d-block mx-auto mb-1" width="24" height="24">
                                        <use xlink:href="#grid" />
                                    </svg> 일반게시판
                                </a>
                            </li>
                            <li>
                                <a href="/notice/list.do" class="nav-link text-white">
                                    <svg class="bi d-block mx-auto mb-1" width="24" height="24">
                                        <use xlink:href="#grid" />
                                    </svg> 공지사항게시판
                                </a>
                            </li>
                            <li>
                                <a href="/free/list.do" class="nav-link text-white">
                                    <svg class="bi d-block mx-auto mb-1" width="24" height="24">
                                        <use xlink:href="#grid" />
                                    </svg> 자유게시판
                                </a>
                            </li>
                        </ul>
                    </div>
                </div>
            </div>
        </header>

        <div class="position-relative overflow-hidden p-3 p-md-8 m-md-8 text-center bg-white">
            <div class="col-md-8 p-lg-8 mx-auto my-5">
                <h4 class="display-4 fw-normal">일반게시판</h4>
                <p class="lead fw-normal">일반게시판 CRUD를 작성해주세요.</p>
            </div>
            <div class="product-device shadow-sm d-none d-md-block"></div>
            <div class="product-device product-device-2 shadow-sm d-none d-md-block"></div>
        </div>

        <div class="container">
            <div class="row">
                <div class="col-md-12">
                    <div class="">
                        <div class="card-header">
                            <div class="card-tools">
                            </div>
                            <h3 class="card-title">일반게시판</h3>
                            <div align="right">
                                <span class="badge bg-dark-subtle border border-dark-subtle text-dark-emphasis rounded-pill">전체 0건</span>
                            </div>
                        </div>
                        <div class="card-body mt-3">
                            <div align="right">
                                <form class="input-group input-group-sm" method="post" id="searchForm" style="width: 440px;">
                                    <select class="form-control" name="searchType">
                                        <option value="title">제목</option>
                                        <option value="writer">작성자</option>
                                    </select>
                                    <input type="text" name="searchWord" class="form-control float-right" value="" placeholder="Search">
                                    <div class="input-group-append">
                                        <button type="submit" class="btn btn-default">
                                            <i class="fas fa-search"></i>검색
                                        </button>
                                    </div>
                                </form>
                            </div>
                            <div class="mt-2">
                                <table class="table">
                                    <thead class="table-dark">
                                        <tr>
                                            <th style="width: 6%">#</th>
                                            <th style="width: px">제목</th>
                                            <th style="width: 12%">작성자</th>
                                            <th style="width: 12%">작성일</th>
                                            <th style="width: 10%">조회수</th>
                                        </tr>
                                    </thead>
                                    <tbody>
                                    	<c:set value="${pagingVO.dataList }" var="boardList" />
                                    	<c:choose>
                                    		<c:when test="${empty boardList }">
		                                        <tr>
		                                            <td colspan="5">조회하신 게시글이 존재하지 않습니다.</td>
		                                        </tr>
                                    		</c:when>
                                    		<c:otherwise>
                                    			<c:forEach items="${boardList }" var="board">
			                                        <tr>
			                                            <td>${board.boNo }</td>
			                                            <td>
			                                            	<a href="/board/detail.do?boNo=${board.boNo }">${board.boTitle }</a>
			                                            </td>
			                                            <td>${board.boWriter }</td>
			                                            <td>${board.boDate }</td>
			                                            <td>${board.boHit }</td>
			                                        </tr>
                                    			</c:forEach>
                                    		</c:otherwise>
                                    	</c:choose>
                                    </tbody>
                                </table>
                            </div>
                            <div align="left">
                                <a href="/board/form.do" class="btn btn-primary">등록</a>
                            </div>
                        </div>
                        <div class="card-footer clearfix mt-4" id="pagingArea">
                       		${pagingVO.pagingHTML }
                        </div>
                    </div>
                </div>
                <div class="col-md-12"><br /><br /><br /></div>
            </div>
        </div>
    </main>
</body>

</html>

- http://localhost/board/list.do


[list.jsp]

<%@ page contentType="text/html; charset=utf-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>

<head>
    <link href="${pageContext.request.contextPath }/resources/css/bootstrap.min.css" rel="stylesheet">
    <link rel="stylesheet" href="${pageContext.request.contextPath }/resources/css/headers.css">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
    <title>메인화면</title>
</head>
<style>
    .bi {
        vertical-align: -.125em;
        fill: currentColor;
    }
</style>

<body>
    <svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
        <symbol id="bootstrap" viewBox="0 0 118 94">
            <title>Bootstrap</title>
            <path fill-rule="evenodd" clip-rule="evenodd" d="M24.509 0c-6.733 0-11.715 5.893-11.492 12.284.214 6.14-.064 14.092-2.066 20.577C8.943 39.365 5.547 43.485 0 44.014v5.972c5.547.529 8.943 4.649 10.951 11.153 2.002 6.485 2.28 14.437 2.066 20.577C12.794 88.106 17.776 94 24.51 94H93.5c6.733 0 11.714-5.893 11.491-12.284-.214-6.14.064-14.092 2.066-20.577 2.009-6.504 5.396-10.624 10.943-11.153v-5.972c-5.547-.529-8.934-4.649-10.943-11.153-2.002-6.484-2.28-14.437-2.066-20.577C105.214 5.894 100.233 0 93.5 0H24.508zM80 57.863C80 66.663 73.436 72 62.543 72H44a2 2 0 01-2-2V24a2 2 0 012-2h18.437c9.083 0 15.044 4.92 15.044 12.474 0 5.302-4.01 10.049-9.119 10.88v.277C75.317 46.394 80 51.21 80 57.863zM60.521 28.34H49.948v14.934h8.905c6.884 0 10.68-2.772 10.68-7.727 0-4.643-3.264-7.207-9.012-7.207zM49.948 49.2v16.458H60.91c7.167 0 10.964-2.876 10.964-8.281 0-5.406-3.903-8.178-11.425-8.178H49.948z"></path>
        </symbol>
        <symbol id="home" viewBox="0 0 16 16">
            <path d="M8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4.5a.5.5 0 0 0 .5-.5v-4h2v4a.5.5 0 0 0 .5.5H14a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146zM2.5 14V7.707l5.5-5.5 5.5 5.5V14H10v-4a.5.5 0 0 0-.5-.5h-3a.5.5 0 0 0-.5.5v4H2.5z" />
        </symbol>
        <symbol id="grid" viewBox="0 0 16 16">
            <path d="M1 2.5A1.5 1.5 0 0 1 2.5 1h3A1.5 1.5 0 0 1 7 2.5v3A1.5 1.5 0 0 1 5.5 7h-3A1.5 1.5 0 0 1 1 5.5v-3zM2.5 2a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5h-3zm6.5.5A1.5 1.5 0 0 1 10.5 1h3A1.5 1.5 0 0 1 15 2.5v3A1.5 1.5 0 0 1 13.5 7h-3A1.5 1.5 0 0 1 9 5.5v-3zm1.5-.5a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5h-3zM1 10.5A1.5 1.5 0 0 1 2.5 9h3A1.5 1.5 0 0 1 7 10.5v3A1.5 1.5 0 0 1 5.5 15h-3A1.5 1.5 0 0 1 1 13.5v-3zm1.5-.5a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5h-3zm6.5.5A1.5 1.5 0 0 1 10.5 9h3a1.5 1.5 0 0 1 1.5 1.5v3a1.5 1.5 0 0 1-1.5 1.5h-3A1.5 1.5 0 0 1 9 13.5v-3zm1.5-.5a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5h-3z" />
        </symbol>
    </svg>
    <main>
        <header>
            <div class="px-3 py-2 text-bg-dark">
                <div class="container">
                    <div class="d-flex flex-wrap align-items-center justify-content-center justify-content-lg-start">
                        <a href="/" class="d-flex align-items-center my-2 my-lg-0 me-lg-auto text-white text-decoration-none">
                            <svg class="bi me-2" width="40" height="32" role="img" aria-label="Bootstrap">
                                <use xlink:href="#bootstrap" />
                            </svg>
                        </a>
                        <ul class="nav col-12 col-lg-auto my-2 justify-content-center my-md-0 text-small">
                            <li>
                                <a href="/main.do" class="nav-link text-white">
                                    <svg class="bi d-block mx-auto mb-1" width="24" height="24">
                                        <use xlink:href="#home" />
                                    </svg> Home
                                </a>
                            </li>
                            <li>
                                <a href="/board/list.do" class="nav-link text-white">
                                    <svg class="bi d-block mx-auto mb-1" width="24" height="24">
                                        <use xlink:href="#grid" />
                                    </svg> 일반게시판
                                </a>
                            </li>
                            <li>
                                <a href="/notice/list.do" class="nav-link text-white">
                                    <svg class="bi d-block mx-auto mb-1" width="24" height="24">
                                        <use xlink:href="#grid" />
                                    </svg> 공지사항게시판
                                </a>
                            </li>
                            <li>
                                <a href="/free/list.do" class="nav-link text-white">
                                    <svg class="bi d-block mx-auto mb-1" width="24" height="24">
                                        <use xlink:href="#grid" />
                                    </svg> 자유게시판
                                </a>
                            </li>
                        </ul>
                    </div>
                </div>
            </div>
        </header>

        <div class="position-relative overflow-hidden p-3 p-md-8 m-md-8 text-center bg-white">
            <div class="col-md-8 p-lg-8 mx-auto my-5">
                <h4 class="display-4 fw-normal">일반게시판</h4>
                <p class="lead fw-normal">일반게시판 CRUD를 작성해주세요.</p>
            </div>
            <div class="product-device shadow-sm d-none d-md-block"></div>
            <div class="product-device product-device-2 shadow-sm d-none d-md-block"></div>
        </div>

        <div class="container">
            <div class="row">
                <div class="col-md-12">
                    <div class="">
                        <div class="card-header">
                            <div class="card-tools">
                            </div>
                            <h3 class="card-title">일반게시판</h3>
                            <div align="right">
                                <span class="badge bg-dark-subtle border border-dark-subtle text-dark-emphasis rounded-pill">전체 ${pagingVO.totalRecord }건</span>
                            </div>
                        </div>
                        <div class="card-body mt-3">
                            <div align="right">
                                <form class="input-group input-group-sm" method="post" id="searchForm" style="width: 440px;">
                                    <input type="hidden" name="page" id="page" />
                                    <select class="form-control" name="searchType">
                                        <option value="title">제목</option>
                                        <option value="writer">작성자</option>
                                    </select>
                                    <input type="text" name="searchWord" class="form-control float-right" value="" placeholder="Search">
                                    <div class="input-group-append">
                                        <button type="submit" class="btn btn-default">
                                            <i class="fas fa-search"></i>검색
                                        </button>
                                    </div>
                                </form>
                            </div>
                            <div class="mt-2">
                                <table class="table">
                                    <thead class="table-dark">
                                        <tr>
                                            <th style="width: 6%">#</th>
                                            <th style="width: px">제목</th>
                                            <th style="width: 12%">작성자</th>
                                            <th style="width: 12%">작성일</th>
                                            <th style="width: 10%">조회수</th>
                                        </tr>
                                    </thead>
                                    <tbody>
                                    	<c:set value="${pagingVO.dataList }" var="boardList" />
                                    	<c:choose>
                                    		<c:when test="${empty boardList }">
		                                        <tr>
		                                            <td colspan="5">조회하신 게시글이 존재하지 않습니다.</td>
		                                        </tr>
                                    		</c:when>
                                    		<c:otherwise>
                                    			<c:forEach items="${boardList }" var="board">
			                                        <tr>
			                                            <td>${board.boNo }</td>
			                                            <td>
			                                            	<a href="/board/detail.do?boNo=${board.boNo }">${board.boTitle }</a>
			                                            </td>
			                                            <td>${board.boWriter }</td>
			                                            <td>${board.boDate }</td>
			                                            <td>${board.boHit }</td>
			                                        </tr>
                                    			</c:forEach>
                                    		</c:otherwise>
                                    	</c:choose>
                                    </tbody>
                                </table>
                            </div>
                            <div align="left">
                                <a href="/board/form.do" class="btn btn-primary">등록</a>
                            </div>
                        </div>
                        <div class="card-footer clearfix mt-4" id="pagingArea">
                       		${pagingVO.pagingHTML }
                        </div>
                    </div>
                </div>
                <div class="col-md-12"><br /><br /><br /></div>
            </div>
        </div>
    </main>
</body>
<script type="text/javascript">
	$(function(){
		var pagingArea = $("#pagingArea");
		var searchForm = $("#searchForm");
		
		pagingArea.on("click", "a", function(event){
			event.preventDefault();
			var pageNo = $(this).data("page");
			searchForm.find("#page").val(pageNo);
			searchForm.submit();
		});
	});
</script>
</html>

@Controller
@RequestMapping("/board")
public class BoardRetreiveController {

	@Inject
	private IBoardService boardService;
	
	@RequestMapping(value = "/list.do")
	public String boardList(

- http://localhost/board/list.do

제목 검색 시
작성자 검색 시


[form.jsp]

<script type="text/javascript">
	$(function(){
		CKEDITOR.replace("boContent"); // CKEDITOR를 textarea에 등록
		
		var addBtn = $("#addBtn"); // 등록 버튼
		var cancelBtn = $("#cancelBtn"); // 취소 버튼
		var listBtn = $("#listBtn"); // 목록 버튼
		var boTitle = $("#boTitle");
		var boContent = $("#boContent");
		var boardForm = $("#boardForm"); // 등록 form
		
		// 등록 버튼 이벤트
		addBtn.on("click", function(){
			var title = boTitle.val(); // 제목 데이터
			
			// 일반적인 데이터를 가져올 때 사용하는 방법(textarea 사용시)
			//var content = boContent.val(); // 내용 데이터
			
			// CKEDITOR를 사용해서 데이터를 가져와야 할 때 사용하는 방법(editor 사용 시)
			var content = CKEDITOR.instances.boContent.getData(); // 내용 데이터
			
			// 제목을 입력하지 않았을 때 발생할 이벤트
			if(!title) {
				alert("제목을 입력해주세요!");
				boTitle.focus();
				return false;
			}
			
			// 내용을 입력하지 않았을 때 발생할 이벤트
			if(!content) {
				alert("내용을 입력해주세요!");
				boContent.focus();
				return false;
			}
			
			// 수정일 때, 경로를 변경한다. (기존은 등록 경로로 설정되어 있음)
			if($(this).val() == "수정"){
				boardForm.attr("action", "/board/update.do");
			}
			
			boardForm.submit();
		});
		
		// 취소 버튼 이벤트
		cancelBtn.on("click", function(){
			location.href = "/board/detail.do?boNo=${board.boNo}";
		});
		
		// 목록 버튼 이벤트
		listBtn.on("click", function(){
			location.href = "/board/list.do";
		});
	});
</script>