본문 바로가기
Study/Spring

Controller 예외 처리 | Spring Spring boot JPA Study

by M개발자 2022. 2. 11.
반응형

✔ Java 11

✔ Gradle 7

✔ Spring boot 2.6.3

Controller 예외 처리

1. Error Response

@Getter
@AllArgsConstructor
public class ErrorResponse {
    private int status;
    private String message;
}

밑의 json 형태로 반환하기 위해 response entity를 생성한다.

 

{
    "status" : 200,
    "message" : "ok"
}

 

2. Enum - ErrorCode

@Getter
@AllArgsConstructor
public enum ErrorCodeEnum {
    USER_NOT_FOUND(HttpStatus.NOT_FOUND.value(), "User Not Found"),
    USER_ALREADY_EXIST(HttpStatus.CONFLICT.value(), "User Already Exist");

    private final int status;
    private final String message;
}

json으로 반환할 값을 enum으로 생성한다.

enum명(int status, String message) 형태로 필요한 에러를 선언하면 된다.

 

3. Custom Error

@Getter
public class ApiException extends RuntimeException {
    private final ErrorCodeEnum errorCodeEnum;

    public ApiException(ErrorCodeEnum e){
        super(e.getMessage());
        this.errorCodeEnum = e;
    }
}

예외 처리를 사용하기 위해 공통 Exception을 구현한다.

 

예외를 보낼 곳에 이런 형태로 사용할 수 있다.

throw new ApiException(ErrorCodeEnum.USER_NOT_FOUND);

 

4. RestControllerAdvice

@Slf4j

  • loging API 제공하는 메소드로 log를 사용할 수 있다.

@RestControllerAdvice

  • 전역에서 발생되는 예외를 처리하기 위한 어노테이션
  • 예외 발생 시 json 형태로 반환한다.

@ExceptionHandler

  • 예외 발생 시 직접 정의한 핸들링을 통해 json 형태로 에러 메시지를 전달한다.
@Slf4j
@RestControllerAdvice
public class ApiExceptionAdvice {

    @ExceptionHandler({ApiException.class})
    protected ResponseEntity<ErrorResponse> exceptionHandler(final ApiException e) {
        return new ResponseEntity<>(
                        new ErrorResponse( // json 형태
                                e.getErrorCodeEnum().getStatus(), 
                                e.getErrorCodeEnum().getMessage()
                        ), 
                        HttpStatus.valueOf( // http 상태 코드
                                e.getErrorCodeEnum().getStatus()
                        ));
    }

    @ExceptionHandler({MethodArgumentNotValidException.class}) //400
    protected ResponseEntity<ErrorResponse> MethodArgumentNotValidExceptionHandler(final MethodArgumentNotValidException e) {
        log.error(e.getCause().getMessage()); // 에러 로그로 해당 메시지를 찍을 수 있음
        return new ResponseEntity<>(new ErrorResponse(HttpStatus.BAD_REQUEST.value(), e.getLocalizedMessage()), HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler({Exception.class}) //500
    protected ResponseEntity<ErrorResponse> ExceptionHandler(final Exception e) {
        log.error(e.getCause().getMessage());
        return new ResponseEntity<>(new ErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getLocalizedMessage()), HttpStatus.INTERNAL_SERVER_ERROR);
    }

    @ExceptionHandler({DataIntegrityViolationException.class}) //409
    protected ResponseEntity<ErrorResponse> DataIntegrityViolationExceptionHandler(final DataIntegrityViolationException e) {
        log.error(e.getCause().getMessage());
        return new ResponseEntity<>(new ErrorResponse(HttpStatus.CONFLICT.value(), e.getLocalizedMessage()), HttpStatus.CONFLICT);
    }

    @ExceptionHandler({MethodArgumentTypeMismatchException.class}) //400
    protected ResponseEntity<ErrorResponse> MethodArgumentTypeMismatchExceptionHandler(final MethodArgumentTypeMismatchException e) {
        log.error(e.getCause().getMessage());
        return new ResponseEntity<>(new ErrorResponse(HttpStatus.BAD_REQUEST.value(), e.getLocalizedMessage()), HttpStatus.BAD_REQUEST);
    }
    @ExceptionHandler({HttpRequestMethodNotSupportedException.class}) //405
    protected ResponseEntity<ErrorResponse> HttpRequestMethodNotSupportedExceptionHandler(final HttpRequestMethodNotSupportedException e) {
        log.error(e.getCause().getMessage());
        return new ResponseEntity<>(new ErrorResponse(HttpStatus.METHOD_NOT_ALLOWED.value(), e.getLocalizedMessage()), HttpStatus.METHOD_NOT_ALLOWED);
    }

}

ApiException

  • 위에서 직접 정의한 예외로 커스텀한 메시지를 전달할 수 있다.

MethodArgumentNotValidException

  • @Valid 어노테이션으로 인한 에러를 잡을 수 있다.

Exception

  • 500 상태 코드 예외 처리
  • NullPointException 등이 잡힌다.

DataIntegrityViolationException

  • lower level persistence exception과 관련된 예외를 잡아준다.
  • sql문이나 data가 잘못된 경우 잡힌다.

MethodArgumentTypeMismatchException

  • enum type이 일치하지 않아 바인딩을 못할 경우 발생한다.

HttpRequestMethodNotSupportedException

  • 지원하지 않는 http 메소드를 호출할 때 발생한다.

 

sql문이 잘못된 경우

 

2. userService - 예외 처리

1. 회원 생성

@Transactional
    public void createUser(UserRequestDto userRequestDto) {
    // 예외 처리
        if(userRepository.existsByUserId(userRequestDto.getUserId())){
            throw new ApiException(ErrorCodeEnum.USER_ALREADY_EXIST);
        }

        UserMinj user = UserMinj.createUser(userRequestDto.getUserId(), userRequestDto.getUserName(), userRequestDto.getUserPassword());
        userRepository.save(user);
    }

회원을 생성할 때 이미 존재하는 회원 아이디인지 체크를 해주어야 한다.

직접 예외를 생성하지 않는다면 500이 반환되어, 409와 함께 이미 존재하는 회원이라는 예외를 던져준다.

 

entity > user > repository > userRespository.java

Boolean existsByUserId(String userId);

해당 메소드는 userId가 존재할 경우 true를 반환한다.

 

exception > ErrorCodeEnum.java

USER_ALREADY_EXIST(HttpStatus.CONFLICT.value(), "User Already Exist");

409를 반환하는 에러 코드를 추가해준다.

 

중복된 회원일 경우

 

2. 회원 상세 조회 / 회원 삭제 / 회원 수정

@Transactional
    public UserResponseDto detailsUser(long id) {
                // 예외 처리
        UserMinj userMinj = userRepository.findById(id).orElseThrow(() ->{
            throw new ApiException(ErrorCodeEnum.USER_NOT_FOUND);
        });
        return UserResponseDto.of(userMinj);
    }

id가 존재하지 않을 경우 404와 함께 존재하지 않는 회원이란 예외를 던져준다.

 

exception > ErrorCodeEnum.java

USER_NOT_FOUND(HttpStatus.NOT_FOUND.value(), "User Not Found");

404를 반환하는 에러 코드를 추가해준다.

 

존재하지 않는 아이디일 경우


참고 글

예외 처리 방법 참조 - https://leeys.tistory.com/30

예외 처리 핸들러 참조 - https://cheese10yun.github.io/spring-guide-exception/

handler 설명 참고 - https://sun-22.tistory.com/12

restAdvice 참고 - https://shinsunyoung.tistory.com/54

 

회의록

반응형

댓글