simDev1234
심플하고 차분하게
simDev1234
전체 방문자
오늘
어제
  • 분류 전체보기
    • Computer Science
      • Basic Math
      • Data Structure
      • Algorithm
      • Database
      • OS
    • Language
      • Java
      • Kotlin
      • SQL
    • Framework
      • Spring
      • Orm&Mapper
      • 프로젝트로 스프링 이해하기
      • 스프링 라이브러리
    • Infra
      • Cloud
      • Docker
      • Redis
      • AWS, Azure
      • Device
    • Etc
      • CleanCoding
    • Git,Github

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • 자바프로그램
  • 404
  • null
  • 자바
  • 컨트롤러
  • 스프링
  • scanner #next() #nextLine()
  • 자바프로그래밍
  • controllerTest
  • 참조타입
  • 참조변수
  • JVM메모리구조
  • 자바메모리구조

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
simDev1234

심플하고 차분하게

[스프링] 스프링 MVC - 필터, 인터셉터
Framework/Spring

[스프링] 스프링 MVC - 필터, 인터셉터

2022. 9. 11. 07:07

|  MVC 구조로 보는 필터와 인터셉터

- 요청사항에 대해 필터와 인터셉터를 거친다 

출처:https://blog.naver.com/platinasnow/220035316135

- 사용되는 메소드의 전반적인 흐름

init -> doFilter -> preHandler -> AOP -> postHandler -> afterCompletion -> doFilter -> destroy

 

- 필터와 인터셉터와 AOP

  필터 인터셉터 AOP
공통점 공통처리 기능
관련 웹의 URL주소나, 프로토콜 등과 관련 자바코드(Annotation, package)
위치 스프링 외부  스프링 내부 스프링 내부
특징 - 대부분의 공통사항 필터에서 처리
- low level 처리 가능
- sql injection, CSRF 등 해킹을 사전에 막는용도로 자주 사용한다.
- Controller에 가기 전 공통 처리 
- 실제 매핑된 handler 정보 확인 가능
- 필터보다 상세한 조건식,
  세부적인 스펙(pre,post,after)
- 구체적인 시점에 구체적인 동작 가능
- 인터셉터보다 구체적인 조건
(ex. Annotation, parameter, 주소),
- 인터셉터보다 구체적인 동작위치
(ex. afterThrowing)
메소드 init
dofilter
destroy
preHandler
postHandler(응답성공)
afterCompletion(항상)
@before, @after,
@AfterReturning, @AfterThrowing
pointcut에 자유롭게 메소드 생성
더보기

[ 서블릿의 생명주기 ]

 

init() 서블릿을 처음 메모리에 올릴 때에만 한 번 실행
service() 요청 및 응답에 대한 처리 (GET이면 doGet(), POST면 doPost()로 분기)
destroy() 서블릿 종료 요청 시 실행

 

|  필터 실습

[1] config 패키지 안에 Filter 클래스를 만든다.

- Filter 클래스는 Filter 인터페이스를 구현하여 만들면 된다.

- Filter 클래스를 만들 때엔 doFilter() 메소드에서 chain.doFilter()를 꼭 해주어야 같은 필터로 인식한다.

// import는 생략

@Slf4j
public class LogFilter implements Filter{
	
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
                         throws IOException, ServletException {
        // 외부 -> filter (-> 처리 ->) filter -> 외부
        log.info("Hello LogFilter : " + Thread.currentThread());
        chain.doFilter(request, response); // chain.doFilter()는 꼭 필요
        log.info("Bye LogFilter : " + Thread.currentThread());
    }
}

[2] Filter 클래스를 구현할 때 방법 두가지

(1) @Component로 Filter 클래스를 스캐너가 자동 스캔할 수 있도록 하기

// import는 생략

@Slf4j
@Component
public class LogFilter implements Filter{
	
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
                         throws IOException, ServletException {
        // 외부 -> filter (-> 처리 ->) filter -> 외부
        log.info("Hello LogFilter : " + Thread.currentThread());
        chain.doFilter(request, response); // chain.doFilter()는 꼭 필요
        log.info("Bye LogFilter : " + Thread.currentThread());
    }
}

(2) 별도의 DI 설정 클래스에서 Filter클래스를 Bean으로 등록하기

- 위의 Filter 클래스에서 @Component는 생략한다.

- FilterRegistrationBean<Filter> 클래스를 통해 필터를 등록한다.

- FilterRegistrationBean의 메소드들

setFilter(new 클래스()) 필터를 세팅하기 (하나만 넣어준다)
setOrder(순번) 필터의 순번을 결정
addUrlPatterns("url패턴") 해당 패턴(ex. "/order", "/*") 의 url에서만 필터가 적용된다.
package com.example.websample.config;

// import 생략

@Configuration
public class WebConfig{
	
    @Bean
    public FilterRegistrationBean logginFilter(){
    	
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        
        filterRegistrationBean.setFilter(new LogFilter());
        filterRegistrationBean.setOrder(1);
        filterRegistrationBean.addUrlPatterns("/*");
        
        return filterRegistrationBean;
    }
}

>> 기존에 컨트롤러는 아래의 것을 사용하고, 실행해보면

@GetMapping("/order/{orderId}")
public String getOrder(
        @PathVariable("orderId") String orderId,
        @RequestParam("orderAmount") Integer orderAmount)
{
    log.info("Get some order");
    return "OrderId : "+ orderId +",  orderAmount : " +  orderAmount;
}

>> 요청

http://localhost:8080/order/252?orderAmount=1000

>> 응답

OrderId : 252, orderAmount : 1000

>> 로그

com.example.websample.config.LogFilter   : Hello LogFilter : Thread[http-nio-8080-exec-1,5,main]
com.example.websample.config.LogFilter   : Bye LogFilter : Thread[http-nio-8080-exec-1,5,main]
c.e.w.controller.SampleController        : Get some order

 

|  인터셉터 실습

- 일반적으로 Filter로는 간단한 해킹 방지 코드를 작성하고, 인터셉터를 활용해서 세부적인 공통코드를 작성한다.

-  preHandle, postHandle(응답 성공), afterCompletion(항상)

[1] 인터셉터는 HandlerInterceptor 인터페이스를 구현하여 만들 수 있다.

packages com.exmaple.websample.config;

// import 생략

@Slf4j
public class LogInterceptor implements HandlerInterceptor{
	
}

[2] 인터셉터는 세가지의 메소드를 구현해야하는데, 각각의 특징은 아래와 같다.

메소드 언제? 매개변수
preHandle() Handler로 넘어가기 전 Object handler - handler의 위치를 알 수 있다.
postHandle() Handler의 처리가 성공적일 때 ModelAndView - 처리 후 응답 정보를 알 수 있다.
afterCompletion() Handler의 처리의 성공 여부와 상관 없이 항상 Exception ex - 예외 상황 발생 시 확인 가능
packages com.exmaple.websample.config;

// import 생략

@Slf4j
public class LogInterceptor implements HandlerInterceptor{
	@Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) throws Exception {
        log.info("preHandle LogInterceptor: " + Thread.currentThread());
        log.info("preHandle handler: " + handler);

        return true; // 이 다음 인터셉터가 진행되길 바라면 true, 아니면 false
    }

    @Override
    public void postHandle(HttpServletRequest request,
                           HttpServletResponse response,
                           Object handler,
                           ModelAndView modelAndView) throws Exception {

        log.info("postHandle LogInterceptor: " + Thread.currentThread());
    }

    @Override
    public void afterCompletion(HttpServletRequest request,
                                HttpServletResponse response,
                                Object handler,
                                Exception ex) throws Exception {
        log.info("afterHandle LogInterceptor: " + Thread.currentThread());

        if (ex != null) {
            log.error("afterCompletion exception : " + ex.getMessage());
        }

    }
}

[3] @Configuration 클래스에서 WebMvcConfigurer를 구현하고, addInterceptors()를 통해 인터셉터를 추가한다.

- WebMvcConfigurer의 addInterceptors()는 InterceptorResgistration을 반환한다.

- 내부적으로 아래와 같은 메소드들이 있는데 이들 모두 InterceptorResgistration을 반환하므로 메소드 체이닝을 한다.

- 메소드들

addInterceptor() 인터셉터 추가
order() 순번
addPathPatterns() 어떤 url패턴(경로)에서 실행할 건지
excludePathPatterns() 일반적으로 css나 images와 같이 정적 페이지들을 대상으로 제외처리
package com.example.websample.config;

// import 생략

@Configuration
public class WebConfig implements WebMvcConfigurer{
	
    // Filter 내용 생략
    
    // Interceptor 내용
    @Override
    public void addInterceptors(InterceptorRegistry registry){
    	// registry : 등록 내용 관리부
        registry.addInterceptor(new LogInterceptor())
                .order(1)
                .addPathPatterns("/**")
                .excludePathPatterns("/css/*", "/images/*");
    }
}

>> 기존의 컨트롤러에 예외 사항을 임의로 추가

@GetMapping("/order/{orderId}")
public String getOrder(
        @PathVariable("orderId") String orderId,
        @RequestParam("orderAmount") Integer orderAmount) throws IllegalAccessException {

    log.info("Get some order");

    if ("500".equals(orderId)) {
        throw new IllegalAccessException("500 is not valid orderId");
    }

    return "OrderId : " + orderId + ",  orderAmount : " + orderAmount;
}

>> 요청

http://localhost:8080/order/500?orderAmount=1000

>> 로그

// 필터 진입
Hello LogFilter : Thread[http-nio-8080-exec-1,5,main]

// preInterceptor
preHandle LogInterceptor: Thread[http-nio-8080-exec-1,5,main]
preHandle handler: com.example.websample.controller.SampleController#getOrder(String, Integer)

// handler
Get some order

// ! postInterceptor로 가지 않았다.

// afterInterceptor
afterHandle LogInterceptor: Thread[http-nio-8080-exec-1,5,main]
afterCompletion exception : 500 is not valid orderId

// exception 메세지 - 오류 코드 나머지는 생략
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.IllegalAccessException: 500 is not valid orderId] with root cause
java.lang.IllegalAccessException: 500 is not valid orderId

// 브라우저에 페이지 로딩하며 다시 실행
preHandle LogInterceptor: Thread[http-nio-8080-exec-1,5,main]
preHandle handler: org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController#errorHtml(HttpServletRequest, HttpServletResponse)
postHandle LogInterceptor: Thread[http-nio-8080-exec-1,5,main]
afterHandle LogInterceptor: Thread[http-nio-8080-exec-1,5,main]

 

|  정리

- 필터, 인터셉터, AOP는 모두 공통 기능을 처리하기 위해 사용된다.

- 주로 필터, 인터셉터는 웹에 관한 처리(ex.URL주소 및 프로토콜)를 하며, AOP는 자바 코드에 관해 처리한다.

- 필터는 Filter 인터페이스를 구현하고, doFilter()안에서 chain.doFilter(request, response)를 작성하여 만들 수 있다.

- 인터셉터는 HandlerInterceptor 인터페이스를 구현하고, preHandle()/postHandle()/afterCompletion()을 작성하여 만들 수 있다.

- 필터와 인터셉터 모두 @Configuration 클래스에서 @Bean으로 등록해주는 것이 필요한데,

  - 필터의 경우, FilterRegistrationBean 클래스를 만들고 setFilter()/setOrder()/addUrlPatterns()를 설정한다.

  - 인터셉터의 경우, WebMvcConfigurer를 구현한 후, addInterceptor()를 통해 이미 등록된 registry의 메소드들을 사용한다.

 

 

[ 참조 및 출처 ]

부트캠프 수업 후 내용 정리

서블릿의 생명주기 https://kadosholy.tistory.com/47

[Spring] Interceptor, filter, AOP의 차이|작성자 심해펭귄

'Framework > Spring' 카테고리의 다른 글

[스프링] 프로젝트 전 꼭 알아두면 좋은 것들  (0) 2022.09.13
[스프링] 스프링 MVC - 예외처리  (1) 2022.09.11
[스프링] 스프링MVC - HTTP 요청 및 응답  (1) 2022.09.11
[스프링] 스프링의 주요기술  (0) 2022.09.05
[스프링] OOP의 SOLID 원칙과 스프링  (0) 2022.08.29
    'Framework/Spring' 카테고리의 다른 글
    • [스프링] 프로젝트 전 꼭 알아두면 좋은 것들
    • [스프링] 스프링 MVC - 예외처리
    • [스프링] 스프링MVC - HTTP 요청 및 응답
    • [스프링] 스프링의 주요기술
    simDev1234
    simDev1234
    TIL용 블로그. * 저작권 이슈가 있는 부분이 있다면 댓글 부탁드립니다.

    티스토리툴바