
@NotNull : null만 허용하지 않음
@NotBlank: null, ""(빈문자열)," "(공백)을 허용하지 않음
@NotEmpty : null과 ""(빈문자열)을 허용하지 않음
package kr.spring.board.vo;
import java.sql.Date;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
public class BoardVO {
private int num;
@NotBlank
private String writer;
@NotEmpty
private String title;
@NotBlank
private String passwd;
@NotEmpty
private String content;
private Date reg_date;
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public String getWriter() {
return writer;
}
public void setWriter(String writer) {
this.writer = writer;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getPasswd() {
return passwd;
}
public void setPasswd(String passwd) {
this.passwd = passwd;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Date getReg_date() {
return reg_date;
}
public void setReg_date(Date reg_date) {
this.reg_date = reg_date;
}
@Override
public String toString() {
return "BoardVO [num=" + num + ", writer=" + writer + ", title=" + title + ", passwd=" + passwd + ", content="
+ content + ", reg_date=" + reg_date + "]";
}
}
에러 문구 작성
NotBlank.writer= 작성자는 필수 입력 항목입니다. 공백 없이 작성해주세요.
NotEmpty.title= 제목은 필수 입력 항목입니다.
NotBlank.passwd= 비밀번호는 필수 입력 항목입니다.
NotEmpty.content= 내용은 필수 입력 항목입니다.
invalidPassword = 비밀번호가 불일치합니다.
리소스 번들 지정
컨트롤러 빈 자동 스캔을 통해서 자동으로 빈 컨테이너에 연결 될 수 있도록 해줌
<!-- Controller 빈 자동 스캔 -->
<context:component-scan base-package="kr.spring.board.controller"/>
<!-- 리소스 번들 지정 -->
<beans:bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<beans:property name="basenames">
<beans:list>
<beans:value>messages.validation</beans:value>
</beans:list>
</beans:property>
</beans:bean>
로그 레벨
FATAL : 가장 심각한 오류를 의미
ERROR : 일반적인 오류
WARN : 주의를 요하는 경우
INFO : 런타임시 관심있는 경우
DEBUG : 시스템 흐름과 관련된 상세 정보
TRACE : 가장 상세한 정보
package kr.spring.board.controller;
import javax.validation.Valid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import kr.spring.board.vo.BoardVO;
@Controller
public class BoardController {
// 로그 처리(로그 대상 지정)
private static final Logger log = LoggerFactory.getLogger(BoardController.class);
// 유효성 체크를 위한 폼 초기화
@ModelAttribute
public BoardVO initCommand() {
return new BoardVO();
}
@RequestMapping("/list.do")
public ModelAndView process() {
ModelAndView mav = new ModelAndView();
mav.setViewName("selectList");
return mav;
}
// 폼 호출하기
@GetMapping("/insert.do")
public String form() {
return "insertForm";
}
// 폼 입력하기
@PostMapping("/insert.do")
public String submit(@ModelAttribute @Valid BoardVO vo, BindingResult result) {
log.debug("<<BoardVO>> : " + vo);
// 유효성 체크 결과 오류가 있다면 폼을 호출한다
if(result.hasErrors()) {
return form();
}
return "redirect:/list.do";
}
}

jdbc.driverClassName = oracle.jdbc.OracleDriver
jdbc.url = jdbc:oracle:thin:@localhost:1251:xe
jdbc.username = user01
jdbc.password = 1234
service와 dao 자동 스캔
kr.spring.board를 지정해서 자동 스캔하도록 했는데 위에서 이미 controller를 자동스캔 하도록 지정했기 때문에 controller는 제외할 수 있게 아래 설정 부분에 추가
JDBC 연결을 위한 필수 설정
el 표기법으로 불러오기 가능함
JDBC 템플릿을 사용하고 있기 때문에 코드를 더 간결하게 표현할 수 있음
어노테이션 방식을 사용할 수 있도록 추가해줌
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 빈 자동 스캔 - servlet-context.xml에서 Controller를
자동 스캔 설정해서 아래 설정에서는 Controller 자동 스캔 제외 -->
<context:component-scan base-package="kr.spring.board">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- properties 컨테이너에 등록 -->
<context:property-placeholder location="classpath:config/jdbc.properties"/>
<!-- 커넥션 풀을 이용한 DataSource 설정 -->
<!-- 내용 불러오는데 el 태그로 불러올 수 있음 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<!-- 최대 커넥션 개수 -->
<property name="maxActive" value="50"/>
<!-- 접속이 없을 경우 최대 유지 커넥션 개수 -->
<property name="maxIdle" value="30"/>
<!-- 접속이 없을 경우 최소 유지 커넥션 개수 -->
<property name="minIdle" value="20"/>
<!-- 최대 대기시간(초) : 초과시 연결실패 오류 발생 -->
<property name="maxWait" value="5"/>
</bean>
<!-- JdbcTemplate 객체 생성 -->
<bean name="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- JDBC 기반 트랜잭션 관리자 설정 -->
<bean id="transactionManager" p:dataSource-ref="dataSource"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager" />
<!-- 어노테이션 방식으로 처리할 때 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
Interface에서 정의된 메소드만 호출이 가능하다
Interface에서 명시되어있는 메소드는 꼭 호출을 하겠다 라는 의미로 해석할 수 있다.
Interface에 있는 틀이 중요한 역할을 실행한다.
다른 형태가 나올 수 없도록 틀을 만들어 주는 역할을 한다고 보면 된다.
package kr.spring.board.dao;
import java.util.List;
import kr.spring.board.vo.BoardVO;
public interface BoardDAO {
public void insertBoard(BoardVO board);
public int getBoardCount();
public List<BoardVO> getBoardList(int startRow, int endRow);
public BoardVO getBoard(int num);
public void updateBoard(BoardVO board);
public void deleteBoard(int num);
}
SQL 문장을 상수 형태로 만든다.
바로 바로 SQL 문장을 찾아서 수정하고, 해결할 수 있어 장점이 있다.
JDBC의 특성이 record안에 자동으로 매핑이 어렵기 때문에
RowMapper 객체를 생성하여 mapRow를 생성한다.
ResultSet으로부터 데이터를 추출하고 이를 자바 객체로 변환하는 역할을 한다.
private RowMapper<BoardVO> rowMapper = new RowMapper<BoardVO>() {
public BoardVO mapRow(ResultSet rs, int rowNum) throws SQLException {
BoardVO board = new BoardVO();
board.setNum(rs.getInt("num"));
board.setWriter(rs.getString("writer"));
board.setTitle(rs.getString("title"));
board.setPasswd(rs.getString("passwd"));
board.setContent(rs.getString("content"));
board.setReg_date(rs.getDate("reg_date"));
return board;
}};
JDBC의 UPDATE 메소드를 사용하여 sql을 실행한다.
INSERT_SQL 문자열에서 각 ? 자리에 new Object[] 배열의 값을 대입
완성된 SQL 문을 데이터베이스에 실행하여 aboard에 새로운 레코드를 삽입
@Override
public void insertBoard(BoardVO board) {
jdbcTemplate.update(INSERT_SQL, new Object[] {board.getWriter(), board.getTitle(), board.getPasswd(), board.getContent()});
}
JdbcTemplate의 query 메서드를 사용하여 SQL 쿼리를 실행하고, 그 결과를 BoardVO 객체 리스트로 매핑한다.
new Object[] {startRow, endRow}
rowMapper
@Override
public List<BoardVO> getBoardList(int startRow, int endRow) {
// list 형태로 만들어서 반환시켜준다
List<BoardVO> list = jdbcTemplate.query(SELECT_LIST_SQL, new Object[] {startRow,endRow},rowMapper);
return list;
}
package kr.spring.board.dao;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;
import kr.spring.board.vo.BoardVO;
@Repository
public class BoardDAOImpl implements BoardDAO{
private static final String INSERT_SQL = "INSERT INTO aboard (num, writer, title, passwd, content, reg_date) VALUES(aboard_seq.nextval,?,?,?,?,SYSDATE)";
private static final String SELECT_COUNT_SQL = "SELECT COUNT(*) FROM aboard";
private static final String SELECT_LIST_SQL = "SELECT * FROM (SELECT a.*, rownum rnum FROM("
+ "SELECT * FROM aboard ORDER BY reg_date DESC)a)"
+ "WHERE rnum >= ? AND rnum <= ?";
private static final String SELECT_DETAIL_SQL = "SELECT * FROM aboard WHERE num=?";
private static final String UPDATE_SQL = "UPDATE aboard SET writer=?, title=?, content=? WHERE num=?";
private static final String DELETE_SQL = "DELETE FROM aboard WHERE num=?";
// 하나의 레코드의 데이터를 자바빈에 매핑
private RowMapper<BoardVO> rowMapper = new RowMapper<BoardVO>() {
public BoardVO mapRow(ResultSet rs, int rowNum) throws SQLException {
BoardVO board = new BoardVO();
board.setNum(rs.getInt("num"));
board.setWriter(rs.getString("writer"));
board.setTitle(rs.getString("title"));
board.setPasswd(rs.getString("passwd"));
board.setContent(rs.getString("content"));
board.setReg_date(rs.getDate("reg_date"));
return board;
}};
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public void insertBoard(BoardVO board) {
jdbcTemplate.update(INSERT_SQL, new Object[] {board.getWriter(), board.getTitle(), board.getPasswd(), board.getContent()});
}
@Override
public int getBoardCount() {
// 내부적으로 실행 후에 integer 값으로 반환
return jdbcTemplate.queryForObject(SELECT_COUNT_SQL, Integer.class);
}
@Override
public List<BoardVO> getBoardList(int startRow, int endRow) {
// list 형태로 만들어서 반환시켜준다
List<BoardVO> list = jdbcTemplate.query(SELECT_LIST_SQL, new Object[] {startRow,endRow},rowMapper);
return list;
}
@Override
public BoardVO getBoard(int num) {
return jdbcTemplate.queryForObject(SELECT_DETAIL_SQL, new Object[] {num},rowMapper);
}
@Override
public void updateBoard(BoardVO board) {
jdbcTemplate.update(UPDATE_SQL, new Object[] {board.getWriter(), board.getTitle(), board.getContent(), board.getNum()});
}
@Override
public void deleteBoard(int num) {
jdbcTemplate.update(DELETE_SQL, new Object[] {num});
}
}
스프링은 jdbc 처리할 때 메소드로 표현했기 때문에 서비스 안에서 트랜잭션을 처리할 수 있도록 해준다.
Service는 Controller에서 호출한다. 그 때 Interface 타입을 사용한다.
Interface 안에 명시되지 않은 메소드는 호출되지 않는다.
Interface 내부에 없는 코드는 접근 제한이 된다고 생각하면 더 쉽게 이해가 될 것이다.
그렇다면 서비스를 사용하는 이유는 어떻게 될까?
1. 비즈니스 로직의 분리
서비스 계층은 비즈니스 로직을 포함하고 있으며, 데이터 접근 로직(DAO)과 웹 계층(Controller)을 분리합니다. 이렇게 하면 다음과 같은 이점이 있습니다:
2. 트랜잭션 관리
서비스 계층은 트랜잭션을 관리하는 적절한 위치입니다. @Transactional 어노테이션을 사용하여 서비스 메서드가 트랜잭션 내에서 실행되도록 할 수 있습니다. 이를 통해 데이터 일관성을 유지하고 롤백 메커니즘을 쉽게 구현할 수 있습니다.
3. 컨트롤러의 간결성
컨트롤러는 주로 HTTP 요청을 처리하고, 요청 데이터를 서비스 계층에 전달하며, 서비스 계층의 결과를 뷰로 반환하는 역할을 합니다. 서비스 계층을 사용하면 컨트롤러가 간결해지고, 비즈니스 로직과 데이터 접근 로직을 포함하지 않게 되어 코드의 가독성과 유지보수성이 향상됩니다.
4. 의존성 주입 및 결합도 감소
서비스 계층을 사용하면 컨트롤러와 DAO 사이의 결합도를 줄일 수 있습니다. 컨트롤러는 서비스 인터페이스에 의존하고, 서비스 구현체는 DAO에 의존하는 형태로 구성됩니다. 이를 통해 애플리케이션의 모듈화와 테스트 용이성이 높아집니다.
5. 테스트 용이성
서비스 계층을 통해 비즈니스 로직이 캡슐화되므로, 서비스 계층을 단위 테스트할 수 있습니다. 모의 객체(Mock)를 사용하여 DAO와 독립적으로 서비스 계층을 테스트할 수 있습니다. 또한, 통합 테스트에서는 서비스 계층과 DAO가 함께 동작하는지를 확인할 수 있습니다.
package kr.spring.board.service;
import java.util.List;
import kr.spring.board.vo.BoardVO;
public interface BoardService {
public void insertBoard(BoardVO board);
public int getBoardCount();
public List<BoardVO> getBoardList(int startRow, int endRow);
public BoardVO getBoard(int num);
public void updateBoard(BoardVO board);
public void deleteBoard(int num);
}
package kr.spring.board.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import kr.spring.board.dao.BoardDAO;
import kr.spring.board.vo.BoardVO;
@Service
@Transactional
public class BoardServiceImpl implements BoardService{
@Autowired
private BoardDAO boardDAO;
@Override
public void insertBoard(BoardVO board) {
boardDAO.insertBoard(board);
}
@Override
public int getBoardCount() {
return boardDAO.getBoardCount();
}
@Override
public List<BoardVO> getBoardList(int startRow, int endRow) {
return boardDAO.getBoardList(startRow, endRow);
}
@Override
public BoardVO getBoard(int num) {
return boardDAO.getBoard(num);
}
@Override
public void updateBoard(BoardVO board) {
boardDAO.updateBoard(board);
}
@Override
public void deleteBoard(int num) {
boardDAO.deleteBoard(num);
}
}
package kr.spring.board.controller;
import java.util.List;
import javax.validation.Valid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import kr.spring.board.service.BoardService;
import kr.spring.board.vo.BoardVO;
import kr.spring.util.PagingUtil;
@Controller
public class BoardController {
@Autowired
private BoardService boardService;
/*
로그 레벨
FATAL : 가장 심각한 오류를 의미
ERROR : 일반적인 오류
WARN : 주의를 요하는 경우
INFO : 런타임시 관심있는 경우
DEBUG : 시스템 흐름과 관련된 상세 정보
TRACE : 가장 상세한 정보
*/
// 로그 처리(로그 대상 지정)
private static final Logger log = LoggerFactory.getLogger(BoardController.class);
// 유효성 체크를 위한 폼 초기화
@ModelAttribute
public BoardVO initCommand() {
return new BoardVO();
}
@RequestMapping("/list.do")
// value = pageNum, 따로 들어오는 값이 없다면 1유지
public ModelAndView process(@RequestParam(value = "pageNum", defaultValue = "1") int currentPage) {
int count = boardService.getBoardCount();
log.debug("<<count>> : "+ count );
// 페이지 처리하기
PagingUtil page = new PagingUtil(currentPage, count, 20,10, "list.do");
// 목록 호출
List<BoardVO> list = null;
if(count > 0) {
list = boardService.getBoardList(page.getStartRow(), page.getEndRow());
}
ModelAndView mav = new ModelAndView();
mav.setViewName("selectList");
mav.addObject("count", count);
mav.addObject("list", list);
mav.addObject("page",page.getPage());
return mav;
}
// 폼 호출하기
@GetMapping("/insert.do")
public String form() {
return "insertForm";
}
// 폼 입력하기
@PostMapping("/insert.do")
public String submit(@Valid BoardVO vo, BindingResult result) {
log.debug("<<BoardVO>> : " + vo);
// 유효성 체크 결과 오류가 있다면 폼을 호출한다
if(result.hasErrors()) {
return form();
}
// 글 등록하기
boardService.insertBoard(vo);
return "redirect:/list.do";
}
// 글 상세보기
@RequestMapping("/detail.do")
public ModelAndView detail(int num) {
log.debug("<<num>> : " + num);
BoardVO board = boardService.getBoard(num);
// 뷰 이름 속성명 속성값
return new ModelAndView("selectDetail", "board",board);
}
// 수정하는 폼
@GetMapping("/update.do")
public String formUpdate(int num, Model model) {
model.addAttribute("boardVO", boardService.getBoard(num));
return "updateForm";
}
// 수정하기(전송된 데이터 처리)
@PostMapping("/update.do")
public String submitUpdate(@Valid BoardVO vo, BindingResult result) {
log.debug("<<UpdateBoardVO>> : "+ vo);
if(result.hasErrors()) {
return "updateForm";
}
// DB에서 저장된
BoardVO db_board = boardService.getBoard(vo.getNum());
if(!db_board.getPasswd().equals(vo.getPasswd())) {
result.rejectValue("passwd", "invalidPassword");
return "updateForm";
}
boardService.updateBoard(vo);
return "redirect:/list.do";
}
// 삭제 폼
@GetMapping("/delete.do")
public String formDelete(BoardVO vo) {
return "deleteForm";
}
// 전송된 데이터 처리하기
@PostMapping("/delete.do")
public String submitDelete(@Valid BoardVO vo, BindingResult result) {
log.debug("<<deleteBoard>> : " + vo);
// 비밀번호만 유효성 체크 결과 오류가 있다면 폼 호출
if(result.hasFieldErrors("passwd")) {
return "deleteForm";
}
// DB에서 저장된
BoardVO db_board = boardService.getBoard(vo.getNum());
if(!db_board.getPasswd().equals(vo.getPasswd())) {
result.rejectValue("passwd", "invalidPassword");
return "deleteForm";
}
boardService.deleteBoard(vo.getNum());
return "redirect:/list.do";
}
}
<%@ page language="java" contentType="text/html; charset=EUC-KR"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>게시판 목록</title>
<link rel="stylesheet" href="${pageContext.request.contextPath}/resources/css/style.css" type="text/css">
</head>
<body>
<div class="page-main">
<h2>게시판 목록</h2>
<div class="align-right">
<input type="button" value="글쓰기" onclick="location.href='insert.do'">
</div>
<c:if test="${count == 0 }">
<div class="result-display">표시할 게시물이 없습니다.</div>
</c:if>
<c:if test="${count > 0 }">
<table>
<tr>
<th>번호</th>
<th>제목</th>
<th>작성자</th>
<th>작성일</th>
</tr>
<c:forEach var="board" items="${list }">
<tr>
<td>${board.num }</td>
<td><a href="detail.do?num=${board.num }">${board.title }</a></td>
<td>${board.writer }</td>
<td>${board.reg_date }</td>
</tr>
</c:forEach>
</table>
<div class="align-center">${page }</div>
</c:if>
</div>
</body>
</html>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>글 쓰기</title>
<link rel="stylesheet" href="${pageContext.request.contextPath}/resources/css/style.css" type="text/css">
</head>
<body>
<div class="page-main">
<h2>글 쓰기</h2>
<form:form action="insert.do" modelAttribute="boardVO">
<ul>
<li>
<form:label path="writer">작성자</form:label>
<form:input path="writer"/>
<form:errors element="div" path="writer" cssClass="error-color"/>
</li>
<li>
<form:label path="title">제목</form:label>
<form:input path="title"/>
<form:errors element="div" path="title" cssClass="error-color"/>
</li>
<li>
<form:label path="passwd">비밀번호</form:label>
<form:password path="passwd"/>
<form:errors element="div" path="passwd" cssClass="error-color"/>
</li>
<li>
<form:label path="content" >내용</form:label>
<form:textarea path="content" cols="5" rows="20"/>
<form:errors element="div" path="content" cssClass="error-color"/>
</li>
</ul>
<div class="align-center">
<form:button>등록</form:button>
<input type="button" value="목록" onclick="location.href='list.do'">
</div>
</form:form>
</div>
</body>
</html>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>글 상세 보기</title>
<link rel="stylesheet" href="${pageContext.request.contextPath}/resources/css/style.css" type="text/css">
</head>
<body>
<div class="page-main">
<h2> ${board.title}</h2>
<p>
글 번호 : ${board.num}<br>
작성자 : ${board.writer}<br>
작성일 : ${board.reg_date}<br>
</p>
<hr width="100%" size="1" noshade="noshade">
<p>
${board.content}
</p>
<hr width="100%" size="1" noshade="noshade">
<div class="align-right" id="detail_btn">
<input type="button" value="수정" onclick="location.href='update.do?num=${board.num}'">
<input type="button" value="삭제" onclick="location.href='delete.do?num=${board.num}'">
<input type="button" value="목록" onclick="location.href='list.do'">
</div>
</div>
</body>
</html>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>글 수정</title>
<link rel="stylesheet" href="${pageContext.request.contextPath}/resources/css/style.css" type="text/css">
</head>
<body>
<div class="page-main">
<h2>글 수정</h2>
<form:form action="update.do" modelAttribute="boardVO">
<form:hidden path="num"/>
<ul>
<li>
<form:label path="writer">작성자</form:label>
<form:input path="writer"/>
<form:errors element="div" path="writer" cssClass="error-color"/>
</li>
<li>
<form:label path="title">제목</form:label>
<form:input path="title"/>
<form:errors element="div" path="title" cssClass="error-color"/>
</li>
<li>
<form:label path="passwd">비밀번호</form:label>
<form:password path="passwd"/>
<form:errors element="div" path="passwd" cssClass="error-color"/>
</li>
<li>
<form:label path="content" >내용</form:label>
<form:textarea path="content" cols="5" rows="20"/>
<form:errors element="div" path="content" cssClass="error-color"/>
</li>
</ul>
<div class="align-center">
<form:button>수정</form:button>
<input type="button" value="목록" onclick="location.href='list.do'">
</div>
</form:form>
</div>
</body>
</html>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE html>**텍스트**
<html>
<head>
<meta charset="UTF-8">
<title>글 삭제</title>
<link rel="stylesheet" href="${pageContext.request.contextPath}/resources/css/style.css" type="text/css">
</head>
<body>
<div class="page-main">
<h2>글 삭제</h2>
<form:form action="delete.do" modelAttribute="boardVO">
<form:hidden path="num"/>
<ul>
<li>
<form:label path="passwd">비밀번호</form:label>
<form:password path="passwd" />
<form:errors element="div" path="passwd" cssClass="error-color"/>
</li>
</ul>
<div class="align-center">
<form:button>삭제</form:button>
<input type="button" value="목록" onclick="location.href='list.do'">
</div>
</form:form>
</div>
</body>
</html>
결과 화면들