ExceptionHandler 를 통한 예외처리

알파로그·2023년 5월 24일
0

Spring Boot

목록 보기
47/57

✏️ 필요성

  • Rest Controller 를 구현할 때 비즈니스 로직 수행 중 문제가 발생할 경우 이를 핸들링 하기 위한 기능이다.
  • 서버의 문제인지 클라이언트의 문제인지 원인을 클라이언트에게 응답할 수 있다.
    • 만약 예외처리를 하지 않는다면 모든 exception 이 500 에러로 발생하고,
      정확한 원인의 메시지도 받을 수 없다.
    • 500 에러는 서버의 문제이기 때문에 클라이언트 입장에선 혼란스러울 수 밖에 없다.

✏️ ExceptionHandler 구현

📍 Exception 발생시키기

  • Servcice 계층에 username 으로 Member 객체를 찾아서 반환해주는 간단한 비즈니스 로직이다.
    • 만약 Optional 에 값이 없다면 Exception 이 발생하도록 했다.
      • NotFoundException 은 직접 커스텀한 예외이다.
public Member findByUsername(String username) {
    Optional<Member> byUsername = memberRepository.findByUsername(username);

    if (byUsername.isPresent())
        return byUsername.get();

    throw new NotFoundException("존재하지 않는 username");
}
  • 이대로 예외를 발생시키면 잘못된 요청임에도 불구하고 500 에러가 발생한다.
    - 클라이언트 입장에선 500 이외의 정보를 얻을 수 없기 때문에 서버에 문제가 있다고 생각할 수 밖에 없다.

📍 Response 객체 생성

  • Exception 의 Msg 를 맵핑해 클라이언트에게 전달하는 객체이다.
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@AllArgsConstructor
public class ErrorResponse {

    protected String errorMsg;
}

📍 Exception 객체

  • 발생되는 Exception 의 제목을 정해주는 작업이라고 생각하면 된다.
    • 예제에서는 값을 찾지 못했을 때 사용할 예외를 생성했다.
import com.atowz.global.exception.ui.ErrorStatus;
import io.jsonwebtoken.JwtException;
import lombok.Getter;

@Getter
public class InvalidJwtException extends JwtException {

    private final int status;

    public InvalidJwtException(ErrorStatus response) {
        super(response.getErrorMessage());
        this.status = response.getStatus();
    }
}

📍 Exception status 객체

  • 커스텀 예외들의 상태를 직접 정의해 통일성과 유지보수성을 높힐 수 있다.
import lombok.Getter;

@Getter
public enum ErrorStatus {

    //-- about jwt --//
    JWT_INVALID("유효하지 않은 토큰", 401),
    JWT_EXPIRED("만료된 토큰", 401),
    JWT_NOT_FOUND("토큰을 찾을 수 없음", 400),
    JSON_DOSE_NOT_SUPPORT("json String 으로 변환 실패", 400);

    private final String errorMessage;
    private final int status;

    ErrorStatus(String errorMessage, int status) {
        this.errorMessage = errorMessage;
        this.status = status;
    }
}

📍 RestControllerAdvice 계층

  • ExceptionHandler 를 적용하기 위한 객체이다.
    • @RestControllerAdvice 를 선언하지 않는다면 Exception 이 발생해도 method 가 실행되지 않는다.
  • @ExceptionHandler
    • Excption 을 매핑하는 어노테이션이다.
    • 속성값에 Exception 을 명시하지 않으면 모든 Exception 에 대한 핸들링을 하기 때문에 속성값을 꼭 적어주어야 한다.
import com.atowz.global.exception.jwt.InvalidJwtException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@Slf4j
@RestControllerAdvice
public class ExceptionController {

    @ExceptionHandler(InvalidJwtException.class)
    public ResponseEntity<ErrorResponse> invalidJwtExceptionHandler(InvalidJwtException e) {
        log.error(e.getMessage());
        return ResponseEntity.status(e.getStatus()).body(new ErrorResponse(e.getMessage()));
    }
}

📍 사용 방법

  • exception 을 발생시키고 인자에 enum 으로 생성한 ErrorStatus 를 값으로 주면 통일성을 향상시킬 수 있다.
public void isAccessTokenValid(String accessToken) {
    String value = redisUtil.getValue(accessToken);

    if (value != null) {
        throw new InvalidJwtException(JWT_EXPIRED);
    }
}
  • 이제 Exception 이 발생하면 오류코드와 함께 message 를 같이 응답 할 수 있다.

profile
잘못된 내용 PR 환영

0개의 댓글