Spring 자주 발생하는 예외 GlobalExceptionHandler로 처리하기

김현찬·2025년 6월 1일

MissingPathVariableException

  • @PathVariable의 값이 URL 경로에서 누락된 경우 발생한다.
@GetMapping("/posts/{id}")
public String getPost(@PathVariable Long id) { ... }

API 사용할 경우 URL경로를 /posts/{id}로 보내야 하는데, 이 때 /posts로 보내고 id값이 누락되는 경우 MissingPathVariableException이 발생한다.

URL 경로에 변수가 누락됐을 때 발생한다.

MissingServletRequestParameterException

  • @RequestParam이 필수인데 요청에서 누락된 경우 발생한다.
@GetMapping("/search")
public String search(@RequestParam String keyword) { ... }

위 코드의 경우 @RequestParam을 사용했으므로 API호출 시 /search?keyword="??"와 같은 형식으로 보내야 한다. 이 때 keyword값이 누락된 경우 MissingServletRequestParameterException이 발생한다.

@RequestParam의 파라미터가 누락된 경우 발생한다.

MethodArgumentTypeMismatchException

  • 파라미터가 잘못된 타입으로 들어와 변환 실패한 경우에 발생한다.
@GetMapping("/posts/{id}")
public String getPost(@PathVariable Long id) { ... }

위와 같은 코드에서 /posts/{id}id값은 Long타입이다. 그러나 API 호출 시 /posts/abc이와 같이 호출한 경우 MethodArgumentTypeMismatchException이 발생한다.

요청 URL의 타입이 일치하지 않을 때 발생한다.

HttpMessageNotReadableException

  • **@RequestBody로 받은 JSON 본문이 비었거나, 파싱이 불가능할 때 발생한다.
@PostMapping("/posts")
public String create(@RequestBody PostDto dto) { ... }

위 코드는 @RequestBody를 사용하였으므로 요청 시 Body를 함께 보내주어야 한다. 이 때 요청 시 Body가 비어있거나 { title: "hello", content: }처럼 요청값이 잘못된 경우 HttpMessageNotReadableException가 발생한다.

잘못된 JSON 형식으로 요청 시 발생한다.

HttpMediaTypeNotAcceptableException

  • 클라이언트의 헤더에 맞는 타입을 서버가 제공하지 못할 때 발생한다.

예를들어 컨트롤러에선 json만 반환이 가능한데, 클라이언트에서 xml형식으로 요청을 하게 될 경우 xml을 제공할 수 없으므로 HttpMediaTypeNotAcceptableException 예외가 발생한다.

클라이언트가 요청한 형식을 서버가 지원하지 못할 때 발생한다.

NoResourceFoundException

  • **요청한 경로에 해당하는 Controller가 아예 없을 때 발생한다.
  • Spring 6부터 도입된 예외로, 이전에는 404에러만 반환했다.

프로그램의 컨트롤러 중 해당 URL을 처리할 수 있는 핸들러가 없을 때 NoResourceFoundException예외가 발생한다.

존재하지 않는 URL을 요청했을 때 발생한다.

HttpRequestMethodNotSupportedException

  • **해당 URL은 특정 HTTP 메서드만 허용하는데, 다른 메섣드로 호출한 경우 발생한다.
@GetMapping("/posts")
public String getPosts() { ... }

위 코드는 @GetMapping을 사용했기 때문에 호출 시 GET 메서드로 호출해야 한다. 이 때 클라이언트에서 GET이 아닌 다른 메서드로 API를 호출할 경우 HttpRequestMethodNotSupportedException예외가 발생한다.

호출 HTTP Method가 일치하지 않을 때 발생한다.

HttpMediaTypeNotSupportedException

  • **클라이언트가 보낸 컨텍스트 타입을 서버가 처리할 수 없을 때 발생한다.
@PostMapping("/posts")
public String create(@RequestBody PostDto dto) { ... }

위 코드의 경우 @RequestBody를 사용했으므로 JSON형태로 API를 호출해야 한다. 이 때 컨텍스트 타입이 JSON이 아닌 다른 타입일 경우 HttpMediaTypeNotSupportedException예외가 발생한다.

HTTP 본문의 형식이 서버가 처리할 수 있는 타입이 아닐 때 발생한다.

GlobalExceptionHandler

  • 위와 같은 예외들을 각각의 컨트롤러에서 처리하게 될 경우 만일 컨트롤러의 수가 수십가지 이상이라면, 수십가지 이상의 중복된 작업을 해야한다. 따라서 해당 예외들을 한 번에 처리할 수 있는 GlobalExceptionHandler를 생성하여 이곳에서 예외를 관리 및 처리한다.

  • @RestControllerAdvice 또는 @ControllerAdvice를 이용한다.

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public ResponseEntity<ErrorResponse> handleTypeMismatch(MethodArgumentTypeMismatchException ex) {
        return buildError("잘못된 타입의 파라미터입니다.", HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler(HttpMessageNotReadableException.class)
    public ResponseEntity<ErrorResponse> handleUnreadable(HttpMessageNotReadableException ex) {
        return buildError("요청 본문을 읽을 수 없습니다.", HttpStatus.BAD_REQUEST);
    }

    private ResponseEntity<ErrorResponse> buildError(String message, HttpStatus status) {
        return new ResponseEntity<>(new ErrorResponse(status.value(), message), status);
    }

    public static class ErrorResponse {
        private int status;
        private String message;

        public ErrorResponse(int status, String message) {
            this.status = status;
            this.message = message;
        }
        // getter/setter 생략 가능
    }
}

@ExceptionHandler로 처리할 예외 클래스를 지정해준 뒤, 매개변수로 입력받는다.

응답 형식을 통일하고싶다면, 다음과 같은 DTO클래스를 생성한다.

public class ErrorResponse {
    private int status;
    private String message;

    public ErrorResponse(int status, String message) {
        this.status = status;
        this.message = message;
    }
}

이 후 예외 발생 지점에 해당 기능을 예외로 던지면 된다.

0개의 댓글