오늘은 handler interceptor 에 대한 이야기를 조금 해보고자 한다.

Filter 는 자바 servlet 에서 제공하는 기술이지만 interceptor는 spring에서 제공하는 기술로써

세션 인증과 관련된 부분을 공부하다가 적어본다.

 

▶ 그래서 interceptor 가 뭔가요?

spring MVC 에서 인터셉터란 결국

지정된 URI에 관한 호출을 가로채는 역할을 하는 기능입니다.

이러한 호출을 가로채서 뭐하냐 싶지만

우리는 로그인을 한다거나 어떠한 게시물에 조회수를 올린다거나 하는 작업을 할 때 와 같은

공통된 관심사를 컨트롤 하기 위해서 사용하는 것 이다.

 

이는 이전 게시글인 Filter 와 같은데 두개의 차이점은 확실히 존재한다.

  Filter 와 Interceptor의 차이점

Filter와 Interceptor는 그 기능이 매우 흡사하여 나와같은 초보들이 가장 많이 헷갈려하는게

두 기능은 확연한 차이가 존재한다.

 

spring MVC request LifeCycle

위 사진을 보면 해당 차이를 조금 더 확실하게 알 수 있는데

Filter는 요청을 보내면 Tomcat과 같은 WAS 서버에 들어오자마자 작동되는 것으로

Spring의 영역에 접근하여 사용하기가 어렵습니다.

 

반면에 interceptor는 Spring 내에서 관리되는 기능이기에 Spring의 영역 내에서 사용되고 있는 것을 볼 수 있습니다.

 

이것으로 인해서 알 수 있는 차이는 생성된 Bean에 접근이 용이한가로 볼 수 있습니다.

 

◆ 차이점 정리

▶ Interceptor와 Filter는 호출 시점의 차이

  • Filter는 Dispatcher Servlet 호출 전(WAS의 단계)
  • Interceptor은 Dispatcher Servlet 호출 후 실행 됨

 

▶ Interceptor 의 용도

  • 인증 인가 등과 같은 공통 작업
  • Controller로 넘어가는 정보의 가공
  • API 호출에 대한 로깅 또는 감사

▶ Filter의 용도

  • 보안 관련 공통 작업
  • 모든 요청에 대한 로깅 
  • 이미지, 데이터 압축 및 문자열 인코딩

이제 이어지는 사용법을 알아보자

 

Handler Interceptor는 Interface 이므로 class 에 implements 하여 사용하면 된다.

public interface HandlerInterceptor {
    // controller 호출 전에 실행 (핸들러 어댑터 실행 전)
    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return true;
    }

	// controller 호출 후에 실행 (핸들러 어댑터 호출 후)
    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
    }

	// view의 랜더링 이후 실행
    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
    }
}

위 코드가 HandlerInterceptor의 기본 모습이며 사용처는 내부에 적어두었다.

 

이를 사용하여 만든 간단한 예문을 적고 마무리 이 글을 마무리 하고자 한다.

 

▶ 예문

LOG 를 찍어 흐름 확인하기

@Slf4j
public class LogInterceptor implements HandlerInterceptor {

    public static final String LOG_ID = "logId";

    @Override // controller 호출 전에 실행 (핸들러 어댑터 실행 전)
    public boolean preHandle(HttpServletRequest request,
    			HttpServletResponse response,
                Object handler) throws Exception {

        String reqURI = request.getRequestURI();

        String uuid = UUID.randomUUID().toString();
        request.setAttribute(LOG_ID, uuid);


        //@RequestMapping : HandlerMethod
        // 정적 리소스 : ResourceHttpRequestHandler
        if (handler instanceof HandlerMethod) {
            HandlerMethod hm = (HandlerMethod) handler;
            // 호출할 컨트롤러의 메서드의 모든 정보
        }

        log.info("REQ [{}][{}][{}]",uuid, reqURI,handler);


        return true;
    }

    @Override // controller 호출 후에 실행 (핸들러 어댑터 호출 후)
    public void postHandle(HttpServletRequest request, 
    			HttpServletResponse response, 
                Object handler,
                ModelAndView modelAndView) throws Exception {
                
        log.info("postHandle [{}]", modelAndView);
        
    }

    @Override // view의 랜더링 이후 실행
    public void afterCompletion(HttpServletRequest request,
                HttpServletResponse response, 
                Object handler, 
                Exception ex) throws Exception {
                
        String reqURI = request.getRequestURI();
        String logId = (String) request.getAttribute(LOG_ID);
        log.info("RES [{}][{}]", logId, reqURI);
        if (ex != null) {
            log.error("afterCompletion error!", ex);
        }
        
    }
}

 

※ Interceptor를 정의한 클레스

 

@Configuration
public class WebConfig implements WebMvcConfigurer {
	
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // logInterceptor register
        registry.addInterceptor(new LogInterceptor()) // 인터셉터 등록
                .order(1) // 인터셉터의호출 순서를 지정, 낮을수록 먼저 호출
                .addPathPatterns("/**") // 인터셉터를 적용할 URL패턴 지정
                .excludePathPatterns("/css/**", "/*.ico", "/error"); //인터셉터에서 제외할 패턴 지정


}

 

※ Interceptor 등록

 

 

 

♣ 참고 및 강의

인터페이스 차이점과 정의 관련 참고 블로그

 

https://inf.run/GMo43 - 김영한의 스프링MVC2 

 

스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 강의 - 인프런

웹 애플리케이션 개발에 필요한 모든 웹 기술을 기초부터 이해하고, 완성할 수 있습니다. MVC 2편에서는 MVC 1편의 핵심 원리와 구조 위에 실무 웹 개발에 필요한 모든 활용 기술들을 학습할 수 있

www.inflearn.com

 

'Web > spring' 카테고리의 다른 글

servlet filter  (0) 2024.01.19

2024.01.19

오늘 정리하고자 하는 내용은 서블릿 필터에 관한 내용이다.

김영한의 MVC2 강의 내용정리

우리는 흔히 로그인 시스템을 만들었을 때 로그인 유지 및 그에 따른 사용자 구분 등을 어떻게 해결할 지 고민하게 되는데

이 고민을 해결해 줄 부분이 servlet filter 이다. servlet filter 는 각 로직에서 공통적으로 사용되는 공통 관심사

(영어로는 cross-cutting) 를 하나로 통일시켜 우리를 훨씬 편하게 만들어줄 수 있는 친구다.

 

물론 이러한 부분에 대해서는 AOP 가 정말 대표적이지만 필자는 아직 AOP 공부안해봤음...ㅎ...

 

어쨋든 공부하다가 보니 웹에서는 servlet filter나 spring interceptor 을 사용하는게 좋다고 하길래 적는 내용.

 

필터는 javax.servlet 으로 import 할 수 있는 인터페이스다.

public interface Filter {
      public default void init(FilterConfig filterConfig) throws ServletException{
      
      }
      
      
      public void doFilter(ServletRequest request, ServletResponse response,
              FilterChain chain) throws IOException, ServletException;
              
              
      public default void destroy() {
      
      }
 }

 

기본적으로는 이렇게 생겼는데

init() 의 경우 필터  초기화 메서드로, 서블릿 컨테이너가 생성될 때 호출된다.

doFilter() 의 경우 요청이 올 때 마다 해당 메서드가 호출되는데, 여기서 필터의 로직을 구현하면 된다.

destroy() 의 경우 필터 종료 메서드로, 서블릿 컨테이너가종료될 때 호출된다.

 

이러한 필터는 체인 형태로 구성되어 중간중간에 자유롭게 추가가 가능하다.

 

이게 무슨소린가 싶은 미래의 나를 위해 그림 추가 ↓

 

 

더보기
필터의 전체적인 흐름
필터의 작동 흐름
필터 체인의 자유로운 추가

 

 


사용법

package hello.login.web.filter;


import lombok.extern.slf4j.Slf4j;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.UUID;

/**
 * 필터를 사용하기 위한 필터 인터페이스 구현
 */
@Slf4j
public class LogFilter implements Filter {


    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
//        Filter.super.init(filterConfig);
        log.info("log filter init");
    }

    /**
     * HTTP요청 시 doFilter 가 호출
     */
    @Override
    public void doFilter(ServletRequest request, 
    			ServletResponse response, 
                FilterChain chain) throws IOException, ServletException {

        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        // public void doFilter(ServletRequest request...) 
        // 이 부분은 HTTP요청이 아닌 경우 (HTTPS 등) 까지 고려해서 만든 인터페이스이다.
        // HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        // 로 다운케스팅 해서 사용하면 된다.

        String requestUrl = httpServletRequest.getRequestURI();

        String uuid = UUID.randomUUID().toString();
        //요청 구분을 위한 임의의 uuid 생성

        try {

            log.info("REQ [{}] [{}]", uuid, requestUrl);
            chain.doFilter(request, response);
            // 다음 필터가 존재하면 필터를 호출, 필터가 없으면 서블릿을 호출
            // 만일 이부분이 없으면 다음 단계로 진행되지 않음.

        } catch (Exception e) {
            throw e;
        } finally {
            log.info("RES [{}] [{}]", uuid, requestUrl);
        }
    }

    @Override
    public void destroy() {
//        Filter.super.destroy();
        log.info("log filter destroy");
    }
}

위 와 같이 필터로 지정할 메서드를 하나 만들어주고

 

package hello.login;

...

import javax.servlet.Filter;

@Configuration
public class WebConfig {

    @Bean
    public FilterRegistrationBean logFilter() {
        FilterRegistrationBean<Filter> filterFilterRegistrationBean 
        							= new FilterRegistrationBean<>();

        filterFilterRegistrationBean.setFilter(new LogFilter());
        // 등록할 필터 지정
        filterFilterRegistrationBean.setOrder(1);
        // 필터는 체인으로 동작하는데 이 때 순서가 필요하다. 낮을 수록 먼저 동작하게 된다.
        filterFilterRegistrationBean.addUrlPatterns("/*");
        // 필터를 적용할 URL패턴을 지정. 한번에 여러 패턴을 지정할 수 있다.


        return filterFilterRegistrationBean;
    }

}

@Bean을 통해 bean 등록을 진행해준다.

 

★ 만약 스프링 부트를 사용한다면 FilterRegistrationBean 을 사용해서 등록

 

이러한 필터를 적용할 때 중요한 부분이 있는데 이것이

whitelist

이다.

 

항상 코딩하면서 생각하는거지만 별거아니라고 생각되는 무언가가 제일 날 화나게해..

 

Filter 메서드를 Override 해보면 알겠지만 이 whitelist란 해당 웹사이트에서 이 필터가 적용되지 말아야 할 부분은 어디인가를 제외하는 문자열 배열이다.

 

public class LoginCheckFilter implements Filter {

    private static final String[] whitelist 
    	= {"/", "/signin", "/login", "/logout", "/css/*"};
 	
    ...
}

 

 

이런식으로 작성해서 해당 클레스에서만 사용되도록 되어있는데 따로 config로 빼서 관리해도 좋을 듯 싶다. (개인적인 생각)

 

이렇게 빈으로써 등록해두면 어디에 접속하든 로그에

2024-01-19 20:53:34.742  ...  : RES [16eacd30-94dc-4f11-b046-663d3c291f21] [/login]
2024-01-19 20:53:39.443  ...  : REQ [69525432-505e-4cac-9076-f34c996176ea] [/]

이런식으로 찍히는 걸 볼 수 있다.

 

이렇게 찍히는게 보였으면 filter 적용완료이다.

 

 

 

 

'Web > spring' 카테고리의 다른 글

servlet interceptor  (0) 2024.01.22

2024.01.17 

김영한의 Spring MVC2 편을 보면서 이해가 가지않은 부분이 있었다.

해당 편의 로그인 처리하기 - Servlet HTTP Session 2  부분이었는데

 

public String homeLogin(@SessionAttribute(name = SessionConst.LOGIN_MEMBER, required = false)
            Member loginMember,
            Model model) {

        if (loginMember == null) {
            return "home";
        }

        model.addAttribute("member", loginMember);
        return "loginHome";
    }

 

위 코드에서 @SessionAttribute(name = SessionConst.LOGIN_MEMBER, ...) 부분이다.

 

해당 부분에서 name 값을 상수로 고정하는데

 

해당 부분에 대해 나온 코멘트는 

 

HttpSession 에 데이터를 보관하고 조회할 때, 같은 이름이 중복 되어 사용되므로, 상수를 하나 정의했다.

 

였는데 이 말만으로는 정확하게 이해가 가지 않았다.

 

저렇게 정의해두면 하나의 대상만을 가져오지 않는가?

 

라는 의문이 들었다.

 

이전 코드를 보면 login 의 코드를 보면

 

// create session by HTTPSession
        HttpSession session = request.getSession();
        /**
         * request.getSession(true);
         * 세션이 있으면 기존 세션을 반환
         * 없으면 새로운 세션 생성 및 반환

         * request.getSession(false);
         * 세션이 있으면 기존 세션 반환
         * 없으면 새로운 세션을 생성하지 않고 null 을 반환
         */


        // set login member info in session
        session.setAttribute(SessionConst.LOGIN_MEMBER, loginMember);

 

 

위와 같이 session에 상수값을 key, 멤버를 value 로 저장하게 되는데 이러면 동시 로그인에서 문제가 생기진 않을까? 하는 의문이 들었다.

 

해당 부분에 대해서 검색해본 결과 어떤 블로거분의 글을 보게 되었다.

 

해당 글 링크

 

웹 기초] HTTP Session 공부

HttpSession을 공부하게 된 이유. www.youtube.com/watch?v=3ArYMq5AomI 우연히 jojoldu.tistory.com/ 블로그의 주인님(?)이신 조졸두님(향로님)이 나오는 유튭영상을 보고 정리하게 됐습니다. 향로님의 대표적인 질

jjunii486.tistory.com

 

이 부분은 이전 유튜브에서도 나온 질문이라는데 나의 의문에 대한 답변이 나와있다.

[인터넷 네트워크]

2024.01.17

오늘 정리할 부분은 인터넷 네트워크에 관한 부분입니다.

개발자로 취업 후에 기초 개념 부분에서 가장 부족했던 제게 많은 도움이 되었던 강의를 다시 보고 정리하고자 합니다.

 

인프런 - 김영한 강의 http웹 기초 정리 https://inf.run/8ZEU8

 

모든 개발자를 위한 HTTP 웹 기본 지식 강의 - 인프런

실무에 꼭 필요한 HTTP 핵심 기능과 올바른 HTTP API 설계 방법을 학습합니다., [사진] 📣 확인해주세요!본 강의는 자바 스프링 완전 정복 시리즈의 세 번째 강의입니다. 우아한형제들 최연소 기술

www.inflearn.com

 

 

 

1. 인터넷 통신

인터넷에서 두 컴퓨터가 통신하는 방법은

클라이언트(User)가 서버로 요청을 보내고 서버는 그에 응답하는 식이다.

 

중요한것은 이때 클라이언트 방향에서 보낸 요청은 거대한 인터넷의 세계를 탐험하게 되는데.

수많은 클라이언트와 서버가 연결된 복잡한 인터넷 망 안에서

수많은 노드들을 보고 정확한 서버를 찾아서 해당 요청에 따른 정보를 전달하고

그에 대한 응답을 받아오는 것은 매우 복잡하고 어려운 방식이다.

 

따라서 우리는 아래의 개념을 명확하게 이해하여야 한다.

 

2. IP(Internet Protocol)

IP란 각 노드들에 부여되는 하나의 주소이다.

클라이언트가 보낸 요청은 상대 서버에 부여된 주소를 보고 찾아갈 수 있는 것이며

이때 요청과 응답에 담긴 정보들은 하나의 패킷이라는 통신단위로 전달된다.

 

IP 패킷에 담긴 정보 = 출발지IP, 목적지IP, 기타 정보 및 데이터

 

- IP프로토콜의 한계

  • 비연결성
    • 대상이 패킷을 받을 수 없거나 불능 상태여도 패킷 전송
  • 비신뢰성
    • 패킷의 소실 가능성
    • 패킷 정보의 순서 구분
  • 프로그램(애플리케이션) 구분
    • 같은 IP를 공유하는 하나의 서버내 통신하는 애플리케이션(프로그램)이 두개 이상이라면 패킷 소실 가능성이 있음

 

 

3.TCP,UDP

 

TCP (Transmission Control Protocol) 즉 전송 제어 프로토콜로 해석할 수 있다.

연결형 서비스를 지원하는 프로토콜로 인터넷 환경에서 기본으로 사용된다.
이러한 TCP는 IP와 함께 사용되는것이 보편적인데 이때 IP는 데이터의 배달을 처리하는 부분이며
TCP는 패킷을 추적 및 관리하게 된다. 

 

 

[TCP의 특징]

  • 연결 지향 방식으로 3-way handshacke 방식을 사용하여 연결을 수립하고 4-way. handshacke과정을 통해 해제한다.(가상 연결)
  • 데이터 전달 보증 및 순서를 보장 (높은 신뢰도)
  • 데이터의 흐름 및 혼잡 제어
  • UDP보다 느림

 

UDP (User Datagram Protocol) 즉 사용자 *데이터그램 프로토콜 로 해석할 수 있다.

이는 비연결형 프로토콜로 연결을 위한 논리적 경로가 없다.

 

* Datagram 은 독립적 관계를 지니는 패킷이라는 의미

 

[UDP의 특징]

  • 하얀 도화지에 비유 (기능이 거의 없음)
  • 비연결형 서비스로 연속성이 더욱 우선된다.
  • 데이터 전달 보증 X 및 순서 보장 X (낮은 신뢰도)
  • IP와 거의 같으며 PORT와 CheckSum 필드를 통해 최소한의 오류만을 검사한다.
  • 애플리케이션 내 추가작업이 필수이다.

 



TCP, UDP는 기본적으로 전송 계층이다.

개인적으로 저는 전공자가 아니라서 이 부분에 대해서 이해하기 쉽지 않았습니다.

OSI 7계층? TCP/IP 4계층?

처음에 접했을 때는 완전히 다른 부분에서 사용되는 개념인 줄 알았고 무엇에 사용되는 개념인지도 잘 몰랐습니다.

이는 두 개념 전부 네트워크 통신을 나눈 단계 입니다.

https://shlee0882.tistory.com/110

 

OSI 7 계층이란?, OSI 7 계층을 나눈 이유

1. OSI 7 계층이란? OSI 7 계층은 네트워크에서 통신이 일어나는 과정을 7단계로 나눈 것을 말한다. 1.1 OSI 7 계층을 나눈이유는? 계층을 나눈 이유는 통신이 일어나는 과정이 단계별로 파악할 수 있

shlee0882.tistory.com

https://velog.io/@dyunge_100/Network-TCPIP-4계층에-대하여

 

[Network] TCP/IP 4계층에 대하여

두 계층 모두 데이터 통신을 표현한 계층이긴 하지만 OSI 7계층은 데이터 통신에 필요한 계층과 역할을 정확하게 정의하려고 한 모델이다. 그에 반해 TCP/IP 4계층은 현재 인터넷에서 사용되는 프

velog.io

 

 

4.PORT

하나의 컴퓨터에서 여러개의 작업을 할 경우 컴퓨터 서버내 PORT라는 것을 반드시 알아야 한다.

실무를 진행하면서 하나의 서버에서 여러개의 포트를 사용해서 복합적인 프로그램을 사용하는 것을 보고

이런 방식으로 포트라는 것이 사용되며 이러한 포트를 통해 프로세스를 구분하여 사용된다는 것을 알았다.

개인적으로는 반드시 완벽히 이해해야하는 개념이라고 생각한다.

 

PORT 란 "논리적인 접속장소" 를 뜻한다.

이는 TCP/IP 를 사용 할 때 클라이언트의 프로그램 및 요청이 네트워크 상의 특정 서버, 특정 프로그램을 지정하는 방법으로 사용된다.

풀어서 이야기하자면 위 개인의 경험으로 알게되었듯이

하나의 요청이 여러개의 프로그램이 작동되고 있는 서버 내에서 정확한 프로그램을 찾아 가기 위해 연결된 접속장소 인 것이다.

개발자라면 3000 or 8080 을 정말 많이 들어보았을텐데

1xx.xxx.xx.xx:3000

2xx.xxx.xx.xx:8080

서버(집)의 주소와 상세 주소 정도로 생각하면 좋을 듯 싶다.

• 0 ~ 65535 할당 가능
• 0 ~ 1023: 잘 알려진 포트, 사용하지 않는 것이 좋음
• FTP - 20, 21
• TELNET - 23
• HTTP - 80
• HTTPS - 443

 

 

5.DNS

DNS란 (Domain Name System) 의 약자로 즉 IP 주소에 이름을 붙혀서 명명하는 시스템이다.

예시를 들어보면 125.209.222.141 이 IP 주소는 NAVER 의 IP 주소로 

우리는 흔히 주소창에 naver.com 을 치면 위 도메인 네임이 125.209.222.141 로 변환되어 요청되는 방식이다.

또한 이러한 IP는 변경될 수 있다는 것을 생각하면 도메인은 상당히 편한 시스템이 아닐 수 없다.

 

 

 

+ Recent posts