Exception 처리
Web Application 의 입장에서 바라 보았을 때, 에러가 났을 때 내려줄 수 있는 방법은 많지 않다.
1. 에러 페이지
2. 4XX Error or 5XX Error
3. Client가 200 외에 처리를 하지 못 할 때는 200을 내려주고 별도의 에러 Message 전달
Web Application 입장에서는 이런 에러는 try-catch로 그 때 마다 묶는 것이 아닌 한 곳에 모아서 처리하는게 제일 편리하다.
@ControllerAdvice | Global 예외 처리 및 특정 package / Controller 예외 처리 |
@ExceptionHandler | 특정 Controller의 예외처리 |
package com.example.exception.advice;
import org.springframework.http.HttpStatus;
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 GlobalControllerAdvice {
@ExceptionHandler(value = Exception.class)
public ResponseEntity exception(Exception e) {
System.out.println(e.getClass().getName());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("");
}
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public ResponseEntity methodArgumentNotValidException(MethodArgumentNotValidException e) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
}
}
@RestControllerAdvice 어노테이션을 설정 함으로써 Spring Web Application 안의 Exception 들을 Global하게 처리할 수 있다. basePackages 값을 넣으면 원하는 패키지도 설정이 가능하다.
@ExceptionHandler 를 이용하여 특정 Exception에 대해 어떤 처리를 할 지 메소드를 정의할 수 있다.
package com.example.exception.controller;
import com.example.exception.dto.User;
import jakarta.validation.Valid;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api")
public class ApiController {
@PostMapping("/post")
public User post(@Valid @RequestBody User user) {
return user;
}
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public ResponseEntity methodArgumentNotValidException(MethodArgumentNotValidException e) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
}
}
컨트롤러 내부에 @ExceptionHandler가 정의되어 있는 경우 @RestControllerAdvice, @ControllerAdvice 등 보다 우선순위가 높아 컨트롤러 개별 Exception 처리를 할 수 있다.
추가 예제 (Validation & Exception)
package com.example.exception.controller;
import com.example.exception.dto.User;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api")
@Validated
public class ApiController
@GetMapping("/get")
public User get(
@NotNull
@Size(min=2)
@RequestParam String name,
@NotNull
@Min(1)
@RequestParam Integer age) {
User user = new User();
user.setName(name);
user.setAge(age);
int a = 10+age;
return user;
}
@PostMapping("/post")
public User post(@Valid @RequestBody User user) {
return user;
}
}
@Validated은 스프링에서 제공하는 어노테이션으로써 컨트롤러뿐만 아니라 서비스, 레포지토리 등 모든 계층의 그룹 기반 유효성 검증을 지원한다. 예외로 ConstraintViolationException 발생한다.
package com.example.exception.dto;
public class Error {
private String field;
private String message;
private String invalidValue;
// get/set , toString 메소드 생략
}
package com.example.exception.dto;
import java.util.List;
public class ErrorResponse {
String statusCode;
String requestUrl;
String code;
String message;
String resultCode;
List<Error> errorList;
// get/set , toString 메소드 생략
}
package com.example.exception.advice;
import com.example.exception.controller.ApiController;
import com.example.exception.dto.Error;
import com.example.exception.dto.ErrorResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ConstraintViolationException;
import jakarta.validation.ElementKind;
import jakarta.validation.Path;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.ArrayList;
import java.util.List;
@RestControllerAdvice(basePackageClasses = ApiController.class)
public class ApiControllerAdvice {
@ExceptionHandler(value = MissingServletRequestParameterException.class)
public ResponseEntity missingServletRequestParameterException(MissingServletRequestParameterException e, HttpServletRequest request) {
List<Error> errorList = new ArrayList<>();
String fieldName = e.getParameterName();
String invalidValue = e.getMessage();
Error errorElement = new Error();
errorElement.setField(fieldName);
errorElement.setMessage(invalidValue);
errorList.add(errorElement);
ErrorResponse errorResponse = new ErrorResponse();
errorResponse.setErrorList(errorList);
errorResponse.setMessage("missingServletRequestParameterException");
errorResponse.setRequestUrl(request.getRequestURI());
errorResponse.setStatusCode(HttpStatus.BAD_REQUEST.toString());
errorResponse.setResultCode("FAILED");
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse);
}
}
컨트롤러에서 validation 단계에서 exception이 발생한 경우 위와 같이 클라이언트의 잘못된 요청에 대하여 원하는 응답을 내려줄 수 있다.
{
"statusCode": "400 BAD_REQUEST",
"requestUrl": "/api/get",
"code": null,
"message": "constraintViolationException",
"resultCode": "FAILED",
"errorList": [
{
"field": "age",
"message": "1 이상이어야 합니다",
"invalidValue": "0"
}
]
}
'Spring' 카테고리의 다른 글
서블릿 컨테이너와 Spring 컨텍스트 (0) | 2025.04.15 |
---|---|
Filter (1) | 2025.04.15 |
Validation (0) | 2025.03.24 |
Java Bean 규약 (0) | 2025.03.24 |
AOP 관점지향 프로그램 (0) | 2025.03.23 |