1.3 (SpringMVC-db,자바빈연결,service,cookie,유효성검사)
일반적으로 db연동 후 자바빈에 담고 정보를 처리하기 때문에 자바빈을 만들어서 처리해볼것임
자바빈
kr.spring.ch03.vo
NewArticleVO
package kr.spring.ch03.vo;
public class NewArticleVO {
private int parentId;
private String title;
private String content;
public int getParentId() {
return parentId;
}
public void setParentId(int parentId) {
this.parentId = parentId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
jsp에서 했던 방식과 스프링에서 하는 방식은 다름
jsp = 객체 생성, set 사용해서 저장
스프링 = 어노테이션 사용 (자바빈생성 후 자바빈과 파라미터 네임이 일치하면 자동으로 세팅됨)
컨트롤러
NewArticleController.java
package kr.spring.ch03.controller;
import org.springframework.stereotype.Controller;
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.RequestParam;
import kr.spring.ch03.vo.NewArticleVO;
@Controller
public class NewArticleController {
//Get 요청이 들어올 때 호출
@GetMapping("/article/newArticle.do")
public String form() {
return "article/newArticleForm";
}
//Post 요청이 들어올 때 호출
@PostMapping("/article/newArticle.do")
public String submit(@ModelAttribute("vo") NewArticleVO newArticleVO) {
System.out.println("parentId : " + newArticleVO.getParentId());
System.out.println("title : " + newArticleVO.getTitle());
System.out.println("content : " + newArticleVO.getContent());
return "article/newArticleSubmitted";
}
}
public String submit(@ModelAttribute("vo") NewArticleVO newArticleVO){
일단 콘솔에 출력되게 하기 위해 출력구문
return "article/newArticleSubmitted";
}
@ModelAttribute = 객체 생성 후 전송된 데이터를 다 넣어줌 (우리가 request에 저장하지 않아도 다 해줌)
위 구문 덕분에 request안에는 vo라고 자바빈이 저장되어 있는거임
@ModelAttribute("vo") NewArticleVO newArticleVO
==> NewArticleVO 객체 생성되면서 전송한 데이터가 다 프로퍼티에 저장되어있음
그 데이터를 get을 통해 출력하면 됨
[화면출력]
게시글이 등록되었습니다.
[console]
parentId : 1
title : 제목test
content : 내용test
뷰 파일
views
article
newArticleSubmitted.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>게시글 쓰기 완료</title>
</head>
<body>
게시글이 등록되었습니다.<br>
제목 : ${vo.title}<br>
내용 : ${vo.content}
</body>
</html>
index.jsp 실행 시
[화면출력]
게시글이 등록되었습니다.
제목 : 제목입니다
내용 : 내용입니다
***
@ModelAttribute를 명시할 때 속성명을 생략할 수 있음
속성명을 생략하면 클래스명의 첫 글자를 소문자로 속성명을 자동 생성하게 됨
컨트롤러
NewArticleController.jsp
//Post 요청이 들어올 때 호출
@PostMapping("/article/newArticle.do")
public String submit(@ModelAttribute NewArticleVO newArticleVO) {
System.out.println("parentId : " + newArticleVO.getParentId());
System.out.println("title : " + newArticleVO.getTitle());
System.out.println("content : " + newArticleVO.getContent());
return "article/newArticleSubmitted";
}
@ModelAttribute("vo")라 저장했던 것을 속성명을 지웠음
뷰 파일
newArticleSubmitted.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>게시글 쓰기 완료</title>
</head>
<body>
게시글이 등록되었습니다.<br>
제목 : ${newArticleVO.title}<br>
내용 : ${newArticleVO.content}
</body>
</html>
속성명을 지웠기 때문에 자연스럽게 vo -> newArticleVO가 됨
index.jsp를 실행하면 오류없이 화면/ 콘솔에 출력됨
[화면출력]
게시글이 등록되었습니다.
제목 : 제목 test
내용 : 내용 test
***
@ModelAttribute 도 생략 가능 (위처럼 정상출력됨)
호출 메서드에 인자명만 명시 예) NewArticleVO vo
속성명을 지운것과 동일하게 request에 저장되는 속성명은 클래스명의 첫글자를 소문자로 자동 생성
예) NewArticleVO -> newArticleVO
컨트롤러
NewArticleController.jsp
//Post 요청이 들어올 때 호출
@PostMapping("/article/newArticle.do")
public String submit(NewArticleVO newArticleVO) {
System.out.println("parentId : " + newArticleVO.getParentId());
System.out.println("title : " + newArticleVO.getTitle());
System.out.println("content : " + newArticleVO.getContent());
return "article/newArticleSubmitted";
}
<SpringMVC - Service>
지금까지 했던 작업들을 DB에 연동한다고 가정하면 DAO뿐만 아니라 Service도 필요함
Service = 하나하나 명시하는 메서드들을 하나로 묶어서 트랜잭션 처리하는 것
아직은 연동을 안하지만 연동한다고 가정하고 service라는 클래스 생성 후 주입해줄 것임
@를 이용해서 주입받고 메서드 사용 후 db연동한 것처럼 처리
서비스클래스
kr.spring.ch03.service
ArticleService
package kr.spring.ch03.service;
import kr.spring.ch03.vo.NewArticleVO;
public class ArticleService {
public void writeArticle(NewArticleVO vo){
System.out.println("신규 게시글 등록 : " +vo);
}
}
vo라고만 명시했기 때문에 실행한다면 참조값이 나오는데
toString을 generate해서 프로퍼티 값이 나오게 할 것임
자바빈 (toString 넣는 작업)
kr.spring.ch03.vo
NewArticleVO
package kr.spring.ch03.vo;
public class NewArticleVO {
private int parentId;
private String title;
private String content;
public int getParentId() {
return parentId;
}
public void setParentId(int parentId) {
this.parentId = parentId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
@Override
public String toString() {
return "NewArticleVO [parentId=" + parentId + ", title=" + title + ", content=" + content + "]";
}
}
source -> generate toString -> 기본값 그대로 generate
ArticleService를 생성했기 때문에 의존관계를 주입시켜야함
service-context.xml에서 빈설정을 해야한다는 뜻
설정파일
WEB-INF
spring
appServlet
servlet-context.xml
<!-- @Autowired 어노테이션 사용 -->
<context:annotation-config/>
<!-- POST 방식으로 데이터 전송 -->
<beans:bean id="newArticleController" class="kr.spring.ch03.controller.NewArticleController"/>
<beans:bean id="articleService" class="kr.spring.ch03.service.ArticleService"/>
NewArticleController에서 ArticleService를 사용하기 위해 어노테이션을 사용해서
ArticleService의 필요한 객체를 컨트롤러에 넣어주려고 함
그렇기 위해선 @Autowired 사용한다는 context구문을 설정파일에 추가하고, 컨트롤러에도 넣어줘야함
컨트롤러 (@Autowired 설정)
kr.spring.ch03.controller
NewArticleController
package kr.spring.ch03.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
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.RequestParam;
import kr.spring.ch03.service.ArticleService;
import kr.spring.ch03.vo.NewArticleVO;
@Controller
public class NewArticleController {
@Autowired
private ArticleService articleService;
//@Autowired를 멤버변수에 명시하면 멤버변수에 대응하는 set 메서드를 자동 생성함
/*
public void setArticleService(ArticleService articleService) { //articleService를 주입받기 위해 setter를 넣음
this.articleService = articleService;
}
*/
//Get 요청이 들어올 때 호출
@GetMapping("/article/newArticle.do")
public String form() {
return "article/newArticleForm";
}
/*
* @ModelAttribute 어노테이션을 이용해서 전송된 데이터를 자바빈(VO)에 담기
* [기능]
* 1. 자바빈(VO) 생성
* 2. 전송된 데이터를 자바빈에 저장
* 3. View에서 사용할 수 있도록 request에 자바빈(VO)를 저장
*
* [형식]
* 1. @ModelAttribute(속성명) NewArticleVO newArticleVO
* 지정한 속성명으로 JSP에서 request에 접근해서 자바빈(VO) 호출 가능
* 예) ${속성명.title}
*
* 2. @ModelAttribute를 명시할 때 속성명을 생략할 수 있음
* 속성명을 생략하면 클래스명의 첫글자를 소문자로 속성명을 자동 생성
*
* 3. @ModelAttribute 생략
* 호출 메서드에 인자명만 명시
* 예) NewArticleVO vo와 같이 인자명만 명시
* request에 저장되는 속성명은 클래스명의 첫글자를 소문자로 자동 생성
* 예) NewArticleVO -> newArticleVO
*/
//Post 요청이 들어올 때 호출
@PostMapping("/article/newArticle.do")
public String submit(NewArticleVO newArticleVO) {
articleService.writeArticle(newArticleVO);
return "article/newArticleSubmitted";
}
}
보통 @Autowired 를 컨트롤러에 추가하게 된다면
private ArticleService articleService 처럼 멤버 변수 명시 후 setter도 생성해야 함
but @Autowired를 멤버 변수에 명시하면 멤버 변수에 대응하는 set메서드를
자동으로 생성해주기 때문에 만들 필요가 없음
자바빈을 통해 데이터를 연동해야하기 때문에 articleService를 호출해서 자바빈에 전달하려고 함
(@Autowired를 통해 이미 주입 받았음)
//Post 요청이 들어올 때 호출
@PostMapping("/article/newArticle.do")
public String submit(NewArticleVO newArticleVO) {
articleService.writeArticle(newArticleVO);
return "article/newArticleSubmitted";
}
ArticleService의 writeArticle메서드(자바빈)을 전달 -> System.out.println("신규 게시글 등록 : " +vo); 출력됨
index.jsp에서 실행하면 정상실행됨
[출력화면] -- newArticleSubmitted.jsp
게시글이 등록되었습니다.
제목 : 제목
내용 : 내용
[console]
신규 게시글 등록 : NewArticleVO [parentId=1, title=제목, content=내용]
==> toString을 재정의했기 때문에 generate값이 나옴 (전송된 데이터가 정상적으로 담겨있는지 확인 가능)
<Cookie>
원래는 request.getCookies 해서 모든 쿠키를 읽어왔어야 하는데
spring에서는 어노테이션을 통해 우리가 원하는 쿠키 하나만 읽어올 수 있음
쿠키를 생성하고, 생성한 쿠키를 읽어오는 예제 학습할 것임
컨트롤러
kr.spring.ch04.controller
CookieController
package kr.spring.ch04.controller;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class CookieController {
@RequestMapping("/cookie/make.do")
public String make(HttpServletResponse response) {
//쿠키를 생성해서 클라이언트에 전송
response.addCookie(new Cookie("auth","1"));
return "cookie/make"; //cookie=뷰경로/make=뷰이름
}
}
HttpServletResponse 인자 받고 response.addCookie(new Cookie("auth", "1")) 를 통해
이름이 auth이고, 값이 1인 쿠키를 생성함
설정파일 (CookieController 빈 설정)
servlet-context.xml
<!-- @CookieValue 어노테이션을 이용한 쿠키 매핑 -->
<beans:bean id="cookieController" class="kr.spring.ch04.controller.CookieController"/>
뷰 파일
views
cookie (folder)
make.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>쿠키</title>
</head>
<body>
쿠키를 생성함
</body>
</html>
실행파일 (jsp 주소 복사)
index.jsp
<a href="${pageContext.request.contextPath}/cookie/make.do">CookieController - make.do</a><br>
[출력화면]
CookieController - make.do 태그 누르면
쿠키를 생성함 이라고 보여짐
주소: http://localhost:8080/ch10-Spring_MVC/cookie/make.do
창 우클릭-검사-application-cookies
보면
name=auth value=1 이 출력됨
여기까지 쿠키 생성
서블릿을 이용해서 읽어오는 것은 똑같지만 어노테이션을 통해 하나의 쿠키만 가져올 수 있음
servlet을 사용해야 하는데 (request, response) 사용할 경우에는 doget, dopost를 사용해서 인자로 만들었지만
여기서는 호출하는 메서드의 인자로 명시하면 됨 (약속되어져있기 때문 // 컨테이너가 HttpServletResponse를 쓰면 됨)
request도 마찬가지
이번엔 클라이언트로부터 쿠키정보를 받아서 읽어오기
메서드는 한 파일에 여러 개 만들 수 있기 때문에 기존 쿠키 파일에 이어서 명시
컨트롤러
kr.spring.ch04.controller
CookieController
package kr.spring.ch04.controller;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class CookieController {
//쿠키 생성하는 메서드
@RequestMapping("/cookie/make.do")
public String make(HttpServletResponse response) {
//쿠키를 생성해서 클라이언트에 전송
response.addCookie(new Cookie("auth","1"));
return "cookie/make"; //cookie=뷰경로/make=뷰이름
}
//쿠키 읽는 메서드
@RequestMapping("/cookie/view.do")
public String view(@CookieValue("auth") String auth) { //auth:쿠키명
System.out.println("auth 쿠키 : " + auth);
return "cookie/view";
}
}
@CookieValue("auth") // console 출력
뷰 파일
views
cookie
view.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>쿠키 확인</title>
</head>
<body>
쿠키 확인
</body>
</html>
화면에는 출력하지 않고 콘솔에 출력할 예정
실행파일
index.jsp
<a href="${pageContext.request.contextPath}/cookie/view.do">CookieController - view.do</a><br>
index 실행하고
make 를 눌러서 쿠키부터 만들고
view 를 눌러서 확인해야함
(만들고 확인해야 오류가 안남)
[화면출력]
쿠키 확인
[console]
auth 쿠키 : 1
***
쿠키 읽는 메서드에서 쿠키명을 적지 않고 @CookieValue만 넣었을 때 오류나는가? --> X 정상 실행됨
컨트롤러
CookieController
//쿠키 읽는 메서드
@RequestMapping("/cookie/view.do")
public String view(@CookieValue String auth) { //auth:쿠키명
System.out.println("auth 쿠키 : " + auth);
return "cookie/view";
}
본래 == public String view(@CookieValue("auth") String auth)
에서 auth 제외하고 명시함
index실행
make -> view 하면
정상 작동됨
[console]
auth 쿠키 : 1
***
@CookieValue도 지웠을 때에도 오류없이 실행되는가? --> X 오류나진 않지만 console에 null값이 출력됨
컨트롤러
CookieController
//쿠키 읽는 메서드
@RequestMapping("/cookie/view.do")
public String view(String auth) {
System.out.println("auth 쿠키 : " + auth);
return "cookie/view";
}
index실행
make -> view 하면
정상 작동됨
[console]
auth 쿠키 : null
@RequestParam이 동작한거임
@CookieValue는 꼭 적어줘야함
***
쿠키 전달없이 (make cookie) 쿠키를 확인하면 당연히 오류남
but 쿠키 전달 X + required=false 명시하면 오류 나는가? --> 어노테이션 명시 + required=false 명시하면 오류 안남
컨트롤러
CookieController
//쿠키 읽는 메서드
@RequestMapping("/cookie/view.do")
public String view(@CookieValue(value="auth", required=false) String auth) { //auth:쿠키명
System.out.println("auth 쿠키 : " + auth);
return "cookie/view";
}
index.jsp 실행
make.do 안누르고 view.do 눌러도 오류 안남 => 쿠키가 없어도 에러가 안나고 null인정
[화면출력]
쿠키 확인
[console]
auth 쿠키 : null
@cookievalue 명시하면 반드시 값 입력해야한다는 것!!!!!!
@CookieValue(value="auth", required=false) 아니면
@CookieValue(value="auth", defaultValue="0") 값을 줘야 값이 없어도 오류가 안남
+++
@CookieValue(value="auth", defaultValue="0") 시
[화면출력]
쿠키 확인
[console]
auth 쿠키 : 0 (기본값 설정하는 것임)
기존 post방식으로 데이터 넘기는 방식 연습 예제 더 할거임
컨트롤러
kr.spring.ch05.controller
GameSearchController (class)
package kr.spring.ch05.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class GameSearchController {
@RequestMapping("/search/main.do")
public String main() {
return "search/main";
}
}
index.jsp 에서 search/main.do와 연결된 링크를 클릭하면 views/search/main.jsp를 호출하는 원리
설정파일
컨테이너에 넣기(빈설정)
WEB-INF
spring
appServlet
servlet-context.xml
<!-- 전송된 데이터 자바빈에 담기 -->
<beans:bean class="kr.spring.ch05.controller.GameSearchController"/>
처음에 배웠던 문법대로 다 id를 명시했지만 controller는 id를 안넣어도 됨
웹에서는 url (make.do) 로 호출하기 때문에 보통 id빼고 class만 명시함
뷰클래스
views
search (folder)
main.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>게임 검색 메인</title>
</head>
<body>
<form action="game.do" method="get">
<select name="type">
<option value="1">전체</option>
<option value="2">아이템</option>
<option value="3">캐릭터</option>
</select>
<input type="search" name="query">
<input type="submit" value="검색">
</form>
</body>
</html>
앞전 jsp 할 때 keyfield, keyword 넘겼던 것처럼 처리할거라 get방식으로 넣었음
실행파일
index.jsp
<a href="${pageContext.request.contextPath}/search/main.do">GameSearchController</a><br>
[출력화면]
실행하고 GameSearchController 누르면
검색바가 화면에 출력 뜸
위 검색창에서 데이터를 전송했을 때 데이터가 2개 전송이 됨
type과 query임
이때 @RequestParam 처리 해도 되고, @ModelAttribute + 자바빈 객체 생성해서 처리해도 되는데
자바빈 만들어서 처리하는 방법을 연습해볼거임
자바빈 생성
kr.spring.ch05.vo
SearchVO
package kr.spring.ch05.vo;
public class SearchVO {
private String type;
private String query;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getQuery() {
return query;
}
public void setQuery(String query) {
this.query = query;
}
@Override
public String toString() {
return "SearchVO [type=" + type + ", query=" + query + "]";
}
}
전송된 데이터를 전부 searchVO에 담을거임
서비스클래스
kr.spring.ch05.service
SearchService
db와 연동했다고 가정 후 만드는 것이기 때문에 service클래스도 만들기
package kr.spring.ch05.service;
import kr.spring.ch05.vo.SearchVO;
public class SearchService {
public String search(SearchVO vo) {
System.out.println(vo);
return "검색 완료!";
}
}
설정파일
servlet-context.xml
객체 생성 후 의존관계 맺어주기
<!-- 전송된 데이터 자바빈에 담기 -->
<beans:bean class="kr.spring.ch05.controller.GameSearchController"/>
<beans:bean class="kr.spring.ch05.service.SearchService"/>
컨트롤러 수정
kr.spring.ch05.controller
GameSearchController
데이터 주입받기
package kr.spring.ch05.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import kr.spring.ch05.service.SearchService;
import kr.spring.ch05.vo.SearchVO;
@Controller
public class GameSearchController {
@Autowired //set메서드 생략 가능(Autowired가 자동으로 생성해줌)
private SearchService searchService;
@RequestMapping("/search/main.do")
public String main() {
return "search/main";
}
@RequestMapping("/search/game.do")
public ModelAndView search(SearchVO searchVO) { //@ModelAttribute 생략
System.out.println("검색어 : " + searchVO.getQuery());
//searchService호출해서 검색완료라는 결과값 가져오기
String result = searchService.search(searchVO);
ModelAndView mav = new ModelAndView();
//뷰이름 지정
mav.setViewName("search/game");
//뷰에 전달할 데이터 지정
mav.addObject("result", result);
return mav;
}
}
main.jsp(form action="game.do")에서 보낸 데이터를 처리하기 위해 @RequestMapping("/search/game.do") 코드 작성
호출화면
search
game.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>게임 검색 결과</title>
</head>
<body>
검색 결과 : ${result}
</body>
</html>
index.jsp에서 실행
[화면출력]
검색 결과 : 검색 완료!
[console]
SearchVO [type=2, query=짱구]
==> SearchService에서 return은 검색완료 / System.out.println(vo) 라고 했기 때문에
이와 같이 출력되는 것
순서가 중요하니까 계속 반복해서 그 패턴을 익히는 게 중요
===========================
GameSearchController처럼
하나의 컨트롤러에 여러 개 메서드를 만들 수 있으니까 보통 게시판 기능 controller 1개,
회원관리 controller 1개씩만 만들어서 그 안에 계속 나열함
근데 너무 많아지면 그때 또다른 컨트롤러를 만들면 됨
유효성 체크
js로도 할 수 있고, spring에서 자체적으로 제공하는 방법으로도 해도 상관 없음
spring의 유효성 체크 방법
1) 유효성 체크하는 객체를 만들어서 객체로 유효성 체크
2) 어노테이션으로 체크
1. 유효성 체크하는 객체를 만들어서 객체로 유효성 체크하는 방법
회원가입하는 클래스
kr.spring.ch06.controller
CreateAccountController
package kr.spring.ch06.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class CreateAccountController {
@GetMapping("/account/create.do")
public String form() {
//뷰 이름 지정
return "account/creationForm";
}
}
설정파일
servlet-context.xml
<!-- 유효성 체크 -->
<beans:bean class="kr.spring.ch06.controller.CreateAccountController"/>
뷰 파일
views
account (folder)
creationForm.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>계정 생성</title>
</head>
<body>
<form action="create.do" method="post">
아이디 : <input type="text" name="id">
<br>
이름 : <input type="text" name="name">
<br>
우편번호 : <input type="text" name="zipcode">
<br>
주소 : <input type="text" name="address1">
<br>
상세주소 : <input type="text" name="address2">
<br>
<input type="submit" value="전송">
</form>
</body>
</html>
실행파일
index.jsp
<a href="${pageContext.request.contextPath}/account/create.do">CreateAccountController</a><br>
실행결과
데이터 받는건 자바빈을 만들어서 작업할 것
kr.spring.ch06.vo
MemberInfo
package kr.spring.ch06.vo;
public class MemberInfo {
private String id;
private String name;
private String zipcode;
private String address1;
private String address2;
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;
}
public String getZipcode() {
return zipcode;
}
public void setZipcode(String zipcode) {
this.zipcode = zipcode;
}
public String getAddress1() {
return address1;
}
public void setAddress1(String address1) {
this.address1 = address1;
}
public String getAddress2() {
return address2;
}
public void setAddress2(String address2) {
this.address2 = address2;
}
@Override
public String toString() {
return "MemberInfo [id=" + id + ", name=" + name + ", zipcode=" + zipcode + ", address1=" + address1
+ ", address2=" + address2 + "]";
}
}
컨트롤러
CreateAccountController
package kr.spring.ch06.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import kr.spring.ch06.vo.MemberInfo;
@Controller
public class CreateAccountController {
@GetMapping("/account/create.do")
public String form() {
//뷰 이름 지정
return "account/creationForm";
}
@PostMapping("/account/create.do")
public String submit(@ModelAttribute("vo") MemberInfo memberInfo) {
return "account/created";
}
}
creationform에서 post방식으로 데이터를 보낼거임( 요청 url이 같지만 방식이 달라서 충돌X)
@ModelAttribute("vo") MemberInfo memberInfo 는 그냥 MemberInfo memberInfo 만 명시해도 됨
뷰 파일
account (folder)
created.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>계정 생성</title>
</head>
<body>
계정이 생성되었습니다.<br>
<ul>
<li>아이디 : ${vo.id}</li>
<li>이름 : ${vo.name}</li>
<li>우편번호 : ${vo.zipcode}</li>
<li>주소 : ${vo.address1} ${vo.address2}</li>
</ul>
</body>
</html>
위 컨트롤러에서 자바빈을 vo라고 명시했기 때문에 (@ModelAttribute("vo")) vo로 받음
실행파일
index.jsp
Validator 파일을 만들어서 유효성 체크
kr.spring.ch06.validator
MemberInfoValidator
package kr.spring.ch06.validator;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
import kr.spring.ch06.vo.MemberInfo;
//유효성 체크하는 클래스
public class MemberInfoValidator implements Validator{
//Validator가 검증할 수 있는 타입인지를 검사
@Override
public boolean supports(Class<?> clazz) {
return MemberInfo.class.isAssignableFrom(clazz);
}
@Override
public void validate(Object target, Errors errors) {
MemberInfo memberInfo = (MemberInfo)target;
if(memberInfo.getId() == null || memberInfo.getId().isEmpty()) {
//비어있다면 errors에 기록
errors.rejectValue("id", null, "필수 항목입니다.");
}
if(memberInfo.getName() == null || memberInfo.getName().isEmpty()) {
errors.rejectValue("name", null, "필수 항목입니다.");
}
if(memberInfo.getZipcode() == null || memberInfo.getZipcode().isEmpty()) {
errors.rejectValue("zipcode", null, "필수 항목입니다.");
}
if(memberInfo.getAddress1() == null || memberInfo.getAddress1().isEmpty()) {
errors.rejectValue("address1", null, "필수 항목입니다.");
}
if(memberInfo.getAddress2() == null || memberInfo.getAddress2().isEmpty()) {
errors.rejectValue("address2", null, "필수 항목입니다.");
}
}
}
1. validator interface를 implements 해야함
2. spring의 유효성 검사에는 규칙이 있음. js는 개별적으로 하나하나 체크했지만 spring은 자바빈에 담겨져 있는
것만 체크 가능함
따라서 validator 파일의 supports에서 자바빈인지 아닌지 체크 ->
자바빈에 담겨있다 == 아래에 있는 validate의 target
자바빈에 담겨있지 않다 == errors에 들어감
컨트롤러에서 validator객체 생성을 하고 그 안에 데이터를 넘겨줘야함
컨트롤러
CreateAccountController
package kr.spring.ch06.controller;
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.ch06.validator.MemberInfoValidator;
import kr.spring.ch06.vo.MemberInfo;
@Controller
public class CreateAccountController {
@GetMapping("/account/create.do")
public String form() {
//뷰 이름 지정
return "account/creationForm";
}
@PostMapping("/account/create.do")
public String submit(@ModelAttribute("vo") MemberInfo memberInfo, BindingResult result) {
//인자로 BindingResult 받음(errors의 자식객체)
//자바빈에 담겨있는 데이터 확인하기
System.out.println("데이터 전송 후 : " + memberInfo);
//전송된 데이터 유효성 체크
new MemberInfoValidator().validate(memberInfo, result); //memberInfo, BindingResult (errors의 자식객체)
//BindingResult에 유효성 체크 결과 오류에 대한 내용이 저장되어 있으면
//폼을 다시 호출함
if(result.hasErrors()) { //하나라도 에러가 있다면 true, 없다면 false
return "account/creationForm";
}
return "account/created";
}
}
submit 메서드의 인자 중 BindingResult = errors의 자식객체임
유효성 체크 시 빈 데이터와 에러 데이터 둘다 받음
print구문을 이용해서 콘솔에 데이터 확인 후
new MemberInfoValidator().validate(memberInfo, result) 를 이용해 유효성 검사를 함
그 결과 result에 hasErrors() 라면 creationForm 재호출+에러메시지 표시
else라면 created 폼으로 가게 return 함
유효성 검사 후 에러메시지가 들어갈 수 있게끔 폼 수정
creationForm.jsp
<%@ 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>
</head>
<body>
<form:form action="create.do">
아이디 : <form:input path="id"/>
<form:errors path="id"/> <%-- errors 객체 안에 저장할 때 field (자바빈 프로퍼티) --%>
<br>
이름 : <form:input path="name"/>
<form:errors path="name"/>
<br>
우편번호 : <form:input path="zipcode"/>
<form:errors path="zipcode"/>
<br>
주소 : <form:input path="address1"/>
<form:errors path="address1"/>
<br>
상세주소 : <form:input path="address2"/>
<form:errors path="address2"/>
<br>
<form:button>전송</form:button>
</form:form>
</body>
</html>
form: 형식인 custom 태그를 사용해서 문구를 읽어옴 (내부적으로 연결되어 있기 때문에
custom 태그를 사용해야 Binding을 처리할 수 있음) 꼭! 커스텀 태그를 사용해야함
<custom 태그>
:form -- method="post"를 명시하지 않아도 기본값이 post로 동작
메서드 안 쓸 시 기본값
<form> --> get방식
<form:form> --> post방식
=============================
자바빈에 담겨있는지 아닌지 체크 후 자바빈에 없어서 BindingResult에 들어간 값들을 :errors path가 찾아줌
=============================
커스텀태그가 자바빈과 연동되기 때문에 처음 구동 시켰을 때 자바빈이 있어야함
만약 폼을 처음 구동시켰을 때 자바빈이 없으면 정보처리가 제대로 되지 않음
그래서
이름 : <input type="text" name="name">
이런식으로 적었던 내용을 커스텀 태그를 사용해서 <form:input path="id"/> 라고 적어야함
자바빈이 연동된 폼이 실행되면 <form:input path="id"/> 이 자동으로 위 input type 처럼 바뀌는걸
페이지 소스보기에서 볼 수 있음
컨트롤러
CreateAccountController
처음 호출할 때 사용할 수 있는 자바빈 생성 및 초기화
폼에서 사용할 수 있는 자바빈을 만들어줘야지 커스텀태그랑 연동해서 사용할 수 있음
@Controller
public class CreateAccountController {
//유효성 체크를 할 경우 자바빈(VO) 초기화 필수
//자바빈(vo)를 초기화하면 request에 "vo" 속성명으로 저장
@ModelAttribute("vo")
public MemberInfo initCommand() {
return new MemberInfo();
}
command=spring에서의 자바빈을 뜻함
MemberInfo initCommand() 자바빈 초기화 메서드
폼 설정
creationForm.jsp
<form:form action="create.do"> --> <form:form action="create.do" modelAttribute="vo">
아래와 같이 form 부분에 modelAttribute 기재 후 초기화한 자바빈을 등록해줌
동작원리
폼이 호출되기 전 request에 자바빈 생성 -> id/namd 등 필드들이 연동 ->
CreateAccountController <form:input path="name"/> 등의 구문들이 자바빈에서 path로 값을 찾음
실행파일
index.jsp
수업 끝난 후 집에서 깃허브 연동하고 캡쳐하는거라 properties 가 만들어져서 필수 항목이라고 뜨지만 (다른 파일)
원래는 validator에서 입력한 defaultmessage인 필수 항목입니다 가 떠야함 ^^..
한 칸에 기재 후 전송을 누르면 기재한 칸은 입력한 데이터가 미리보기로 보여짐
자바빈이 입력한 항목도 미리보기 해주는거임
creationForm.jsp
<form:form action="create.do" modelAttribute="vo">
vo에서
<form:input path="id"/> 이 커스텀 태그를 통해 미리보기가 되는거임
=============================
그리고 커스텀 태그로 작성했지만
view-source:http://localhost:8080/ch10-Spring_MVC/account/create.do
여기서 페이지 소스보기를 하면
일반 태그로 변환돼서 보임
<유효성 검사 차이점>
script -> 하나하나씩 alert창 띄움
spring validator -> 모두 한꺼번에 화면에 보여줌
***
주의해야할 점 + 순서
1) CreateAccountController
//유효성 체크를 할 경우 자바빈(VO) 초기화 필수
//자바빈(vo)를 초기화하면 request에 "vo" 속성명으로 저장
@ModelAttribute("vo")
public MemberInfo initCommand() {
return new MemberInfo();
}
동작되면서 초기 자바빈을 생성함
(**근데 아래에 있는 public String submit(@ModelAttribute("vo") 메서드와 자바빈 이름이 동일해야함)
2)
데이터 미입력한 항목이 있을 시 유효성 체크
MemberInfoValidator if문에서 걸리는거임 (공백이 있을 때)
3) creationForm.jsp
위 2번에서 error가 있기 때문에 bindingResult와 연결되어있기 때문에 error정보를 읽어와서 Form에 표시
errors.rejectValue("address2", null, "필수 항목입니다.");
필수항목입니다 라는 에러 메시지를 클래스에 넣을 수도 있고, 프로퍼티즈 파일에 넣을 수도 있음
위처럼 코드를 파일에 한글로 표시하게 되면 영어 사이트에도 한글로 표시되기 때문에 외국에서 접속했을 때에 한계가 있다고 하심
언어에 따라서 글자가 달라지게 하려고 하면 클래스에 명시하지 않고 프로퍼티즈 파일에 명시해서 읽어오면 됨 (다양한 언어처리 가능)
파일에 넣을 때 이점
==> 일괄적으로 메시지 관리 (수정 시 용이함)
이처럼 다양한 언어로 표시하기 위해 프로퍼티즈 파일 만들고 수정할 것임
properties
src/main/java
messages (package)
validation.properties (file)
#에러코드=에러문구
required=필수 항목
에러코드(key)=에러문구(value)
properties에 저장하는 것들은 한글로 저장하는 게 아니라 유니코드로 저장됨
보이는 것만 한글일 뿐임
Validator 수정
MemberInfoValidator
package kr.spring.ch06.validator;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
import kr.spring.ch06.vo.MemberInfo;
//유효성 체크하는 클래스
public class MemberInfoValidator implements Validator{
//Validator가 검증할 수 있는 타입인지를 검사
@Override
public boolean supports(Class<?> clazz) {
return MemberInfo.class.isAssignableFrom(clazz);
}
@Override
public void validate(Object target, Errors errors) {
MemberInfo memberInfo = (MemberInfo)target;
if(memberInfo.getId() == null || memberInfo.getId().isEmpty()) {
//항목이 비어있다면 errors에 기록
// 필드 에러코드 (default message)
errors.rejectValue("id", "required");
}
if(memberInfo.getName() == null || memberInfo.getName().isEmpty()) {
errors.rejectValue("name", "required");
}
if(memberInfo.getZipcode() == null || memberInfo.getZipcode().isEmpty()) {
errors.rejectValue("zipcode", "required");
}
if(memberInfo.getAddress1() == null || memberInfo.getAddress1().isEmpty()) {
errors.rejectValue("address1", "required");
}
if(memberInfo.getAddress2() == null || memberInfo.getAddress2().isEmpty()) {
errors.rejectValue("address2", "required");
}
}
}
properties에서 required 에러코드가 뜨면 필수 항목이라고 메시지가 뜨도록 설정했기 때문에
default message (필수 항목입니다) 는 지웠음
설정파일에 properties위치 등록
servlet-context.xml
<!-- 리소스 번들 지정 -->
<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>
리소스 번들 안에 validation.properties의 위치를 지정해주면 됨
============================
properties파일은 여러 개 만들 수 있기 때문에 지정할 때
컬렉션타입 프로퍼티즈 전달할 때처럼 beans:list 로 감싸서 전달하면 됨
실행 파일
index.jsp
위처럼 필수 항목입니다 대신 필수 항목이 떠야함