Spring예외처리

통합적인 예외 처리

예외 처리 에서 알아본 내용을 기반으로 실제로 Spring Boot / Restful Api Controller 기반으로 구현

1. @RestControllerAdvice란?

@RestControllerAdvice@ControllerAdvice와 유사하지만, REST API 전용이다. 기본적으로 @ResponseBody가 포함되어 있어 예외를 JSON 형식으로 반환하며 응답 형식을 통일하고, 예외 처리를 중앙 집중화할 수 있다.


2. 예외 처리 설정

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
 
@RestControllerAdvice
public class GlobalRestExceptionHandler {
    @ExceptionHandler(Exception.class)
    public ResponseEntity<Map<String, Object>> handleAllExceptions(Exception ex){
        Map<String, Object> response = new HashMap<>();
        response.put("timestamp", LocalDateTime.now());
        response.put("message", ex.getMessage());
        response.put("status", HttpStatus.INTERNAL_SERVER_ERROR.value());
 
        return new ResponseEntity<>(response, HttpStatus.INTERNAL_SERVER_ERROR);
    }
    
    @ExceptionHandler(IllegalArgumentException.class)
    public ResponseEntity<Map<String, Object>> handleIllegalArgumentException(IllegalArgumentException ex) {
        Map<String, Object> response = new HashMap<>();
        response.put("timestamp", LocalDateTime.now());
        response.put("message", ex.getMessage());
        response.put("status", HttpStatus.BAD_REQUEST.value());
 
        return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
    }
    
    @ExceptionHandler(ResourceNotFoundException.class)
    public ResponseEntity<Map<String, Object>> handleResourceNotFoundException(ResourceNotFoundException ex) {
        Map<String, Object> response = new HashMap<>();
        response.put("timestamp", LocalDateTime.now());
        response.put("message", ex.getMessage());
        response.put("status", HttpStatus.NOT_FOUND.value());
 
        return new ResponseEntity<>(response, HttpStatus.NOT_FOUND);
    }
}

@RestControllerAdvice 어노테이션을 사용해 Handler를 설정한다.

3. REST 컨트롤러

import org.springframework.web.bind.annotation.*;
 
@RestController
@RequestMapping("/api/test")
public class TestController {
 
    @GetMapping("/null")
    public String getNullPointer() {
        throw new NullPointerException("NullPointerException 발생!");
    }
 
    @GetMapping("/illegal-argument")
    public String getIllegalArgument() {
        throw new IllegalArgumentException("IllegalArgumentException 발생!");
    }
 
    @GetMapping("/not-found")
    public String getResourceNotFound() {
        throw new ResourceNotFoundException("리소스를 찾을 수 없습니다.");
    }
 
    @PostMapping("/validate")
    public String validateInput(@RequestBody TestDTO testDTO) {
        if (testDTO.getValue() == null || testDTO.getValue().isEmpty()) {
            throw new IllegalArgumentException("값은 필수입니다!");
        }
        return "유효한 값입니다.";
    }
}
 
// 내가 직접 만든 Custom Exception 등록
@ResponseStatus(HttpStatus.NOT_FOUND)
class ResourceNotFoundException extends RuntimeException {
    public ResourceNotFoundException(String message) {
        super(message);
    }
 
}
 
// 테스트용 DTO
class TestDTO {
    private String value;
    public String getValue() {
        return value;
    }
 
    public void setValue(String value) {
        this.value = value;
    }
}

테스트용 Controller 작성

Test 결과

  • 테스트는 Postman환경을 활용했습니다.
  • RestApi이기 때문에 결과는 모두 JSON
  1. NullPointerException { “timestamp”: “2024-11-12T12:34:56”, “message”: “NullPointerException 발생!”, “status”: 500 }
  2. IllegalArgumentException
    { “timestamp”: “2024-11-12T12:35:56”, “message”: “IllegalArgumentException 발생!”, “status”: 400 }
  3. ResourceNotFoundException { “timestamp”: “2024-11-12T12:36:56”, “message”: “리소스를 찾을 수 없습니다.”, “status”: 404 }
  4. 유효성 검증 예외
    • Body {   “value”: "" }

    • 결과 {     “timestamp”: “2024-11-12T12:37:56”,     “message”: “값은 필수입니다!”,     “status”: 400 }