예외 · 에러 코드 · 로깅
본 문서는 오류를 표현하고 바깥으로 내보내는 방식, 그리고 기록 범위를 정의합니다. HTTP 응답 형식 자체는 API 설계 3절이 소유합니다.
1. 예외
비즈니스 오류는 항상 예외로 던집니다. 에러 코드를 담은 BusinessException 계열을 쓰며 컨트롤러가 ResponseEntity 를 직접 조립하지 않습니다. @RestControllerAdvice 전역 핸들러가 RFC 7807 응답으로 변환합니다.
예외의 배치는 성격으로 가릅니다.
| 성격 | 자리 | 예 |
|---|---|---|
| 불변식 위반 | domain |
형식에 맞지 않는 값으로 값 객체를 만들려는 시도 |
| 흐름과 정책 실패 | 해당 피쳐 | 시도 횟수 초과, 만료된 대기 상태 |
2. 에러 코드
컨텍스트별 열거형으로 정의하고 문자열은 <컨텍스트>_<사유> 형태를 따릅니다.
접두사 등록부는 코드입니다. 열거형 집합이 등록부 역할을 하므로 새 컨텍스트를 추가할 때 기존 것과 접두사가 겹치는지만 확인합니다. 등록부를 문서로 따로 두면 두 벌이 되고 한쪽이 낡습니다.
사유를 구분하되 열거 가능성을 만들지 않습니다. 대상이 없는 경우와 값이 틀린 경우를 다른 코드로 나누면 그 차이가 곧 존재 여부를 알려 주는 신호가 됩니다. 존재하지 않는 대기 레코드와 틀린 코드가 같은 AUTH_OTP_INVALID 인 것이 그 적용 예입니다.
하이픈은 요구사항 식별자, 언더스코어는 에러 코드로 구분을 강제합니다. 추적성 검색이 오염되지 않게 하기 위함입니다.
3. 로깅
| 구분 | 준수 지침 (Do) | 금지 지침 (Don't) |
|---|---|---|
| 요청 추적 | MDC 의 traceId 와 응답 헤더 X-Trace-Id 로 상관관계를 잇습니다 |
추적 식별자를 키나 식별자 용도로 씁니다 |
| 기록 대상 | 식별자만 기록합니다 | 비밀번호 · 토큰 · 개인정보 원문을 기록합니다 |
주의
수신자를 남겨야 하는 자리도 마스킹합니다
메일 발송 실패처럼 누구에게 실패했는지를 남겨야 하는 경우에도 주소를 가립니다. 가리지 않으면 발송 장애가 곧 가입자 목록이 로그로 새는 경로가 됩니다. 사고가 났을 때 한 번에 대량으로 새므로 평시에는 드러나지 않습니다.
마스킹 대상 목록의 원본은 설정입니다. 민감 필드를 가진 DTO나 검색 파라미터를 추가하면 그 목록을 함께 갱신합니다.
4. 필터 단계의 오류
전역 핸들러는 컨트롤러 진입 이전, 즉 필터 단계의 오류를 잡지 못합니다.
필터에서 실패를 내보내야 하면 RFC 7807 본문을 직접 쓰는 전용 컴포넌트(ProblemResponseWriter)를 경유합니다. 그러지 않으면 컨테이너 기본 오류 페이지가 나가 형식이 갈리고, 프론트엔드가 그 경로만 다르게 처리하게 됩니다(보안 검토 F7).
인증과 인가 실패가 대부분 이 경로로 나가므로 가장 자주 마주치는 응답이 형식에서 빠지는 상황이 됩니다.