servlet-context.xml
에 지정해두면, 해당 요청 URL들이 호출될 때 요청을 낚아채 그 페이지의 로케일만 변경하는 방식servlet-context.xml
의 <beans:beans>
태그를 다음처럼 수정<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
servlet-context.xml
의 <beans:beans>
태그 사이에 다음의 내용을 추가 <!-- localChangeInterceptor를 이용한 로케일 처리 -->
<beans:bean id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor" p:paramName="language"/>
<interceptors>
<interceptor>
<mapping path="/login/login.do"/>
<beans:ref bean="localeChangeInterceptor"/>
</interceptor>
</interceptors>
index.jsp
의 <body>
태그 사이에 다음 내용을 추가<a href="${pageContext.request.contextPath}/login/login.do?language=ko">/login/login.do?language=ko</a><br>
<a href="${pageContext.request.contextPath}/login/login.do?language=en">/login/login.do?language=en</a><br>
----------------------------<br>
WEB-INF
에 새 파일 file.txt
생성kr.spring.ch10.view
생성하고 새 클래스 DownloadView
생성package kr.spring.ch10.view;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.servlet.view.AbstractView;
public class DownloadView extends AbstractView {
public DownloadView() {
setContentType("application/download; charset=utf-8");
}
@Override
protected void renderMergedOutputModel(Map<String, Object> model,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
File file = (File) model.get("downloadFile");
response.setContentType(getContentType());
response.setContentLength((int) file.length());
String userAgent = request.getHeader("User-Agent");
boolean ie = userAgent.indexOf("MSIE") > -1;
String fileName = null;
if (ie) {
fileName = URLEncoder.encode(file.getName(), "utf-8");
} else {
fileName = new String(file.getName().getBytes("utf-8"),
"iso-8859-1");
}
response.setHeader("Content-Disposition", "attachment; filename=\""
+ fileName + "\";");
response.setHeader("Content-Transfer-Encoding", "binary");
OutputStream out = response.getOutputStream();
FileInputStream fis = null;
try {
fis = new FileInputStream(file);
FileCopyUtils.copy(fis, out);
} finally {
if (fis != null)
try {
fis.close();
} catch (IOException ex) {
}
}
out.flush();
}
}
kr.spring.ch10.controller
생성하고 새 클래스 DownloadController
생성package kr.spring.ch10.controller;
import java.io.File;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class DownloadController implements ApplicationContextAware {
private WebApplicationContext context;
@RequestMapping("/file.do")
public ModelAndView download() throws Exception {
// 상대 경로를 통해서 절대 경로 구하기
String path = context.getServletContext().getRealPath("/WEB-INF/file.txt");
File downloadFile = new File(path);
return new ModelAndView("download", "downloadFile", downloadFile); // view 이름, 속성명, 속성값을 생성자 인자로 전달
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = (WebApplicationContext)applicationContext;
}
}
servlet-context.xml
의 <beans:beans>
태그 사이에 다음의 내용을 추가 <!-- view의 이름과 bean의 이름이 같으면 해당 bean을 view로 호출 -->
<beans:bean id="viewResolver" class="org.springframework.web.servlet.view.BeanNameViewResolver" p:order="0"/>
<!-- 파일 다운로드 -->
<beans:bean class="kr.spring.ch10.controller.DownloadController"/>
<beans:bean id="download" class="kr.spring.ch10.view.DownloadView"/>
servlet-context.xml
의 <beans:beans>
태그 사이의 내용을 다음처럼 수정 <!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
<beans:property name="order" value="1"/> <!-- View Resolver가 2개이므로 충돌하지 않도록 우선순위를 서로 다르게 지정 -->
</beans:bean>
index.jsp
의 <body>
태그 사이에 다음 내용을 추가<a href="${pageContext.request.contextPath}/file.do">DownloadController</a><br>
@NotEmpty
javax.validation.constraints
패키지에 포함되어 있음@Size(min=a, max=b)
javax.validation.constraints
패키지에 포함되어 있음@Range(min=a, max=b)
org.hibernate.validator.constraints
패키지에 포함되어 있음@Email
javax.validation.constraints
패키지에 포함되어 있음@Valid
javax.validation
패키지에 포함되어 있음pom.xml
에서 <dependencies>
태그 사이에 다음의 내용을 추가 <dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.18.Final</version>
</dependency>
kr.spring.ch11.vo
생성하고 새 클래스 MemberVO
생성package kr.spring.ch11.vo;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import org.hibernate.validator.constraints.Range;
public class MemberVO {
@Pattern(regexp="^[0-9a-zA-Z]+$") // 정규표현식으로 패턴 검사
private String id;
@Size(min=4, max=10) // 문자열의 길이 지정
private String password;
@NotEmpty
private String name;
@Range(min=1, max=200)
private int age;
@Email @NotEmpty
private String email;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "MemberVO [id=" + id + ", password=" + password + ", name=" + name + ", age=" + age + ", email=" + email
+ "]";
}
}
validation.properties
하단에 다음의 내용을 추가# 어노테이션을 이용한 유효성 검증시에는 어노테이션명.필드명 형식으로 에러 코드 사용
NotEmpty.id=아이디는 필수 항목
Pattern.id=아이디는 영문자와 숫자만 입력 가능
Size.password=비밀번호는 4자 이상 10자 이하로 입력
NotEmpty.name=이름은 필수 항목
Range.age=나이는 1세 이상 200세 이하로 입력
typeMismatch.age=나이는 숫자만 입력 가능
NotEmpty.email=이메일은 필수 항목
Email.email=이메일 형식에 맞게 입력
kr.spring.ch11.controller
생성하고 새 클래스 MemberWriteController
생성package kr.spring.ch11.controller;
import javax.validation.Valid;
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 kr.spring.ch11.vo.MemberVO;
@Controller
public class MemberWriteController {
// 자바빈 초기화
@ModelAttribute("vo")
public MemberVO initCommand() {
return new MemberVO();
}
// 폼 호출
@GetMapping("/member/write.do")
public String form() {
return "member/write";
}
// 폼에서 전송된 데이터 처리
@PostMapping("/member/write.do")
public String submit(@ModelAttribute("vo") @Valid MemberVO memberVO, BindingResult result) {
System.out.println("전송된 데이터 : " + memberVO);
// 전송된 데이터 유효성 검증 결과 오류가 있으면 폼 호출
if(result.hasErrors()) {
return form();
}
return "redirect:/index.jsp";
}
}
views
폴더에 하위 폴더로 member
생성하고 새 JSP 파일 write.jsp
생성<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>회원 가입</title>
</head>
<body>
<form:form modelAttribute="vo">
<form:errors element="div"/>
<ul>
<li>
<form:label path="id">아이디</form:label>
<form:input path="id"/>
<form:errors path="id"/>
</li>
<li>
<form:label path="password">비밀번호</form:label>
<form:password path="password"/>
<form:errors path="password"/>
</li>
<li>
<form:label path="name">이름</form:label>
<form:input path="name"/>
<form:errors path="name"/>
</li>
<li>
<form:label path="age">나이</form:label>
<form:input path="age"/>
<form:errors path="age"/>
</li>
<li>
<form:label path="email">이메일</form:label>
<form:input path="email"/>
<form:errors path="email"/>
</li>
</ul>
<div>
<form:button>전송</form:button>
</div>
</form:form>
</body>
</html>
servlet-context.xml
의 <beans:beans>
태그 사이에 다음의 내용을 추가 <!-- 어노테이션을 이용한 유효성 검증 -->
<beans:bean class="kr.spring.ch11.controller.MemberWriteController"/>
index.jsp
의 <body>
태그 사이에 다음 내용을 추가<a href="${pageContext.request.contextPath}/member/write.do">MemberWriteController</a><br>
정규표현식 | 의미 | 적용 |
---|---|---|
\d{6}-[1234]\d{6} |
주민등록번호 | 900101-1234567 |
\w+@\w+\.\w+ |
이메일 | test@test.com |
pom.xml
에서 <dependencies>
태그 사이에 다음의 내용을 추가 <dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.5</version>
</dependency>
kr.spring.ch12.controller
생성하고 새 클래스 PageRank
생성package kr.spring.ch12.controller;
public class PageRank {
private int rank;
private String page;
public PageRank() {}
public PageRank(int rank, String page) {
this.rank = rank;
this.page = page;
}
public int getRank() {
return rank;
}
public void setRank(int rank) {
this.rank = rank;
}
public String getPage() {
return page;
}
public void setPage(String page) {
this.page = page;
}
}
PageReportController
생성package kr.spring.ch12.controller;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class PageReportController {
@RequestMapping("/pageJsonReport.do")
@ResponseBody // @ResponseBody 어노테이션은 응답 화면을 생성
public List<PageRank> jsonReport() { // 잭슨 라이브러리 사용시 메서드가 반환한 List나 Map, 자바빈 객체를 JSON 문자열로 변환
List<PageRank> pageRanks = new ArrayList<PageRank>();
pageRanks.add(new PageRank(1, "/board/list.do"));
pageRanks.add(new PageRank(2, "/board/detail.do"));
pageRanks.add(new PageRank(3, "/board/write.do"));
pageRanks.add(new PageRank(4, "/board/update.do"));
return pageRanks;
}
}
servlet-context.xml
의 <beans:beans>
태그 사이에 다음의 내용을 추가 <!-- JSON 파싱 -->
<beans:bean class="kr.spring.ch12.controller.PageReportController"/>
index.jsp
의 <body>
태그 사이에 다음 내용을 추가<a href="${pageContext.request.contextPath}/pageJsonReport.do">PageReportController</a><br>
kr.spring.ch13.vo
생성하고 새 클래스 MemberVO
생성package kr.spring.ch13.vo;
public class MemberVO {
private String id;
private String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
kr.spring.ch13.controller
생성하고 새 클래스 RestMainController
생성package kr.spring.ch13.controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import kr.spring.ch13.vo.MemberVO;
/*
* @RestController는 @ResponseBody와 @Controller 어노테이션의 조합
* 일반적으로 REST 컨트롤러를 생성할 때 사용
*/
@RestController
public class RestMainController {
@RequestMapping("/member") // REST 스타일에서는 요청 URL에 확장자를 사용하지 않음
public MemberVO getMember() { // 잭슨 라이브러리 사용시 메서드가 반환한 List나 Map, 자바빈 객체를 JSON 문자열로 변환
MemberVO vo = new MemberVO();
vo.setId("test");
vo.setName("실험");
return vo;
}
/*
* @PathVariable 어노테이션은 URL에 포함된 값을 처리하기 위해 사용
* {id}는 값이 변수임을 의미하며, @PathVariable은 URL의 변수 {id}의 값이 메서드 인자 id에 바인딩되도록 함
*/
@RequestMapping("/member/id/{id}/name/{name}")
public MemberVO getMember2(@PathVariable String id, @PathVariable String name) {
MemberVO vo = new MemberVO();
vo.setId(id);
vo.setName(name);
return vo;
}
}
servlet-context.xml
의 <beans:beans>
태그 사이에 다음의 내용을 추가 <!-- @RestController 사용 -->
<beans:bean class="kr.spring.ch13.controller.RestMainController"/>
web.xml
의 <servlet-mapping>
태그 사이의 내용을 다음처럼 수정 <url-pattern>/</url-pattern>
index.jsp
의 <body>
태그 사이에 다음 내용을 추가<a href="${pageContext.request.contextPath}/member">RestMainController</a><br>
<a href="${pageContext.request.contextPath}/member/id/sleepy/name/도로롱">/member/id/sleepy/name/도로롱</a><br>
ch05JDBC
, 최상위 패키지명을 kr.spring.jdbc
로 지정하고 Spring MVC Project 선택pom.xml
에서 <properties>
태그 사이의 내용을 다음처럼 변경 <java-version>1.8</java-version>
<org.springframework-version>5.0.20.RELEASE</org.springframework-version>
pom.xml
에서 <properties>
태그 종료 이후, <dependencies>
태그 시작 이전에 다음의 내용을 추가 <!-- Oracle 드라이버 설정 시작 -->
<repositories>
<repository>
<id>oracle</id>
<name>ORACLE JDBC Repository</name>
<url>https://code.lds.org/nexus/content/groups/main-repo</url>
</repository>
</repositories>
<!-- Oracle 드라이버 설정 끝 -->
pom.xml
에서 <dependencies>
태그 사이에 다음의 내용을 추가 <!-- 라이브러리 추가 시작 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.20.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.3</version>
</dependency>
<dependency>
<groupId>commons-pool</groupId>
<artifactId>commons-pool</artifactId>
<version>1.3</version>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.1</version>
</dependency>
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
<version>19.7.0.0</version>
</dependency>
<!-- 라이브러리 추가 끝 -->
src/main/webapp/WEB-INF
의 web.xml
열고 <web-app>
태그 사이에 다음의 내용을 추가 <filter> <!-- POST 방식 전송시 인코딩 처리를 위해 내장된 필터를 사용; 필터는 요청이 DispatcherServlet에 의해 다뤄지기 전,후에 동작 -->
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping> <!-- 컨텍스트 루트 하위의 모든 요청(*.do뿐만 아니라 *.jsp 등 확장자가 다른 경우까지 포함)에 대해 인코딩 필터 처리하도록 설정 -->
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
src/main/webapp/WEB-INF/spring/appServlet
에서 servlet-context.xml
열고 <context:component-scan>
태그를 다음처럼 수정 <context:component-scan base-package="kr.spring.board.controller" />
servlet-context.xml
의 <beans:beans>
태그 사이에 다음의 내용을 추가 <!-- messageSource 지정 -->
<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>
src/main/resources
에 새 폴더 config
생성하고 새 파일 jdbc.properties
생성jdbc.driverClassName=oracle.jdbc.OracleDriver
jdbc.url=jdbc:oracle:thin:@localhost:1521:xe
jdbc.username=scott
jdbc.password=tiger
src/main/webapp/WEB-INF/spring
에서 root-context.xml
열고 <beans>
태그를 다음처럼 수정<?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-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
<!--
bean 자동 스캔 : servlet-context.xml에서 Controller를 자동 스캔 설정하였으므로 root-context.xml에서는 Controller 자동 스캔 제외(이중 스캔 방지)
-->
<context:component-scan base-package="kr.spring.board">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
</beans>
src/main/resources
에 새 폴더 messages
생성하고 새 파일 validation.properties
생성required.name=작성자는 필수 항목
required.title=제목은 필수 항목
required.passwd=비밀번호는 필수 항목
required.content=내용은 필수 항목
invalidPassword=비밀번호 불일치
webapp
폴더 오른쪽 클릭하고 새 JSP 파일 index.jsp
생성<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
response.sendRedirect(request.getContextPath() + "/list.do");
%>
kr.spring.jdbc
패키지 삭제src/main/webapp/resources
오른쪽 클릭하고 새 폴더 css
생성 후 새 CSS 파일 style.css
생성@CHARSET "UTF-8";
.page-main{
width:700px;
margin:0 auto;
}
.result-display{
width:400px;
height:200px;
/*정중앙에 div를 배치하기 위한 설정*/
position:absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
/*테두리*/
border:1px solid #000;
/*하위 요소를 수직으로 쌓을 수 있는 공간을 만듬*/
display: flex;
/*세로 정렬*/
align-items: center;
/*가로 정렬*/
justify-content: center;
}
.align-right{
text-align:right;
}
.align-center{
text-align:center;
}
/*목록*/
table{
width:100%;
border:1px solid #000;
border-collapse:collapse;
}
table td, table th{
border:1px solid #000;
padding:5px;
}
/*등록,수정폼*/
form{
width:500px;
margin:0 auto;
border:1px solid #000;
padding:10px 10px 10px 30px;
}
ul{
list-style:none;
}
label{
width:100px;
float:left;
}
.error-color{
color:red;
}
root-context.xml
에서 <beans>
태그 사이에 다음의 내용을 추가 <!-- jdbc.properties 등록 -->
<context:property-placeholder location="classpath:config/jdbc.properties"/>
<!-- 커넥션 풀을 이용한 DataSource 설정 -->
<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>
root-context.xml
에서 <beans>
태그 사이에 다음의 내용을 추가 <!-- JdbcTemplate 객체 생성 -->
<bean name="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
kr.spring.board.controller
생성하고 새 클래스 BoardController
생성package kr.spring.board.controller;
import java.util.List;
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.validator.BoardValidator;
import kr.spring.board.vo.BoardVO;
import kr.spring.util.PagingUtil;
@Controller // @Controller, @Service, @Repository는 @Component가 확장된 어노테이션이라 자동 스캔 설정했을 때 별도로 @Component 어노테이션 추가할 필요 없음
public class BoardController {
@Autowired
private BoardService boardService;
// 자바빈 초기화
@ModelAttribute
public BoardVO initCommand() {
return new BoardVO();
}
// 글쓰기 폼 호출
@GetMapping("/insert.do")
public String form() {
return "insertForm";
}
// 글쓰기 폼에서 전송된 데이터 처리
@PostMapping("/insert.do")
public String submit(BoardVO boardVO, BindingResult result) {
// 유효성 검증
new BoardValidator().validate(boardVO, result);
// 유효성 검증 결과 오류가 있으면 폼 호출
if(result.hasErrors()) {
return form();
}
// 글 등록
boardService.insertBoard(boardVO);
return "redirect:/list.do";
}
// 목록
@RequestMapping("/list.do")
public ModelAndView process(@RequestParam(value="pageNum", defaultValue="1") int currentPage) {
int count = boardService.getBoardCount();
// 페이지 처리
PagingUtil page = new PagingUtil(currentPage, count, 10, 10, "/list.do");
List<BoardVO> list = null;
if(count>0) {
list = boardService.getBoardList(page.getStartCount(), page.getEndCount());
}
ModelAndView mav = new ModelAndView();
mav.setViewName("selectList");
mav.addObject("count", count);
mav.addObject("list", list);
mav.addObject("pagingHtml", page.getPagingHtml());
return mav;
}
// 상세
@RequestMapping("/detail.do")
public ModelAndView detail(@RequestParam int num) {
BoardVO board = boardService.getBoard(num);
return new ModelAndView("selectDetail", "board", board); // view 이름, 속성명, 속성값
}
// 수정 폼 호출
@GetMapping("/update.do")
public String formUpdate(@RequestParam int num, Model model) {
BoardVO vo = boardService.getBoard(num);
model.addAttribute("boardVO", vo);
return "updateForm";
}
// 수정 폼에서 전송된 데이터 처리
@PostMapping("/update.do")
public String submitUpdate(BoardVO boardVO, BindingResult result) {
// 유효성 검증
new BoardValidator().validate(boardVO, result);
// 유효성 검증 결과 오류가 있으면 수정 폼 호출
if(result.hasErrors()) {
return "updateForm";
}
// DB에 저장된 비밀번호 구하기
BoardVO db_board = boardService.getBoard(boardVO.getNum());
// 비밀번호 검증
if(!db_board.getPasswd().equals(boardVO.getPasswd())) { // DB에 저장된 비밀번호와 사용자가 입력한 비밀번호가 불일치하면
result.rejectValue("passwd", "invalidPassword");
return "updateForm";
}
// 글 수정
boardService.updateBoard(boardVO);
return "redirect:/list.do";
}
// 삭제 폼 호출
@GetMapping("/delete.do")
public String formDelete(@RequestParam int num, Model model) {
model.addAttribute("boardVO", boardService.getBoard(num));
return "deleteForm";
}
// 삭제 폼에서 전송된 데이터 처리
@PostMapping("/delete.do")
public String submitDelete(BoardVO boardVO, BindingResult result) {
// 유효성 검증
new BoardValidator().validate(boardVO, result);
// 유효성 검증 결과 비밀번호 필드에 오류가 있으면 삭제 폼 호출
if(result.hasFieldErrors("passwd")) { // hasErrors() 메서드는 모든 필드를 검사하고, hasFieldErros() 메서드는 지정한 필드만 검사
return "deleteForm";
}
// DB에 저장된 비밀번호 구하기
BoardVO db_board = boardService.getBoard(boardVO.getNum());
// 비밀번호 검증
if(!db_board.getPasswd().equals(boardVO.getPasswd())) { // DB에 저장된 비밀번호와 사용자가 입력한 비밀번호가 불일치하면
result.rejectValue("passwd", "invalidPassword");
return "deleteForm";
}
// 글 삭제
boardService.deleteBoard(boardVO.getNum());
return "redirect:/list.do";
}
}
src/main/webapp/WEB-INF/views
에서 home.jsp
삭제하고 새 JSP 파일 selectList.jsp
생성<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>게시판 목록</title>
<link rel="stylesheet" href="${pageContext.request.contextPath}/resources/css/style.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.name}</td>
<td>${board.reg_date}</td>
</tr>
</c:forEach>
</table>
<div class="align-center">${pagingHtml}</div>
</c:if>
</div>
</body>
</html>
views
폴더에 새 JSP 파일 insertForm.jsp
생성<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>글쓰기</title>
<link rel="stylesheet" href="${pageContext.request.contextPath}/resources/css/style.css">
</head>
<body>
<div class="page-main">
<h2>글쓰기</h2>
<form:form modelAttribute="boardVO">
<ul>
<li>
<form:label path="name">작성자</form:label>
<form:input path="name"/>
<form:errors path="name" cssClass="error-color"/>
</li>
<li>
<form:label path="title">제목</form:label>
<form:input path="title"/>
<form:errors path="title" cssClass="error-color"/>
</li>
<li>
<form:label path="passwd">비밀번호</form:label>
<form:password path="passwd"/>
<form:errors path="passwd" cssClass="error-color"/>
</li>
<li>
<form:label path="content">내용</form:label>
<form:textarea path="content"/>
<form:errors 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>
kr.spring.board.vo
생성하고 새 클래스 BoardVO
생성package kr.spring.board.vo;
import java.sql.Date;
public class BoardVO {
private int num;
private String name;
private String title;
private String passwd;
private String content;
private Date reg_date;
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
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;
}
}
kr.spring.board.validator
생성하고 새 클래스 BoardValidator
생성package kr.spring.board.validator;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;
import kr.spring.board.vo.BoardVO;
public class BoardValidator implements Validator {
@Override
public boolean supports(Class<?> clazz) {
return BoardVO.class.isAssignableFrom(clazz);
}
@Override
public void validate(Object target, Errors errors) {
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "name", "required"); // errors, 필드명, 에러 코드를 전달하면 자바빈에서 해당 필드가 비어 있거나 공백인지 여부를 확인하여 reject 처리
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "title", "required");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "passwd", "required");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "content", "required");
}
}
webapp
폴더에 하위 폴더로 sql
생성하고 새 파일 table.sql
생성CREATE TABLE tboard(
num NUMBER NOT NULL PRIMARY KEY,
name VARCHAR2(30) NOT NULL,
title VARCHAR2(150) NOT NULL,
passwd VARCHAR2(10) NOT NULL,
content VARCHAR2(4000) NOT NULL,
reg_date DATE NOT NULL
);
CREATE SEQUENCE tboard_seq;
kr.spring.board.dao
생성하고 새 인터페이스 BoardDAO
생성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);
}
BoardDAOImpl
생성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 // DAO 전용 어노테이션; 자동 스캔되기 위해 필요
public class BoardDAOImpl implements BoardDAO {
private static final String INSERT_SQL
= "INSERT INTO tboard (num, name, title, passwd, content, reg_date) "
+ "VALUES (tboard_seq.NEXTVAL, ?, ?, ?, ?, SYSDATE)";
private static final String SELECT_COUNT_SQL
= "SELECT COUNT(*) FROM tboard";
private static final String SELECT_LIST_SQL
= "SELECT * FROM (SELECT a.*, ROWNUM AS rnum FROM "
+ "(SELECT * FROM tboard ORDER BY reg_date DESC) a) WHERE rnum>=? AND rnum<=?";
private static final String SELECT_DETAIL_SQL
= "SELECT * FROM tboard WHERE num=?";
private static final String UPDATE_SQL
= "UPDATE tboard SET name=?, title=?, content=? WHERE num=?";
private static final String DELETE_SQL
= "DELETE FROM tboard WHERE num=?";
// ResultSet 처리를 위해 RowMapper가 구현된 익명 클래스 객체 생성
private RowMapper<BoardVO> rowMapper = new RowMapper<BoardVO>() { // RowMapper 인터페이스를 구현한 익명 클래스를 정의하고(=RowMapper<BoardVO>() {}), 그 익명 클래스의 객체를 생성하여(=new) 변수 rowMapper에 할당
public BoardVO mapRow(ResultSet rs, int rowNum) throws SQLException { // RowMapper 인터페이스의 추상 메서드 mapRow()를 구현
BoardVO board = new BoardVO();
board.setNum(rs.getInt("num"));
board.setName(rs.getString("name"));
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.getName(), board.getTitle(), board.getPasswd(), board.getContent()}); // 인자로 SQL문과 ?에 바인딩될 데이터가 담긴 Object 배열을 전달
}
@Override
public int getBoardCount() {
return jdbcTemplate.queryForObject(SELECT_COUNT_SQL, Integer.class); // 인자로 SQL문과 반환하는 데이터의 자료형을 전달
}
@Override
public List<BoardVO> getBoardList(int startRow, int endRow) {
List<BoardVO> list = jdbcTemplate.query(SELECT_LIST_SQL,
new Object[] {startRow, endRow}, rowMapper); // 인자로 SQL문, ?에 바인딩될 데이터가 담긴 Object 배열, RowMapper가 구현된 익명 클래스 객체를 전달
return list;
}
@Override
public BoardVO getBoard(int num) {
BoardVO board = jdbcTemplate.queryForObject(SELECT_DETAIL_SQL,
new Object[] {num}, rowMapper); // 인자로 SQL문, ?에 바인딩될 데이터가 담긴 Object 배열, RowMapper가 구현된 익명 클래스 객체를 전달
return board;
}
@Override
public void updateBoard(BoardVO board) {
jdbcTemplate.update(UPDATE_SQL,
new Object[] {board.getName(), board.getTitle(), board.getContent(), board.getNum()}); // 인자로 SQL문과 ?에 바인딩될 데이터가 담긴 Object 배열을 전달
}
@Override
public void deleteBoard(int num) {
jdbcTemplate.update(DELETE_SQL, new Object[] {num}); // 인자로 SQL문과 ?에 바인딩될 데이터가 담긴 Object 배열을 전달
}
}
kr.spring.board.service
생성하고 새 인터페이스 BoardService
생성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);
}
BoardServiceImpl
생성package kr.spring.board.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import kr.spring.board.dao.BoardDAO;
import kr.spring.board.vo.BoardVO;
@Service // Service 전용 어노테이션; 자동 스캔되기 위해 필요
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);
}
}