2025. 2. 28. 00:15ㆍSpring Framework/Web on Servlet Stack
Spring MVC에서 인터셉터(Interceptor)는 Http request가 컨트롤러(Handler)에 도달하기 전/후, 또는 요청이 완전히 완료된 후에 특정 로직을 실행하는 기능입니다.
이를 활용하면 요청 전/후에 공통적으로 적용해야 하는 기능(예: 로깅, 인증, 권한 검사, 성능 모니터링 등)을 손쉽게 구현할 수 있습니다.
1. Spring MVC의 인터셉터(HandlerInterceptor) 개념
Spring MVC의 모든 HandlerMapping 구현체는 인터셉터(Interceptor)를 지원합니다.
인터셉터는 HandlerInterceptor
인터페이스를 구현하여 사용할 수 있습니다.
📌 인터셉터의 주요 메서드
메서드 | 실행 시점 | 반환 타입 | 설명 |
---|---|---|---|
preHandle(..) |
컨트롤러 실행 전 | boolean |
- true 반환 → 컨트롤러 실행 계속 진행- false 반환 → 실행 중단 (응답 직접 처리 가능) |
postHandle(..) |
컨트롤러 실행 후, 뷰 렌더링 전 | void |
- 컨트롤러가 실행된 후 실행됨 - 모델(Model) 데이터를 수정할 수 있음 |
afterCompletion(..) |
요청 완료 후 (뷰 렌더링 이후) | void |
- 요청 처리가 완료된 후 실행됨 - 리소스 정리, 로깅 등에 사용 |
📌 즉, preHandle → 컨트롤러 실행 → postHandle → 뷰 렌더링 → afterCompletion 순서로 실행됨.
2. 인터셉터(Interceptor) 구현 방법
인터셉터를 사용하려면 HandlerInterceptor 인터페이스를 구현하면 됩니다.
✅ 예제: 요청 실행 전/후에 로깅하는 인터셉터
import org.springframework.web.servlet.HandlerInterceptor;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
public class LoggingInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("[preHandle] 요청 시작: " + request.getRequestURI());
return true; // true → 컨트롤러 실행 계속 진행
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("[postHandle] 컨트롤러 실행 후");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("[afterCompletion] 요청 완료");
}
}
📌 설명
preHandle()
: 요청이 컨트롤러에 도달하기 전에 실행됨 (여기서false
를 반환하면 요청이 중단됨)postHandle()
: 컨트롤러 실행 후, 뷰가 렌더링되기 전에 실행됨afterCompletion()
: 뷰 렌더링이 완료된 후 실행됨 (예외가 발생해도 실행됨)
3. 인터셉터 등록 방법
인터셉터를 만든 후, Spring MVC에 등록해야 합니다.
✅ 예제: WebMvcConfigurer를 사용하여 인터셉터 등록
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.context.annotation.Configuration;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoggingInterceptor())
.addPathPatterns("/api/**") // 특정 경로에만 적용
.excludePathPatterns("/api/login"); // 특정 경로 제외
}
}
📌 설명
/api/**
경로의 모든 요청에 대해LoggingInterceptor
를 적용/api/login
요청은 인터셉터 적용에서 제외
4. 인터셉터 동작 방식
요청이 들어왔을 때, 인터셉터가 실행되는 순서는 다음과 같습니다.
📌 인터셉터 실행 흐름
[preHandle 실행] → 컨트롤러 실행 → [postHandle 실행] → 뷰 렌더링 → [afterCompletion 실행]
📌 preHandle()에서 false
반환 시 흐름
[preHandle 실행] → 요청 종료 (컨트롤러 실행되지 않음)
➡ 즉, preHandle에서 false를 반환하면 요청 처리가 중단되고, 이후 로직이 실행되지 않습니다.
5. @ResponseBody
, ResponseEntity
와 인터셉터의 한계
@ResponseBody
또는 ResponseEntity
를 사용하면 응답이 postHandle()
실행 전에 커밋(Commit)됩니다.
즉, postHandle()에서 응답을 변경할 수 없습니다.
📌 예제: JSON 응답을 반환하는 컨트롤러
@RestController
public class UserController {
@GetMapping("/user")
public ResponseEntity<String> getUser() {
return ResponseEntity.ok("User Data");
}
}
📌 postHandle()에서 응답을 수정하려 해도 실패
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
response.setHeader("X-Custom-Header", "CustomValue"); // 동작하지 않음!
}
➡ postHandle()
실행 전에 응답이 커밋되었기 때문에, 헤더 추가가 적용되지 않음.
🚀 해결 방법: ResponseBodyAdvice
사용
이 문제를 해결하려면 ResponseBodyAdvice를 사용하여 응답 데이터를 가공할 수 있습니다.
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
@ControllerAdvice
public class CustomResponseAdvice implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(Class<? extends HttpMessageConverter<?>> converterType, Class<?> clazz) {
return true; // 모든 컨트롤러에 적용
}
@Override
public Object beforeBodyWrite(Object body, HttpInputMessage inputMessage, HttpOutputMessage outputMessage, HttpHeaders headers, Class<? extends HttpMessageConverter<?>> converterType, Class<?> clazz) {
headers.add("X-Custom-Header", "CustomValue"); // 응답 헤더 추가
return body;
}
}
📌 설명
ResponseBodyAdvice
는@ResponseBody
와ResponseEntity
응답을 수정할 수 있도록 도와줌.beforeBodyWrite()
에서 응답을 가공할 수 있음.
6. 인터셉터를 보안(Security) 목적으로 사용하면 안 되는 이유
인터셉터는 보안 기능(예: 인증, 권한 관리)에 사용할 수도 있지만, 완벽한 보안 레이어로 사용하기에는 한계가 있습니다.
🚨 인터셉터를 보안 목적으로 사용하면 발생하는 문제점
1️⃣ 컨트롤러의 @RequestMapping
경로와 일치하지 않을 가능성
- Spring Security는 메서드 레벨에서 보안 검사를 하지만, 인터셉터는 경로 기반으로 작동함.
- 특정 컨트롤러에는 적용되지 않을 수도 있음.
2️⃣ Spring Security보다 늦게 실행됨
- Spring Security는 Servlet Filter에서 먼저 실행됨.
- 인터셉터는 컨트롤러 호출 전에 실행되지만, 필터보다 늦게 실행됨.
📌 해결책: 보안은 Spring Security를 사용!
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(auth -> auth
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
);
return http.build();
}
}
➡ 보안은 인터셉터가 아니라, Spring Security의 Filter
에서 처리하는 것이 바람직합니다.
🚀 Summary
- 인터셉터는 HandlerInterceptor를 구현하여 요청 전/후에 공통 로직을 실행하는 기능.
preHandle()
,postHandle()
,afterCompletion()
을 통해 요청의 흐름을 제어할 수 있음.ResponseBodyAdvice
를 활용하면@ResponseBody
응답도 수정 가능.- 보안(Authentication, Authorization)은 Spring Security를 사용하는 것이 가장 안전함. 🚀
'Spring Framework > Web on Servlet Stack' 카테고리의 다른 글
View Resolution (0) | 2025.02.28 |
---|---|
Exceptions (0) | 2025.02.28 |
Path Matching (0) | 2025.02.27 |
Processing (0) | 2025.02.27 |
Servlet Config (0) | 2025.02.27 |