1) JUnit Test Case 파일 추가
2) @Test 애너테이션이 추가된 테스트 메소드 작성
3) [Run As] - [JUnit Test]
1) @Test : 실제 테스트 수행
2) @Before : @Test 이전에 수행
3) @BeforeAll : JUnit Test Case(BookUnitTest.java) 수행 이전, static 필수
4) @After : @Test 이후에 수행
5) @AfterAll : JUnit Test Case(BookUnitTest.java) 수행 이후, static 필수
1) 영속 계층(Dao)을 테스트 한다.
2) WAS(Tomcat)의 개입이 없으므로 WAS가 필요한 코드는 테스트 불가하다.
3) 메소드 이름을 한글로 작성해도 상관없다.

테스트 성공시 녹색바 , 실패시 적색바

테스트로 등록된 정보

*lib
hamcrest-core-1.3.jar
junit-4.13.2.jar
lombok.jar
mybatis-3.5.13.jar
ojdbc8.jar
taglibs 2종
*sql
[book.sql]
DROP TABLE BOOK_T;
CREATE TABLE BOOK_T (
BOOK_NO NUMBER NOT NULL,
TITLE VARCHAR2(100 BYTE) NOT NULL,
AUTHOR VARCHAR2(100 BYTE),
PRICE NUMBER,
PUBDATE DATE,
CONSTRAINT PK_BOOK PRIMARY KEY(BOOK_NO)
);
DROP SEQUENCE BOOK_SEQ;
CREATE SEQUENCE BOOK_SEQ NOCACHE;
쿼리문을 저장하는 장소, 설정 등
[mybatis-config.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>
<!--환경 설정(Transaction,Connection Pool)-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="oracle.jdbc.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:@localhost:1521:xe"/>
<property name="username" value="GD"/>
<property name="password" value="1111"/>
</dataSource>
</environment>
</environments>
<!--매퍼 설정(매퍼의 위치와 이름) -->
<!--매퍼는 쿼리문을 저장하는 장소 (매퍼의 파일 이름을 통상적으로 테이블의 이름을 따라서 지음) -->
<mappers>
<mapper resource="mybatis/mapper/book.xml"/>
</mappers>
</configuration>
[BookUnitTest.java]
package junit;
import static org.junit.Assert.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.Test;
import domain.BookDto;
import repository.BookDao;
/*
*/
public class BookUnitTest {
// Dao
private BookDao dao = BookDao.getDao();
// @Test
public void 책_등록_테스트() {
// 등록할 BookDto 생성
BookDto dto = BookDto.builder()
.title("테스트제목")
.author("테스트저자")
.price(999999)
.build();
// BookDto 등록
int addResult = dao.bookAdd(dto);
// 등록 결과 확인
assertEquals(1, addResult);
}
// @Test
public void 책_조회_테스트() {
// 조회할 책 번호
int bookNo = 1;
// 조회
BookDto dto = dao.bookDetail(bookNo);
// 조회 결과 확인
assertNotNull(dto);
}
// @Test
public void 책_목록_테스트() {
// begin, end를 가진 Map 생성
Map<String, Object> map = new HashMap<String, Object>();
map.put("begin", 1);
map.put("end", 20);
// 목록 가져오기
List<BookDto> list = dao.bookList(map);
// 결과 확인
assertEquals(1, list.size());
}
// @Test
public void 책_수정_테스트() {
// 수정할 BookDto 생성
BookDto dto = BookDto.builder()
.bookNo(1)
.title("[수정]제목")
.author("[수정]저자")
.price(-999999)
.build();
// 수정
int modifyResult = dao.bookModify(dto);
// 결과 확인
assertEquals(1, modifyResult);
}
@Test
public void 책_삭제_테스트() {
// 삭제할 책 번호
int bookNo = 1;
// 삭제
int deleteResult = dao.bookDelete(bookNo);
// 결과 확인
assertEquals(1, deleteResult);
}
}
테스트를 위한 쿼리문(테스트지만 실제 db에 저장된다.)
[book.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="mybatis.mapper.book" >
<select id="bookCount" resultType="int">
SELECT COUNT(*)
FROM BOOK_T
</select>
<!--BookDao의 bookList메소드에서 에서 bookList라는 select태그를 호출 그래서 아이디 필요-->
<select id="bookList" parameterType="Map" resultType="domain.BookDto">
SELECT A.BOOK_NO, A.TITLE, A.AUTHOR, A.PRICE, A.PUBDATE
FROM(SELECT BOOK_NO, TITLE, AUTHOR, PRICE, PUBDATE, ROW_NUMBER() OVER(ORDER BY BOOK_NO DESC)AS RN
FROM BOOK_T)A
WHERE A.RN BETWEEN #{begin} AND #{end}
</select>
<select id="bookDetail" parameterType="int" resultType="domain.BookDto">
SELECT BOOK_NO, TITLE, AUTHOR, PRICE, PUBDATE
FROM BOOK_T
WHERE BOOK_NO = #{bookNo}
</select>
<insert id="bookAdd" parameterType="domain.BookDto" >
INSERT INTO BOOK_T(
BOOK_NO
,TITLE
,AUTHOR
,PRICE
,PUBDATE
)VALUES(
BOOK_SEQ.NEXTVAL
,#{title}
,#{author}
,#{price}
,SYSDATE
)
</insert>
<update id="bookModify" parameterType="domain.BookDto">
UPDATE BOOK_T
SET TITLE = #{title}
, AUTHOR= #{author}
, PRICE= #{price}
WHERE BOOK_NO= #{bookNo}
</update>
<delete id="bookDelete" parameterType="domain.BookDto">
DELETE FROM BOOK_T
WHERE BOOK_NO = #{bookNo}
</delete>
</mapper>
[index.jsp]
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<c:set var="contextPath" value="<%=request.getContextPath()%>" />
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
</head>
<body>
<div>
<a href="${contextPath}/book/list.do">도서목록</a>
</div>
</body>
</html>

[BookDto]
package domain;
import java.sql.Date;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@AllArgsConstructor
@Data
@Builder
public class BookDto {
private int bookNo;
private String title;
private String author;
private int price;
private Date pubdate;
}
[BookDao]
package repository;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import domain.BookDto;
public class BookDao {
// mybatis의 SqlSession을 만들 수 있는 SqlSessionFactory 선언
private SqlSessionFactory factory;
// Singleton Pattern
private static BookDao dao = new BookDao();
private BookDao() {
// SqlSessionFactory 생성
try {
String resource = "mybatis/config/mybatis-config.xml";
InputStream in = Resources.getResourceAsStream(resource);
factory = new SqlSessionFactoryBuilder().build(in);
} catch (Exception e) {
e.printStackTrace();
}
}
public static BookDao getDao() {
return dao;
}
// 매퍼의 namespace
private final String NS = "mybatis.mapper.book.";
// 전체 개수 반환 메소드
public int bookCount() {
SqlSession ss = factory.openSession();
int count = ss.selectOne(NS + "bookCount");
ss.close();
return count;
}
// 목록 반환 메소드
public List<BookDto> bookList(Map<String, Object> map) {
SqlSession ss = factory.openSession();
List<BookDto> list = ss.selectList(NS + "bookList", map);
ss.close();
return list;
}
// 상세 반환 메소드
public BookDto bookDetail(int bookNo) {
SqlSession ss = factory.openSession();
BookDto dto = ss.selectOne(NS + "bookDetail", bookNo);
ss.close();
return dto;
}
// 등록 메소드
public int bookAdd(BookDto dto) {
SqlSession ss = factory.openSession(false); // false : 내가 커밋하겠다.
int addResult = ss.insert(NS + "bookAdd", dto);
if(addResult == 1) {
ss.commit();
}
ss.close();
return addResult;
}
// 수정 메소드
public int bookModify(BookDto dto) {
SqlSession ss = factory.openSession(false);
int modifyResult = ss.update(NS + "bookModify", dto);
if(modifyResult == 1) {
ss.commit();
}
ss.close();
return modifyResult;
}
// 삭제 메소드
public int bookDelete(int bookNo) {
SqlSession ss = factory.openSession(false);
int deleteResult = ss.delete(NS + "bookDelete", bookNo);
if(deleteResult == 1) {
ss.commit();
}
ss.close();
return deleteResult;
}
}
[PageVo.java]
package util;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@Data
public class PageVo {
private int page; // 현재 페이지 번호(요청 파라미터로 받는다.)
private int total; // 전체 항목의 개수(DB에서 구한 뒤 받는다.)
private int display; // 한 페이지에 표시할 항목의 개수(요청 파라미터로 받는다.)
private int begin; // 한 페이지에 표시되는 항목의 시작 번호(계산한다.)
private int end; // 한 페이지에 표시되는 항목의 종료 번호(계산한다.)
private int totalPage; // 전체 페이지의 개수(계산한다.)
private int pagePerBlock = 2; // 한 블록에 표시되는 페이지의 개수(임의로 정한다.)
private int beginPage; // 한 블록에 표시되는 페이지의 시작 번호(계산한다.)
private int endPage; // 한 블록에 표시되는 페이지의 종료 번호(계산한다.)
public void setPaging(int page, int total, int display) {
/* 한 페이지를 나타낼 때 필요한 정보 */
// 받은 정보 저장
this.page = page;
this.total = total;
this.display = display;
// 계산한 정보 저장
begin = (page - 1) * display + 1;
end = begin + display - 1;
if(end > total) {
end = total;
}
/* 전체 페이지를 나타낼 때 필요한 정보 */
// 전체 페이지 계산
totalPage = (int)Math.ceil((double)total / display);
// 각 블록의 시작 페이지와 종료 페이지 계산
beginPage = ((page - 1) / pagePerBlock) * pagePerBlock + 1;
endPage = beginPage + pagePerBlock - 1;
if(endPage > totalPage) {
endPage = totalPage;
}
}
public String getPaging(String url) {
StringBuilder sb = new StringBuilder();
sb.append("<div>");
// 이전 블록
if(beginPage == 1) {
sb.append("<span>이전</span>");
} else {
sb.append("<a href=\"" + url + "?page=" + (beginPage - 1) + "\">이전</a>");
}
// 페이지 번호
for(int p = beginPage; p <= endPage; p++) {
if(p == page) {
sb.append("<span>" + p + "</span>");
} else {
sb.append("<a href=\"" + url + "?page=" + p + "\">" + p + "</a>");
}
}
// 다음 블록
if(endPage == totalPage) {
sb.append("<span>다음</span>");
} else {
sb.append("<a href=\"" + url + "?page=" + (endPage + 1) + "\">다음</a>");
}
sb.append("</div>");
return sb.toString();
}
}
[BookFilter.java]
package filter;
import java.io.IOException;
import java.util.Arrays;
import java.util.Map;
import java.util.Map.Entry;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet Filter implementation class BookFilter
*/
@WebFilter("*.do")
public class BookFilter extends HttpFilter implements Filter {
/**
* @see HttpFilter#HttpFilter()
*/
public BookFilter() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see Filter#destroy()
*/
public void destroy() {
// TODO Auto-generated method stub
}
/**
* @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)
*/
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 컨트롤러가 실행되기 이전에 처리되는 코드
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
// 요청 인코딩
req.setCharacterEncoding("UTF-8");
// 요청 주소 확인
System.out.println(req.getRequestURI());
// 요청 파라미터 확인
Map<String, String[]> map = req.getParameterMap();
for(Entry<String, String[]> entry : map.entrySet()) {
System.out.println(entry.getKey() + ":" + Arrays.toString(entry.getValue()));
}
// pass the request along the filter chain
chain.doFilter(request, response);
}
/**
* @see Filter#init(FilterConfig)
*/
public void init(FilterConfig fConfig) throws ServletException {
// TODO Auto-generated method stub
}
}
[BookController.java]
package controller;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import common.ActionForward;
import service.BookService;
import service.BookServiceImpl;
/**
* Servlet implementation class BookController
*/
@WebServlet("*.do")
public class BookController extends HttpServlet {
private static final long serialVersionUID = 1L;
private BookService bookService = new BookServiceImpl();
/**
* @see HttpServlet#HttpServlet()
*/
public BookController() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// BookFilter 실행 후 Controller 실행
// 요청 인코딩(BookFilter가 수행함) + 응답 타입과 인코딩
// request.setCharacterEncoding("UTF-8");
response.setContentType("text/html; charset=UTF-8");
// 요청 주소 확인
String requestURI = request.getRequestURI();
String contextPath = request.getContextPath();
String urlMapping = requestURI.substring(contextPath.length());
// 어디로 어떻게 이동할 것인지 알고 있는 ActionForward 객체
ActionForward af = null;
// 요청에 따른 처리
switch(urlMapping) {
// 단순 이동 (forward 처리)
case "/book/write.do":
af = new ActionForward("/book/write.jsp", false);
break;
case "/index.do":
af = new ActionForward("/index.jsp", false);
break;
// 서비스 처리
case "/book/add.do":
af = bookService.bookAdd(request);
break;
case "/book/list.do":
af = bookService.bookList(request);
break;
case "/book/detail.do":
af = bookService.bookDetail(request);
break;
case "/book/edit.do":
af = bookService.bookEdit(request);
break;
case "/book/modify.do":
af = bookService.bookModify(request);
break;
case "/book/delete.do":
af = bookService.bookDelete(request);
break;
}
// 이동
if(af != null) {
if(af.isRedirect()) {
response.sendRedirect(af.getPath());
} else {
request.getRequestDispatcher(af.getPath()).forward(request, response);
}
}
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
[BookService.java]
package service;
import javax.servlet.http.HttpServletRequest;
import common.ActionForward;
public interface BookService {
public ActionForward bookList(HttpServletRequest request);
public ActionForward bookDetail(HttpServletRequest request);
public ActionForward bookAdd(HttpServletRequest request);
public ActionForward bookEdit(HttpServletRequest request);
public ActionForward bookModify(HttpServletRequest request);
public ActionForward bookDelete(HttpServletRequest request);
}
[BookServiceImpl.java]
package service;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import javax.servlet.http.HttpServletRequest;
import common.ActionForward;
import domain.BookDto;
import repository.BookDao;
import util.PageVo;
public class BookServiceImpl implements BookService {
private BookDao dao = BookDao.getDao();
private PageVo pageVo = new PageVo();
@Override
public ActionForward bookList(HttpServletRequest request) {
Optional<String> opt = Optional.ofNullable(request.getParameter("page"));
int page = Integer.parseInt(opt.orElse("1"));
int total = dao.bookCount();
int display = 10;
pageVo.setPaging(page, total, display);
Map<String, Object> map = new HashMap<String, Object>();
map.put("begin", pageVo.getBegin());
map.put("end", pageVo.getEnd());
List<BookDto> bookList = dao.bookList(map);
request.setAttribute("bookList", bookList);
request.setAttribute("paging", pageVo.getPaging(request.getContextPath() + "/book/list.do"));
return new ActionForward("/book/list.jsp", false);
}
@Override
public ActionForward bookDetail(HttpServletRequest request) {
Optional<String> opt = Optional.ofNullable(request.getParameter("bookNo"));
int bookNo = Integer.parseInt(opt.orElse("0"));
BookDto book = dao.bookDetail(bookNo);
request.setAttribute("book", book);
return new ActionForward("/book/detail.jsp", false);
}
@Override
public ActionForward bookAdd(HttpServletRequest request) {
String title = request.getParameter("title");
String author = request.getParameter("author");
int price = Integer.parseInt(request.getParameter("price"));
BookDto dto = BookDto.builder()
.title(title)
.author(author)
.price(price)
.build();
int addResult = dao.bookAdd(dto);
String path = null;
switch(addResult) {
case 0: path = request.getContextPath() + "/index.do"; break;
case 1: path = request.getContextPath() + "/book/list.do"; break;
}
return new ActionForward(path, true);
}
@Override
public ActionForward bookEdit(HttpServletRequest request) {
Optional<String> opt = Optional.ofNullable(request.getParameter("bookNo"));
int bookNo = Integer.parseInt(opt.orElse("0"));
BookDto book = dao.bookDetail(bookNo);
request.setAttribute("book", book);
return new ActionForward("/book/edit.jsp", false);
}
@Override
public ActionForward bookModify(HttpServletRequest request) {
int bookNo = Integer.parseInt(request.getParameter("bookNo"));
String title = request.getParameter("title");
String author = request.getParameter("author");
int price = Integer.parseInt(request.getParameter("price"));
BookDto dto = BookDto.builder()
.bookNo(bookNo)
.title(title)
.author(author)
.price(price)
.build();
int modifyResult = dao.bookModify(dto);
String path = null;
switch(modifyResult) {
case 0: path = request.getContextPath() + "/index.do"; break;
case 1: path = request.getContextPath() + "/book/detail.do?bookNo=" + bookNo; break;
}
return new ActionForward(path, true);
}
@Override
public ActionForward bookDelete(HttpServletRequest request) {
Optional<String> opt = Optional.ofNullable(request.getParameter("bookNo"));
int bookNo = Integer.parseInt(opt.orElse("0"));
int deleteResult = dao.bookDelete(bookNo);
String path = null;
switch(deleteResult) {
case 0: path = request.getContextPath() + "/index.do"; break;
case 1: path = request.getContextPath() + "/book/list.do"; break;
}
return new ActionForward(path, true);
}
}
[detail.jsp]
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<c:set var="contextPath" value="<%=request.getContextPath()%>" />
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
<style>
.link a {
margin-right: 20px;
}
</style>
<script>
$(function(){
fnDelete();
})
function fnDelete(){
$('#delete_link').click(function(event){
if(!confirm('도서 정보를 삭제할까요?')){
event.preventDefault();
return;
}
})
}
</script>
</head>
<body>
<div class="link">
<a href="${contextPath}/book/write.do">작성하러가기</a>
<a href="${contextPath}/book/list.do">목록으로이동</a>
<a href="${contextPath}/book/edit.do?bookNo=${book.bookNo}">수정하러가기</a>
<a id="delete_link" href="${contextPath}/book/delete.do?bookNo=${book.bookNo}">삭제하기</a>
</div>
<hr>
<div>
<div>책번호: ${book.bookNo}</div>
<div>제목: ${book.title}</div>
<div>저자: ${book.author}</div>
<div>가격: ${book.price}</div>
<div>출판일: ${book.pubdate}</div>
</div>
</body>
</html>

[list.jsp]
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<c:set var="contextPath" value="<%=request.getContextPath()%>" />
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
<style>
.book_list table {
border-collapse: collapse;
}
.book_list table td {
border-top: 1px solid gray;
border-bottom: 1px solid gray;
}
</style>
<script>
$(function(){
fnEvent();
})
function fnEvent(){
$('.btn_detail').click(function(){
location.href = '${contextPath}/book/detail.do?bookNo=' + $(this).parent().data('book_no');
})
$('.btn_edit').click(function(){
location.href = '${contextPath}/book/edit.do?bookNo=' + $(this).parent().data('book_no');
})
$('.btn_delete').click(function(){
if(confirm('도서 정보를 삭제할까요?')){
location.href = '${contextPath}/book/delete.do?bookNo=' + $(this).parent().data('book_no');
}
})
}
</script>
</head>
<body>
<div>
<a href="${contextPath}/book/write.do">작성하러가기</a>
</div>
<hr>
<div class="book_list">
<table>
<thead>
<tr>
<td>책번호</td>
<td>제목</td>
<td>저자</td>
<td></td>
</tr>
</thead>
<tbody>
<c:forEach var="book" items="${bookList}">
<tr>
<td>${book.bookNo}</td>
<td>${book.title}</td>
<td>${book.author}</td>
<td data-book_no="${book.bookNo}">
<button type="button" class="btn_detail">상세조회</button>
<button type="button" class="btn_edit">정보수정</button>
<button type="button" class="btn_delete">정보삭제</button>
</td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
<div>${paging}</div>
</body>
</html>

[write.jsp]
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<c:set var="contextPath" value="<%=request.getContextPath()%>" />
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
<script>
$(function(){
fnList();
fnAdd();
})
function fnList(){
$('#btn_list').click(function(){
location.href = '${contextPath}/book/list.do';
})
}
function fnAdd(){
$('#frm_add').submit(function(event){
if($('#title').val() === ''){
alert('제목은 필수입니다.');
$('#title').focus();
event.preventDefault();
return;
}
})
}
</script>
</head>
<body>
<div>
<form id="frm_add" method="post" action="${contextPath}/book/add.do">
<div>
<label for="title">제목</label>
<input type="text" id="title" name="title">
</div>
<div>
<label for="author">저자</label>
<input type="text" id="author" name="author">
</div>
<div>
<label for="price">가격</label>
<input type="text" id="price" name="price">
</div>
<div>
<button type="submit">작성완료</button>
<button type="reset">작성초기화</button>
<button type="button" id="btn_list">목록으로이동</button>
</div>
</form>
</div>
</body>
</html>

[edit.jsp]
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<c:set var="contextPath" value="<%=request.getContextPath()%>" />
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
<script>
$(function(){
fnList();
fnModify();
})
function fnList(){
$('#btn_list').click(function(){
location.href = '${contextPath}/book/list.do';
})
}
function fnModify(){
$('#frm_edit').submit(function(event){
if($('#title').val() === ''){
alert('제목은 필수입니다.');
$('#title').focus();
event.preventDefault();
return;
}
})
}
</script>
</head>
<body>
<div>
<form id="frm_edit" method="post" action="${contextPath}/book/modify.do">
<div>
<label for="title">제목</label>
<input type="text" id="title" name="title" value="${book.title}">
</div>
<div>
<label for="author">저자</label>
<input type="text" id="author" name="author" value="${book.author}">
</div>
<div>
<label for="price">가격</label>
<input type="text" id="price" name="price" value="${book.price}">
</div>
<div>
<input type="hidden" name="bookNo" value="${book.bookNo}">
<button type="submit">수정완료</button>
<button type="reset">작성초기화</button>
<button type="button" id="btn_list">목록으로이동</button>
</div>
</form>
</div>
</body>
</html>

