일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 체크에러
- 예외추상화
- 스프링 파일 업로드
- 김영한
- ExceptionResolver
- HTTP메시지
- 객체지향
- http
- 웹 스코프
- 커밋로그
- BeanDefnition
- REST #REST API #HTTP 메서드
- 서블릿
- 언체크에러
- HTTP 응답 메시지
- BeanValidation
- 백준4256
- 깃허브 저장소 합치기
- 의존관계
- 프로토콜 스택 4계층
- 프로토타입 스코프
- 백준 2263
- 스프링 컨테이너
- 스프링 예외변환기
- BasicErrorController
- 스프링 타입컨버터
- HTTP 요청 메시지
- 스프링
- 스프링 빈
- DI
- Today
- Total
Enthusiasm! Enthusiasm!
스프링 인터셉터 본문
스프링 인터셉터가 필요한 상황
로그인 한 사용자만 특정 페이지에 접근할 수 있도록 설계를 해야하는 상황이라 가정하자. 컨트롤러에서 각 URL마다 로그인 여부를 체크하는 로직을 하나하나 작성하는것은 중복이 많고, 추후에 변경이 있을 시 모든 로직을 수정해야하는 불편함이 있다. 이렇게 애플리케이션 여러 로직에서 공통으로 관심이 있는 것을 공통 관심사(cross-cutting)이라 한다. 이는 스프링의 AOP로도 해결 할 수 있지만, 웹과 관련된 공통 관심사는 서블릿 필터 또는 스프링 인터셉터를 사용하는 것이 좋다.
서블릿 필터
서블릿 필터를 적용하면 필터가 호출 된 다음에 서블릿이 호출된다. 필터의 흐름은 다음과 같다.
HTTP 요청 -> WAS -> 필터(필터1,필터2,...) -> 서블릿 -> 컨트롤러 //로그인 사용자
HTTP 요청 -> WAS -> 필터(적절하지 않은 요청이라 판단, 서블릿 호출X) //비 로그인 사용자
서블릿 필터로도 처음에 제시한 공통 관심 사항을 해결 할 수있다. 그러나 사용방법이 스프링 인터셉터보다 불편하고, 스프링 인터셉터가 더 정교하고 다양한 기능을 제공하기 때문에 스프링 인터셉터를 사용하는게 더 편리하다.
스프링 인터셉터
서블릿 필터가 서블릿이 제공하는 기술이라면, 스프링 인터셉터는 스프링 MVC가 제공하는 기술이다. 둘다 웹과 관련된 공통 관심 사항을 처리하지만, 적용되는 순서와 범위, 그리고 사용방법이 다르다. 스프링 인터셉터의 흐름은 다음과 같다.
HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 스프링 인터셉터(인터셉터1,인터셉터2,...) -> 컨트롤러 //로그인 사용자
HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 스프링 인터셉터(적절하지 않은 요청이라 판단, 컨트롤러 호출 X) // 비 로그인 사용자
스프링 인터셉터 인터페이스
스프링 인터셉터를 사용하려면 HandlerInterceptor 인터페이스를 구현하면 된다.
public interface HandlerInterceptor {
default boolean preHandle(HttpServletRequest request,HttpServletResponse response,Object handler) throws Exception {}
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 {}
}
- 인터셉터는 컨트롤러 호출 전( preHandle ), 호출 후( postHandle ), 요청 완료 이후( afterCompletion )와 같이 단계적으로 세분화 되어 있다.
- 어떤 컨트롤러( handler )가 호출되는지 호출 정보를 받을 수 있으며, 어떤 modelAndView 가 반환되는지 응답 정보도 받을 수 있다.
- 참고로 메서드에 default가 붙어있으면, 오버라이딩이 필수가 아니다. 따라서 필요한 메서드만 오버라이딩 하면 된다.
스프링 인터셉터 호출 흐름
- preHandle : 컨트롤러 호출 전에 호출된다. (더 정확히는 핸들러 어댑터 호출 전에 호출된다.) preHandle의 응답값이 true 이면 다음으로 진행하고, false면 더는 진행하지 않는다. false인 경우 나머지 인터셉터는 물론이고, 핸들러 어댑터도 호출되지 않는다. 그림에서 1번에서 끝이나버린다.
- postHandle : 컨트롤러 호출 후에 호출된다. 컨트롤러에서 예외가 발생하면 postHandle은 호출되지 않는다.
- afterCompletion : 뷰가 렌더링 된 이후에 호출된다. afterCompletion은 예외가 발생해도 항상 호출되며, 예외를 파라미터로 받아서 어떤 예외가 발생했는지도 알 수 있다. 단 preHandle에서 예외가 발생하면 afterCompletion도 실행되지 않는다.
스프링 인터셉터 사용 예제
로그인 인증 체크 기능을 하는 스프링 인터셉터를 설계해보자.
HandlerInterceptor를 구현한 LoginCheckInterceptor와 이를 등록하는 WebConfig를 만들면 된다.
LoginCheckInterceptor
@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만 구현하면 된다.
- 여기에 사용하지는 않았지만 preHandle에 지정한 값을 postHandle이나 afterCompletion에서 함께 사용하고 싶으면, request.setAttribute를 사용하여 값을 보관하고 getAttribute로 이를 꺼내 사용하면 된다. 지역변수로 선언하여 싱글톤을 깨면 안된다.
WebConfig
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginCheckInterceptor())
.order(1)
.addPathPatterns("/**")
.excludePathPatterns("/", "/members/add", "/login", "/logout",
"/css/**", "/*.ico", "/error");
}
}
- 인터셉터를 적용하거나 하지 않을 부분은 addPathPatterns 와 excludePathPatterns 에 작성하면 된다. 기본적으로 ( /** )를 사용하여 모든 경로에 해당 인터셉터를 적용하고, 홈( / ), 회원가입( /members/add ), 로그인( /login ), 리소스 조회( /css/** ), 오류( /error )와 같은 부분은 로그인 체크 인터셉터를 적용하지 않는다. 이와 같이 인터셉터를 사용하면 경로를 자세하고 세밀하게 설정할 수 있다.
이 포스팅은 Inflearn 김영한님의 스프링 강의 및 강의자료를 참고하여 작성하였습니다.
'자바 스프링 > 스프링MVC' 카테고리의 다른 글
웹 애플리케이션의 이해 (0) | 2023.04.02 |
---|---|
스프링 파일 업로드 (0) | 2023.03.16 |
스프링 타입 컨버터 (0) | 2023.03.15 |
스프링 예외처리 (0) | 2023.03.15 |
검증 - BeanValidation (0) | 2023.03.03 |