*필터
첫 번째 필터(서블릿 API) : 서블릿컨테이너와 디스팻쳐 사이에 꽂는// 디스팻쳐 실행 전
두 번째 필터 (Interceptor 스프링): 디스패쳐와 컨트롤러사이(컨트롤러 실행 전) , 디스패쳐와 jsp 사이(jsp 실행 후 리턴때) // 무언가 작업을 삽입하고 싶을 때
AOP 필터(프록시 기술): 서비스나 dao의 메서드 호출 전 후 꽂는거 , 모든 객체의 매서드 호출 전 / 원래 객체를 건들지 않고도 그 객체의 기능을 사용할 수 있음.
*Interceptor
// 인터셉터 만들기
// => 프론트 컨트롤러와 페이지 컨트롤러 사이에 코드를 삽입하는 기술
// => 프론트 컨트롤러와 뷰 컴포넌트 사이에 코드를 삽입하는 기술
// 인터셉터를 배치하기
// => 프론트 컨트롤러의 IoC 설정 파일에 배치 정보를 추가한다.
public class Controller04_1_Interceptor1 implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// 페이지 컨트롤러의 핸들러를 호출하기 전에 이 메서드가 먼저 호출된다.
System.out.println("Interceptor1.preHandle()");
// 다음 인터셉터나 페이지 컨트롤러를 계속 실행하고 싶다면 true를 리턴한다.
// 여기서 요청 처리를 완료하고 싶다면 false를 리턴한다.
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// 페이지 컨트롤러의 핸들러가 리턴한 후 이 메서드가 호출된다.
System.out.println("Interceptor1.postHandle()");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
// JSP를 실행한 후 이 메서드가 호출된다.
System.out.println("Interceptor1.afterCompletion()");
}
}
appconfig- 인터셉터 등록
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 1) 모든 요청에 대해 실행할 인터셉터 등록하기
// => 인터셉터를 적용할 URL을 지정하지 않으면
// 현재 프론트 컨트롤러의 모든 요청에 대해 적용된다.
WebMvcConfigurer.super.addInterceptors(registry);
}
// 2) 특정 요청에 대해 실행할 인터셉터 등록하기
// => 패턴: /c04_1/*
// 적용 가능:
// /c04_1/x
// /c04_1/y
// 적용 불가:
// /x
// /c03_1/x
// /c04_1/a/x
// /c04_1/a/b/x
// 즉, /c04_1/ 바로 밑의 있는 자원에 대해서만 인터셉터를 적용한다.
//
registry
.addInterceptor(new Controller04_1_Interceptor2())
.addPathPatterns("/c04_1/*");
// 3) 특정 요청에 대해 실행할 인터셉터 등록하기
// => 패턴: /c04_1/**
// 적용 가능:
// /c04_1/x
// /c04_1/y
// /c04_1/a/x
// /c04_1/a/b/x
// 적용 불가:
// /x
// /c03_1/x
// 즉, /c04_1/ 의 모든 하위 경로에 있는 자원에 대해서만 인터셉터를 적용한다.
//
registry
.addInterceptor(new Controller04_1_Interceptor3())
.addPathPatterns("/c04_1/**");
// 4) 특정 요청에 대해 인터셉터 적용을 제외하기
// => 패턴: /c04_1/** (include), /c04_1/a/** (exclude)
// 적용 가능:
// /c04_1/x
// /c04_1/y
// /c04_1/b/x
// /c04_1/b/c/x
// 적용 불가:
// /x
// /c03_1/x
// /c04_1/a/x
// /c04_1/a/b/x
// 즉, /c04_1/ 의 모든 하위 경로에 있는 자원에 대해서만 인터셉터를 적용한다.
// 단 /c04_1/a/ 의 모든 하위 경로에 있는 자원은 제외한다.
registry
.addInterceptor(new Controller04_1_Interceptor4())
.addPathPatterns("/c04_1/**")
.excludePathPatterns("/c04_1/a/**");
@ComponentScan("bitcamp.app2")
// => 지정된 패키지를 뒤져서 @Component, @Controller 등 붙은 클래스에 대해 인스턴스를 생성한다.
//
@EnableWebMvc
// => Web MVC 관련 객체를 등록하고 설정한다.
// => WebMvcConfigurer 구현체를 찾아 메서드를 호출한다.
//
public class App2Config implements WebMvcConfigurer {
// DispatcherServlet의 기본 ViewResolver를 교체하기
// 1) XML 설정
// <bean id="viewResolver"
// class="org.springframework.web.servlet.view.InternalResourceViewResolver">
// <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
// <property name="prefix" value="/WEB-INF/jsp/"/>
// <property name="suffix" value=".jsp"/>
// </bean>
//
// 2) Java Config 설정
@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver vr = new InternalResourceViewResolver(
"/WEB-INF/jsp2/", // prefix
".jsp" // suffix
);
return vr;
// => prefix + 페이지컨트롤러 리턴 값 + suffix
// 예) "/WEB-INF/jsp2/" + "board/list" + ".jsp" = /WEB-INF/jsp2/board/list.jsp
}
// @MatrixVariable 애노테이션 처리를 활성화시킨다.
// => 이 작업을 수행하려면 MVC 관련 설정 작업을 수행할 수 있도록
// WebMvcConfigurer 인터페이스를 구현해야 한다.
// => 그리고 다음 메서드를 오버라이딩 하여 기존 설정을 변경한다.
//
// DispatcherServlet 이 MVC 관련 설정을 처리하는 과정
// => WebMVC 설정을 활성화시켰는지 검사한다.
// => 활성화시켰으면, IoC 컨테이너가 관리하는 객체 중에서
// WebMvcConfigurer 구현체를 찾는다.
// 관리하는 객체?
// - IoC 컨테이너에 들어 있는 객체
// - @Component, @Service, @Controller, @RestController, @Repository
// 애노테이션이 붙은 클래스들은 IoC 컨테이너가 자동으로 객체를 생성하여 보관한다.
// - 하지만 WebMvcConfigurer 구현체는 Java Config 클래스이기 때문에
// 일반 객체로 표시하지 말고, @Configuration 을 붙여
// Java Config 클래스임을 명확히 하는 것이 유지보수에 좋다.
// 물론, @Configuration 가 붙은 클래스도 객체가 생성되어 IoC 컨테이너에 보관되는 것은 같다.
//
// => 객체를 찾았으면, WebMvcConfigurer 규칙에 따라 메서드를 호출하여
// 설정을 추가하거나 기존 설정을 변경한다.
// => WebMVC 설정을 활성화시키지 않으면,
// WebMvcConfigurer 구현체가 있다 하더라도 무시한다.
// => WebMVC 설정을 활성화시키는 방법
// 1) XML 설정
// <mvc:annotation-driven/>
// 2) Java Config 설정
// @EnableWebMvc 애노테이션 표시
//
스프링 웹 엠브이씨에 메세지 컨버터 GSON, JACKSON만 구분할 수 있는 기능이 있다
그래서 두 개 중 하나 라이브러리 준비 IMPORT
//import com.google.gson.Gson;
@Controller
@RequestMapping("/c05_1")
public class Controller05_1 {
ArrayList<Board> list = new ArrayList<>();
public Controller05_1() {
list.add(new Board(1, "제목입니다1", "내용", "홍길동", 10, Date.valueOf("2019-5-1")));
list.add(new Board(2, "제목입니다2", "내용", "홍길동2", 11, Date.valueOf("2019-5-2")));
list.add(new Board(3, "제목입니다3", "내용", "홍길동3", 12, Date.valueOf("2019-5-3")));
list.add(new Board(4, "제목입니다4", "내용", "홍길동4", 13, Date.valueOf("2019-5-4")));
list.add(new Board(5, "제목입니다5", "내용", "홍길동5", 14, Date.valueOf("2019-5-5")));
list.add(new Board(6, "제목입니다6", "내용", "홍길동6", 15, Date.valueOf("2019-6-1")));
list.add(new Board(7, "제목입니다7", "내용", "홍길동7", 16, Date.valueOf("2019-6-1")));
list.add(new Board(8, "제목입니다8", "내용", "홍길동8", 17, Date.valueOf("2019-6-1")));
list.add(new Board(9, "제목입니다9", "내용", "홍길동9", 18, Date.valueOf("2019-6-1")));
list.add(new Board(10, "제목입니다10", "내용", "홍길동10", 19, Date.valueOf("2019-7-1")));
list.add(new Board(11, "제목입니다11", "내용", "홍길동11", 11, Date.valueOf("2019-8-1")));
list.add(new Board(12, "제목입니다12", "내용", "홍길동12", 12, Date.valueOf("2019-9-1")));
list.add(new Board(13, "제목입니다13", "내용", "홍길동13", 13, Date.valueOf("2019-10-1")));
}
// 1) JSP에서 JSON 형식의 콘텐트 출력하기
// 직접 테스트:
// http://.../app2/c05_1/h1
// HTML에서 AJAX 요청 테스트:
// http://.../html/app2/c05_1.html
//
@GetMapping("h1")
public void handler1(Model model) {
model.addAttribute("list", this.list);
//request handler에서 view 컴포넌트의 URL을 리턴하지 않으면
// 프론트 컨트롤러는 요청 URL을 사용하여 JSP를 찾는다.
// 요청 URL: /c05_1/h1
// 요청 URL을 InternalResourceViewResolver에서 전달하면
// 요청 URL 앞뒤에 접두사, 접미사를 붙여 최종 URL을 완성한다.
// => /WEB-INF/jsp2/c05_1/h1.jsp
// => InternalResourceViewResolver가 완성한 URL을 가지고
// 프론트 컨트롤러는 해당 뷰를 실행한다.
}
// 2) Google Gson 라이브러리를 사용하여 JSON 형식의 콘텐트 출력하기
// => mvnrepository.com에서 gson 검색하여 라이브러리 정보를 가져온다.
// => build.gradle 파일에 추가한다.
// => '$gradle eclipse' 실행한다.
// => 이클립스에서 프로젝트를 리프래시 한다.
// 테스트:
// http://.../app2/c05_1/h2
// @GetMapping(value="h2", produces="text/plain;charset=UTF-8")
// @ResponseBody
// public String handler2() {
// return new Gson().toJson(this.list);
// }
// 3) Google Gson 라이브러리를 사용하여 JSON 형식의 콘텐트 출력하기 II
// => 페이지 컨트롤러의 리턴 값이 String이 아니면
// 프론트 컨트롤러는
// Google의 Gson 라이브러리나 Jackson 라이브러리를 사용하여
// 자동으로 JSON 형식의 문자열로 만들어 클라이언트로 출력한다.
// => 단 Gson 또는 Jackson 라이브러리가 있어야 한다.
// 둘 다 있다면 Jackson 라이브러리가 기본으로 사용된다.
// => build.gradle 파일에서 gson 또는 jackson 라이브러리를 추가하는 부분의
// 주석을 참고하라!
// 테스트:
// http://.../app2/c05_1/h3
@GetMapping("h3")
@ResponseBody
public Object handler3() {
return this.list; // JSON 형식의 문자열은 자동으로 UTF-8로 인코딩 된다.
}
}
* HttpMessasgeConvertor
JSON 형식을 다루는 라이브러리
@Controller가 붙은 일반적인 페이지 컨트롤러의 요청 핸들러를 실행할 때
요청 파라미터의 문자열을 int나 boolean 등으로 바꾸기 위해
기본으로 장착된 변환기를 사용한다.
그 변환기는 HttpMessageConverter 규칙에 따라 만든 변환기이다.
또한 요청 핸들러가 리턴한 값을 문자열로 만들어 클라이언트로 출력할 때도
이 HttpMessageConverter를 사용한다.
즉 클라인트가 보낸 파라미터 값을 핸들러의 아규먼트 타입으로 바꿀 때도 이 변환기를 사용하고
핸들러의 리턴 값을 클라이언트로 보내기 위해 문자열로 바꿀 때도 이 변환기를 사용한다.
스프링이 사용하는 기본 데이터 변환기는 MappingJackson2HttpMessageConverter 이다.
만약 이 변환기가 없다면 Google의 Gson 변환기를 사용한다.
구글의 Gson 변환기 마저 없다면 컨버터가 없다는 예외를 발생시킨다.
컨버터가 하는 일은 JSON 데이터로 변환하는 것이다.
클라이언트가 보낸 JSON 요청 파라미터 ===> 자바 객체
핸들러가 리턴하는 자바 객체 ===> JSON 형식의 문자열
MappingJackson2HttpMessageConverter?
=> 요청 파라미터로 JSON 문자열을 받으면 요청 핸들러를 호출할 때 자바 객체로 변환시킨다.
=> 요청 핸들러가 자바 객체를 리턴할 때 JSON 문자열로 변환한다.
주의!
=> MappingJackson2HttpMessageConverter를 사용하려면
다음과 같이 의존하는 라이브러리를 추가해야 한다.
implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.0'
=> 그런데 JSON 데이터를 처리할 때
MappingJackson2HttpMessageConverter 대신 GsonHttpMessageConverter 를 사용할 수 있다.
단 GsonHttpMessageConverter를 사용하려면
다음과 같이 이 클래스가 들어있는 의존 라이브러리를 추가해야 한다.
=> 만약 동시에 추가한다면 기본으로 Jackson 라이브러리를 사용한다.
implementation group: 'com.google.code.gson', name: 'gson', version: '2.8.9'
일반
@Controller
@ResponseBody 붙여야 브라우저에 출력함.
@RestController
// => 페이지 컨트롤러를 @RestController로 선언하면,
// 리턴 값은 HttpMessageConverter에 의해 자동으로 변환된다.
// => @ResponseBody를 붙일 필요가 없다.
//
--
// 1) 요청 파라미터 값을 낱개로 입력 받기
// 테스트
// http://.../app2/c05_3/h1?no=1&title=ok&writer=kim&viewCount=100
@RequestMapping(value="h1", produces="text/plain;charset=UTF-8")
public Object handler1(
int no,
String title,
String writer,
int viewCount) {
return String.format("%d,%s,%s,%d", no, title, writer, viewCount);
}
// 2) 요청 파라미터 값을 객체로 입력 받기
// 테스트
// http://.../app2/c05_3/h2?no=1&title=ok&writer=kim&viewCount=100
@RequestMapping(value="h2", produces="text/plain;charset=UTF-8")
public Object handler2(Board board) {
return board.toString();
}
// 3) JSON 형식의 요청 파라미터 값을 통째로 문자열로 받기
// 테스트
// http://.../html/app2/c05_3.html
@RequestMapping(value="h3", produces="text/plain;charset=UTF-8")
public Object handler3(@RequestBody String content) throws Exception {
System.out.println(content);
System.out.println(URLDecoder.decode(content, "UTF-8"));
return "OK!";
}
@RequestBody 를 사용하면 url 디코딩 안 하고 통채로 꼽음.
출력값보면 확인 가능, 한글 전체로 받고 싶으면 url디코딩시켜야함
// 4) JSON 형식의 요청 파라미터 값을 맵 객체로 받기
// => HttpMessageConverter 구현체(예:MappingJackson2HttpMessageConverter)가
// 클라이언트가 보낸 데이터를 Map 객체에 담아준다.
// => 이 기능을 쓰고 싶다면 Gson 또는 Jackson 라이브러리를 반드시 포함해야 한다.
// 그래야 스프링의 DispatcherServlet에서 찾는다.
// 테스트
// http://.../html/app2/c05_3.html
@RequestMapping(value="h4", produces="text/plain;charset=UTF-8")
public Object handler4(@RequestBody Map<String,Object> content) throws Exception {
System.out.println(content);
return "OK!";
}
// 5) JSON 형식의 요청 파라미터 값을 도메인 객체로 받기
// => HttpMessageConverter 구현체(예: MappingJackson2HttpMessageConverter)가
// 클라이언트가 보낸 데이터를 도메인 객체(예: Board, Member, Lesson 등)에 담아준다.
// => Json 데이터의 프로퍼티 명과 도메인 객체의 프로퍼티 명이 일치해야 한다.
//
// 테스트
// http://.../html/app2/c05_3.html
@RequestMapping(value="h5", produces="text/plain;charset=UTF-8")
public Object handler5(@RequestBody Board content) throws Exception {
System.out.println(content);
// 주의!
// => 클라이언트에서 보낸 날짜 데이터의 문자열 형식이 yyyy-MM-dd 형태여야 한다.
// 그래야 java.util.Date 타입의 값으로 변환해 준다.
// 예) 2019-05-01 ===> java.util.Date 객체 변환 성공!
// => 만약 이 형태가 아니면 변환할 수 없어 실행 오류가 발생한다.
// 예) 05/01/2020 또는 2020-5-1 ===> 변환 오류!
//
// @JsonFormat 애노테이션 사용
// => 이 애노테이션은 MappingJackson2HttpMessageConverter를 위한 것이다.
// GsonHttpMessageConverter는 이 애노테이션을 인식하지 않는다.
// => 도메인 객체의 프로퍼티에 이 애노테이션을 붙이면
// 2019-05-01 이나 2019-5-1 모두 처리할 수 있다.
// => 뿐만 아니라, 도메인 객체를 JSON 문자열로 변환할 때도
// 해당 형식으로 변환된다.
//
return "OK!";
//
// 테스트
// http://.../html/app2/c05_3.html
@RequestMapping(value="h5", produces="text/plain;charset=UTF-8")
public Object handler5(@RequestBody Board content) throws Exception {
System.out.println(content);
// 주의!
// => 클라이언트에서 보낸 날짜 데이터의 문자열 형식이 yyyy-MM-dd 형태여야 한다.
// 그래야 java.util.Date 타입의 값으로 변환해 준다.
// 예) 2019-05-01 ===> java.util.Date 객체 변환 성공!
// => 만약 이 형태가 아니면 변환할 수 없어 실행 오류가 발생한다.
// 예) 05/01/2020 또는 2020-5-1 ===> 변환 오류!
//
// @JsonFormat 애노테이션 사용
// => 이 애노테이션은 MappingJackson2HttpMessageConverter를 위한 것이다.
// GsonHttpMessageConverter는 이 애노테이션을 인식하지 않는다.
// => 도메인 객체의 프로퍼티에 이 애노테이션을 붙이면
// 2019-05-01 이나 2019-5-1 모두 처리할 수 있다.
// => 뿐만 아니라, 도메인 객체를 JSON 문자열로 변환할 때도
// 해당 형식으로 변환된다.
//
@GetMapping("error1")
public void error1() throws Exception {
throw new Exception("request handler 오류 발생!");
// Request Handler에서 예외를 던졌을 때 처리 절차!
// 1) 페이지 컨트롤러 안에 예외 처리기가 있다면,
// => 해당 메서드를 호출한다.
// 2) @ControllerAdvice 객체에 예외 처리기가 있다면, (App config)
// => 해당 메서드를 호출한다.
// 3) web.xml 에 지정된 오류 처리 기본 페이지가 설정되어 있다면,
// => 해당 페이지를 실행한다.
// 4) 서블릿 컨테이너의 기본 오류 처리 페이지를 실행한다.
//
}
// 예외 다루기
package bitcamp.app2;
import java.io.IOException;
import java.sql.SQLException;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller
@RequestMapping("/c06_1")
public class Controller06_1 {
// 테스트:
// http://.../app2/c06_1/get
@GetMapping("get")
public void get() {}
// 테스트:
// http://.../app2/c06_1/post
@PostMapping("post")
public void post() {}
// 테스트:
// http://.../app2/c06_1/error1
@GetMapping("error1")
public void error1() throws Exception {
throw new Exception("request handler에서 Exception 발생!");
// Request Handler에서 예외를 던졌을 때 처리 절차!
// 1) 페이지 컨트롤러 안에 예외 처리기가 있다면,
// => 그 메서드를 호출한다.
// 2) @ControllerAdvice 객체에 예외 처리기가 있다면,
// => 그 메서드를 호출한다.
// 3) web.xml 에 지정된 오류 처리 기본 페이지가 설정되어 있다면,
// => 그 페이지를 실행한다.
// 4) 서블릿 컨테이너의 기본 오류 처리 페이지를 실행한다.
//
}
// 테스트:
// http://.../app2/c06_1/error2
@GetMapping("error2")
public void error() throws Exception {
throw new IOException("request handler에서 IOException 발생!");
}
// 테스트:
// http://.../app2/c06_1/error3
@GetMapping("error3")
public void error3() throws Exception {
throw new SQLException("request handler에서 SQLExeption 오류 발생!");
}
//@ExceptionHandler
public ModelAndView exceptionHandler(Exception ex) {
System.out.println("Controller06_1. exceptionHandler() 호출됨 ");
ModelAndView mv = new ModelAndView();
mv.addObject("error", ex);
mv.setViewName("error6");
return mv;
}
}
'[네이버클라우드] 클라우드 기반의 개발자 과정 7기 > 웹프로그래밍' 카테고리의 다른 글
9/13 (0) | 2023.09.13 |
---|---|
9/12 스프링부트 (0) | 2023.09.12 |
[NC7기-95일차(9월11일)] - 웹프로그래밍 76일차 (0) | 2023.09.11 |
[NC7기-94일차(9월8일)] - 웹프로그래밍 75일차 (0) | 2023.09.08 |
9/7 프로젝트 (0) | 2023.09.07 |