-
Spring Boot 고급 시리즈 3편 – 커스텀 예외와 오류 코드 설계 전략개발/Spring Boot 2025. 4. 18. 15:28728x90SMALL
Spring Boot에서 비즈니스 예외를 커스터마이징하고, 오류 코드를 통합 관리하는 방법을 소개합니다. 글로벌 예외 핸들러, ErrorCode 설계 전략까지 포함합니다.
Spring Boot 고급 시리즈 3편 – 커스텀 예외와 오류 코드 설계 전략
API 개발에서 가장 중요한 요소 중 하나는 오류를 예측 가능하고 일관성 있게 처리하는 것입니다.
이번 글에서는 Spring Boot 환경에서 커스텀 예외 처리 구조, 오류 코드 설계, 전역 예외 핸들러 구조화 방법을 실무 관점에서 정리해보겠습니다.
📌 1. 예외 처리 왜 구조화해야 하나?
- 클라이언트가 오류를 예측 가능하게 해야 함
- 프론트/앱팀과의 협업에서 통일된 에러 코드 필수
- 로그와 운영 모니터링에서도 유리
- 테스트 코드에서 예상 가능한 에러 흐름 필요
🧱 2. 핵심 설계 요소
1️⃣ ErrorCode – 오류 분류 및 메시지 관리
@Getter @AllArgsConstructor public enum ErrorCode { INVALID_INPUT_VALUE(400, "C001", "잘못된 입력입니다."), DUPLICATE_EMAIL(409, "C002", "이미 존재하는 이메일입니다."), USER_NOT_FOUND(404, "U001", "사용자를 찾을 수 없습니다."), INTERNAL_SERVER_ERROR(500, "S001", "서버 오류가 발생했습니다."); private final int status; private final String code; private final String message; }
- HTTP 상태 코드 + 시스템 코드 + 사용자 메시지로 구성
- 계층적 코드 체계 예시:
- Cxxx → Common
- Uxxx → User
- Pxxx → Post 등
2️⃣ BusinessException – 기본 커스텀 예외
@Getter public class BusinessException extends RuntimeException { private final ErrorCode errorCode; public BusinessException(ErrorCode errorCode) { super(errorCode.getMessage()); this.errorCode = errorCode; } }
- 모든 비즈니스 예외는 BusinessException을 상속받아 단일 처리 흐름을 갖습니다.
- 도메인별로 UserNotFoundException 등 하위 예외로 확장 가능
🛠️ 3. GlobalExceptionHandler – 전역 예외 핸들러 구현
@RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(BusinessException.class) public ResponseEntity<ErrorResponse> handleBusinessException(BusinessException ex) { ErrorCode code = ex.getErrorCode(); return ResponseEntity.status(code.getStatus()) .body(ErrorResponse.of(code)); } @ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntity<ErrorResponse> handleValidationException(MethodArgumentNotValidException ex) { return ResponseEntity.badRequest().body(ErrorResponse.of(ErrorCode.INVALID_INPUT_VALUE, ex)); } @ExceptionHandler(Exception.class) public ResponseEntity<ErrorResponse> handleAllUnhandled(Exception ex) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body(ErrorResponse.of(ErrorCode.INTERNAL_SERVER_ERROR)); } }
📦 4. ErrorResponse 구조 설계
@Getter @AllArgsConstructor public class ErrorResponse { private String code; private String message; private List<FieldError> errors; public static ErrorResponse of(ErrorCode code) { return new ErrorResponse(code.getCode(), code.getMessage(), List.of()); } public static ErrorResponse of(ErrorCode code, BindingResult bindingResult) { List<FieldError> fieldErrors = bindingResult.getFieldErrors().stream() .map(e -> new FieldError(e.getField(), e.getDefaultMessage())) .collect(Collectors.toList()); return new ErrorResponse(code.getCode(), code.getMessage(), fieldErrors); } }
@Getter @AllArgsConstructor public class FieldError { private String field; private String message; }
- 응답 형식은 항상 JSON 구조로 통일
- 클라이언트는 에러 코드를 기준으로 분기 처리 가능
🧠 실무 적용 팁
전략 내용
도메인별 예외 상속 UserNotFoundException extends BusinessException 형태로 구성 외부 API 에러 처리 ExternalApiException → 로그 + 사용자 메시지 분리 가능 로깅 일관성 확보 GlobalExceptionHandler 내부에 log.error() 삽입 필수 에러 코드 문서화 API 문서와 함께 .md 또는 Notion에 에러 코드 정의 공유
✅ 마무리 요약
항목 설명
오류 코드 상태코드 + 시스템코드 + 메시지 (Enum으로 관리) 예외 구조 BusinessException 중심의 단일 처리 흐름 글로벌 처리 @RestControllerAdvice로 일괄 응답 처리 응답 포맷 JSON 형식 통일, ErrorResponse 구조 설계 확장 전략 도메인별 세분화, 외부 시스템 연계 예외 포함
📌 다음 편 예고
Spring Boot 고급 시리즈 4편: Validation 고급 전략 – 커스텀 검증 애노테이션과 도메인 유효성 설계
728x90LIST'개발 > Spring Boot' 카테고리의 다른 글
Spring Boot 고급 시리즈 5편 – REST API 버전 관리 전략: 실무에 강한 설계와 적용법 (0) 2025.04.21 Spring Boot 고급 시리즈 4편 – 커스텀 Validation과 도메인 유효성 설계 전략 (0) 2025.04.21 Spring Boot 고급 시리즈 2편 – 유지보수 가능한 서비스 아키텍처 설계 전략 (0) 2025.04.18 Spring Boot 고급 시리즈 1편 – @Transactional 완전 정복: 실무 트랜잭션 설계 전략 (0) 2025.04.17 Spring Boot 시리즈 5편 – API 응답 구조 표준화 전략: 일관된 성공/실패 응답 설계하기 (0) 2025.04.17