Repository navigation
fix(backend): 잘못된 요청을 500 대신 4xx 로 응답 - #250
Merged
Merged
Conversation
권한 경계 점검 중, 본문 없는 PUT 이 404 가 아니라 500 을 내는 걸 발견했다. 파보니 권한 문제가 아니라 전역 문제였다 — GlobalExceptionHandler 에 핸들러가 없어서 네 종류의 클라이언트 오류가 전부 catch-all 로 떨어져 500 + ERROR 로그 + 스택트레이스를 남기고 있었다. 운영 로그의 SYS_INTERNAL_ERROR 11건을 세어보니 **전부 이 네 종류**였고 진짜 서버 버그는 0건이었다. 즉 실제 장애가 나도 오타 URL·봇 스캔에 묻혀 구분할 수 없는 상태다. 특히 존재하지 않는 경로(/api/nope)가 500 이라, 스캐너 한 대면 에러 로그가 오염된다. - HttpMessageNotReadableException → 400 MALFORMED_REQUEST (본문 누락·깨진 JSON) - MethodArgumentTypeMismatchException → 400 MALFORMED_REQUEST (/api/sessions/abc) - NoResourceFoundException → 404 ENDPOINT_NOT_FOUND (로그는 debug — 봇 스캔 상시) - HttpRequestMethodNotSupportedException → 405 METHOD_NOT_ALLOWED - 전부 WARN 이하로 낮춰 ERROR 는 진짜 장애만 남게 했다. 파싱 실패 응답에는 원문 예외 메시지를 싣지 않는다 — 깨진 본문 조각(사용자 입력)이 그대로 반사된다. 고정 문구만 내보내고 원인은 예외 타입으로만 로그에 남긴다. 권한 경계 자체는 이상 없었다: 타 사용자 세션·전사·피드백·문서·오디오 GET 12종이 전부 404(존재 여부도 흘리지 않음), 내부 API 는 JWT 사용자에게 403.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
발견 경위
인증·권한 경계를 점검하다가 본문 없는
PUT /api/sessions/8/messages/1/bookmark가 404 가 아니라 500 을 내는 걸 봤습니다. 권한 우회인가 싶어 파봤더니 권한 문제가 아니라 전역 문제였습니다.GlobalExceptionHandler가 처리하는 예외는 6종뿐이고, 아래 네 가지가 빠져 catch-all 로 떨어지고 있었습니다.왜 고쳐야 하는가
운영 로그의
SYS_INTERNAL_ERROR를 전수 집계했습니다:500 의 100% 가 클라이언트 오류입니다. 지금 실제 장애가 나면 오타 URL과 구분되지 않습니다. 특히 존재하지 않는 경로(
/api/nope)가 500 이라 스캐너 한 대면 에러 로그가 통째로 오염됩니다.부수적으로 클라이언트 재시도 로직도 망가집니다 — 5xx 는 보통 재시도 대상이라, 영원히 성공하지 못할 요청을 반복하게 됩니다.
변경 내용
MALFORMED_REQUEST/api/sessions/abc)MALFORMED_REQUESTENDPOINT_NOT_FOUNDMETHOD_NOT_ALLOWED로그 레벨도 낮췄습니다 —
NoResourceFoundException은debug(봇 스캔이 상시 들어옵니다), 나머지는warn. ERROR 에는 진짜 장애만 남습니다.파싱 실패 응답에 원문 예외 메시지를 싣지 않습니다.
HttpMessageNotReadableException의 메시지에는 깨진 본문 조각(= 사용자 입력)이 들어오기 때문에 그대로 돌려주면 입력 반사가 됩니다. 고정 문구만 내보내고 원인은 예외 타입으로만 로그에 남깁니다. 회귀 테스트로 고정했습니다.권한 경계 점검 결과 (이 PR 의 계기, 문제 없음)
user3 로그인 상태에서 다른 사용자(1·2·4·5) 리소스에 읽기만 시도했습니다:
SESSION_NOT_FOUNDDOC_NOT_FOUND/api/internal/*(JWT 사용자로)ACCESS_DENIED403 이 아니라 404 로 존재 여부조차 흘리지 않는 점이 좋았습니다.
/actuator/**는 Spring 설정상 permitAll 이지만 nginx 가 백엔드로 프록시하지 않아 외부 노출은 없고, 노출 대상도health,info로 제한돼 있습니다.테스트
GlobalExceptionHandlerClientErrorTest6건 — 네 예외의 상태·코드, 입력 반사 방지, 전 코드 4xx 보장./gradlew test— 실패는 Testcontainers 5개 클래스뿐(로컬 Docker 없음, 이 변경과 무관, CI 에서 검증)영향 범위
리뷰어 체크포인트
NoResourceFoundException을debug로 낮췄습니다. 404 를 아예 안 보고 싶지 않다면info가 나을 수 있습니다.🤖 Generated with Claude Code