프로젝트를 진행하면서 발생했던 이슈와 이러한 이슈를 어떻게 해결했는지 시리즈로 만들어 작성하려고 한다.
서버에서 에러가 발생하면 클라이언트에게 요청이 실패했다는 응답을 전달해야 한다. 이때 단순히 HTTP 상태코드만으로 구체적으로 어떤 에러가 발생했는지 보여주기 어렵다.
따라서 서버에서 에러가 발생하면 상태 코드, 에러 코드, 에러 메시지를 body에 실어 응답을 보내도록 REST API를 설계했다.
{
"resultCode" : Number
"errorCode" : String
"message" : String
}
여기서 에러 코드란 에러 식별자이다. 프로젝트 내에선 이러한 에러 코드를 Enum으로 관리하였다.
먼저 Enum은 공통적으로 상태 코드와 에러 코드를 가지고 있다. 따라서 Enum이 공통으로 구현할 interface를 생성했다.
public interface ErrorCode {
int getResultCode();
String getCode();
}
아래는 프로젝트 내에서 인증과 관련된 에러 코드를 관리하는 Enum이다. interface를 구현한 Enum은 상태코드와 에러코드를 인스턴스 변수로 사용하고 있다.
public enum AuthErrorCode implements ErrorCode {
INVALIED_TOKEN(401, "INVALIED_TOKEN"),
WRONG_TOKEN_TYPE(401, "WRONG_TOKEN_TYPE"),
NOT_EXISTS(401, "NOT_EXISTS"),
private int resultCode;
private String code;
AuthErrorCode(int resultCode, String code) {
this.resultCode = resultCode;
this.code = code;
}
@Override
public int getResultCode() {
return resultCode;
}
@Override
public String getCode() {
return code;
}
}
이제 Enum으로 정의한 에러 코드를 클라이언트에게 전달해보자. 아래는 RuntimeException을 상속한 Custom Exception이다. 예외 객체가 에러 코드를 인스턴스 변수로 가지도록 한다.
public class CustomException extends RuntimeException {
private ErrorCode errorCode;
public TokenInvalidException(String s, ErrorCode errorCode) {
super(s);
this.errorCode = errorCode;
}
public ErrorCode getErrorCode() {
return errorCode;
}
}
만약 요청을 처리하는 도중 Custom Exception 객체를 인스턴스화하여 던졌다. 이러한 예외를 catch한 뒤 예외에서 에러 코드와 에러 메시지를 getter로 가져와 바디에 싣는다.
Spring에서 REST API는 @ExceptionHandler 어노테이션으로 예외를 처리한다.
@ExceptionHandler(CustomException.class)
public ResponseEntity<ResponseError> customException(CustomException e) {
ResponseError responseError = new ResponseError(e.getErrorCode(), e.getMessage());
return new ResponseEntity<>(responseError, HttpStatus.BAD_REQUEST);
}
참고로 body에 담길 객체는 상태 코드, 에러 코드, 에러 메시지를 인스턴스 변수로 가지고 있다.
@Getter
public class ResponseError {
private int resultCode;
private String errorCode;
private String message;
public ResponseError(ErrorCode errorCode, String message) {
this.resultCode = errorCode.getResultCode();
this.errorCode = errorCode.getCode();
this.message = message;
}
}
참고
https://www.baeldung.com/rest-api-error-handling-best-practices