Spring MVC: 동적 요청 경로 추출하기 (PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE)
대규모 애플리케이션에서 요청 흐름을 명확히 관리하려면 예외 처리와 요청 전후 가로채기가 필수입니다. 이 글에서는 글로벌 예외 처리와 인터셉터를 실무 관점에서 정리하며, Spring MVC: 동적 요청 경로 추출하기 (PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE) 같은 핵심 개념을 함께 설명합니다.
1. 🚨 글로벌 예외 처리: `@ControllerAdvice`
컨트롤러별로 흩어진 예외 처리는 유지보수를 어렵게 만듭니다. `@ControllerAdvice`는 애플리케이션 전역에서 발생하는 예외를 한곳에서 관리해 일관된 에러 응답 형식을 만들고, 비즈니스 로직을 깔끔하게 분리합니다. 또한 Spring MVC: 동적 요청 경로 추출하기 (PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE)와 같은 요청 관련 정보를 활용해 더 세밀한 예외 로깅을 구현할 수 있습니다.
`try-catch`를 각 핸들러에 반복하는 대신, `@ExceptionHandler`와 함께 사용하면 공통 응답 규격, 상태 코드 매핑, 그리고 로깅 전략을 중앙에서 통제할 수 있습니다.
Java GlobalExceptionHandler.java
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
// 모든 컨트롤러에 걸쳐 예외 처리를 적용하라는 어노테이션
@ControllerAdvice
public class GlobalExceptionHandler {
// CustomException이 발생했을 때 처리
@ExceptionHandler(CustomException.class)
public ResponseEntity<String> handleCustomException(CustomException ex) {
// HTTP 상태 코드와 메시지를 정의하여 반환
return new ResponseEntity<>(
"ERROR: " + ex.getMessage(),
HttpStatus.BAD_REQUEST // 400 Bad Request
);
}
// RuntimeException 계열의 모든 예외를 처리 (최종 fallback)
@ExceptionHandler(RuntimeException.class)
public ResponseEntity<String> handleAllExceptions(RuntimeException ex) {
return new ResponseEntity<>(
"Internal Server Error: " + ex.getMessage(),
HttpStatus.INTERNAL_SERVER_ERROR // 500 Internal Server Error
);
}
}
핵심: `@ControllerAdvice`는 전역 예외 처리의 표준입니다. 일관된 에러 바디와 상태 코드를 제공하고, 요청 경로 정보(예: Spring MVC: 동적 요청 경로 추출하기 (PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE))를 로깅에 활용하면 문제 원인 파악이 쉬워집니다.
2. 🛣️ HandlerInterceptor를 활용한 공통 로직 처리
인터셉터는 DispatcherServlet이 컨트롤러를 호출하기 전후의 흐름을 가로채 공통 작업을 처리할 수 있게 합니다. 인증·권한 검사, 요청/응답 로깅, 성능 측정 같은 횡단 관심사를 인터셉터로 분리하면 컨트롤러가 본연의 역할에만 집중할 수 있습니다. 또한 인터셉터 단계에서 Spring MVC: 동적 요청 경로 추출하기 (PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE)를 참조하면 요청별 세부 로직을 적용하기 용이합니다.
Java LoggingInterceptor.java
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
public class LoggingInterceptor implements HandlerInterceptor {
// 1. Controller 실행 전에 호출됨 (주로 인증/권한 체크)
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
long startTime = System.currentTimeMillis();
request.setAttribute("startTime", startTime); // 시작 시간 기록
System.out.println("요청 시작: " + request.getRequestURI());
return true; // true를 반환해야 Controller로 진행
}
// 2. Controller 실행 후, View 렌더링 직전에 호출됨 (주로 Model/View 조작)
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
if (modelAndView != null) {
System.out.println("뷰 이름: " + modelAndView.getViewName());
}
}
// 3. View 렌더링 포함 모든 작업 완료 후 호출됨 (주로 리소스 정리 및 로깅)
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
long startTime = (Long) request.getAttribute("startTime");
long endTime = System.currentTimeMillis();
System.out.println("요청 완료 시간: " + (endTime - startTime) + "ms");
}
}
인터셉터 등록 (WebMvcConfigurer)
구현한 인터셉터는 WebMvcConfigurer에 등록해야 활성화됩니다. 경로 패턴을 신중히 설계하면 퍼포먼스와 보안 모두 개선됩니다.
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// /api/** 경로로 들어오는 모든 요청에 대해 인터셉터 적용
registry.addInterceptor(new LoggingInterceptor())
.addPathPatterns("/api/**")
.excludePathPatterns("/api/public/**"); // 예외 경로 설정
}
}
함께 보면 좋은 엔터프라이즈 사례
🚀 이 주제, 우리 서비스에 어떻게 적용할까요?
Spring MVC: 동적 요청 경로 추출하기 (PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE)를 실제 서비스와 조직에 녹여보고 싶다면, 현재 아키텍처와 운영 방식을 한 번 점검해 보는 것부터 시작해 보세요. 팀 위키나 기술 블로그, 사내 스터디 주제로도 아주 좋습니다.
이 글이 도움이 됐다면, 비슷한 엔터프라이즈 사례 글들도 함께 살펴보면서 우리 조직에 맞는 운영 상용구를 정의해 보세요.




댓글
댓글 쓰기