RestController 에서는 postHandle 을 쓸 일이 거의 없다
modelAndView 를 처리할 일이 거의 없기 때문이다
또한 예외가 발생하면 실행이 안되므로.. 주로 afterCompletion을 사용

Interceptor
컨트롤러의 실행 전후를 가로채 처리하는 컴포넌트
Aspect와 차이점
MVC의 실행 순서로 보았을 때, Aspect (의 @Before) 보다 실행 순서가 전이다
Controller의 레벨 자체의 접근과 접근 후를 처리하는 것이 Interceptor
메서드의 실행 레벨에 접근하여 처리하는 것이 Aspect
Aspect 가 파라미터의 값을 검증하는데 쓰이지만, 값도 바인딩 단계 이후에 처리된다
즉, 이 파라미터 자체가 바인딩이 되었는지를 확인 하는 것은
컨트롤러 레벨에 접근 하기 전에 하는 것이 맞다
그러므로 Interceptor 가 맞다
전체적인 흐름은 아래와 같다
요청 → Filter → DispacterServlet → Interceptor.prehandle
→ AOP @Before → Controller 실행 → AOP @After → Interceptor.postHandle() → View의 렌더링 → Interceptor.afterCompletion()
HandlerInterceptor
컨트롤러의 실행 전, 실행 후, 요청 완료 후 세가지의 처리를 할 수 있는
인터셉터 핸들러용 인터페이스
보통 이 인터페이스를 임플리먼트 하여 Intercepor를 구현한다
public interface HandlerInterceptor {
// 1. Controller 실행 전
boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception;
// 2. Controller 실행 후 (View 렌더링 전 or 데이터 전달 전)
void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception;
// 3. 요청 완료 후 (View 렌더링 후 or 데이터 전달 후)
void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) throws Exception;
}HandlerMethod 와 같이 많이 쓰이는데,
HTTP 요청을 처리할 때 매핑된 컨트롤러 메서드를 캡슐화하는 객체이다
파라미터인 handler 가 실행될 컨트롤러의 메서드 정보를 담고 있는 객체이므로
instanceof 가 true 라면 요청을 처리시 매핑된 컨트롤러 메서드 임을 증빙
preHandle (전처리)
요청이 식별되고 컨트롤러 레이어의 접근 전을 처리
@Component
@Slf4j
public class preHandleSomethingInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(
HttpServletRequest request,
HttpServletResponse response,
Object handler
) throws Exception {
log.info("preHandle 실행");
//현재 실행될 메서드가 컨트롤러 메서드인지 확인
//if(handler instanceof HandlerMethod) {
// 인증 등 수행
// return true;
//}
// true: Controller 실행
// false: Controller 실행 안함 (여기서 끝)
// 영향 받지 않는 것들은 통과 되도록
return true;
}
}postHandle (후처리)
컨트롤러 레이어 접근 후 컨트롤러가 실행이 되고 난 뒤를 처리
예외가 실행 중 발생했다면 postHandle은 처리되지 않는다
렌더링 단계도 접근 가능하다
@Component
@Slf4j
public class postHandleSomethingInterceptor implements HandlerInterceptor {
@Override
public void postHandle(
HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView
) throws Exception {
// 예외가 실행 중 발생했다면 postHandle은 실행되지 않음
log.info("postHandle 실행");
// Controller 실행 완료
// View 렌더링 전이니 modelAndView 로 요소 접근 후 수정 가능
}
}
}afterCompletion (모든 완료 후)
컨트롤러 레이어 접근 후 컨트롤러와 렌더링도 실행이 되고 난 뒤를 처리
finally 구문과 성질이 비슷하다
예외가 발생해도 실행되는 것을 알아야한다, 예외 정보도 받아올 수 있다
@Component
@Slf4j
public class LoggingInterceptor implements HandlerInterceptor {
@Override
public void afterCompletion(
HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex // 예외 정보 (예외가 발생 했다면)
) throws Exception {
// 항상 실행됨 (예외 발생해도)
log.info("afterCompletion 실행");
if (ex != null) {
log.error("예외 발생: {}", ex.getMessage());
}
// 리소스 정리 등
}
}afterCompletion 을 통한 DB 리소스 정리
@Component
@Slf4j
public class ResourceCleanupInterceptor implements HandlerInterceptor {
private static final String DB_CONNECTION = "dbConnection";
@Override
public boolean preHandle(
HttpServletRequest request,
HttpServletResponse response,
Object handler
) throws Exception {
// 리소스 획득
Connection connection = getConnection();
request.setAttribute(DB_CONNECTION, connection);
log.info("DB 연결 획득");
return true;
}
@Override
public void afterCompletion(
HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex
) throws Exception {
// 리소스 정리 (항상 실행)
Connection connection = (Connection) request.getAttribute(DB_CONNECTION);
if (connection != null) {
connection.close();
log.info("DB 연결 해제");
}
if (ex != null) {
log.error("예외 발생으로 인한 정리: {}", ex.getMessage());
}
}
private Connection getConnection() {
// 실제 DB 연결
return null;
}
}💡
Spring MVC에 등록
Spring MVC의 설정을 커스터마이징하는 설정 클래스
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@AutoWired
private SomethingInterceptor somethingInterceptor;
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(somethingInterceptor);
}
}