티스토리 뷰
0. BasicErrorController
:: Spring 기본 에러 처리 (페이지 기반)
:: 적절한 ExceptionResolver가 없으면 해당 컨트롤러를 통해 기본적인 에러 페이지를 제공
:: 에러 발생 시, 기본적으로 /error로 에러 요청을 다시 전달 - WebMvcAutoConfiguration를 통한 기본 설정
- BasicErrorController까지의 흐름도
:: 출처: https://mangkyu.tistory.com/204 [MangKyu's Diary:티스토리]
WAS(톰캣) -> 필터 -> 서블릿(디스패처 서블릿) -> 인터셉터 -> 컨트롤러
컨트롤러(예외발생) -> 인터셉터 -> 서블릿(디스패처 서블릿) -> 필터 -> WAS(톰캣)
WAS(톰캣) -> 필터 -> 서블릿(디스패처 서블릿) -> 인터셉터 -> 컨트롤러(BasicErrorController)
- 예외 처리는 위 설명한 순서대로 ExceptionResolver 들을 순회하며 처리하고
- 적한 ExceptionResolver 가 없으면 마지막으로는 예외가 서블릿까지 전달
- 서블릿은 SpringBoot 가 진행한 자동 설정에 맞게 BasicErrorController 로 요청을 다시 전달
1. 스프링이 제공하는 예외 처리 방법들
:: try-catch를 모든 코드에 붙이는 것은 비효율적
:: 예외 처리를 위한 try-catch 사용하지 않고, 공통 관심사(cross-cutting concerns)를 메인 로직에서 분리
:: 이를 적용한 HandlerExceptionResolver를 만듦
1.0 HandlerExceptionResolver의 개념
:: 예외 처리 전략을 추상화한 인터페이스 (전략 패턴 Strategy)
:: 대부분의 경우 Exception을 catch하고 HTTP 상태나 응답 메세지 등을 설정
:: WAS 입장에서는 요청에 대한 정상적인 응답으로 인식
HandlerExceptionResolver 구현체 종류 | 역할 |
1) DefaultErrorAttributes | - 예외 속성(statuts, message)등 저장 - 직접 예외를 처리는 X |
2) ExceptionHandlerExceptionResolver | - @ ExceptionHandler를 사용해 예외처리 전략 제공 - 특정 예외를 처리하기 위한 메서드를 지정하여 해당 예외가 발생했을 때, 이 메서드로 예외를 전달 |
3) ResponseStatusExceptionResolver | - @ResponseStatus 또는 ResponseStatusException을 활용해 특정 HTTP 상태 코드와 함께 예외를 처리 - 예외 클래스에 @ResponseStatus 어노테이션을 사용하면, 해당 예외 발생 시 자동으로 특정 HTTP 상태 코드와 메시지를 반환 |
4) DefaultHandlerExceptionResolver | - Spring 내부에서 발생하는 예외들, 예를 들어 NullPointerException, TypeMismatchException 등의 기본적인 예외들을 처리 - Spring에서 미리 정의된 예외에 대해 자동으로 처리해주는 기본 처리기 |
- HandlerExceptionResolverComposite
:: 직접 예외를 처리하지 않는 DefaultErrorAttributes를 제외한 2,3,4들을 모아서 관리
2. ExceptionResolver의 예외 처리 방법
2. 1 @ResponseStatus
:: Spring에서 예외 발생 시, 해당 예외에 대한 HTTP Status Code 설정 가능
:: 예외 발생 시, 자동으로 HTTP Response Code와 메세지 설정
단점 | 1. 응답 내용(Payload) 수정 불가능 2. 예외 클래스 단위로 상태를 정의하여, 예외 클래스 내 세부적으로 HTTP 상태 분기 불가능 3. 같은 예외 클래스 내 별도의 응답 상태가 필요하다면 예외 클래스를 그 수에 맞춰 추가 |
- 적용가능한 경우
- Exception 클래스 자체
- 메서드에 @ExceptionHandler와 함께
- 클래스에 @RestControllerAdvice와 함께
- EX) value를 통해 해당 응답 코드를 지정 가능
@ResponseStatus(value = HttpStatus.NOT_FOUND) // ResourceNotFoundException 발생 시, HTTP 404 반환
public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException(String message) {
super(message);
}
}
2. 2 @ResponseStatusException
::외부 라이브러리에서 정의한 코드에 수정 X이기 때문 @ResponseStatus 적용 X
:: @ResponseStatus의 대체로 spring 5부터 사용가능
:: 예외 발생과 동시에 예외에 해당하는 에러 HTTP 상태를 동시에 정의
- ResponseStatusException은 HttpStatus와 메시지를 생성자에 전달하여 예외를 던짐
- @ResponseStatus와는 달리 동적으로 예외 메시지나 상태 코드를 설정
- EX)
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Item Not Found");
@GetMapping("/product/{id}")
public ResponseEntity<Product> getProduct(@PathVariable String id) {
try {
return ResponseEntity.ok(productService.getProduct(id));
} catch (NoSuchElementFoundException e) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Item Not Found");
}
}
// 출처: https://mangkyu.tistory.com/204 [MangKyu's Diary:티스토리]
2. 3 @ExceptionHandler
:: 전체 방법론 중 가장 유연한 예외처리 방식
- 적용가능한 경우
- 컨트롤러의 메소드
- @ControllerAdvice나 @RestControllerAdvice가 있는 클래스의 메소드
- EX)
@Controller
public class MyController {
@ExceptionHandler(ResourceNotFoundException.class) // 해당 예외 발생 시
public String handleResourceNotFound(ResourceNotFoundException ex) {
// 예외 처리 로직
return "errorPage";
}
}
@RestController
@RequiredArgsConstructor
public class ProductController {
private final ProductService productService;
@GetMapping("/product/{id}")
public Response getProduct(@PathVariable String id){
return productService.getProduct(id);
}
@ExceptionHandler(NoSuchElementFoundException.class)
public ResponseEntity<String> handleNoSuchElementFoundException(NoSuchElementFoundException exception) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(exception.getMessage());
}
}
// 출처: https://mangkyu.tistory.com/204 [MangKyu's Diary:티스토리]
2. 4 @ControllerAdvice, @RestControllerAdvice
:: 전역 예외처리를 담당하는 클래스에 사용하는 어노테이션
:: 애플리케이션 내 모든 컨트롤러에서 발생하는 예외를 하나의 클래스로 처리 가능
- @RestControllerAdvice == RESTful API에서 주로 사용(JSON 등의 값)
- EX)
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<String> handleResourceNotFound(ResourceNotFoundException ex) {
return new ResponseEntity<>(ex.getMessage(), HttpStatus.NOT_FOUND);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<String> handleGeneralException(Exception ex) {
return new ResponseEntity<>("An unexpected error occurred", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@RestControllerAdvice
public class GlobalRestExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<Map<String, String>> handleResourceNotFound(ResourceNotFoundException ex) {
Map<String, String> response = new HashMap<>();
response.put("error", ex.getMessage());
return new ResponseEntity<>(response, HttpStatus.NOT_FOUND);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<Map<String, String>> handleGeneralException(Exception ex) {
Map<String, String> response = new HashMap<>();
response.put("error", "An unexpected error occurred");
return new ResponseEntity<>(response, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
3. Custom Exception
:: @Controller 에서 Exception 처리 시, 다양한 Exception 에 대해 대응하기 위해 Exception 다중화
- 클래스 단위의 Exception 다중화 = RuntimeException 상속
- 필드 단위의 Exception 다중화 = Enum 을 통한 자체 예외 케이스 관리
3.1 클래스 단위 Exception 다중화
:: RuntimeException 상속
- @Controller에서 Exception 처리 X시, 500 표준 오류 반환
- @Controller에서 Exception 처리 O시(try-catch), 반환 타입은 ResponeEntity
// 사용자 정의 예외 클래스 1
public class InvalidInputException extends RuntimeException {
public InvalidInputException(String message) {
super(message);
}
}
// 사용자 정의 예외 클래스 2
public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException(String message) {
super(message);
}
}
// ...
:: But, 상황에 맞게 예외를 나타내야 함 -> 무지성적인 RuntimeException은 XX
:: try-catch 적용 시, 미쳐 생각 못한 예외를 잡기 위해
} catch(Exception e){
// ...
}
를 해줘야 함
3.2 필드 단위의 Exception 다중화
:: Enum을 통한 자체 예외 케이스 관리
:: RuntimeException 예외에 타입 필드를 추가하여 예외 객체로 다양한 예외를 표현
- RuntimeException 예외 내 타입 필드는 발생지에서 에러 유형에 따라 타입 필드를 명시하고
throw new CustomException("INVALID_REQUEST", "아이디는 10을 넘을 수 없습니다 - id : " + id);
- 처리지에서 타입 필드를 읽어 에러 유형에 따른 처리 수행
try {
UserResponseDto response = userService.retrieve(id);
return ResponseEntity.ok(response);
} catch (CustomException e) {
if (e.getType().equals("INVALID_REQUEST")) {
log.warn(e.getMessage());
e.printStackTrace();
return ResponseEntity.badRequest().build();
} else if (e.getType().equals("NOT_EXIST")) {
log.warn(e.getMessage());
e.printStackTrace();
return ResponseEntity.notFound().build();
} else {
log.error(e.getMessage());
e.printStackTrace();
return ResponseEntity.internalServerError().build();
}
}
4. Loggin
- Exception 처리에서 중요한것은 해당 예외상황(문제, 이슈) 해결을 위해 힌트를 제대로 남겨야함
- Where? 어디서 발생했는지 :: e.**printStackTrace**();
- Why? 왜 발생했는지(오류 메세지) :: log.warn(e.**getMessage**());
4.1 SLF4J (Simple Logger Facade for Java)
:: Java에서 로깅을 위한 추상화된 인터페이스를 제공하는 라이브러리
:: Logback, Log4j 와 같은 Logging Framework 들에 대한 Facade(추상체) 제공
:: Logging Framework 변경에도 아무런 문제없이 로깅 수행
-> lombok의 내장 slf4j를 사용하기 때문에 lombok의존성만 있으면 ㅇㅋ
Ex) @Slf4j 애너테이션 사용
import lombok.extern.slf4j.Slf4j;
@Slf4j // @Slf4j 애너테이션을 사용하여 Logger 자동 생성
public class MyService {
public void performTask(String input) {
log.trace("Trace level log: {}", input);
log.debug("Debug level log: {}", input);
log.info("Informational log: {}", input);
log.warn("Warning log: {}", input);
log.error("Error log: {}", input);
}
}
Ex) @Slf4j 로깅 사용 예시
@Slf4j
public class MyService {
public void processOrder(String orderId) {
log.info("Processing order with ID: {}", orderId);
try {
// 일부 비즈니스 로직 수행
int result = 10 / 0; // 예외 발생
} catch (Exception e) {
log.error("An error occurred while processing the order", e);
}
}
}
참고
ASAC 수업자료
https://mangkyu.tistory.com/204
[Spring] 스프링의 다양한 예외 처리 방법(ExceptionHandler, ControllerAdvice 등) 완벽하게 이해하기 - (1/2)
예외 처리는 애플리케이션을 만드는데 매우 중요한 부분을 차지한다. Spring 프레임워크는 매우 다양한 에러 처리 방법을 제공하는데, 어떠한 방법들이 있고 가장 좋은 방법(Best Practice)은 무엇인
mangkyu.tistory.com
https://thkim610.tistory.com/133#%40ExceptionHandler-1
ExceptionResolver
스프링에서는 어노테이션을 기반으로 ExceptionResolver를 활용하여 API 예외처리를 한다. 스프링 부트가 제공하는 ExceptionResolver는 다음과 같다. ( 위에서부터 우선순위가 높은 ExceptionResolver이다.) Exce
thkim610.tistory.com
'정리용 > SpringBoot' 카테고리의 다른 글
[Spring] 5. Spring - 예외처리 (0) | 2025.02.19 |
---|---|
[Spring] 5. Spring - @Controller (0) | 2025.02.19 |
[Spring] 4. 3-Layered Architecture Pattern (0) | 2025.02.19 |
[Spring] 3. SpringContext - Bean 주입 (0) | 2025.02.19 |
[Spring] 3. SpringContext - Bean 등록 (1) | 2025.02.19 |
- Total
- Today
- Yesterday
- useContext
- useMemo
- useState
- asac#asac7기
- useEffect
- useCallback
- asac7#asac
- acac
- useReducer
- Nginx
- acas#acas7기
- memo
- ASAC
- useRef
- git
- useLayoutEffect
- asac7
- asac7기
- react
- ssh
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |