WebMvcConfigurer을 통해 개발자가 쉽게 Mvc 설정을 커스텀할 수 있다
호출 시 컨트롤러를 거치지 않고 즉시 view를 전달하는 ParameterizableViewController를 정의할 수 있다.
Java에서 처리하는 로직이 없는 단순 뷰 이름 매핑에만 사용 가능하다. 모델이나 로직이 없는 정적페이지/간단한 라우팅 용도로만 사용해야 한다.
addViewControllers
메서드를 사용하면 컨트롤러를 작성하지 않고도 뷰를 응답할 수 있다.
@Configuration
public class WebMvcConfiguration implements **WebMvcConfigurer** {
// addViewControllers를 통해 로직 없는 단순 뷰 이름 매핑을 할 수 있다.
@Override
**** public void **addViewControllers(**ViewControllerRegistry registry) {
// 다음과 같이 addViewController를 통해 경로를 지정할 수 있다.
registry.addViewController("/").setViewName("hello");
**** }
...
}
WebMvcConfigurer
를 사용하는 방식과 직접 Controller 를 작성하는 방식 중 어떤 방식이 더 좋을까?
→ 당연히 모든 코드 상황마다 다르겠지만, 일반적으로 직접 Controller를 작성하는 방식이 더 좋을 것이다.
WebMvcConfigurer를 커스텀한다는 것은 스프링을 알고 있지만 해당 커스텀 옵션을 모르는 개발자에게 혼란을 줄 수 있다. (프로젝트에 신규 투입되는 사람이라면 놓칠 가능성이 높다고 봄)
따라서 한시적으로 사용하고, 일반적인 경우 @Controller 방식으로 구현하는 것을 지향하자.
View Controllers :: Spring Framework
Spring의 Interceptor는 HTTP 요청의 사전/사후 처리에 사용된다. 컨트롤러로 요청을 전달하기 전과 후에 특정 로직을 실행시킬 수 있다.
WebMvcConfigurer
가 제공하는 addInterceptor
메서드를 통해 특정 패턴에 대해 인터셉터가 동작하도록 설정할 수 있다.
다음의 인터페이스를 구현한 구현체를 인터셉터로 등록할 수 있다.
public interface HandlerInterceptor {
// Handler 작업 전에 호출된다.
// 반환값 true면 Handler 정상 실행, false면 Handler 미실행
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
// Handler 작업 이후에 호출된다. (콜백)
// ResponseBody/Entity 사용 시 응답은 이미 HandlerAdaptor에 작성되고 커밋되므로 여기서 응답을 수정할 수 없다.
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 {
}
}
아무튼 Interceptor는 아래와 같이 사용 가능하다. 마찬가지로 WebMvcConfigurer
내부에서 addInterceptors()
메서드에 인터셉터를 추가할 수 있다.
addPathPatterns()
이용해서 특정 요청으로 매핑할 수도 있다. (매핑하지 않으면 전체 요청에 대해 Interceptor가 동작하는 것 같다. 잘 되던 이전 테스트가 먹혔다)
→ excludePathPatterns()
사용하면 제외할 패턴도 지정 가능하다
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
..
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new CheckLoginInterceptor())
.addPathPatterns("/admin/**");
}
}
public class CheckLoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String accessToken = request.getHeader("Authorization");
if (accessToken == null) {
throw new AuthorizationException();
}
return true;
}
}
패턴 문법 정리 (Spring의 Ant-style path pattern)
패턴 | 설명 |
---|---|
/admin/* | /admin/ 하위의 한 단계 경로에만 매칭. 예: /admin/user , X: /admin/user/edit |
/admin/** | /admin/ 하위의 모든 경로에 매칭. 예: /admin , /admin/user , /admin/user/edit |
/** | 모든 요청 경로에 매칭 |
/static/**.css | /static/ 하위의 .css 파일들 |
/*.html | 루트 경로의 .html 파일들. 예: /index.html , X: /pages/index.html |
Filter와의 차이
항목 | Filter | HandlerInterceptor |
---|---|---|
위치 | ServletContainer 수준 | Spring MVC DispatcherServlet 내부 |
대상 | 모든 요청 (정적 리소스 포함) | Spring MVC에서 처리되는 요청만 |
사용 목적 | 보안, 로깅 등 아주 전역적인 기능 | 컨트롤러 전/후 공통 처리 로직 |
API 수준 | Servlet 표준 | Spring 전용 |
스프링이라는 프레임워크가
HandlerInterceptor
라는 인터페이스를 제공하고, 이런 행위를 지원하는 이유는 무엇일까?
→ 관심사 분리, 확장성
*공식 문서에서 공통적으로 말하는 것은, Interceptor는 보안 수단이 아니므로 보안 목적으로 사용하는 경우 Spring Security를 사용하는 것이 좋다.
HandlerMapping
구현체는 요청 전반에 공통 기능을 적용할 수 있도록 핸들러 인터셉터(handler interceptor) 를 지원합니다. HandlerInterceptor
는 다음과 같은 콜백 메서드를 구현할 수 있습니다:preHandle(..)
– 실제 핸들러가 실행되기 직전에 호출되는 콜백으로, boolean
을 반환합니다. true
를 반환하면 실행이 계속되고, false
를 반환하면 실행 체인이 중단되며 핸들러는 호출되지 않습니다.postHandle(..)
– 핸들러 실행 이후에 호출되는 콜백입니다.afterCompletion(..)
– 요청 전체가 완료된 후에 호출되는 콜백입니다.참고
@ResponseBody
나ResponseEntity
를 사용하는 컨트롤러 메서드의 경우, 응답은 HandlerAdapter 내부에서 이미 작성되고 커밋되므로,postHandle
이 호출될 시점에는 응답을 수정할 수 없습니다 (예: 헤더 추가 등).
이 경우에는ResponseBodyAdvice
를 구현하여@ControllerAdvice
로 등록하거나,RequestMappingHandlerAdapter
에 직접 설정해야 합니다.
경고
인터셉터는 보안 계층으로는 이상적인 수단이 아닙니다.
그 이유는 애노테이션 기반 컨트롤러의 경로 매핑 방식과 인터셉터 매핑이 불일치할 수 있기 때문입니다.
보안을 위해서는 Spring Security를 사용하거나, Servlet 필터 체인에 통합된 보안 처리 방식을 사용하는 것이 바람직하며, 가능한 한 이른 시점에 적용해야 합니다.
EnableWebMvc와 WebMvcConfigurer 사용 시 주의
@EnableWebMvc
애노테이션이 붙은@Configuration
클래스를 사용할 수는 있지만, 이 경우 Spring MVC의 모든 설정을 직접 제어해야 하며, 모든 구성을 수동으로 커스터마이징해야 합니다.
또한 이 방식은 Spring Boot의 자동 설정(auto-configuration)과 충돌이 발생할 수 있어, 애플리케이션을 올바르게 설정하는 것이 더 어려워질 수 있습니다.
Be careful when using @Configuration classes with @EnableWebMvc in Spring Boot