일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- HttpServletResponse
- 인터페이스
- 오버라이딩
- ocp
- 김영한
- 코딩테스트
- DI
- html form
- 티스토리챌린지
- 스프링컨테이너
- equals()
- 테스트코드
- 코드트리
- 코드트리조별과제
- 백준
- 프록시
- 싱글톤
- 오블완
- java
- http 메시지 컨버터
- 스프링
- objecterror
- 의존관계
- 추상클래스
- JSON
- fielderror
- 다형성
- 참조변수
- 서블릿
- @configuration
- Today
- Total
minOS
로그인 처리2 - 스프링 인터셉터 소개 , 스프링 인터셉터 요청 로그 , 스프링 인터셉터 인증 체크 본문
로그인 처리2 - 스프링 인터셉터 소개 , 스프링 인터셉터 요청 로그 , 스프링 인터셉터 인증 체크
minOE 2024. 9. 27. 20:46스프링 인터셉터 소개
스프링 인터셉터도 서블릿 필터와 같이 웹과 관련된 공통 관심 사항을 효과적으로 해결할 수 있는 기술이다.
서블릿 필터가 서블릿이 제공하는 기술이라면, 스프링 인터셉터는 스프링 MVC가 제공하는 기술이다. 둘다 웹과 관련
된 공통 관심 사항을 처리하지만, 적용되는 순서와 범위, 그리고 사용방법이 다르다.
스프링 인터셉터 흐름
HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 스프링 인터셉터 -> 컨트롤러
- 스프링 인터셉터는 디스패처 서블릿과 컨트롤러 사이에서 컨트롤러 호출 직전에 호출 된다.
- 스프링 인터셉터는 스프링 MVC가 제공하는 기능이기 때문에 결국 디스패처 서블릿 이후에 등장하게 된다. 스프링 MVC의 시작 점이 디스패처 서블릿이라고 생각해보면 이해가 될 것이다.
- 스프링 인터셉터에도 URL 패턴을 적용할 수 있는데, 서블릿 URL 패턴과는 다르고, 매우 정밀하게 설정할 수 있다.
스프링 인터셉터 제한
HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 스프링 인터셉터 -> 컨트롤러 //로그인 사용자 HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 스프링 인터셉터(적절하지 않은 요청이라 판단, 컨트롤러 호출 X) // 비 로그인 사용자
인터셉터에서 적절하지 않은 요청이라고 판단하면 거기에서 끝을 낼 수도 있다. 그래서 로그인 여부를 체크하기에 적합하다.
스프링 인터셉터 체인
HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 인터셉터1 -> 인터셉터2 -> 컨트롤러
스프링 인터셉터는 체인으로 구성되는데, 중간에 인터셉터를 자유롭게 추가할 수 있다. 예를 들어서 로그를 남기는 인터셉터를 먼저 적용하고, 그 다음에 로그인 여부를 체크하는 인터셉터를 만들 수 있다.
스프링 인터셉터 인터페이스스프링의 인터셉터를 사용하려면 `HandlerInterceptor` 인터페이스를 구현하면 된다.
public interface HandlerInterceptor { default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return true; } default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception { } default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception { } }
- 서블릿 필터의 경우 단순하게 `doFilter()` 하나만 제공된다. 인터셉터는 컨트롤러 호출 전(`preHandle` ), 호출 후 (`postHandle` ), 요청 완료 이후(`afterCompletion` )와 같이 단계적으로 잘 세분화 되어 있다.
- 서블릿 필터의 경우 단순히 `request` , `response` 만 제공했지만, 인터셉터는 어떤 컨트롤러(`handler` )가 호출되는지 호 출 정보도 받을 수 있다. 그리고 어떤 `modelAndView` 가 반환되는지 응답 정보도 받을 수 있다.
스프링 인터셉터 호출 흐름
정상 흐름
-`preHandle` : 컨트롤러 호출 전에 호출된다. (더 정확히는 핸들러 어댑터 호출 전에 호출된다.)
ㄴ`preHandle` 의 응답값이 `true` 이면 다음으로 진행하고, `false` 이면 더는 진행하지 않는다. `false` 인 경우 나머지 인 터셉터는 물론이고, 핸들러 어댑터도 호출되지 않는다. 그림에서 1번에서 끝이 나버린다.
- `postHandle` : 컨트롤러 호출 후에 호출된다. (더 정확히는 핸들러 어댑터 호출 후에 호출된다.)
-`afterCompletion` : 뷰가 렌더링 된 이후에 호출된다.
스프링 인터셉터 예외 사황 (postHandle()과 afterCompletion()의 차이)
- postHandle(): 컨트롤러 실행 후, 뷰 렌더링 전에 호출된다. 컨트롤러에서 예외가 발생하면 호출되지 않는다. Model 데이터를 조작하거나 추가적인 속성을 설정하는 데 유용하다.
- afterCompletion(): 뷰 렌더링을 포함한 모든 처리가 완료된 후 호출된다. 예외 발생 여부와 관계없이 항상 호출된다. 리소스 해제, 로깅 완료 등의 작업에 유용하다. 발생한 예외에 대한 정보를 파라미터로 받을 수 있어, 예외 처리 후 추가 작업이 가능하다.
스프링 인터셉터 - 요청 로그
LogInterceptor - 요청 로그 인터셉터
import lombok.extern.slf4j.Slf4j; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.UUID; @Slf4j public class LogInterceptor implements HandlerInterceptor { public static final String LOG_ID = "logId"; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String requestURI = request.getRequestURI(); String uuid = UUID.randomUUID().toString(); request.setAttribute(LOG_ID,uuid); if(handler instanceof HandlerMethod){ HandlerMethod hm = (HandlerMethod) handler; //호출할 컨트롤러 메서드의 모든 정보가 포함되어 있다. } log.info("REQUEST [{}][{}][{}] ", uuid,requestURI,handler); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { log.info("postHandle[{}]",modelAndView); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { String requestURI = request.getRequestURI(); Object uuid = (String) request.getAttribute(LOG_ID); log.info("RESPONSE [{}][{}][{}] ", uuid,requestURI,handler); if(ex != null){ log.info("afterCompletion error!!",ex); } } }
- ` String uuid = UUID.randomUUID().toString()`
ㄴ 요청 로그를 구분하기 위한 `uuid` 를 생성한다.
- `request.setAttribute(LOG_ID, uuid)`
ㄴ 서블릿 필터의 경우 지역변수로 해결이 가능하지만, 스프링 인터셉터는 호출 시점이 완전히 분리되어 있다.
따라서 `preHandle` 에서 지정한 값을 `postHandle` , `afterCompletion` 에서 함께 사용하려면 어딘가에 담아두어 야 한다. `LogInterceptor`도 싱글톤 처럼 사용되기 때문에 맴버변수를 사용하면 위험하다. 따라서 `request` 에 담아두 었다. 이 값은 `afterCompletion` 에서`request.getAttribute(LOG_ID)`로 찾아서 사용한다.
- `return true`
ㄴ `true` 면 정상 호출이다. 다음 인터셉터나 컨트롤러가 호출된다.
WebConfig - 인터셉터 등록
import javax.servlet.Filter; @Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LogInterceptor()) .order(1) .addPathPatterns("/**") .excludePathPatterns("/css/**","/*.ico","/error"); }
로그 출력`WebMvcConfigurer` 가 제공하는 `addInterceptors()` 를 사용해서 인터셉터를 등록할 수 있다.
-`registry.addInterceptor(new LogInterceptor())` : 인터셉터를 등록한다.
-`order(1)` : 인터셉터의 호출 순서를 지정한다. 낮을 수록 먼저 호출된다.
-`addPathPatterns("/**")` : 인터셉터를 적용할 URL 패턴을 지정한다.
-`excludePathPatterns("/css/**", "/*.ico", "/error")` : 인터셉터에서 제외할 패턴을 지정한다.
스프링의 URL 경로
스프링이 제공하는 URL 경로는 서블릿 기술이 제공하는 URL 경로와 완전히 다르다. 더욱 자세하고, 세밀하게 설정할 수 있다.
PathPattern 공식 문서
http:// https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/ web/util/pattern/PathPattern.html
스프링 인터셉터 - 인증 체크
서블릿 필터에서 사용했던 인증 체크 기능을 스프링 인터셉터로 개발해보자.
LoginCheckInterceptor
import hello.login.web.SessionConst; import lombok.extern.slf4j.Slf4j; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; @Slf4j public class LoginCheckInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String requestURI = request.getRequestURI(); log.info("인증 체크 인터셉터 실행 {}", requestURI); HttpSession session = request.getSession(); if(session == null || session.getAttribute(SessionConst.LOGIN_MEMBER) == null){ log.info("미인증 사용자 요청"); //로그인으로 redirect response.sendRedirect("/login?redirectURL="+ requestURI); return false; } return true; } }
서블릿 필터와 비교해서 코드가 매우 간결하다. 인증이라는 것은 컨트롤러 호출 전에만 호출되면 된다. 따라서
`preHandle` 만 구현하면 된다.
인증체크 등록시 순서 주의, 세밀한 설정 가능
import hello.login.web.filter.LogFilter; import hello.login.web.filter.LoginCheckFilter; import hello.login.web.interceptor.LogInterceptor; import hello.login.web.interceptor.LoginCheckInterceptor; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import javax.servlet.Filter; @Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LogInterceptor()) .order(1) .addPathPatterns("/**") .excludePathPatterns("/css/**","/*.ico","/error"); registry.addInterceptor(new LoginCheckInterceptor()) .order(2) .addPathPatterns("/**") .excludePathPatterns("/","/members/add","/login","/logout", "/css/**","/*.ico","/error"); //비회원도 접근 가능 } }
인터셉터를 적용하거나 하지 않을 부분은 `addPathPatterns` 와 `excludePathPatterns` 에 작성하면 된다. 기본
적으로 모든 경로에 해당 인터셉터를 적용하되 (`/**` ), 홈(`/` ), 회원가입(`/members/add` ), 로그인(`/login` ), 리소
스 조회(`/css/**` ), 오류(`/error` )와 같은 부분은 로그인 체크 인터셉터를 적용하지 않는다. 서블릿 필터와 비교해
보면 매우 편리한 것을 알 수 있다.
정리
서블릿 필터와 스프링 인터셉터는 웹과 관련된 공통 관심사를 해결하기 위한 기술이다.
서블릿 필터와 비교해서 스프링 인터셉터가 개발자 입장에서 훨씬 편리하다는 것을 코드로 이해했을 것이다. 특별한 문
제가 없다면 인터셉터를 사용하는 것이 좋다.
'TIL > 김영한의 스프링 MVC 2편 - 백엔드 웹 개발 핵심 기술' 카테고리의 다른 글
서블릿 예외 처리 - 필터 ,인터셉터 (1) | 2024.10.01 |
---|---|
서블릿 예외 처리 - 시작 ,오류 화면 제공 , 오류 페이지 작동 원리 (1) | 2024.10.01 |
로그인 처리2 - 서블릿 필터 소개 , 서블릿 필터 요청 로그 , 서블릿 필터 인증 체크 (2) | 2024.09.27 |
로그인 처리하기1 - 서블릿 HTTP 세션1, 서블릿 HTTP 세션2,세션 정보와 타임아웃 설정 (1) | 2024.09.24 |
로그인 처리하기1 - 세션 동작 방식 , 세션 만들기, 세션 적용 (0) | 2024.09.24 |