이번 편의 목표: 없는 번호의 조회는 404로, 검증 실패는 짧은 JSON 메시지로 응답합니다.

기본 오류 본문은 길어서 API를 호출하는 쪽이 읽기 어렵습니다. 예외를 한곳에서 응답으로 바꿉니다.

없는 책

BookService.sample은 항상 샘플을 만들었습니다. 번호를 받아 없으면 예외를 던지도록 메서드를 추가합니다. 저장소는 다음 편에 붙이므로, 이 편에서는 1번만 있다고 가정합니다.

Java
public Book find(Long id) {
    if (id == null || id != 1L) {
        throw new ResponseStatusException(
                org.springframework.http.HttpStatus.NOT_FOUND,
                "책을 찾을 수 없습니다.");
    }
    return new Book(1L, "샘플", 0);
}

import로 org.springframework.web.server.ResponseStatusException 과 HttpStatus를 가져와도 됩니다. 컨트롤러의 find는 bookService.find(id)를 반환합니다. GET /api/books/2 는 404입니다. GET /api/books/1 은 샘플입니다.

검증 오류 형식

ApiExceptionHandler.java를 추가합니다.

Java
package com.thinktree.library;

import java.util.Map;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class ApiExceptionHandler {
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<Map<String, String>> handle(
            MethodArgumentNotValidException exception) {
        String message = "입력값을 확인해 주세요.";
        if (!exception.getBindingResult().getFieldErrors().isEmpty()) {
            String field = exception.getBindingResult().getFieldErrors().get(0).getField();
            String reason = exception.getBindingResult().getFieldErrors().get(0).getDefaultMessage();
            message = field + ": " + reason;
        }
        return ResponseEntity.badRequest().body(Map.of("message", message));
    }
}

@RestControllerAdvice는 컨트롤러 밖에서 예외를 응답으로 바꿉니다. 검증 실패의 첫 필드만 메시지에 담습니다. 빈 제목으로 POST하면 상태 400과 message 필드가 있는 JSON이 옵니다.

ResponseStatusException이 만드는 404는 이 클래스에서 다시 처리하지 않습니다. 상태 코드를 이미 예외에 담아 두었기 때문입니다.

직접 해 보기

없는 번호와 빈 제목을 각각 요청해 상태 코드가 404와 400으로 다른지 확인하세요.

해설

없는 번호는 find 안의 ResponseStatusException입니다. 빈 제목은 create 호출 전에 검증이 실패하고 ApiExceptionHandler가 400을 만듭니다. 정상 요청의 상태 코드는 아직 200입니다.