。゚(*´□`)゚。

코딩의 즐거움과 도전, 그리고 일상의 소소한 순간들이 어우러진 블로그

[네이버클라우드] 클라우드 기반의 개발자 과정 7기/웹프로그래밍

9/12

quarrrter 2023. 9. 12. 15:06

*필터 

첫 번째 필터(서블릿 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/**");

http://localhost:8888/app2/c04_1/a/h2
http://localhost:8888/app2/c04_1/b/h3


@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디코딩시켜야함

h3

// 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!";
}

무슨 형식인지 넘길 때 알려줘야한다. csv인지, json인지 html인지 뭔지

// 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!";

브라우저에는 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;
  }
}