본문 바로가기

Spring

[Spring] 예외 처리 종류 & 상태 코드

1. HTTP 상태 코드 (자주 쓰이는 것)

✅ 2xx (성공)

  • 200 OK : 조회 / 수정 성공, 일반적인 성공 응답
  • 201 Created : 리소스 생성 성공 (보통 Location 헤더 포함)
  • 204 No Content : 성공했지만 응답 바디 없음 (삭제 성공 등)

❌ 4xx (클라이언트 요청 문제)

  • 400 Bad Request : 요청 / 값 형식이 잘못됨(검증 실패, 타입 변환 실패, JSON 파싱 실패 등)
  • 401 Unauthorized : 인증 실패 (로그인, 토큰 없음, 만료 등)
  • 403 Forbidden : 인증은 됐지만 권한 없음 (ROLE 부족)
  • 404 Not Found : 리소스 없음 (id로 조회했는데 없음)
  • 405 Method Not Allowed : GET만 되는데 POST로 호출 시
  • 409 Conflict : 중복/충돌 (유니크 제약 위반, "이미 존재 시")
  • 415 Unsupported Media Type : Content-Type이 서버에서 지원하지 않음 (JSON 필요한데 다른 타입인 경우)

❌ 5xx (서버 문제)

  • 500 Internal Server Error : 서버 내부 예외 (예상 못한 오류 발생)
    → Exception.class 로 마지막 안전망을 통해 500 처리 및 로그 표기

 


2. 자주 만나는 예외

입력/검증 예외

  • MethodArgumentNotValidException
    • @Valid DTO 검증 실패 → 400
  • HttpMessageNotReadableException
    • JSON 파싱 실패 → 400
  • MethodArgumentTypeMismatchException
    • 파라미터 타입 변환 실패 → 400
  • MissingServletRequestParameterException
    • 필수 파라미터 누락 → 400
  • IllegalArgumentException
    • 요청 값이 유효하지 않음 (범위/형식/조건 위반 등) → 400

인증/권한 (Security)

  • AuthenticationException
    • 인증 실패 → 401
  • AccessDeniedException
    • 권한 없음 → 403

조회 / 리소스 없음

  • NoSuchElementException / EntityNotFoundException
    • 조회 실패 → 404

비즈니스 상태 충돌 / 처리 불가

  • IllegalStateException
    • 현재 상태 때문에 처리 불가 (중복 생성, 상태 충돌) → 409

DB 무결성 / 중복

  • DataIntegrityViolationException
    • UNIQUE / FOREIGN KEY 위반 등 → 409 또는 400

안전망

  • Exception
    • 예상치 못한 예외 (안전망) → 500

 


3. 🌱 Spring 에서 예외 처리 방식

1. try - catch 를 통한 직접 처리

@GetMapping("/member/{id}")
public ResponseEntity<?> getMember(@PathVariable Long id){
  try{
      return ResponseEntity.ok(service.find(id));
  } catch (NoSuchElementException e) {
      return ResponseEntity.status(HttpStatus.Not_Found).body("not found");
  }
}
  • 장점
    • 빠르게 구현 가능
  • 단점
    • 컨트롤러마다 코드 반복되고, 유지보수가 힘듦

2. @ResponseStatus 로 예외에 상태 코드 붙이기

@ResponseStatus(HttpStatus.NOT_FOUND)
public class MemberNotFoundException extends RuntimeException{
  public MemberNotFoundException(String message){
    super(message);  
  }
}
  • 장점
    • 간단함, 예외 클래스만 만들면 끝
  • 단점
    • 응답 바디 형식 (에러 DTO)을 통일하기 어렵고, 상황별 메시지 / 필드 관리가
      불편할 수 있음

3. @RestControllerAdvice + @ExceptionHandler ⭐️❗️

✅ 전역 (Global)에서 예외를 잡아 응답 형식과 상태 코드 통일

✅ 실무에서 가장 많이 쓰이는 방식 !

@RestControllerAdvice
public class GlobalExceptionHandler{

  @ExceptionHandler(NoSuchElementException.class)
    public ResponseEntity<ErrorResponseDTO> handleNoSuchElement(NoSuchElementException e) {
        return ResponseEntity.status(HttpStatus.NOT_FOUND)
                .body(new ErrorResponseDTO("NOT_FOUND", e.getMessage()));
    }

  @ExceptionHandler(IllegalArgumentException.class)
    public ResponseEntity<ErrorResponseDTO> handleBadRequest(IllegalArgumentException e) {
        return ResponseEntity.status(HttpStatus.BAD_REQUEST)
                .body(new ErrorResponseDTO("BAD_REQUEST", e.getMessage()));
    }

  @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResponseDTO> handleException(Exception e) {
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(new ErrorResponseDTO("INTERNAL_ERROR", "서버 오류가 발생했습니다."));
    }
}
  • 장점
    • 컨트롤러 코드 깔끔, 에러 응답 규격 통일, 확장 가능성 ↑
  • 단점
    • 예외 매핑 규칙을 잘 설계 필요

4. 스프링 기본 에러 처리 (BasicErrorController)

예외 처리를 따로 안 하면 스프링이 /error 로 라우팅 해서 기본 JSON 반환

  • 장점
    • 최소 설정
  • 단점
    • 서비스 규격 (에러 DTO)으로 맞추기 어려움 → 실무에서는 보통 커스터마이징
      혹은 3) 전역 예외 처리로 통일

GitHub