스프링 부트가 제공하는 기본 예외 처리 기능을 사용하기 위해 resources/templates/error 디렉토리에 404.html과 4xx.html을 모두 만들어 두었다. 이 정도면 400번대 모든 에러에 철저히 대비했다고 생각했는데, 실습 중 /error-401에 접속하자 기대했던 4xx.html 화면 대신 404.html이 나타났다.
파일은 모두 준비되어 있는데, 왜 스프링은 하필 404를 선택했을까? 그 내부 동작의 우선순위를 정리해 보았다.
분명 400번대 에러를 통합 처리하는 4xx.html이 있음에도 불고하고, 컨트롤러에 등록되지 않은 주소인 /error-401로 접속했을 때 브라우저는 404.html을 선택해서 보여주었다.
이 현상의 핵심은 요청이 컨트롤러(핸들러)에 닿았느냐에 있다. 에러 페이지가 결정되는 과정은 단순히 에러 번호만 보는 것이 아니라, 다음과 같은 논리 구조를 가진다.
error 디렉토리에서 가장 구체적인 404.html을 찾아 보여준다.response.sendError(401) 처럼 명시적으로 에러 코드를 발생시켜야 비로소 우리가 의도한 401 응답이 확정된다.401.html이 없으면 차선책인 4xx.html을 찾아 보여주게 된다.결국 내 실습에서 404.html이 뜬 이유는, /error-401이라는 주소 자체가 서버 입장에선 "권한이 없는 페이지"이기 이전에 "없는 페이지"였기 때문이다.
결론적으로 resources/templates/error 내의 파일들이 선택되는 기준은 다음과 같다.
404.html 호출4xx, 5xx)의 html 호출이 원리는 400번대뿐만 아니라 모든 예외 처리 프로세스에 공통으로 적용된다. 특정 에러 화면을 테스트하고 싶다면, 반드시 해당 URL이 매핑된 컨트롤러가 있어야 하며, 그 내부에서 에러 코드를 명시적으로 던져야 한다.
단순히 "파일만 만들어 두면 에러 페이지가 알아서 매핑되겠지"라는 추측에서 벗어나, 에러 코드 결정보다 'URL 매칭'이 선행된다는 스프링 MVC의 기본적인 우선순위를 정리해 보았다.
404.html과 4xx.html이 모두 준비되어 있더라도, 요청이 컨트롤러에 닿지 못하면 결국 404가 선택될 수밖에 없다.
이번 정리를 통해 에러 페이지가 호출되는 두 가지 갈래를 명확히 구분할 수 있게 되었고, 자동 설정 뒤에 숨은 핸들러 매핑의 논리를 다시 한 번 확인할 수 있었다.