스프링부트의 예외처리 방식
웹 서비스 애플리케이션에서는 외부에서 들어오는 요청에 담긴 데이터를 처리하는 경우가 많습니다. 그 과정에서 예외가 발생하면 예외를 복구해서 정상으로 처리하기보다는 요청을 보낸 클라이언트에 어떤 문제가 발생했는지 상황을 전달히는 경우가 많습니다.
- 스프링 부트 핵심 가이드 中-
ExceptionManager 클래스 생성
@RestControllerAdvice
public class ExceptionManager {
@ExceptionHandler(SNSAppException.class)
public ResponseEntity<?> SNSAppExceptionHandler(SNSAppException e) {
Map<String, Object> result = new HashMap<>();
result.put("errorCode", e.getErrorCode());
result.put("message", e.getMessage());
return ResponseEntity.status(e.getErrorCode().getStatus())
.body(Response.error("ERROR", result));
}
}
개별 예외 처리를 하기 위해 제작한 SNSAppException이 발생했을 경우 아래와 같은 로직이 실행됩니다. (@ExceptionHandler가 SNSAppException.class가 발생됐을 때 잡음
)
result Map에 errorCode와 message를 담고, ResponseEntity에서 ErrorCode에 담긴 HTTP Status로 웹 상태 코드를 반환시키고, body에 정보를 담아 반환시킵니다.
이렇듯 SNSAppException에서 발생한 DUPLICATED_USER_NAME에 담긴 HttpStatus.CONFLICT를 웹 상태 코드 409로 반환하고, BODY의 resultCode에서는 "ERROR"를 반환, result에서는 해당 에러의 에러 코드, 담긴 메세지까지 함께 반환하게 만들 수 있습니다.
전역 예외 처리를 하기 위해서는 @RestControllerAdvice
를 사용합니다. (@ResponseBody가 필요 없다면 @ControllerAdvice를 사용해도 됩니다.) 해당 어노테이션은 AOP방식으로 모든 컨트롤러의 예외를 한 곳에서 처리할 수 있도록 합니다.
@ExceptionHandler(<예외.class>)
를 사용해서 개별적으로 예외를 처리합니다.
해당 글을 확인하시면 어노테이션의 역할과 전역 예외 처리에 대해 자세히 알 수 있습니다.
개별 예외 클래스 생성
@AllArgsConstructor
@NoArgsConstructor
@Getter
public class SNSAppException extends RuntimeException{
private ErrorCode errorCode;
private String message;
@Override
public String toString() {
if(message == null) return errorCode.getMessage();
return String.format("%s. %s", errorCode.getMessage(), message);
}
}
SNSAppException은 RuntimeException을 상속 받고 있습니다.
ErrorCode
@AllArgsConstructor
@NoArgsConstructor
@Getter
public enum ErrorCode {
DUPLICATED_USER_NAME(HttpStatus.CONFLICT, "UserName이 중복됩니다."),
USERNAME_NOT_FOUND(HttpStatus.NOT_FOUND, "Not founded"),
INVALID_PASSWORD(HttpStatus.UNAUTHORIZED, "패스워드가 잘못되었습니다."),
INVALID_TOKEN(HttpStatus.UNAUTHORIZED, "잘못된 토큰입니다."),
INVALID_PERMISSION(HttpStatus.UNAUTHORIZED, "사용자가 권한이 없습니다."),
POST_NOT_FOUND(HttpStatus.NOT_FOUND, "해당 포스트가 없습니다."),
DATABASE_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "DB에러");
private HttpStatus status;
private String message;
}
Error를 발생시킨 Service
@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
public UserDto join(UserJoinRequest dto) {
userRepository.findByUserName(dto.getUserName())
.ifPresent(user->{
throw new SNSAppException(ErrorCode.DUPLICATED_USER_NAME, String.format("userName = %s", dto.getUserName()));
});
...
}
}
'Server > Spring&Spring Boot' 카테고리의 다른 글
[Spring Boot] Spring Security 6.0 Configuration. (0) | 2023.01.17 |
---|---|
[JPA] Update 후 해당 객체를 Return했을 때 @CreatedDate가 null 이 반환되는 상태에 대한 회고 (0) | 2023.01.06 |
[Spring] Custom Response 생성 (0) | 2022.12.21 |
[Spring Security] config 설정 2 (0) | 2022.12.04 |
[Spring] @RequestBody 바인딩 시, 기본 생성자(@NoArgsConstructor)의 변덕과 필요한 이유 (0) | 2022.12.01 |