기대와 다른 DispatcherServlet 의 동작 방식
회사에서 특정 엔드포인트에 접속 시 톰캣 에러페이지가 그대로 노출되는 이슈를 디버깅하다가 DispatcherServlet 의 기이한(?) 동작을 발견하고 왜 그런지 궁금하여 알아봤다.
컨트롤러가 바인딩되지 않은 경로로 접속을 하면 당연히 DispatcherServlet 에서 NoHandlerFoundException
이 발생할 것으로 기대했지만 실제 동작은 그렇지 않았다. 오히려 HandlerMapping 단계에서 ResourceHttpRequestHandler
라는 핸들러를 아주 잘 매핑하고 있었다.
구글링 후 아래 설정에 따라 동작이 달라진다는 사실을 알 수 있었고, 기본값은 true 다.
spring.web.resources.add-mappings: true (default)
핸들러가 없으면 Resource 경로로 매핑한다.
위 설정 하에서는 매핑되는 컨트롤러(=핸들러)가 없더라도 SimpleUrlHandlerMapping.getHandler
에 의해 ResourceHttpRequestHandler
가 매핑된다. 즉, 핸들러가 없는 /no-path-like-this
라는 경로에 요청이 들어왔을 때, 이 경로를 Resource 경로로 판단한다는 뜻이다.
물론 Resource 디렉토리에 이에 해당하는 리소스가 없을 것이니 404를 잘 응답하겠지만, 존재하지 않는 모든 경로를 Resource 경로로 인식하게 끔 하는 것은 내가 의도한 바가 아니었다.
이 같은 동작을 피하기 위해서는 spring.web.resources.add-mappings: false
로 설정하면 된다.
Why?
SimpleUrlHandlerMapping
이 생성될 때 urlMap
을 생성자로 전달 받는대, 이 값은 스프링 애플리케이션이 실행될 때 SimpleUrlHandlerMapping.registerHandlers
에 의해 SimpleUrlHandlerMapping.handlerMap
에 등록된다.
urlMap (이해를 위해 JSON 으로 표현)
1
2
3
4
5
{
"/webjars/**": "ResourceHttpRequestHandler",
"/**": "ResourceHttpRequestHandler",
"/static/**": "ResourceHttpRequestHandler"
}
따라서 런타임에 실제로 없는 경로 (e.g. /no-path-like-me
)로 요청이 들어오면 해당 경로가 /**
에 매치되면서 ResourceHttpRequestHandler
를 반환하게 되는것이다.