국비지원 과정/Spring

코딩 71 일 / 2022.10.04 / Spring MVC 패턴 흐름, @RequestMapping , @ModelAttribute, @RequestParam 어노테이션, Intercepter

레이커 2022. 10. 4. 12:48

복습

Spring Project (Web Application) 구조

 

환경설정 파일 정리

1. Spring 환경설정 파일

- servlet-context.xml

- root-context.xml

 

2. 프로젝트 환경설정 파일

- web.xml

 

3. Maven 환경설정 파일

- pom.xml

 

root-context.xml

- DB 연동 관련 내용

- 주로 bean 을 만들어서 DB 접속을 처리한다

- 어노테이션 기반 DI + Setter DI 도 사용함

- beans 루트 엘리먼트 안에 bean 을 추가해서 사용

- 어노테이션 방식 + 직접 bean 객체 생성 방식 둘다 사용함

 

servlet-context.xml

- View 파일들이 저장된 최상위 디렉토리 위치를 잡음, 그래야 View 파일로 찾아갈 수 있다

- ViewResolve (prefix, suffix)

- ViewResolve 에도 Setter DI 사용함

 

 

- 어노테이션 기반 DI로 처리됨

 

 어노테이션 기반 DI 사용 조건

1. servlet-context.xml 파일에 base-package 가 지정되어야한다

 

2.해당 클래스위에 4가지 어노테이션 중 하나가 와야한다

3. 주입할 프로퍼티(객체) 위에 @Autowired 작성

 

어노테이션 기반 DI

- Controller 클래스에서 Service 클래스로 넘어가는 법 : Service 객체를 생성해서 메소드를 호출

- Service 클래스에서 DAO 클래스로 넘어가는 방법 : DAO 객체를 생성해서 메소드 호출

- 객체를 생성할때 어노테이션 기반 DI 로 생성하는 것임


Spring Web Application 예제 1

실습 준비

- 기본파일들이 있는 클라우드의 hello 프로젝트를 STS로 import

 

Spring 예제 1

hello 프로젝트 구조

- src/main/java 안에 보통 DAO, Service, Controller 클래스들이 들어있다, 현재는 Controller 클래스만 존재

- View 파일들은 view 폴더 안에 저장되어 있다

- index 파일은 반드시 webapp 폴더 안에 있어야한다

 

- 파일을 하나씩 살펴보자


hello 프로젝트의 환경설정 파일 1 : Maven 환경설정 파일

pom.xml

- Maven 환경설정 파일 pom.xml 에 이미 기본적인 내용이 세팅되어 있다

 

pom.xml 에 작성된 Java 버전, Spring 버전

 

pom.xml 에 추가된 의존 라이브러리 부분 설명

- jacskon 라이브러리 : 콜백함수 비동기로 처리할때 DTO, 리스트를 Json 으로 변환시켜줄 수 있다

- 오라클 비공식 원격저장소, 오라클 JDBC 라이브러리

- MyBatis, MyBatis-Spring 연결 라이브러리 : MyBatis 라이브러리 뿐 아니라 MyBatis-Spring 연결 라이브러리까지 필요

- inject 라이브러리 : @inject 어노테이션 사용할때 필요 (@Autowired 어노테이션과 같은 역할)

- commons-fileupload 라이브러리 : cos 라이브러리를 대신해서 첨부파일 업로드를 위한 라이브러리


hello 프로젝트의 환경설정 파일 2 : 프로젝트 환경설정 파일

web.xml

- Dynamic Project 때부터 계속 만들어졌던 파일

- Apache Tomcat 이 구동될때 자동으로 실행되며, 가장 먼저 읽어오는 파일

- WEB-INF 폴더 내에 있어야 한다

 

web.xml 에 들어가는 주요 3가지 내용

1. Dispatcher Servlet 이름, 위치 및 매핑 설정

- web.xml 부분

- <servlet-class> : Dispatcher Servlet 위치 설정

- <servlet> : 요청시 내부적으로 Dispatcher Servlet (Front Controller) 로 먼저 찾아가기 위해 web.xml 에 Dispatehcer Servlet 의 이름과 위치 등록

- <servlet-mapping> : Dispatcher Servlet 으로 찾아가도록 Servlet 매핑을 잡는 내용

- <servlet-name> : <servlet> 과 <servlet-mapping> 의 <servlet-name> 을 일치시켜야한다

- <url-pattern> : 우리는 이 패턴값만 필요에 따라 조절하면 된다, Model 2 에선 @WebServlet 어노테이션으로 찾아갔었다

 

2. 환경설정 파일 2개 불러오기 : root-context.xml 과 servlet-context.xml 파일

- web.xml 부분

- web.xml 파일은 Apache Tomcat 구동시 자동으로 실행되므로, 자동으로 실행 안되는 다른 환경설정 파일 root-context.xml, servlet-context.xml 을 여기서 부른다

- Application 에서는 main() 메소드에서 환경설정 파일을 직접 읽어왔지만 여기서는 web.xml 이 불러주므로 root-context.xml, servlet-context.xml 은 자동으로 실행됨

+ 만약 이 두 환경설정 파일을 resources 폴더에 넣는다면, classpath: 를 앞에 붙여서 경로를 잡아야한다

 

3. 한글 인코딩

- web.xml 부분

- CharacterEncodingFilter 는 Spring 지정 라이브러리

- 한글값을 post 방식으로 전송할때 한글값을 UTF-8 로 인코딩 시켜주는 역할을 한다

- 자동으로 들어가있지 않고 추가해야하는 코드이다

- 더이상 값이 넘어오는 파일이나, Service 클래스에서 request.setCharacterEncoding("utf-8") 코드를 작성할 필요가 없다


hello 프로젝트의 환경설정 파일 3 : Spring 환경설정 파일

root-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	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">
	
	<!-- Root Context: defines shared resources visible to all other web components -->
		
</beans>

- DB 연동에 필요한 내용이 들어갈 것

- 현재는 DB연동을 안하는 프로젝트 이므로 비어있다

- 주로 필요한 bean 을 직접 안에 추가한다 이때 주입시엔 주로 Setter DI 사용 + Constructor DI 도 사용

- 어노테이션 기반 DI 도 사용한다


hello 프로젝트의 환경설정 파일 4 : Spring 환경설정 파일

servlet-context.xml

- DB 연동에 필요한 파일만 root-context.xml 에서 처리, 나머지 대부분의 환경설정은 주로 servlet-context.xml 이 처리

 

1. 어노테이션 기반으로 bean 을 사용하기 위해 base-package 를 지정

- base-package 로 지정된 패키지안의 모든 클래스들을 읽어온다는 의미

- base-package 로 지정된 패키지안의 클래스들은 4가지 어노테이션 중 하나의 어노테이션이 있어야한다

ex) Service 클래스 위에는 @Servlet 어노테이션, Controller 클래스 위에는 @Controller 어노테이션

+ 현재는 DB연동을 하지 않으므로 Service 클래스는 없고 Controller 클래스와 DTO 클래스가 있음

 

+ top-level 패키지

- Spring Project 만들때 설정했던 top-level 패키지 com.ch.hello 가 src/main/java 안에 들어가 있다

- 나중엔 이 top-level 패키지를 쓰지 않고 다른 패키지를 직접 만들어서 사용함

 

2. ViewResolve 로 View 페이지가 저장될 최상위 디렉토리 설정, 확장자 설정

- Spring 에서 지원되는 InternalResourceViewResolver 클래스로 bean 객체를 생성해서 Setter DI 로 주입하고 있다

- prefix : View 파일들이 저장된 최상위 디렉토리

- suffix : View 파일의 확장자

+ InternalResourceViewRoslver 클래스에는 필드 prefix, suffix 와 Setter 메소드들이 있음

- Controller 클래스에서 리턴할땐 경로인 prefix 와 확장자인 suffix 를 생략해서 return 해야한다

- /WEB-INF/view/home.jsp 가 아닌 "home" 만 작성해서 return 해야함

 

3. 기본적인 매핑을 통해 View 페이지 위치 설정

- 다음에 설명


hello 프로젝트 기능

- index 파일인 index.jsp 를 실행해보자

- hello 클릭시 현재 시간 출력

- 배경색 클릭시 랜덤하게 배경색을 보여줌

- 구구단 클릭시 랜덤하게 구구단 한단을 보여줌

- 회원가입 클릭시 회원가입 폼이 나오고, 회원가입 시킬수 있다 (DB연동은 되어있지 않고 메모리에만 저장)


index.jsp

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

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript">
	function chk() {
		var url = document.getElementById("url").value;
		location.href = url;
	}
</script>
</head>
<body>
	<h2>원하는 작업을 선택하세요</h2>
	<!-- <script type="text/javascript">
			location.href="hello";
		</script> -->
	<select id="url" onchange="chk()">
		<option value="hello">hello</option>
		<option value="color">배경색</option>
		<option value="gugu">구구단</option>
		<option value="joinForm">회원가입</option>
	</select>
</body>
</html>

 

- select 옵션에서 옵션 선택시 change 이벤트 발생, onchage 시 chk() 호출

- chk() 에서 id 가 url 인 태그, 즉 select 태그를 구해오고 value 속성으로 옵션의 value 구해온다

- "gugu" 선택시 location.href 를 "gugu" 로 설정하며 요청하게 됨

 

- 요청을 하면 HomeController.java 의 @RequestMapping 에서 요청을 받는다

+ 옛날 버전에서 사용했던 Handler Mapping 대신 현재는 @RequestMapping 어노테이션으로 요청을 받음


@RequestMapping 어노테이션

- Controller 클래스에서 클라이언트 요청을 받을때 사용하는 어노테이션

- Get / Post 방식으로 요청을 받을 수 있다

ex) index.jsp 에서 요청을 하면 HomeController.java 의 @RequestMapping 에서 요청을 받는다

- value 속성값 : 요청 이름값을 작성, value 값은 서로 다르게 작성해야 한다

- method 속성값 : get 방식으로 요청시 RequestMethod.GET, post 방식으로 요청시 RequestMethod.POST 라 작성

- 그 요청을 받을때 아래의 메소드가 자동으로 실행된다

 

주의

- value 속성값은 "/hello" 처럼 요청이름명 앞에 / 를 붙여도 되고, "hello" 처럼 / 를 빼도 된다.

- 하지만 요청을 보내는 쪽에서는 "/" 를 써선 안된다

- 또한 value 도 생략해도 된다, 바로 value 속성값만 쓰면 된다

- 그리고 Get 방식은 method 속성 자체를 생략해도 된다


Spring MVC 패턴 흐름 설명 예시 1

 

index.jsp 에서 location 객체를 통해 "hello" 로 요청이 왔다면

- select-option 에서 "hello" 를 선택시 "hello" 로 요청이 간다

- 클라이언트 요청 -> Dispatcher Servlet -> Controller 클래스 -> Dispatcher Servlet -> View 페이지

- 이 흐름을 반으로 나눠서 각각 설명

 

클라이언트 요청 -> Dispatcher Servlet -> Controller 클래스

1. 가장 먼저 Dispatcher Servlet 으로 찾아감

- web.xml 에 Servlet 매핑을 "/" 로 잡아뒀으므로 아무거나 요청해도 Dispatcher Servlet 으로 먼저 찾아간다

 

2. 내가 만든 Controller 클래스에서 @RequestMapping 중 요청 이름값과 요청 방식이 일치하는 곳으로 간다

- 아래 코드가 요청 이름값과 요청 방식이 일치하는 곳이다. 

- 요청이름값인 value 는 모두 달라야한다

+ 옛날 버전에서 사용했던 Handler Mapping 대신 현재는 @RequestMapping 어노테이션으로 요청을 받음

 

3. 찾아간 @RequestMapping 아래의 메소드가 자동 호출됨

- hello() 가 자동으로 실행됨

+ 주로 요청이름값과 메소드 명을 일치시키는 경우가 많다 ex) "/hello" 요청시 실행되는 메소드는 hello()

+ 메소드 앞의 자료형은 대부분 String 자료형이 온다

 

+ 3-1. 값을 가져갈때만 :

- View 페이지로 값을 가져가기 위해 Model 객체 또는 ModelAndView 객체를 사용

- 매개변수로 Model 을 설정하면 Model 객체가 자동으로 생성된다

- 위에 Model 클래스를 import 하고 있음

+ 매개변수로 Session 을 설정하면 Session 객체가 자동으로 생성된다

 

Controller 클래스 -> Dispatcher Servlet -> View 페이지

+ 현재는 DB연동하지 않으므로 Service 클래스로 가지 않고 Controller 에서 바로 View 로 이동

 

hello() 안의 내용

4. Date 객체 생성하고 DateFormat 객체로 날짜 시간을 LONG 형으로 길게 설정하고 format() 으로 포맷 적용

 

5. Model 객체 model 로 addAttribute() 메소드를 사용해서 키-밸류 형태로 값을 가져간다

 

6. return "home" 처럼 return 뒤에 View 페이지의 경로값 설정

- servlet-context.xml 에서 prefix, suffix 로 설정했던 것을 생략해야함

- 리턴자료형이 String 이므로 String 형으로 리턴

 

7. Dispatcher Servlet 으로 내부적으로 이동 -> ViewResolver 에서 prefix, suffix 붙이기 -> View 인 WEB-INF/views 안의 home.jsp 로 이동

- servlet-context.xml 의 ViewResolver 에서 설정했던 prefix, suffix 를 붙여서 그 경로로 찾아간다

 

8. View 페이지에서 이 값을 가져온다

- home.jsp 부분

- value 값으로 어떤 값의 형태가 오는지에 따라 사용방법이 달라진다 (기본자료형, DTO, 리스트)

 

- "hello" 요청 캡처


Spring MVC 패턴 흐름 설명 예시 2 (간략)

 

index.jsp 에서 location 객체를 통해 "color" 로 요청이 왔다면 (간략)

- select-option 에서 "배경색" 를 선택시 "color" 로 요청이 간다

- 클라이언트 요청 -> Dispatcher Servlet -> Controller 클래스 -> Dispatcher Servlet -> View 페이지

 

1. index.jsp 에서 location.href 를 통해 "color" 로 요청

2. 내부적으로 Dispatcher Servlet 으로 찾아감

3. HomeController 클래스에서 요청 이름값, 요청 방식이 일치하는 @RequestMapping 으로 "color" 요청을 받음

- 이름값이 맞으면 무조건 값을 받는다

- Contorller 클래스는 여러개일 수 있지만 value 값은 달라야함

4. 난수를 발생시켜서 랜덤으로 색 하나를 구한 뒤 그걸 Model 객체 model 에 value 로 저장함

5. return "color" 와 ViewResolver의 prefix, suffix 에 의해 View 페이지인 /WEB-INF/view/color.jsp 로 찾아간다 

6. color.jsp 에서 body 태그의 bgcolor 속성으로 ${color} 로 해당 값 불러오기

 

- "color" 요청 캡처


Spring MVC 패턴 흐름 설명 예시 3 (간략)

 

index.jsp 에서 location 객체를 통해 "gugu" 로 요청이 왔다면 (간략)

- select-option 에서 "배경색" 를 선택시 "gugu" 로 요청이 간다

- 클라이언트 요청 -> Dispatcher Servlet -> Controller 클래스 -> Dispatcher Servlet -> View 페이지

 

1. index.jsp 에서 location.href 를 통해 "gugu" 로 요청

2. 내부적으로 Dispatcher Servlet 으로 찾아감

3. HomeController 클래스에서 요청 이름값, 요청 방식이 일치하는 @RequestMapping 으로 "gugu" 요청을 받음

- 이름값이 맞으면 무조건 값을 받는다

- Contorller 클래스는 여러개일 수 있지만 value 값은 달라야함

4. 난수를 발생시켜서 랜덤으로 2 ~ 9 중 하나의 숫자를 Model 객체 model 에 value 로 저장함

+ View 페이지에 가져갈 값이 없을땐 매개변수에 아무것도 작성하지 않아도 된다

- 값을 가져갈때만 매개변수로 Model 객체를 받는다

5. return "gugu" 와 ViewResolver의 prefix, suffix 에 의해 View 페이지인 /WEB-INF/views/gugu.jsp 로 찾아간다 

- 이때 이 파일은 반드시 설정되어있는 WEB-INF/views 안에 있어야한다

6. gugu.jsp 에서 JSTL forEach 태그 안에서 해당 단의 구구단을 구하기

 

+ JSTL 코어 라이브러리 불러오기

 

- "gugu" 요청 캡처


Spring MVC 패턴 흐름 설명 예시 4 (간략)

 

index.jsp 에서 location 객체를 통해 "joinForm" 로 요청이 왔다면 (간략)

- select-option 에서 "회원가입" 를 선택시 "joinForm" 로 요청이 간다

 

Controller 클래스는 여러개 만들 수 있다, @RequestMapping 값만 서로 다르게 설정하면 된다

- HomeController 클래스 안에는 "joinForm" 요청을 받는 어노테이션이 없다

- JoinController 클래스에서 @RequestMapping("/joinForm") 으로 요청을 받는 어노테이션이 있다

- JoinController 클래스로 간다

 

- 클라이언트 요청 -> Dispatcher Servlet -> Controller 클래스(JoinController.java) -> Dispatcher Servlet -> View 페이지

 

1. index.jsp 에서 location.href 를 통해 "joinForm" 로 요청

2. 내부적으로 Dispatcher Servlet 으로 찾아감

3. JoinController 클래스에서 요청 이름값, 요청 방식이 일치하는 @RequestMapping 으로 "joinForm" 요청을 받음

 

- 이름값이 맞으면 무조건 값을 받는다

- Contorller 클래스는 여러개일 수 있지만 value 값은 달라야함

4. return "joinForm" 와 ViewResolver의 prefix, suffix 에 의해 View 페이지인 /WEB-INF/views/joinForm.jsp 로 찾아간다 

- 바로 회원가입 폼인 "joinForm.jsp 로 간다

- View 페이지에 가져갈 값이 없으므로 매개변수에 아무것도 작성하지 않아도 된다

- 이때 이 파일은 반드시 설정되어있는 WEB-INF/views 안에 있어야한다

6. joinForm.jsp 인 회원가입 폼으로 간다

 

7. 전송버튼 누르면 post 방식 "join" 으로 요청한다

- 다시 Dispatcher Servlet -> Controller 클래스로 간다

- 입력양식의 name 값들을 유심히 보기

- 회원가입 할때 회원 정보를 저장하기 위한 DTO 클래스 Member 의 프로퍼티명과 입력양식의 name 값들을 일치시켜야한다

- 그래야만 매핑을 통해 자동으로 DTO 객체에 입력양식의 값들이 들어간다 * 아래에 자세히 설명

- Member.java 부분

8. Controller 클래스에 @RequestMapping 으로 찾아가서 join() 메소드 실행

9. 매개변수로 이미 값이 자동 세팅된 DTO 객체를 받는다

- DTO 객체인 member 에 값을 세팅하지 않아도 폼에서 넘어온 입력양식의 값들이 자동으로 DTO 객체 member 안에 세팅됨

 

- Member member 앞에 @ModelAttribute 생략된 것임

- 이 @ModelAttribute 가 뒤쪽의 DTO 객체에 Setter 메소드로 자동으로 세팅해준다

- 다만, DTO 객체의 필드명과 입력양식의 name 값들이 일치할때만 가능하다!

- 그 자동 세팅된 객체 member 를 View 로 보내기 위해 addAttribute() 사용

10. Model 객체 model 을 통해 addAttribute() 로 DTO 객체 member 를 저장

11. return "joinResult" 를 통해 WEB-INF/views/joinResult.jsp 로 간다

12. DTO 객체가 Model 객체에 저장되었으므로 View 페이지에서 EL 로 출력할때는 ${공유네임값.필드명} 인 ${member.id} 로 출력

 


@ModelAttribute 어노테이션

- 어노테이션을 선언하고 뒤에는 DTO 객체를 선언한다

- 뒤쪽의 DTO 객체에 폼을 통해 한꺼번에 넘어온 값들을 세팅해준다

- 가입되는 양식의 name 값 과 DTO 클래스의 필드명을 일치시켜야만 가능

- @ModelAttribute 어노테이션을 생략해도 DTO 필드명과 name값이 일치하면 DTO 객체에 값이 자동으로 들어감

- 더이상 Setter 메소드로 넘어온 값들을 DTO 객체에 값을 설정하지 않아도 된다

- 그래서 가입되는 양식의 name 값 과 DTO 클래스의 필드명을 일치시켜야함


+ header.jsp 파일에는 공통적으로 들어가는 내용들이 있다, 그걸 include 하고 있다

- joinForm.jsp 부분

- header.jsp

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="css/bootstrap.min.css" rel="stylesheet">
<script src="js/jquery.js"></script>
<script src="js/bootstrap.min.js"></script>
<style>	.err { color: red; font-size: 20px;  } </style>

- 부트스트랩을 적용, 각종 Javascript 파일을 포함

 

- "joinForm" 요청 캡처

- @ModelAttribuate 로 DTO 객체에 값이 세팅되는지 확인하기 위해 위 코드 작성 후 콘솔 확인

- 아이디를 잘 출력하는 것을 보아 DTO 객체에 값이 잘 세팅되었음을 확인 가능


요청이름명으로 Controller 로 찾아갈때 주의

- value 속성값은 "/hello" 처럼 요청이름명 앞에 / 를 붙여도 되고, "hello" 처럼 / 를 빼도 된다.

- 또한 value 도 생략해도 된다, 바로 value 속성값만 쓰면 된다

- 그리고 Get 방식은 method 속성 자체를 생략해도 된다

 

- 하지만 요청을 보내는 쪽에서는 "/" 를 써선 안된다

 

- joinForm.jsp 부분

- index.jsp 부분


- HomeController.java (Controller 클래스, 전체 코드)

package com.ch.hello;

import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class HomeController {
	private static final Logger logger = LoggerFactory.getLogger(HomeController.class);

	@RequestMapping(value = "/hello", method = RequestMethod.GET)
	public String hello(Locale locale, Model model) {
		logger.info("Welcome home! The client locale is {}.", locale);
		Date date = new Date();
		DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);
		String formattedDate = dateFormat.format(date);
		model.addAttribute("serverTime", formattedDate);
		return "home";
	}

	@RequestMapping("/color")
	public String color(Model model) {
		String[] color = { "red", "orange", "yellow", "green", "blue", "navy", "violet" };
		int num = (int) (Math.random() * 7);
		model.addAttribute("color", color[num]);
		return "color";
	}

	@RequestMapping("/gugu")
	public String gugu(Model model) {
		int num = (int) (Math.random() * 8) + 2;
//		int a = num / 0;
		model.addAttribute("num", num);
		return "gugu";
	}
	/*
	 * @ExceptionHandler(ArithmeticException.class) 
	 * public String err() { 
	 * return
	 * "arr-err"; }
	 */
}

- 새로운 어노테이션을 보기 위해 다른 파일을 보자

 

- person.jsp (수정 전)

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
	
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
	<title>Insert title here</title>
</head>
<body>
	<h2>이름과 주소</h2>
	<form action="addr">
	<!-- <form action="addr2"> -->
		이름 : <input type="text" name="name">
		<p>
			주소 : <input type="text" name="addr">
		<p>
			<input type="submit" value="확인">
	</form>
</body>
</html>

- action 값이 "addr" 인 경우 form 태그 먼저 설명 * action 값이 "addr2" 인 경우는 아래에서 설명

- 이 person.jsp 실행시 이름, 주소 입력 양식

- 이름 입력양식의 name 값은 "name", 이 값을 받기 위해 request.getParameter() 를 사용했었다

- 주소 입력양식의 name 값은 "addr", 이 값을 받기 위해 request.getParameter() 를 사용했었다

- 이제 이걸 어노테이션으로 대체 가능

 

- '확인'을 누르면 이름과 주소가 Dispatcher Servlet -> Controller 클래스로 간다

- Controller 클래스에서 "addr" 을 받는 @RequestMapping 어노테이션을 찾아보자

- PersonController.java 부분

- 위에 @Controller 어노테이션이 붙어있는 Controller 클래스 중 하나이다

- action 값이 "addr" 인 요청을 @RequestMapping 어노테이션이 있다

- 폼에서 넘어온 값을 받아보자

 

폼에서 넘어온 값 받기

- @RequestParam 어노테이션을 통해 앞에서 넘어온 값들을 name 값을 통해 가져와서 매개변수에 넣어준다

 

@RequestParam 어노테이션

- name 으로 값을 받을때 사용하는 어노테이션

- @RequestParam("name") 으로 앞에서 넘어온 값을 받아서 뒤쪽의 String 형 변수 name 에 저장한다

- @ReuqestParam("addr") 으로 앞에서 넘어온 값을 받아서 뒤쪽의 String 형 변수 addr 에 저장한다

+ 다운캐스팅 명시하지 않아도 자동으로 뒤에 쓰인 자료형으로 자동 변환된다

- 그러면 넘어온 name, addr 값을 addr() 메소드 안에서 바로 사용가능

- 넘어온 값을 받을때 request.getParameter("name") 으로 받았던 것을 @RequestParam 으로 받을 수 있다

 

- 즉, 아래 두 줄의 코드는 같은 코드이다

String name = request.getParameter("name")
@RequestParam("name") String name

 

@RequestParam 어노테이션 생략가능한 조건

- 위 코드는 아래처럼 @RequestParam 을 생략할 수도 있다

- 전달되는 name 값과 같은 이름의 변수로 값을 받을때는 @RequestParam 을 생략 가능하다

- 앞에서 넘어온 name 값인 "name", "addr" 과 같은 이름인 "name", "addr" 을 매개변수에 썼으므로, @RequestParam 을 생략가능하다

 

- 이후, 받아온 name, addr 값들을 다시 Model 객체에 저장해서 View 페이지인 WEB-INF/views/addr.jsp 로 간다\

- addr.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
	
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>${name }님 ${addr }에 사시는 군요
</body>
</html>

- Model 객체에 String 형으로 저장되어있으므로 단순히 ${name}, ${addr} 을 통해 View 에서 출력 가능

 


 

- 개별적으로 전달되는 값을 받을때는 @RequestParam 을 사용했다

- 이번에는 전달되는 값을 한번에 받아서 객체에 자동으로 저장하는 @ModelAttribute 를 사용해보자

 

- person.jsp 에서 action 값이 "addr2" 인 경우 form 태그를 설명

- person.jsp (수정 후)

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
	
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
	<title>Insert title here</title>
</head>
<body>
	<h2>이름과 주소</h2>
	<!-- <form action="addr"> -->
	<form action="addr2">
		이름 : <input type="text" name="name">
		<p>
			주소 : <input type="text" name="addr">
		<p>
			<input type="submit" value="확인">
	</form>
</body>
</html>

- 입력하고 '확인' 을 누르면 이름과 주소가 Dispatcher Servlet -> Controller 클래스로 간다

 

- PersonController.java 부분

- 즉 앞에 @ModelAttribute 가 생략되어 있는 것이다

- 폼에서 넘어온 값들을 한번에 DTO Person 객체인 p 에 저장(세팅)한다

- 이때, DTO Person 클래스의 프로퍼티명과 넘어온 값의 name 이 일치하므로, 넘어온 값이 자동 매핑되어 Person 객체에 저장되는게 가능하다

 

- Person.java (DTO 클래스)

package com.ch.hello;

public class Person {
	private String name;
	private String addr;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getAddr() {
		return addr;
	}

	public void setAddr(String addr) {
		this.addr = addr;
	}
}

- 폼에서 넘어온 name, addr 을 받아 저장하도록 프로퍼티가 같은 이름으로 있다

- 그러므로 자동으로 이 Person 객체에 넘어온 값들을 세팅해서 저장 가능

 

- 그리고 그렇게 세팅한 Person 객체 p 를 Model 객체 model 에 저장한 후 WEB-INF/views/addr2.jsp 로 이동

- addr2.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
	
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>${person.name }님 ${person.addr }에 사시는 군요
</body>
</html>

- DTO 객체 p 가 "person" 이란 네임으로 저장되었으므로 View 페이지에서 출력할땐 ${person.필드명} 으로 출력하고 있다

 

+ 객체 p 에 값이 제대로 세팅되었는지 확인해보자

- 위 한줄을 추가하고 person.jsp 실행해서 값 입력 후 전송

- 콘솔창 보기

- 폼에서 넘어온 값들이 DTO Person 객체 p 에 잘 저장되었음을 확인 가능


정리

- @ModelAttribute : (주로 폼을 통해) 넘어온 값을 한번에 받아서 DTO 객체에 저장하고 싶을때 사용

- @RequestParam : 개별적으로 값을 받아 변수에 저장하고 싶을때 사용

- 수십개의 어노테이션이 있다, 최소 10개 정도는 알아야 사용 가능

 

Model 객체에 저장되는 값에 따라 값을 View 에서 출력하는 방법

1. 기본자료형 변수 : ${키} 로 출력

2. DTO 객체 : ${키.필드명} 으로 출력

3. 리스트 : forEach 태그의 items 속성에 ${키} 를 써서 사용



- Controller 클래스에서 메소드 앞에 자료형이 String 이 아닌 DTO, 리스트가 오는 경우를 보자

 

- sample.jsp (수정 전)

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
	
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<script type="text/javascript">
		location.href="sample";
		// location.href = "list";
	</script>
</body>
</html>

- location.href 로 "sample" 을 주는 경우부터 먼저 하자 * "list" 로 주는 경우는 아래에

- sample.jsp 를 실행되자마자 "sample" 로 요청을 한다


- sample.jsp 실행시

- json 형태 데이터를 출력함

- json 은 이런 키:밸류 형태로 되어있다

- 1명에 대한 정보를 DTO 객체에 저장시켜서 JSON 형태로 돌려준 것이다

- pom.xml 에 추가된 jackson-databind 라이브러리가 DTO 객체를 JSON 데이터로 변환시킨것이다


- sample.jsp 에서 "sample" 로 온 요청을 받아주는 Controller 클래스의 @RequestMapping 어노테이션으로 가자

- SimpleController.java 부분

- 보통은 메소드의 리턴자료형이 String 이지만 이 경우엔 DTO 클래스인 SampleVo 클래스가 리턴자료형이다

- 비동기적으로 처리할때 많이 사용함

- DTO SampleVo 객체를 sv 를 생성 후 Setter 메소드로 값을 저장시킴

- DTO SampleVo 객체 sv 를 리턴하면 @ResponseBody 어노테이션때문에 값을 "요청한 곳(호출한 곳)"으로 돌려준다

- 즉, "sample" 로 요청했던 브라우저인 sample.jsp 로 돌려준다

- 돌려줄때 DTO 객체를 JSON 형태로 변환한 후 돌려준다

+ @RestController = @Controller + @ResponseBody

 

- sample.jsp 로 세팅한 DTO 객체를 돌려주는데 JSON 으로 변환해서 콜백함수 형태로 브라우저에 돌려주므로 브라우저에 아래처럼 출력됨 

- 이때, JSON 형태 데이터의 키는 그 DTO SampleVo 클래스의 필드명이고, 밸류는 DTO 객체에 세팅했던 값이 된다

 

@RestController 어노테이션

- Spring 4점대부터 지원하는 어노테이션

- 두가지 어노테이션을 결합한 어노테이션

- @RestController = @Controller + @ResponseBody

- 메소드 위에 @ResponseBody 어노테이션이 붙어있으면, 요청한 곳으로 결과를 콜백함수로 돌려주는 역할을 한다

- 따로 콜백함수를 설정하지 않았으므로 요청한 곳으로 값을 돌려줌, 콜백함수가 있을때는 거기로 돌려준다

 

- @RestController 어노테이션을 @Controller 와 @ResponseBody 로 나누는 방법

- 이러면 아래의 @RestController 만을 사용했던 코드와 같다

 

- SampleVo.java (DTO 클래스) 부분

- mno, firstName, lastName 3개의 프로퍼티가 만들어져있고, getter / setter 메소드가 있다


- sample.jsp 에서 "list" 로 요청하는 경우를 보자

- sample.jsp (수정 후)

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
	
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<script type="text/javascript">
		// location.href="sample";
		location.href = "list";
	</script>
</body>
</html>

 

- SampleController.java 부분

+ Controller 클래스명위에 @Controller 가 있고 여기 함수명 위에는 @ResponseBody 를 붙였다

- 이번엔 List 로 리턴하는 경우이다

- 리스트를 생성한 후 반복문에서 DTO 객체를 생성, 세팅하고 리스트에 추가하는 것을 반복

- jackson 라이브러리가 DTO 객체를 JSON 형태로 변환한 후, 요청한 곳으로 해당 리스트를 돌려준다

- 즉, 요청한 브라우저인 list.jsp 로 돌려준다

 

- sample.jsp 실행시

- JSON 형태로 변환된 DTO 객체의 값들이 출력되어있다


Spring Web Application 예제 2

 

실습 준비

- 클라우드에서 ch07 프로젝트를 다운받아 import

 

Spring Web Application 예제 2\

ch07 프로젝트 구조

- 파일별 자세한 기능은 이전 프로젝트인 hello 프로젝트의 설명을 보기

- 지금은 hello 프로젝트에서 달라진 내용만 설명


추가된 라이브러리

- pom.xml 부분 1

- Spring 에선 cos 라이브러리 대신 주로 fileupload 라이브러리로 첨부파일 업로드

 

cos 라이브러리 vs fileupload 라이브러리

- fileupload 라이브러리는 업로드 시켜주는 코드가 따로 있다

- fileupload 라이브러리는 중복되는 이름의 파일명을 자동처리해주지 않아서 내가 처리해야함

 

- pom.xml 부분 2

- 이메일로 전송해주는 라이브러리

- 아이디, 비밀번호 찾기를 했을때 아이디, 비밀번호를 이메일로 전송해주는 기능을 위해서 이 라이브러리들을 추가해야함


URL 패턴 설정

- web.xml 부분

- <url-pattern> 을 *.do 로 설정했다

- Dispatcher Servlet 으로 찾아갈때 do 확장자를 가진 요청이름값일때만 Dispatcher Servlet 으로 보내겠다는 의미


Spring 환경설정 파일 위치 및 부르기

- 현재는 Web Application 이므로 webapp 폴더가 존재하고, 그 webapp 폴더 아래 어딘가에 servlet-content.xml, root-context.xml 이 있다

- 이 Spring 환경설정 파일들은 위치가 고정된 것이 아니며, 나중에 주로 resources 폴더에 저장시키기도 한다

- resources 폴더에 저장되어있을땐, web.xml 에서 환경설정 파일을 불러올때 classpath: 를 붙여야한다

 

ex) servlet-context.xml 이 resources 폴더에 저장되어있다면 web.xml 에서 불러올때 classpath:servlet-context.xml 으로 경로 설정

- web.xml 부분

	<servlet>
		<servlet-name>appServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:servlet-context.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>

한글값 인코딩

- 위에서 web.xml 에 들어가는 내용 중 Spring 환경설정 파일 부르기, Dispatcher Servlet 위치 및 패턴 지정을 했다

- 마지막인 한글값 인코딩 내용 또한 들어가 있다

- post 로 넘어온 한글값에 대한 인코딩 완료


Spring 환경설정 파일

 

root-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	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">
	<bean id="multipartResolver"
		class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
	</bean>		
		
</beans>

- 직접 bean 을 추가해서 객체를 생성해야한다

 

servlet-context.xml 부분 1

	<context:component-scan base-package="com.ch.ch07" />

- 어노테이션 기반으로 쓰기 위한 첫번째 조건이다

- com.ch.ch07 패키지 안의 모든 클래스를 읽어오겠다는 의미

 

- LoginCheck.java , UploadController.java 클래스를 읽어온다

- 해당 UploadController.java 클래스명 위에 @Controller 어노테이션이 붙어있다

- 어노테이션 기반으로 쓰기 위한 두번째 조건이다

 

servlet-context.xml 부분 2

	<!-- 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:bean>

- View 파일들은 모두 /WEB-INF/views 안에 들어가있어야한다

- 확장자는 jsp 이다

 

servlet-context.xml 부분 3

	<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
	<resources mapping="/resources/**" location="/resources/" />
	<resources mapping="/css/**" location="/WEB-INF/css/" />
	<resources mapping="/js/**" location="/WEB-INF/js/" />
	<resources mapping="/fonts/**" location="/WEB-INF/fonts/" />

- "View 페이지에 사용할" CSS, JS, 폰트 등의 매핑을 설정하는 역할

- CSS 파일들은 /WEB-INF/css/ 폴더 안에 저장되어있어야함

- JS 파일들은 /WEB-INF/js/ 폴더 안에 저장되어있어야함

- Fonts 들은 /WEB-INF/fonts/ 폴더 안에 저장되어있어야함

- 이렇게 매핑이 잡혀있어야만 불러올 수 있음

 

- 여기까지는 아는 내용, 다음은 새로하는 내용

 

servlet-context.xml 부분 4 : Intercepter 설정

	<!-- Interceptor설정 -->	
	<beans:bean id="lc" class="com.ch.ch07.LoginCheck"/>
	<interceptors>
		<interceptor>
			<mapping path="/upload.do"/>
			<beans:ref bean="lc"/>
		</interceptor>
	</interceptors>

* Intercepter 내용 아래에서 자세히 설명

- 회원관리에서 주로 사용


인터셉터 (Intercepter)

- 클라이언트에서 특정 URL 요청시 DispatcherServlet이 Controller를 호출하기 이전에 해당 요청을 가로채는 역할을 수행한다.

 

인터셉터 (Intercepter) 사용 예제

- 세션을 공유한 후, 정상적으로 접근시에는 세션이 공유되지만 비정상적 접근시에는 세션이 공유되지 않는다

- 세션 있는지 확인한 후 없다면 로그인 폼으로 보내는 역할을 Intercepter 가 하게 됨

 

- servlet-context.xml 에서 Intercepter 를 설정한 부분을 보자

- "upload.do" 로 요청이 들어오면 Intercepter 인 LoginCheck 가 가로채도록 설정했다

 

- LoginCheck.java

package com.ch.ch07;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

public class LoginCheck implements HandlerInterceptor {
	
	// DispatcherServlet의 화면 처리가 완료된 상태에서 처리
	public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
			throws Exception {
	}

	// 지정된 컨트롤러의 동작 이후에 처리, Spring MVC의 Front Controller인 DispatcherServlet의 화면 처리가 완료된 상태에서 처리
	public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
			throws Exception {
	}

	// 지정된 컨트롤러의 동작 이전에 가로채는 역할 (세션이 없으면, 로그인 폼으로 이동 하도록 해준다 )
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception {
		HttpSession session = request.getSession();
		if (session.getAttribute("id") == null) {
			response.sendRedirect("loginForm.do");
			return false;
		}
		return true;
	}
}

- LoginCheck.java 는 Intercepter 관련 인터페이스를 구현한 클래스

- preHandle() 메소드안에서 만약 세션이 없다면 로그인 폼으로 보내는 역할을 하고 있다

 

- 이제 제대로 예제를 설명

 

인터셉터 (Intercepter) 사용 예제의 흐름

Intercepter 흐름

- 특정 URL 로 요청시 Dispatcher Servlet 이 Controller 를 호출하기 이전에 Intercepter 가 가로챈다

- 여기 예제에선 "upload.do" 로 요청시 Controller 로 가기 전에 가로채서 로그인이 되어있는지 되어있지 않는지 판별한다

- 로그인이 되어있지 않으면 비정상 접근이라고 생각해서 로그인 폼으로 보낸다

+ Intercepter 는 회원관리 프로그램에서만 사용함

 

인터셉터(Intercepter) 구현 방법 2가지

1. abstract class HandlerInterceptorAdapter 클래스를 상속 받아서 구현

2. interface HandlerInterceptor 인터페이스를 상속 받아서 구현

 

- 이후 그 클래스에서 아래의 3개의 메소드를 오버라이딩 해서 인터셉터 기능을 구현한다

+ 인터페이스를 상속받은 경우에는 반드시 3개를 모두 오버라이딩 해야함

- boolean preHandle()

- void postHandle()

- void afterCompletion()

 

- 인터셉터를 어떻게 구현했는지 LoginCheck.java 에서 보자

- LoginCheck.java

package com.ch.ch07;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

public class LoginCheck implements HandlerInterceptor {
	
	// DispatcherServlet의 화면 처리가 완료된 상태에서 처리
	public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
			throws Exception {
	}

	// 지정된 컨트롤러의 동작 이후에 처리, Spring MVC의 Front Controller인 DispatcherServlet의 화면 처리가 완료된 상태에서 처리
	public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
			throws Exception {
	}

	// 지정된 컨트롤러의 동작 이전에 가로채는 역할 (세션이 없으면, 로그인 폼으로 이동 하도록 해준다 )
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception {
		HttpSession session = request.getSession();
		if (session.getAttribute("id") == null) {
			response.sendRedirect("loginForm.do");
			return false;
		}
		return true;
	}
}

- Intercepter 를 구현하는 2가지 방법 중 HandlerInterceptor 인터페이스를 상속받았다

- 인터페이스의 추상메소드 3개를 모두 오버라이딩 한다

- 이 메소드들은 호출되는 시점이 다르다 * 호출 시점 아래에서 설명

- 현재 예제는 이 중 preHandle() 메소드만 사용하고있고 나머지는 오버라이딩 만하고 비어있다

- preHandle() 에서 session 객체를 생성하고, session 값이 있는지 없는지 따진다

1) session 값이 없다면 sendRedirect() 로 로그인폼으로 가고 return false; 를 돌려준다

2) session 값이 있다면 return true; 를 하면 원래 가던 Controller 클래스로 돌아간다

 

Intercepter 관련 3개의 메소드가 호출되는 시점

- preHandle() 메소드 : Dispatcher Servlet 에서 Controller 로 가기전에 가로채서 자동으로 실행됨

- postHandle() 메소드 : Controller 클래스에서 Dispatcher Servlet 으로 가기 전에 가로채서 자동으로 실행됨

- afterCompletion() 메소드 : View 페이지에서 리턴할때 호출됨 (View 페이지 완성될때)

- 현재 예제는 이 중 preHandle() 메소드만 사용하고있고 나머지는 오버라이딩 만하고 비어있다

- preHandle() 에서 세션이 있는지 없는지 따져서, 없다면 로그인폼으로 보내는 역할을 한다

 

Intercepter 의 장점

- 로그인이 되어있는지(세션이 있는지) 각 페이지마다 신경쓸 필요가 없다

 

Inetercepter 환경 설정 방법

- servlet-context.xml부분

	<!-- Interceptor설정 -->	
	<beans:bean id="lc" class="com.ch.ch07.LoginCheck"/>
	<interceptors>
		<interceptor>
			<mapping path="/upload.do"/>
			<beans:ref bean="lc"/>
		</interceptor>
	</interceptors>

- bean 을 만들고, class 속성값으로는 패키지부터 Intercepter 클래스인 LoginCheck 클래스까지의 경로 설정

- "/upload.do" 로 요청이 들어올떄 매핑을 잡는다, 그럼 id가 "lc" 인 곳의 class 속성으로 설정된 클래스 LoginCheck 가 실행된다


로그인 시도시 흐름

- index.jsp 를 실행해보자

- index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
	
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<script type="text/javascript">
		location.href = "upload.do";
	</script>
</body>
</html>

1. "upload.do" 로 요청하므로 Intercepter 에 걸린다! (servlet-context.xml 에서 설정)

2. 그럼 Intercepter 클래스인 LoginCheck 클래스가 실행된다

3. 세션값이 없으므로 비정상적인 접근으로 인식해서 Intercepter 가 로그인 폼으로 보내준다

- session.getAttribute("id") 가 null 이므로 로그인 폼 loginform.do 로 요청

4. Controller 클래스에서 @RequestMapping 이 맞는 곳으로 간다, "loginForm" 으로 간다

5. loginForm.jsp 가 실행됨

- loginForm.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ include file="header.jsp"%>

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<div class="container">
		<form action="login.do">
			<h2 class="text-primary">로그인</h2>
			<c:if test="${not empty msg }">
				<span class="err">${msg }</span>
			</c:if>
			<table class="table table-bordered">
				<tr>
					<th>아이디</th>
					<td><input type="text" name="id" required="required"></td>
				</tr>
				<tr>
					<th>암호</th>
					<td><input type="password" name="pass" required="required"></td>
				</tr>
				<tr>
					<th colspan="2"><input type="submit" value="확인"></th>
				</tr>
			</table>
		</form>
	</div>
</body>
</html>

+ header.jsp 가 include 되어있다

- header.jsp

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<c:set var="path" value="${pageContext.request.contextPath }" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="${path}/css/bootstrap.min.css" rel="stylesheet">
<script src="${path}/js/jquery.js"></script>
<script src="${path}/js/bootstrap.min..js"></script>
<style>
.err {
	color: red;
	font-size: 20px;
}
</style>

- core 라이브러리 set 태그로 path 변수에 현재 프로젝트명을 저장했다

- ${pageContext.request.contextPath} 는 현재 프로젝트명을 가져옴, 그걸 변수 path 에 저장

- 링크로 부트스트랩, jQuery 파일들을 불러올때 앞에 ${pat} 를 붙여서 부트스트랩, jQuery 파일들의 절대경로를 구해오고 있음

- 아이디, 비번이 틀렸을때 .err 부분을 loginForm.jsp 에서 불러옴

 

6. loginForm.jsp 로그인폼에서 아이디, 비번 입력 후 로그인 시도

- "login.do" 요청되어 Controller 로 간다

UploadController.java 부분

+ 이때 loginForm.jsp 폼에서 넘어온 값이 바로 String id, String pass 에 저장된다

+ 즉 앞에 @RequestParam 이 생략되어있음

1) 아이디, 비번을 맞게 입력하여 로그인에 성공하면 id 를 세션설정하고 loginSuccess.jsp 로 간다

2) 로그인에 실패하면 loginForm.jsp 로 돌아오고 이떄 아래의 if 태그에서 msg 가 비어있지 않게되므로 if 조건을 만족하여 "똑바로 입력해" 메세지를 출력함

loginForm.jsp 부분

- Controller 로 갔다왔을때만 msg 가 비어있지 않게되어서 메세지를 출력함

 

7. 로그인 성공시 loginSuccess.jsp 로 이동

+ 실패시엔 위에작성, loginForm.jsp 로 간다

- loginSuccess.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ include file="header.jsp"%>

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h2 class="text-primary">로그인 성공</h2>
	<a href="upload.do" class="button button-success">업로드</a>
</body>
</html>

- 여기도 '업로드' 클릭시 "upload.do" 로 요청하므로 Intercepter 에 걸린다

- 인터셉터 클래스 LoginCheck 이 실행됨, preHandle() 메소드가 실행됨

- 지금은 세션값이 있는 상태이므로 null 값이 아니다, return true; 해서 원래 가려고 했던 Controller 쪽으로 찾아간다

 

8. 현재는 loginSuccess.jsp 에서 Get 방식으로 요청했으므로 return "upload" 에 의해 upload.jsp 로 간다

- 위로 찾아가서 upload.jsp 로 이동

 


index.jsp 실행 캡처

- 아이디는 "java" 비번은 "1234" 로 입력해야 로그인 성공이다

- 틀린 비번 입력시

 

- 맞는 비번 입력시