fix: 클라이언트 요청 오류를 500 대신 400 으로 응답 - #27
Conversation
미션 API 에 X-Device-Id 헤더를 빠뜨리면 MissingRequestHeaderException 이 핸들러에 안 잡혀 catch-all 이 500(서버 오류)으로 만들었다. 프론트가 자기 요청 문제를 서버 탓으로 오해하게 된다. 클라이언트 잘못인 예외 3종을 400 으로 명시한다. - MissingRequestHeaderException → reasons 에 빠진 헤더명 표기 - HttpMessageNotReadableException → 깨진 JSON / 빈 body - MethodArgumentTypeMismatchException → 경로/쿼리 파라미터 타입 불일치 검증: 헤더 없이 GET /api/missions/today(/status) 호출 시 400 + 빠진 헤더명 응답 확인. clean build 통과. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
📝 WalkthroughWalkthrough
Changes클라이언트 입력 예외 처리
Estimated code review effort: 2 (Simple) | ~10 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/main/java/com/example/hackathon/global/exception/GlobalExceptionHandler.java (1)
66-97: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win핵심 로직은 정확합니다.
세 핸들러 모두
ErrorCode.COMMON_ERROR_400_INVALID_INPUT으로 400을 반환하고,MissingRequestHeaderException은 헤더명,MethodArgumentTypeMismatchException은 파라미터명을reasons에 담아 PR 목적에 부합합니다.getHeaderName()/getName()모두 표준 Spring API이며 non-null이 보장되어 NPE 위험도 없습니다.다만 세 핸들러가
ErrorCode code = ErrorCode.COMMON_ERROR_400_INVALID_INPUT; return ResponseEntity.status(code.getStatus()).body(...)패턴을 반복하고 있습니다(기존handleValidation,handleMethodNotAllowed까지 포함하면 5회 중복). 공통 로직을 private 헬퍼로 추출하면 유지보수성이 개선됩니다.♻️ 중복 제거 제안
+ private ResponseEntity<ErrorResponse> badRequest(String message, HttpServletRequest request, + Map<String, String> reasons) { + ErrorCode code = ErrorCode.COMMON_ERROR_400_INVALID_INPUT; + return ResponseEntity.status(code.getStatus()) + .body(ErrorResponse.of(code, message, request.getRequestURI(), reasons)); + } + `@ExceptionHandler`(MissingRequestHeaderException.class) public ResponseEntity<ErrorResponse> handleMissingHeader(MissingRequestHeaderException e, HttpServletRequest request) { - ErrorCode code = ErrorCode.COMMON_ERROR_400_INVALID_INPUT; - return ResponseEntity.status(code.getStatus()) - .body(ErrorResponse.of(code, "필수 요청 헤더가 없습니다.", request.getRequestURI(), - Map.of(e.getHeaderName(), "필수 헤더입니다."))); + return badRequest("필수 요청 헤더가 없습니다.", request, Map.of(e.getHeaderName(), "필수 헤더입니다.")); }또한
HttpMessageNotReadableException,MissingRequestHeaderException핸들러에는handleUnexpected와 달리 로깅이 전혀 없습니다. 스택트레이스까지는 불필요하지만, 클라이언트 연동 이슈 추적을 위해log.debug/log.warn정도는 남기는 것을 고려해볼 수 있습니다.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/main/java/com/example/hackathon/global/exception/GlobalExceptionHandler.java` around lines 66 - 97, Extract the repeated 400-response construction used by handleMissingHeader, handleUnreadableBody, handleTypeMismatch, handleValidation, and handleMethodNotAllowed into a private helper that accepts the message, request URI, and reasons, then delegate each handler to it while preserving existing responses. Also add lightweight debug or warn logging in handleMissingHeader and handleUnreadableBody, without logging unnecessary stack traces.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@src/main/java/com/example/hackathon/global/exception/GlobalExceptionHandler.java`:
- Around line 66-97: GlobalExceptionHandler의 handleUnreadableBody와
handleTypeMismatch에 대한 `@WebMvcTest/MockMvc` 테스트를 추가하세요. 각 예외를 발생시키는 테스트 엔드포인트 또는
요청을 구성하고, HTTP 상태가 400인지와 응답 ErrorResponse의 에러 코드, 메시지, 요청 URI 및 필드 오류 정보가 기대값과
일치하는지 검증하세요.
---
Nitpick comments:
In
`@src/main/java/com/example/hackathon/global/exception/GlobalExceptionHandler.java`:
- Around line 66-97: Extract the repeated 400-response construction used by
handleMissingHeader, handleUnreadableBody, handleTypeMismatch, handleValidation,
and handleMethodNotAllowed into a private helper that accepts the message,
request URI, and reasons, then delegate each handler to it while preserving
existing responses. Also add lightweight debug or warn logging in
handleMissingHeader and handleUnreadableBody, without logging unnecessary stack
traces.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: e4572504-7231-46e9-ae55-d70302ef0c98
📒 Files selected for processing (1)
src/main/java/com/example/hackathon/global/exception/GlobalExceptionHandler.java
| /** | ||
| * 필수 요청 헤더 누락. 예: 미션 API 에 X-Device-Id 를 빠뜨림. | ||
| * 클라이언트 잘못이므로 400. 잡지 않으면 catch-all 이 500 으로 만든다. | ||
| */ | ||
| @ExceptionHandler(MissingRequestHeaderException.class) | ||
| public ResponseEntity<ErrorResponse> handleMissingHeader(MissingRequestHeaderException e, | ||
| HttpServletRequest request) { | ||
| ErrorCode code = ErrorCode.COMMON_ERROR_400_INVALID_INPUT; | ||
| return ResponseEntity.status(code.getStatus()) | ||
| .body(ErrorResponse.of(code, "필수 요청 헤더가 없습니다.", request.getRequestURI(), | ||
| Map.of(e.getHeaderName(), "필수 헤더입니다."))); | ||
| } | ||
|
|
||
| /** 요청 본문을 읽지 못했다. 깨진 JSON, 빈 body, 잘못된 타입 등. 클라이언트 잘못이므로 400. */ | ||
| @ExceptionHandler(HttpMessageNotReadableException.class) | ||
| public ResponseEntity<ErrorResponse> handleUnreadableBody(HttpMessageNotReadableException e, | ||
| HttpServletRequest request) { | ||
| ErrorCode code = ErrorCode.COMMON_ERROR_400_INVALID_INPUT; | ||
| return ResponseEntity.status(code.getStatus()) | ||
| .body(ErrorResponse.of(code, "요청 본문을 읽을 수 없습니다.", request.getRequestURI(), Map.of())); | ||
| } | ||
|
|
||
| /** 경로/쿼리 파라미터 타입 불일치. 예: Long 자리에 문자열. 클라이언트 잘못이므로 400. */ | ||
| @ExceptionHandler(MethodArgumentTypeMismatchException.class) | ||
| public ResponseEntity<ErrorResponse> handleTypeMismatch(MethodArgumentTypeMismatchException e, | ||
| HttpServletRequest request) { | ||
| ErrorCode code = ErrorCode.COMMON_ERROR_400_INVALID_INPUT; | ||
| return ResponseEntity.status(code.getStatus()) | ||
| .body(ErrorResponse.of(code, code.getMessage(), request.getRequestURI(), | ||
| Map.of(e.getName(), "타입이 올바르지 않습니다."))); | ||
| } | ||
|
|
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# GlobalExceptionHandler 관련 테스트 존재 여부 확인
rg -n --type=java -C2 'HttpMessageNotReadableException|MethodArgumentTypeMismatchException|MissingRequestHeaderException' --glob '*Test*.java'Repository: 8th-COKERTHON/server-team1
Length of output: 164
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# 테스트/소스 구조 확인
printf '\n== files ==\n'
git ls-files | rg '(^|/)(src/test|test)/|GlobalExceptionHandler|ErrorResponse|ErrorCode'
printf '\n== handler references ==\n'
rg -n --type=java 'GlobalExceptionHandler|HttpMessageNotReadableException|MethodArgumentTypeMismatchException|MissingRequestHeaderException' src test
printf '\n== test class names ==\n'
rg -n --type=java 'class .*Test|class .*Tests|class .*IT' src/test testRepository: 8th-COKERTHON/server-team1
Length of output: 2986
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n== handler references in tests ==\n'
rg -n --type=java 'GlobalExceptionHandler|HttpMessageNotReadableException|MethodArgumentTypeMismatchException|MissingRequestHeaderException|MockMvc|WebMvcTest|SpringBootTest' src/test
printf '\n== test file contents (short list) ==\n'
for f in $(git ls-files 'src/test/java/**/*.java'); do
printf '\n--- %s ---\n' "$f"
sed -n '1,220p' "$f"
doneRepository: 8th-COKERTHON/server-team1
Length of output: 50382
GlobalExceptionHandler의 400 예외 처리 테스트를 추가해 주세요 (GlobalExceptionHandler.java 80-90)
HttpMessageNotReadableException과 MethodArgumentTypeMismatchException은 서비스 테스트만으로는 커버되지 않으니, @WebMvcTest/MockMvc로 400 응답과 에러 바디를 검증하는 테스트가 있으면 좋겠습니다.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@src/main/java/com/example/hackathon/global/exception/GlobalExceptionHandler.java`
around lines 66 - 97, GlobalExceptionHandler의 handleUnreadableBody와
handleTypeMismatch에 대한 `@WebMvcTest/MockMvc` 테스트를 추가하세요. 각 예외를 발생시키는 테스트 엔드포인트 또는
요청을 구성하고, HTTP 상태가 400인지와 응답 ErrorResponse의 에러 코드, 메시지, 요청 URI 및 필드 오류 정보가 기대값과
일치하는지 검증하세요.
미션 API 에 X-Device-Id 헤더를 빠뜨리면 MissingRequestHeaderException 이 핸들러에 안 잡혀 catch-all 이 500(서버 오류)으로 만들었다.
예외 3종을 400 으로 명시한다.
검증: 헤더 없이 GET /api/missions/today(/status) 호출 시 400 + 빠진 헤더명 응답 확인. clean build 통과.
Summary by CodeRabbit