발단
현재 저희 프로젝트 코드잽에서는 500대 에러가 5분 동안 10번 이상 발생할 경우 그라파나의 알림 기능을 통해 다음과 같이 문제 노티를 받습니다. 로그 모니터링을 구축하면서 생각보다 500대 에러가 자주 발생되는 것을 파악할 수 있었습니다.
500대 에러가 자주 클라이언트 또는 사용자에게 노출될 경우, 서버에 대한 신뢰가 떨어질 수 있다고 생각되기 때문에 빠른 처리가 필요하다고 생각합니다. 따라서 지속적으로 발생하는 에러의 원인이 무엇인지 확인했는데요.
그렇게 확인한 모든 에러가 NoResourceFoundException였습니다.
사실 처리한 줄 알았던 NoResourceFoundException
사실 모니터링을 구축하기 전부터, NoResourceFoundException 가 자주 발생된다는 사실을 인지하고 있었는데요.
당시에 문제를 파악할 때 NoResourceFoundException가 정적 리소스가 없을 경우 발생하는 에러이며,
스프링은 "/" 즉, 루트 엔드포인트로 사용자가 접속하려 했을 경우, resources/static/ 아래에 있는 html을 찾아 핸들링하는데 이 파일이 없기 때문에 해당 발생한다라고만 인지하고 있었습니다.
따라서 해당 에러를 해결하기 위해 resources/static/ 에 404를 나타내는 html 파일을 추가해주었는데요.
이 파일을 추가하고도 지속해서 같은 에러가 발생하는 것을 알고 단순히 인덱스 파일이 없어서만 발생하는 것이 아닌 것을 알게 되었습니다.
NoResourceFoundException, 너 뭐냐
해당 에러에 대해 검색했을 때, 단지 정적 리소스가 없는 경우 발생한다 라는 설명들이 나왔는데요. 크게 와닺지 않았기 때문에 정확하게 어떤 상황일 경우 발생하는지를 보고자 하였습니다.
일단 현재 제 상황에서 에러 상황을 재연했을 때, 정확한 원인 중 하나는 존재하지 않는 엔드포인트로 요청 할 경우 발생한다는 것을 알 수 있었습니다. 그럼에도 불구하고 NoResourceFoundException가 왜 엔드포인트 문제에 대해서 발생하는지 확신할 수 없었습니다.
확실하게 에러를 400대 에러로 핸들링하기 위해서는 cause를 확인해봐야할 것 같아 문서를 까보기로 했습니다.
NoResourceFoundException는 ResourceHttpRequestHandler가 리소스를 찾지 못할 때 발생합니다.
아직까지는 모르겠지만, 하나의 힌트를 얻을 수 있는데요. 바로 ResourceHttpRequestHandler가 해당 핸들러로 작동된다는 것입니다.
언제 ResourceHttpRequestHandler가 핸들러로 작동하는지를 파악하면 조금 더 원인을 파악하는데 후보를 줄일 수 있을 것 같은데요.
그러나 정적 리소스를 요청한 적이 없는데, 왜 정적 리소스에 대한 요청을 처리하는 핸들러가 매핑되었을지 아직까지 모르겠습니다.
ResourceHttpRequestHandler
에러 상황을 재연하여 ResourceHttpRequestHandler가 매핑되기 전까지의 과정을 봐보겠습니다.
DispatcherServlet는 9개의 핸들러 매핑 중 다른 모든 핸들러 매핑(RequestMappingHandlerMapping 등)이 해당 요청을 처리할 수 있는 핸들러 매핑을 찾지 못해 결국 마지막 SimpleUrlHandlerMapping에게 가게 됩니다.
그 다음 핸들러 매핑으로 등록된 SimpleUrlHandlerMapping는 요청에 맞는 Handler를 찾는 과정을 거칩니다.
부모 클래스인 AbstractUrlHandlerMapping는 UrlHandlerMapping인만큼 엔드포인트를 기반으로 핸들러를 찾는데, 이때 4가지가 모두 ResourceHttpRequestHandler인 것을 알 수 있습니다.
즉 정리하면,
- 없는 엔드포인트로 요청하게 될 경우, DispatchServlet은 SimpleUrlHandlerMapping을 핸들러 매핑으로 지정합니다.
- 이때, SimpleUrlHandlerMapping은 보통 가장 낮은 우선순위를 가지며 따라서 다른 모든 핸들러 매핑이 실패한 후에 호출됩니다.
- SimpleUrlHandlerMapping은 엔드포인트 패턴을 기반으로 해당하는 핸들러를 찾습니다.
- SimpleUrlHandlerMapping의 모든 패턴이 ResourceHttpRequestHandler로 매핑되어 있기 때문에, 우리는 스프링이 모든 매칭되지 않은 요청을 정적 리소스로 처리하려 한다는 것을 알 수 있습니다.
결국 해당 에러는 없는 정적 파일 뿐만 아니라 없는 엔드포인트에 대해 요청하려는 경우 발생된다는 사실을 정확하게 확인할 수 있었습니다.
결론적으로 저는 404으로 핸들링할 것 같습니다.
'우아한테크코스 6기 > 3단계' 카테고리의 다른 글
우아한 스키마 관리를 위한 flyway 도입 (3) | 2024.08.13 |
---|---|
Logback MDC로 쉽게 요청 추적하기 (0) | 2024.08.11 |
팀 로깅 전략 구상기 (0) | 2024.08.05 |
Intellij Debugger, 어디까지 알고 사용하고 있나요? - 신기한 기능편 (2) | 2024.07.20 |
Intellij Debugger, 어디까지 알고 사용하고 있나요? - 기초편 (1) | 2024.07.20 |