[Spring Basic] 예외처리
예외가 발생하고 처리해 주지 않으면 내부 서버 오류인 500번대 오류로 처리한다.
여러 가지 메서드들에 대해서 같은 에러가 발생함을 예상한다면, 각각의 메서드에다 try-catch문을 작성하는 대신 @ExceptionHandler 애너테이션을 사용해 예외를 따로 빼서 처리해 줄 수 있다. 이 때, 매개변수로는 처리할 예외가 들어간다.
@ExceptionHandler 애너테이션으로 특정 예외를 처리하는 메서드를 지정할 수 있다.
예외를 처리하는 메서드는 여러 개가 있을 수 있고, 에러가 처리될 때는 작은 에러부터 시작해서 처리된다.
NullPointerException은 Exception클래스의 자손이니, NullPointerException에러 메서드가 있다면 해당하는 에러를 먼저 찾은 후 없다면 그 조상인 Exception클래스가 에러를 처리한다.
또, 하나의 메서드로 여러 에러를 처리하고 싶다면 에러의 종류를 받는 매개변수에 배열의 형태로 처리해 주면 된다.
예외 처리에 관련된 요소를 더 알아보자.
@ControllerAdvice
해당 애너테이션을 사용해 모든 컨트롤러에서 발생하는 에러를 한 번에 처리할 수 있다.
애너테이션 뒤에 예외를 처리할 패키지를 선언해 줄 수 있고, 해당하는 패키지 안의 다른 클래스들에서 발생한 예외도 함께 처리해 줄 수 있다. (패키지를 지정하지 않을 시 모든 패키지에 적용)
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(IOException.class)
public ResponseEntity<String> handleIOException(IOException e) {
....
}
}
위의 예시는 모든 컨트롤러에서 발생하는 IOException 에러를 처리한다.
@ControllerAdvice 애너테이션 없이 @ExceptionHandler 애너테이션만 사용하려면 해당 컨트롤러 내부에 @ExceptionHandler 애너테이션을 붙여 메서드를 지정하는 방식을 사용한다.
@ControllerAdvice 애너테이션 덕분에 좀 더 편하게 예외처리를 수행할 수 있게 됐다.
ResponseEntity<?> 타입은 스프링에서 HTTP 응답 메세지를 상세하게 작성할 때 사용한다.
status, header, body 등을 빌더 방식으로 생성할 수 있어 편리하다. (제네릭스는 body 타입을 나타낸다)
return ResponseEntity
.status(HttpStatus.CREATED)
.header("MyHeader", "HeaderValue")
.body(myResponseBody);
에러 처리 메서드가 겹칠 때는 가까운 쪽 부터 확인한다.
@ResponseStatus 애너테이션을 사용해 응답 메세지의 상태 코드를 변경할 수 있다.
예외 처리 메서드 혹은 예외 클래스 앞에 붙이며, 200번대 에러를 400번대 에러 혹은 500번대 에러로 변경할 때 사용한다.
에러 번호 혹은 에러 종류에 따라 View 페이지를 만들고, 해당하는 페이지를 보여주도록 작성하자.
예외를 처리할 때는 일관성을 챙겨야 한다.
어떤 에러는 api로, 어떤 에러는 api 말고 다른 방식으로... 이런 식으로 에러마다 다른 방식으로 응답하는건 좋지 않다.
때문에 최상위 에러 클래스를 만들고 에러 처리 코드를 중앙화하는 방식을 사용한다.
@Getter
public abstract class TeamAreaException extends RuntimeException {
public final Map<String, String> validation = new HashMap<>();
public TeamAreaException(String message) {
super(message);
}
public TeamAreaException(String message, Throwable cause) {
super(message, cause);
}
public abstract int getStatusCode();
public void addValidation(String fieldName, String message) {
validation.put(fieldName, message);
}
}
@Getter
public class InvalidRequest extends TeamAreaException {
private String fieldName;
private String message;
private static final String MESSAGE = "존재하지 않는 글입니다.";
public InvalidRequest() {
super(MESSAGE);
}
public InvalidRequest(Throwable cause) {
super(MESSAGE, cause);
}
@Override
public int getStatusCode() {
return 400;
}
public InvalidRequest(String fieldName, String message) {
super(MESSAGE);
addValidation(fieldName, message);
}
}
위의 예시처럼 에러를 나타내는 클래스 계층을 만들고, 상속을 통해 에러를 구체화하는 편이 합리적이다.
백엔드와 프론트엔드가 독립적으로 개발되는 경우 REST API 방식으로 서로 통신하게 되는데, 이 때 위와 같은 방식으로 예외를 처리하게 되면 프론트엔드에게 JSON 타입으로 에러를 쉽게 전달할 수 있어 효과적이다.
'Spring > Spring' 카테고리의 다른 글
[Spring Basic] IoC와 DI 구조 (0) | 2023.06.14 |
---|---|
[Spring Basic] 웹 서버와 웹 애플리케이션 서버의 구조 (1) | 2023.06.14 |
[Spring Basic] 로그 (0) | 2022.08.16 |
[Spring Basic] 빈의 생명주기와 스코프 (0) | 2022.08.09 |
[Spring Basic] 의존관계 주입 (0) | 2022.08.08 |
댓글
이 글 공유하기
다른 글
-
[Spring Basic] IoC와 DI 구조
[Spring Basic] IoC와 DI 구조
2023.06.14 -
[Spring Basic] 웹 서버와 웹 애플리케이션 서버의 구조
[Spring Basic] 웹 서버와 웹 애플리케이션 서버의 구조
2023.06.14 -
[Spring Basic] 로그
[Spring Basic] 로그
2022.08.16 -
[Spring Basic] 빈의 생명주기와 스코프
[Spring Basic] 빈의 생명주기와 스코프
2022.08.09