[TIL] 커스텀 예외를 한번에 처리하는 방법

phdljr·2023년 11월 28일
0

TIL

목록 보기
38/70
post-custom-banner

커스텀 예외를 처리하는 방법 중, ControllerAdvice를 통해 예외를 처리하는 방법을 알게돼서 프로젝트에 적용해보았다.

하지만, 예상보다 만들어지는 커스텀 예외의 수는 많았고, 만들어질 때마다 제목만 다르고 내용은 거의 비슷한, 즉 중복 코드가 많아지는 현상이 발생했다.

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(IllegalBoardTypeException.class)
    public ResponseEntity<ExceptionResponseDto> handleIllegalBoardTypeException() {
        return ResponseEntity
            .status(CustomException.ILLEGAL_BOARD_TYPE.getStatusCode())
            .body(CustomException.ILLEGAL_BOARD_TYPE.toDto());
    }

    @ExceptionHandler(NotFoundBoardException.class)
    public ResponseEntity<ExceptionResponseDto> handleNotFoundBoardException() {
        return ResponseEntity
            .status(CustomException.NOT_FOUND_BOARD.getStatusCode())
            .body(CustomException.NOT_FOUND_BOARD.toDto());
    }

    @ExceptionHandler(NotMatchUserException.class)
    public ResponseEntity<ExceptionResponseDto> handleNotMatchUserException() {
        return ResponseEntity
            .status(CustomException.NOT_MATCH_USER.getStatusCode())
            .body(CustomException.NOT_MATCH_USER.toDto());
    }
    
    ...
    
}

이를 해결할 수 있는 방법에 대해 알아보겠다.


대표 CustomException 클래스를 만들고, 안의 내용을 넣는 방식

대표 커스텀 예외 클래스를 만들고, 안에 내용을 상황에 맞게 설정한다.

public class CustomException extends RuntimeException {
    private final ExceptionCode exceptionCode;

    public CustomException(ExceptionCode exceptionCode) {
        super(exceptionCode.getMessage());
        this.exceptionCode = exceptionCode;
    }

    public ExceptionCode getErrorCode() {
        return exceptionCode;
    }
}

여기서 ExceptionCode는 다음과 같이 enum으로 미리 작성된 객체들이다.

@Getter
@RequiredArgsConstructor
public enum ExceptionCode {

    // CONFLICT
    CONFLICT_ID_EMAIL_NICKNAME_IN_USE(HttpStatus.CONFLICT, "사용자 아이디, 이메일 또는 닉네임이 이미 사용 중 입니다."),
    CONFLICT_EMAIL_IN_USE(HttpStatus.CONFLICT, "중복된 이메일 입니다."),
    CONFLICT_NICK_IN_USE(HttpStatus.CONFLICT, "중복된 닉네임 입니다."),

    // FORBIDDEN
    FORBIDDEN_UPDATE_ONLY_WRITER(HttpStatus.FORBIDDEN, "작성자만 수정 할 수 있습니다."),
    FORBIDDEN_DELETE_ONLY_WRITER(HttpStatus.FORBIDDEN, "작성자만 삭제 할 수 있습니다."),
    FORBIDDEN_YOUR_NOT_COME_IN(HttpStatus.FORBIDDEN, "권한이 없습니다."),

    // NOT_FOUND
    NOT_FOUND_USER(HttpStatus.NOT_FOUND, "해당 유저는 존재하지 않습니다."),
    NOT_FOUND_MOVIE(HttpStatus.NOT_FOUND, "해당 영화는 존재하지 않습니다."),
    NOT_FOUND_REVIEW(HttpStatus.NOT_FOUND, "해당 리뷰는 존재하지 않습니다."),
    NOT_FOUND_REVIEW_COMMENT(HttpStatus.NOT_FOUND, "해당 댓글은 존재하지 않습니다."),

    // BAD_REQUEST
    BAD_REQUEST_ALREADY_WROTE_REVIEW(HttpStatus.BAD_REQUEST, "이미 리뷰를 작성 하셨습니다."),
    BAD_REQUEST_NOT_MATCH_PASSWORD(HttpStatus.BAD_REQUEST, "비밀번호가 일치하지 않습니다.");


    private final HttpStatus httpStatus;
    private final String message;
}

이렇게 구현을 했다면, ControllerAdvice에선 커스텀 예외에 대해 단 하나의 메소드만 작성해주면 된다.

@RestControllerAdvice
public class ExceptionAdviceController {
    @ExceptionHandler(CustomException.class)
    public ResponseEntity<ExceptionResponse> handlerException(CustomException e) {

        ExceptionResponse exceptionResponse = new ExceptionResponse(e.getErrorCode());

        return ResponseEntity.status(exceptionResponse.getStatus()).body(exceptionResponse);
    }
}

예외가 발생하는 부분에선 다음과 같이 커스텀 예외를 발생시키며 상황에 맞는 enum을 매개변수로 넘겨준다.

public List<ReviewResponseDto> getReviewList(Long movieId) {

    Movie movie = movieRepository.findById(movieId)
                .orElseThrow(() -> new CustomException(ExceptionCode.NOT_FOUND_MOVIE));

    return reviewRepository.findAllByMovieOrderByReviewLikeDescCreatedAtDesc(movie).stream()
            .map(ReviewResponseDto::new).collect(Collectors.toList());
}

참조

https://wonowdaily.tistory.com/m/67

profile
난 Java도 좋고, 다른 것들도 좋아
post-custom-banner

0개의 댓글