Spring Boot REST 예외 처리 — /error 리다이렉트 대신 JSON 에러 응답 구성하기
검증 환경
본 절은 원문 사례·문서에 등장한 버전/도구를 정리한 것이다. 별도 실험실 재현이 명시되지 않은 항목은 일반화하지 않는다.
| 항목 | 본문·원문에서 확인된 범위 |
|---|---|
| 런타임/도구 | Spring Boot 1.2.1 |
| 런타임/도구 | Spring Boot 1.4 |
| 런타임/도구 | Spring Boot 1.3.1 |
| 런타임/도구 | Java 8 |
| 문서 정리일 | 2026-08-31 |
- OS/CI 세부 값은 프로젝트마다 다르므로, 적용 전 로컬에서 동일 오류 메시지를 재확인한다.
1. 문제 정의
Spring Boot 기반 REST 서버(Spring Boot 1.2.1 / Spring 4.1.5 / Java 8)에서 @RestController와 @RequestMapping으로 API를 구성하다 보면, 서비스 엔드포인트에서 예외외가 발생했을 때 /error 로 리다이렉트 되는 문제가 발생한다.
Spring Boot 문서에서 명시하듯 “Spring Boot는 기본적으로 /error 매핑핑를 제공하며, 서블릿 컨테이너에 전역 오류 페이지로 등록한다.” REST API 소비 예를 들어 Angular나 jQuery SPA라면 리다이렉트를 따르지 않고 그대로 응답 본문을 기대하기 때문에, 이 /error 리다이렉트는 사실상 쓸모가 없다.
목표: 컨트롤러 메서드에서 의도적으로 던지거나, Spring이 자동으로 생성한 예외(핸들러 매핑이 없는 URL 요청 시의 404 포함)를 가로채서 400/500/503/404 등의 정확한 상태 코드와 함께 일관된 JSON 에러 응답을 반환한다. 특히 예외를 NoSQL에 UUID와 함께 로깅한 뒤, 클라이언트에는 UUID와 HTTP 상태 코드를 JSON으로 돌려주는 패턴이 전형적이다.
대표 증상
| |
2. 먼저 확인할 것
해결책을 고르기 전에, 원인이 “컨트롤러 예외” 인지 “핸들러를 찾지 못한 404” 인지를 먼저 구분해야 한다. 이 둘은 잡는 지점이 다르다.
| 구분 | 컨트롤러 예외 | 핸들러 미발견 (404) |
|---|---|---|
| 발생 지점 | @RequestMapping 메서드 실행 중 | DispatcherServlet이 매핑된 핸들러를 못 찾을 때 |
| 기본 동작 | /error 매핑으로 포워드 | 정적 리소스 핸들러가 가로채거나 /error 로 이동 |
| 잡는 방법 | @ControllerAdvice / @RestControllerAdvice | throw-exception-if-no-handler-found + resources.add-mappings=false 설정 후 같은 advice로 처리 |
| 상태 코드 | 예외/@ResponseStatus에 따라 결정 | 404 Not Found |
이 구분을 먼저 확인한 뒤 아래 문제 정의에 맞는 기존 동작을 재현해 보자.
| |
3. 원인별 분기표 (Fix Decision Tree)
증상이 어떤 갈레인지에 따라 선택할 구성이 달라진다. 아래 분기표를 따라 결정하라.
| # | 상황 | 확인 포인트 | 선택 구성 |
|---|---|---|---|
| A | 컨트롤러에서 던진 예외가 /error 로 포워드됨 | advice 없이도 동작 상태 확인 | @ControllerAdvice / @RestControllerAdvice 추가 |
| B | 존재하지 않는 URL이 404가 아닌 다른 응답 | 정적 리소스 핸들러가 가로챔 | spring.resources.add-mappings=false + spring.mvc.throw-exception-if-no-handler-found=true |
| C | 404도 JSON으로 통일하고 싶음 (A+B 결합) | 위 두 설정 후에도 404가 advice로 안 잡힘 | 두 설정 모두 적용 + advice에서 NoHandlerFoundException 핸들러 |
| D | 기본 /error 응답 형식 자체를 커스터마이즈 | 상태 코드별 응답 본문을 바꾸고 싶음 | ErrorController (또는 BasicErrorController 상속) 오버라이드 |
| E | @EnableWebMvc를 advice에 함께 썼는데 부작용 | 자동 설정 일부가 사라짐 | @EnableWebMvc 제거, Spring Boot 자동 설정 유지 |
4. 상황별 해결책
상황 A — 컨트롤러 예외를 JSON으로 (권장 기본)
별도 코드 없이 컨트롤러 예외를 전역으로 잡으려면 @ControllerAdvice를 하나 만든다. Spring Boot 1.4+/Spring 4.3+ 라면 @RestControllerAdvice를 쓰면 @ResponseBody를 매번 붙일 필요가 없다.
| |
@RestControllerAdvice는 @ControllerAdvice + @ResponseBody의 조합이므로, 위처럼 @ExceptionHandler 메서드에서 객체를 그대로 반환하면 JSON으로 직렬화된다.
필요하다면 특징 예외를 개별 메서드로 분기한다 (Spring의 ResponseEntityExceptionHandler를 상속하면 spring.mvc.throw-exception-if-no-handler-found 로 던져지는 NoHandlerFoundException도 오버라이드 가능):
| |
상황 B — 404를 NoHandlerFoundException으로 (application.properties)
존재하지 않는 URL이 정적 리소스 핸들러에 가로채져서 advice까지 도달하지 못하는 경우가 대부분. Spring Boot 1.3.1+ 에서는 다음 두 줄을 application.properties(또는 application.yml)에 추가한다. Boot 1.2.7 이하면 아래 DispatcherServlet 설정으로 같은 플래그를 켠다.
| |
왜 이 두 줄이 필요한가: Spring Boot 기본 정적 리소스 설정은 리소스 핸들러를 마지막 순서로
/**에 매핑한다. 이 핸들러가 아직 처리가 안 된 요청을 먼저 받으므로 DispatcherServlet이 예외를 던질 수가 없다. 그리하여 404를 예외로 받으려면 리소스 매핑을 끊을 것과 동시에throw-exception-if-no-handler-found를 켜야 한다.
Boot 1.2.7을 쓰는데 properties 키가 동작하지 않는다면, 기존 DispatcherServlet 빈에 플래그만 설정하는 방식이 덜 침습적이다:
| |
상황 D — /error 응답 자체를 커스텀 (ErrorController)
예외 핸들러가 아닌, 기본 /error 응답 형식을 상태 코드별로 바꾸고 싶다면 ErrorController(또는 BasicErrorController 상속)를 구현한다.
| |
5. 검증 명령
구성 후 아래로 상태 코드와 JSON 바디를 확인한다.
| |
통합 테스트로 자동 검증하려면 MockMvc를 사용한다:
| |
6. 예방 조치 (재발 방지)
| 항목 | 조치 |
|---|---|
| 404 JSON화 | REST 전용 앱이면 spring.resources.add-mappings=false + throw-exception-if-no-handler-found=true를 프로젝트 표준 설정으로 고정 |
| advice 표준화 | 팀 공통 @RestControllerAdvice(또는 ResponseEntityExceptionHandler 상속)를 공용 라이브러리에 두고 모든 서비스가 재사용 |
| 예외 로깅 | advice에서 예외를 UUID와 함께 NoSQL에 로그하고, 클라이언트 응답에 UUID 포함하는 계약 표준화 |
| 테스트 | 404/400/500 시나리오의 컨트롤러 테스트를 표준으로 작성해 응답 계약 회귀 방지 |
@EnableWebMvc 주의 | 이 어노테이션은 Spring Boot의 대량의 유용한 자동 설정을 비활성화하므로, 특별한 이유가 없으면 advice에 붙이지 않는다 |
잘못된 해결책 (주의)
- .에러 페이지로 포워드하는 ErrorController 예제만 붙여넣기 : 포워드를 없애지 않으면 여전히 리다이렉트/포워딩이 일어나 REST 클라이언트가 원하는 직접 응답을 못 받는다.
- 예외 타입을 일일이 나열하는 것에 그치기 :
Throwable하나로 전역 커버가 가능하다. 타입마다 나열하느라 누락이 생겨 미처리 예외가 통째로 새는 경우가 많다. - 404만
@RequestMapping("*")폴백으로 처리 :ModelAndView+error.html을 반환하므로 JSON 클라이언트에는 맞지 않는 접근이다. @EnableWebMvc를 함부로 붙이기 : 컨트롤러 예외를 잡는 데 도움이 되어 보이지만 Spring Boot 자동 설정 상당수를 무력화하므로 극도로 주의해야 한다.
DevTrace verdict
이 문제의 핵심은 404가 advice로 안 들어오는 게 아니라, Spring Boot의 정적 리소스 핸들러가 /**로 요청을 먼저 가로채기 때문에 그에 앞서 리소스 매핑을 끄고 NoHandlerFoundException을 켜야 한다는 점이다.
출처: StackOverflow — Spring Boot REST service exception handling (Question 28902374)
본문 중 “일반적인 점검 기준"으로 명시된 부분(검증 명령, 예방 조치 표)은 출처 답변을 바탕으로 정리한 것이며, 적용 보조 환경(Spring Boot 버전 등)에 따라 결과가 다를 수 있습니다.
DevTrace 결론
일관된 JSON 에러 응답의 핵심은 컨트롤러 예외는 @ControllerAdvice로, 핸들러를 찾지 못한 404는 throw-exception-if-no-handler-found 설정으로 각각 잡아내는 두 갈래 구성이라는 점이다.
원문 출처는 문제 발견의 단서이며, 위 판단과 점검 항목은 DevTrace의 독자 분석이다.