문제 정의

검증 환경

본 절은 원문 사례·문서에 등장한 버전/도구를 정리한 것이다. 별도 실험실 재현이 명시되지 않은 항목은 일반화하지 않는다.

항목본문·원문에서 확인된 범위
런타임/도구Spring Boot 1.x
런타임/도구Spring Boot 2.x
런타임/도구Spring Boot 1.1.5
문서 정리일2026-09-02
  • OS/CI 세부 값은 프로젝트마다 다르므로, 적용 전 로컬에서 동일 오류 메시지를 재확인한다.

Spring Boot 애플리케이션에서 기본 Whitelabel 에러 페이지(하얀 배경의 기본 에러 화면)를 제거하기 위해, /error 경로에 사용자 컨트롤러 매핑을 직접 추가했다가 애플리케이션이 시작조차 되지 않는 상황이 발생한다.

1
2
3
4
5
6
7
8
9
@RestController
public class IndexController {

    @RequestMapping(value = "/error")
    public String error() {
        return "Error handling";
    }

}

이 코드를 추가하자 컨텍스트 초기화가 실패하며 아래와 같은 예외가 발생한다.

에러 원문: Spring Boot Remove Whitelabel Error Page 출처: StackOverflow 25356781

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
Exception in thread "AWT-EventQueue-0"
org.springframework.beans.factory.BeanCreationException: Error creating bean with name
'requestMappingHandlerMapping' defined in class path resource
[org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration.class]:
Invocation of init method failed; nested exception is java.lang.IllegalStateException:
Ambiguous mapping found. Cannot map 'basicErrorController' bean method
public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>>
org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
to {[/error],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}:
There is already 'indexController' bean method

핵심 오류 메시지는 두어 가지로 요약된다.

  • Ambiguous mapping found — 동일한 /error URL에 매핑된 핸들러가 중복됨
  • Cannot map 'basicErrorController' bean method — 스프링 부트가 자동 등록한 BasicErrorController를 매핑할 수 없음
  • There is already 'indexController' bean method — 이미 indexController가 같은 경로를 점유한 상태

표면 증상

구분내용
발생 시점컨텍스트 초기화 단계 (requestMappingHandlerMapping 빈 생성)
에러 유형BeanCreationExceptionIllegalStateException
대상 버전Spring Boot 1.x 기준으로 널리 보고됨, 이후 버전에서도 같은 패턴 재현 가능
주요 메시지Ambiguous mapping found. Cannot map 'basicErrorController' bean method ... to {[/error]}
즉시 확인 방법애플리케이션 기동 로그에서 “Ambiguous mapping found” 문자열 검색

중요한 것은, error.whitelabel.enabled=false(또는 server.error.whitelabel.enabled=false)를 설정해도 이 에러는 계속 발생한다는 점이다. 원글 질문자도 이 설정을 추가한 뒤 “still getting the same error"라고 하였다.

이것이 표면 증상과 근본 원인이 갈라지는 핵심 지점이다. Whitelabel 페이지를 끄는 것과 /error 매핑을 사용자가 점유하는 것은 서로 별개의 문제다.

원인 탐구

/error에 직접 @RestController 매핑을 추가하면 예외가 발생할까?

Spring Boot는 에러 처리를 다음 구조로 자동 구성한다.

  1. ErrorMvcAutoConfiguration(스프링 부트 자동 구성 클래스)이 basicErrorController라는 빈을 등록한다.
  2. 이 빈은 ErrorController 인터페이스의 구현체이며, 기본적으로 /error 경로에 매핑된다.
  3. 결과적으로 /error는 Spring Boot가 이미 “예약"한 URL 스페이스다.

여기에 사용자가 같은 /error@RequestMapping을 추가하면, Spring MVC의 RequestMappingHandlerMapping은 같은 URL 조건 {[/error]}에 대해 두 개의 후보 핸들러를 발견하게 된다. 두 개 중 어느 것을 우선해야 할지 결정할 수 없으므로 Ambiguous mapping 예외를 던진다.

즉, 에러가 발생하는 시점은 원하는 “에러 페이지 제거” 가 아니라, 매핑 조건 충돌 때문이다.

1
2
3
4
/error  ──▶ BasicErrorController (Spring Boot가 자동 등록, ErrorController 구현체)
/error  ──▶ IndexController.error()   (사용자가 직접 매핑)
   RequestMappingHandlerMapping: 두 핸들러 겹침 → Ambiguous mapping

근본 원인 분석

근본 원인은 매우 단순하다. Spring Boot는 사용자가 ErrorController 인터페이스의 구현체를 지정하지 않으면 BasicErrorController를 자동으로 /error에 등록한다.

공식 출처(StackOverflow 채택 답변)가 명시적으로 안내한다.

Your code did not work, because Spring Boot automatically registers the BasicErrorController as a Spring Bean when you have not specified an implementation of ErrorController. (코드가 동작하지 않은 이유는, 당신이 ErrorController 구현체를 지정하지 않았기 때문에 Spring Boot가 BasicErrorController를 자동으로 스프링 빈으로 등록했기 때문이다.) — StackOverflow 25356781 채택 답변

실제 구현은 ErrorMvcAutoConfiguration.basicErrorController(Spring Boot 1.x 기준 org/springframework/boot/autoconfigure/web/ErrorMvcAutoConfiguration.java)에서 확인할 수 있다. 이 자동 구성은 조건부로 동작한다 — 사용자 정의 ErrorController 빈이 없을 때만 BasicErrorController를 만든다.

그래서 사용자가 /error에 임의의 @RequestMapping을 추가하면, 자동 등록된 BasicErrorController와 “같은 URL / 서로 다른 빈"이라는 중복 매핑 상태가 만들어져 Spring MVC가 시작에 실패한다.

원인 후보 매트릭스

원인 후보확인 방법해당 시 예상 증상해결 방향
/error에 임의 컨트롤러 매핑으로 BasicErrorController와 중복빈 정의 소스에서 /error 매핑 검색시작 시 Ambiguous mapping ... basicErrorControllerErrorController 구현 + getErrorPath() 반환
whitelabel.enabled=false 만으로 해결하려 함application.properties/application.yml 설정 확인Whitelabel은 꺼지지만 /error가 여전히 점유되어 매핑 충돌 지속/error를 직접 매핑할 때는 ErrorController 구현 필요
자동 구성 전체 제외 (ErrorMvcAutoConfiguration 배제)@SpringBootApplication(exclude=...) 또는 spring.autoconfigure.exclude 확인BasicErrorController가 사라져 매핑 충돌은 해결되나, 서블릿 컨테이너 기본 에러 페이지로 대체됨JSON/일관 응답이 목적이 아니면 보류
서버 기동 불가(= 빈 생성 실패)로그에서 requestMappingHandlerMapping 생성 실패 확인/error 충돌 외 다른 핸들러 매핑도 실패근본 중복 제거 후 전체 빈 재검증

코드 해결책

정석 해결책은 /error를 점유할 컨트롤러가 ErrorController 인터페이스를 구현하고 getErrorPath()를 반환하게 하는 것이다. 그러면 사용자 컨트롤러가 스프링 부트의 자동 등록 BasicErrorController대체하게 되어 중복 매핑이 사라진다.

StackOverflow 채택 답변의 코드:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
@RestController
public class IndexController implements ErrorController {

    private static final String PATH = "/error";

    @RequestMapping(value = PATH)
    public String error() {
        return "Error handling";
    }

    @Override
    public String getErrorPath() {
        return PATH;
    }

}

개념적으로는 BasicErrorController가 수행하던 역할(에러 페이지 렌더링/응답)을 이제 IndexControllerErrorController 구현체로서 대신 수행하게 된다. Spring Boot는 사용자 정의 ErrorController 빈이 존재하므로 자동 구성에서 BasicErrorController를 더 이상 생성하지 않는다.

JSON 응답이 필요할 때 (버전별 주의)

REST API처럼 JSON 형태의 에러 응답을 원한다면 ErrorAttributes를 주입받아 에러 정보를 반환하는 방식이 일반적이다. 단, Spring Boot 버전 1.x와 2.x는 패키지명과 시그니처가 다르다는 점을 주의해야 한다(원문의 여러 답변이 이를 지적한다).

  • Spring Boot 1.x: org.springframework.boot.autoconfigure.web.ErrorController, org.springframework.boot.autoconfigure.web.ErrorAttributes, getErrorAttributes(RequestAttributes, boolean)
  • Spring Boot 2.x: org.springframework.boot.web.servlet.error.ErrorController, org.springframework.boot.web.servlet.error.ErrorAttributes, getErrorAttributes(WebRequest, ErrorAttributeOptions)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Spring Boot 2.x 기준 JSON 에러 응답 예시 (원문 답변 참조)
@RestController
public class CustomErrorController implements ErrorController {

    private static final String PATH = "/error";

    @Autowired
    private ErrorAttributes errorAttributes;

    @RequestMapping(PATH)
    public Map<String, Object> error(WebRequest request, HttpServletResponse response) {
        return getErrorAttributes(request, true);
    }

    private Map<String, Object> getErrorAttributes(WebRequest request, boolean includeStackTrace) {
        ErrorAttributeOptions options = ErrorAttributeOptions.defaults()
                .including(ErrorAttributeOptions.Include.MESSAGE)
                .including(ErrorAttributeOptions.Include.EXCEPTION)
                .including(ErrorAttributeOptions.Include.BINDING_ERRORS);
        if (includeStackTrace) {
            options = options.including(ErrorAttributeOptions.Include.STACK_TRACE);
        }
        return this.errorAttributes.getErrorAttributes(request, options);
    }
}

잘못된 해결책

잘못된 접근왜 권장하지 않는가
error.whitelabel.enabled=false / server.error.whitelabel.enabled=false 만 설정Whitelabel 페이지는 사라질 수 있으나 /error 엔드포인트 자체는 그대로 매핑되어 중복 매핑 충돌은 해결되지 않는다. 원문 질문자의 후속 코멘트가 이를 확인해준다.
@SpringBootApplication(exclude = {ErrorMvcAutoConfiguration.class}) 또는 spring.autoconfigure.exclude로 자동 구성 전체 제외BasicErrorController는 사라져 충돌은 없어지지만, 예외 발생 시 서블릿 컨테이너(Tomcat 등)의 기본 에러 페이지가 대신 표시된다. 원문 답변도 “doing so will probably cause servlet container’s whitelabel pages to show up instead"라고 경고한다.

검증 명령

해결 후 애플리케이션이 정상 기동하는지, 그리고 /error 응답이 기대대로 오는지 명령으로 확인한다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# 1) Maven 기반 프로젝트 실행
./mvnw spring-boot:run

# 2) Gradle 기반 프로젝트 실행
./gradlew bootRun

# 3) 애플리케이션이 기동된 상태에서 /error 응답 검증
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8080/error
# 예상: 컨트롤러가 반환하는 상태(기본 200 또는 설정에 따라 다름)

# 4) Whitelabel이 사라졌는지 확인 (존재하지 않는 경로로 에러 유발)
curl -s -i http://localhost:8080/nonexistent-path
# Whitelabel HTML이 아니라 설정한 응답(문자열/JSON/에러 페이지)이 반환되어야 정상

추가로, 기동 로그에서 기존의 Ambiguous mapping found 예외가 더 이상 나타나지 않는지 확인한다.

1
2
3
# 기동 로그에서 매핑 충돌 문자열이 검색되지 않아야 함
./mvnw spring-boot:run | grep -i "Ambiguous mapping"
# 출력이 없으면 해결 완료

향후 예방 조치

같은 에러를 다시 만나지 않기 위한 체크리스트.

  1. /error는 스프링 부트가 예약한 경로다 — 사용자 컨트롤러로 /error를 다룰 때는 반드시 ErrorController 인터페이스를 구현하고 getErrorPath()를 반환한다.
  2. Whitelabel 제거와 /error 점유를 구분한다server.error.whitelabel.enabled=false는 페이지를 끄는 기능이고, /error URL을 비우는 기능이 아니다. /error를 완전히 다른 경로로 옮기려면 server.error.path=/error-spring처럼 경로를 재지정하면 된다(원문 답변 참조).
  3. 버전별 API를 확인한다ErrorController/ErrorAttributes 패키지와 메시지가 Spring Boot 1.x/2.x에서 다르므로, 의존성 버전을 먼저 확인한 뒤 해당 시그니처를 사용한다.
  4. 빈 매핑 점검 — 새 컨트롤러 추가 후에는 requestMappingHandlerMapping 단계에서 매핑 중복이 없는지 기동 로그로 확인한다.
  5. 자동 구성 배제는 신중히ErrorMvcAutoConfiguration 전체를 배제하면 예상치 못한 서블릿 컨테이너 에러 페이지가 등장할 수 있으므로, 목적이 분명하지 않으면 피한다.

실전 적용 체크

환경확인 사항
로컬curl/error 및 존재하지 않는 경로 응답 확인, 기동 로그에 Ambiguous 없음
CI빌드 단계에서 테스트가 /error 매핑을 검증하는지 확인 (컨텍스트 로드 실패로 빌드가 깨지지 않는지)
프로덕션에러 응답 포맷이 클라이언트/모니터링과 일치하는지, 서블릿 컨테이너 기본 페이지로 회귀하지 않는지 확인

DevTrace 결론

/error를 직접 매핑할 때 발생하는 Ambiguous mapping의 핵심은 “Whitelabel 페이지를 끄는 설정"이 아니라, ErrorController 구현체를 지정하지 않아 Spring Boot가 자동 등록한 BasicErrorController가 같은 URL을 점유하고 있다는 점이다. 즉, ErrorController 인터페이스 구현 + getErrorPath() 반환으로 자동 등록 빈을 대체하면 충돌이 해결된다.

원문 출처는 문제 발견의 단서이며, 위 판단과 점검 항목은 DevTrace의 독자 분석이다.


DevTrace verdict/error를 직접 매핑할 때 발생하는 Ambiguous mapping의 핵심은 “Whitelabel 페이지를 끄는 설정"이 아니라, ErrorController 구현체를 지정하지 않아 Spring Boot가 자동 등록한 BasicErrorController가 같은 URL을 점유하고 있다는 점이다. 즉, ErrorController 인터페이스 구현 + getErrorPath() 반환으로 자동 등록 빈을 대체하면 충돌이 해결된다.


출처:

참고: 위 표와 체크리스트 중 “로그 검색”, “curl 응답 상태”, “CI/프로덕션 점검” 항목은 원문 출처에 명시되지 않은 일반적인 점검 기준으로, 프로젝트 환경에 따라 일부 조정이 필요할 수 있습니다.