코딩 71 일 / 2022.10.04 / Spring MVC 패턴 흐름, @RequestMapping , @ModelAttribute, @RequestParam 어노테이션, Intercepter
복습
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 로 간다
+ 이때 loginForm.jsp 폼에서 넘어온 값이 바로 String id, String pass 에 저장된다
+ 즉 앞에 @RequestParam 이 생략되어있음
1) 아이디, 비번을 맞게 입력하여 로그인에 성공하면 id 를 세션설정하고 loginSuccess.jsp 로 간다
2) 로그인에 실패하면 loginForm.jsp 로 돌아오고 이떄 아래의 if 태그에서 msg 가 비어있지 않게되므로 if 조건을 만족하여 "똑바로 입력해" 메세지를 출력함
- 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" 로 입력해야 로그인 성공이다
- 틀린 비번 입력시
- 맞는 비번 입력시