문제 상황
- JWT 인증을 위한 커스텀 필터(JwtFilter)에서 토큰 유효성을 검사할 때, 아래와 같이 JwtValidationException이라는 커스텀 예외를 던지도록 구현하였다
catch (SecurityException | MalformedJwtException e) {
throw new JwtValidationException("유효하지 않는 JWT 서명입니다.", HttpServletResponse.SC_UNAUTHORIZED);
} catch (ExpiredJwtException e) {
throw new JwtValidationException("만료된 JWT 토큰입니다.", HttpServletResponse.SC_UNAUTHORIZED);
} catch (UnsupportedJwtException e) {
throw new JwtValidationException("지원되지 않는 JWT 토큰입니다.", HttpServletResponse.SC_BAD_REQUEST);
} catch (Exception e) {
throw new JwtValidationException("내부 서버 오류", HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
그리고 @ControllerAdvice를 통해 예외를 전역 처리하려 했다:
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(JwtValidationException.class)
public ResponseEntity<ErrorResponse> handleJwtException(JwtValidationException e) {
return ResponseEntity
.status(e.getHttpStatus())
.body(new ErrorResponse(e.getMessage()));
}
// ... 기타 핸들러
}
하지만 Filter에서 발생한 예외는 위 핸들러로 처리되지 않고, Spring Security 기본 에러 페이지 또는 빈 응답으로 리턴되었다.
2. 원인 분석
- Spring에서 @ControllerAdvice는 DispatcherServlet 이후의 흐름에서 발생한 예외만을 처리한다.
- 반면, Filter는 DispatcherServlet 앞단에 위치하기 때문에, 여기서 발생한 예외는 @ControllerAdvice까지 도달하지 않는다.
[Filter] → DispatcherServlet → Controller → @ControllerAdvice
↑
예외는 여기서 끝남
3. 해결 방법
- Filter에서 발생한 예외는 직접 HttpServletResponse에 에러 응답을 작성하는 방식으로 처리하였다.
- 추가로, 예외 메시지와 HTTP 상태코드를 일관된 방식으로 관리할 수 있도록 enum 을 추가했다.
catch (SecurityException | MalformedJwtException | TokenNotFoundException e) {
response.sendError(ServletResponseError.INVALID_JWT_SIGNATURE.getHttpStatus(), ServletResponseError.INVALID_JWT_SIGNATURE.getMessage());
} catch (ExpiredJwtException e) {
response.sendError(ServletResponseError.EXPIRED_JWT_TOKEN.getHttpStatus(), ServletResponseError.EXPIRED_JWT_TOKEN.getMessage());
} catch (UnsupportedJwtException e) {
response.sendError(ServletResponseError.UNSUPPORTED_JWT.getHttpStatus(), ServletResponseError.UNSUPPORTED_JWT.getMessage());
} catch (Exception e) {
response.sendError(ServletResponseError.INTERNAL_SERVER_ERROR.getHttpStatus(), ServletResponseError.INTERNAL_SERVER_ERROR.getMessage());
}
'Spring > 문제 해결 (Troubleshooting)' 카테고리의 다른 글
context-path와 Security: 예상과 다른 경로 매칭 문제 (0) | 2025.07.15 |
---|---|
@Cacheable 사용 시 역직렬화 오류 해결 (1) | 2025.07.15 |
Spring Security 에러 핸들링 오류 해결, /error 접근 권한 문제 (0) | 2025.05.29 |
@LastModifiedDate 와 DB의 ON UPDATE CURRENT_TIMESTAMP 동시 사용 문제 (1) | 2025.05.23 |
JPA 변경사항이 즉시 반영되지 않을 때 - 영속성 컨텍스트와 DB 값 불일치 문제 (2) | 2025.05.23 |