Api 공통 응답 포맷 만들기

KHoney·2022년 10월 6일
1

Java

목록 보기
9/10

Api Response 공통 포맷화 하기

공통 포맷 개발의 장점

  • 서버는 정상, 에러에 대한 response 포맷이 동일하여 어떠한 response 던지 공통 포맷 형태로 반환하면 된다.
  • Api 를 사용하는 클라이언트는 공통 포맷을 보고 정상응답, 에러를 구분하기 쉽다.
    • 따라서 성공 혹은 실패했을 때 response를 프런트 단에서 조작하기 쉽도록 서버에서 전달해 주는게 좋다.

구현

ApiResult

Reponse와 request의 성공여부를 가질 객체 ApiResult 를 생성한다.

요청의 성공, 실패를 나타낼 success 와 성공일때의 데이터 response , 실패일때의 에러정보 ApiError 를 가지도록 한다.

이때 reponse 를 Generic 으로 선언하여 Type 에 자유롭도록 한다.

public class ApiResult<T> {
    private final boolean success;
    private final T response;
		private final ApiError error;

    public ApiResult(boolean success, T response, ApiError error) {
        this.success = success;
        this.response = response;
				this.error = error;
    }

    public boolean isSuccess() {
        return success;
    }

    public T getResponse() {
        return response;
    }
}

ApiError

에러 정보를 가질 class ApiError 는 다음과 같다.

간단하게 에러 메세지와 Status를 가지는 객체고,

이후 Custom Exception 을 만들어 사용할때 편의를 위해 Throwable 을 넣었을 때도 바로 message로 객체를 생성하도록 하였다.

public class ApiError {
    private final String message;
    private final int status;

    public ApiError(String message, int status) {
        this.message = message;
        this.status = status;
    }

    public ApiError(Throwable throwable, HttpStatus status) {
        this(throwable.getMessage(), status);
    }

    public ApiError(String message, HttpStatus status) {
        this(message, status.value());
    }
}

ApiUtils

Controller 에서 직접 return 될 class ApiUtils 를 생성한다.

public class ApiUtils {
    public static <T> ApiResult<T> success(T response) {
        return new ApiResult<>(true, response);
    }

		public static ApiResult<?> error(Throwable throwable, HttpStatus status) {
        return new ApiResult<>(false, null, new ApiError(throwable, status));
    }

    public static ApiResult<?> error(String message, HttpStatus status) {
        return new ApiResult<>(false, null, new ApiError(message, status));
    }
}

사용예시

(쉬운 예시를 위해 service 단을 생략하고 repository를 바로 사용했다.)

따로 method 만 import 해서 사용할수도 있지만, 클래스 이름을 명시하는 형식으로 사용하는게 보기 편한 것 같다.

@GetMapping("/api/test/{id}")
    public Object apiTest(@PathVariable String id){
        try{
            var testId = userRepository.findById(id)
							.orElseThrow(() -> new NullPointerException("No Id found"));
						// 간단한 예시를 위해 NullPointerException 사용
            return ApiUtils.success(testId);
        } catch (Exception e){
            return error(e,HttpStatus.BAD_REQUEST);
        }
    }
profile
좋은 개발자가 되고싶은

1개의 댓글

comment-user-thumbnail
2023년 9월 6일

포스팅 잘 봤습니다.
ApiUtil의 해당 코드에서
public static ApiResult success(T response) {
return new ApiResult<>(true, response);
}
new ApiResult<>(true, response) 는 어디에 정의되어 있는지 궁금합니다.
ApiResult의 생성자는 하나밖에 없는것 같아 어떻게 처리되는지 모르겠습니다...

답글 달기