ApiResponse 를 사용하자
ResponseBody 로 응답을 하는 RestController 의 경우 ResponseEntity body 에 data 가 객체로 담기면 Json 으로 변환해 내려주게된다. 그러나 String 이 담긴다면 plain/test 형태로 내려주기에 응답의 형태가 달라지게 된다.
-> 즉,
HttpResponse 응답을 내려줄때 정상응답/에러 등등 에서 데이터의 형식을 json 으로 통일시켜줄 필요가 있다.
이를 위해 공통 응답 클래스인 ApiResponse 를 만들어 사용하게 된다.
ApiResponse 클래스는 status, data, message 세가지 필드를 갖는다.
status : 정상(success), 예외(error), 오류(fail) 중 한 값을 갖는다.
data : 정상(success)의 경우 실제 전송될 데이터를, 오류(fail)의 경우 유효성 검증에 실패한 데이터의 목록을 응답한다. 어떤 타입도 처리할 수 있도록 제네릭을 사용한다.
message : 예외(error)의 경우 예외 메시지를 응답한다.
ApiResponse.java
@NoArgsConstructor
@Getter
@Setter
public class ApiResponse<T> {
private String code;
private String message;
private String messageCode; // 추후 message.properties 와 활용도 가능
private T data;
ApiResponse(ErrorCode errorCode) {
this(errorCode, null, null);
}
ApiResponse(ErrorCode errorCode, T data) {
this.code = errorCode.getCode();
this.message = errorCode.getDefaultMessage();
this.data = data;
}
ApiResponse(ErrorCode errorCode, String responseMessage, T data) {
this.code = errorCode.getCode();
this.message = (responseMessage == null ? errorCode.getDefaultMessage() : responseMessage);
this.data = data;
}
ApiResponse(ErrorCode errorCode, String message, String messageCode, T data) {
this.code = errorCode.getCode();
this.message = message;
this.messageCode = messageCode;
this.data = data;
}
public T getData() {
return data;
}
}
Error 코드를 따로 정의해 자체적인 role 를 정립해 사용할 수도 있다.
ErrorCode.java
@Getter
public enum ErrorCode {
SUCCESS ("2000", "OK"),
/* 1001 ~ 2000 logic error*/
SYSTEM_PERMISSION_ERROR ("1001", "시스템 권한 오류"),
...
/* 9001 ~ system error*/
SYSTEM_ERROR ("9001", "시스템 오류"),
...
private final String code;
private final String defaultMessage;
ErrorCode(String code, String defaultMessage) {
this.code = code;
this.defaultMessage = defaultMessage;
}
}
ApiResponse 객체를 생산할 팩토리 클래스를 정의하자
ApiResponseGenerator.java
public class ApiResponseGenerator {
private static final ApiResponse<Void> RESULT_SUCCESS = new ApiResponse<>(ErrorCode.SUCCESS);
private static final ApiResponse<Void> RESULT_ERROR = new ApiResponse<>(ErrorCode.UNKNOWN_ERROR);
private ApiResponseGenerator() {
}
public static ApiResponse<Void> success() {
return RESULT_SUCCESS;
}
public static <T> ApiResponse<T> success(T data) {
return new ApiResponse<>(ErrorCode.SUCCESS, null, data);
}
public static ApiResponse<Void> fail() {
return RESULT_ERROR;
}
public static ApiResponse<Void> fail(ErrorCode code) {
return new ApiResponse<>(code);
}
public static <T> ApiResponse<T> fail(ErrorCode code, T data) {
return new ApiResponse<>(code, data);
}
public static ApiResponse<Void> fail(ErrorCode code, String msg) {
return new ApiResponse<>(code, msg, null);
}
public static ApiResponse<String> fail(ErrorCode code, String msg, String messageCode) {
return new ApiResponse<>(code, msg, messageCode,null);
}
}
보통 ApiResponse 처럼 프로그램의 비즈니스에 관련되지않고 여러 모듈에 적용되는 utils 속하는 파일은 support 모듈을 만들어 관리하게된다. 때문에 support 모듈을 루트 build.gradle 에 implements 해줘서 subProject 들에서 사용할 수 있게 하자.
build.gradle
subprojects {
...
dependencies {
implementation project(":support")
...
}
}
사용은 다음과 같이 사용하면된다.
BlogController.java
@RestController
@RequestMapping("/blog")
public class BlogController {
@GetMapping
public ApiResponse<String> returnOk() {
return ApiResponseGenerator.success("성공");
}
@GetMapping("/error")
public ApiResponse<String> returnError() {
return ApiResponseGenerator.fail(ErrorCode.BAD_REQUEST_ERROR, "요청실패입니다.", null);
}
}
그러면 결과는 다음과 같이 Json 형태로 통일되어 나타난다.


마찬가지로 범용적인 Error 처리 또한 해보자