Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor: 공통 응답 분기 Advice #10

Merged
merged 5 commits into from
Jul 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 18 additions & 39 deletions src/main/java/slvtwn/khu/toyouserver/common/ApiResponse.java
Original file line number Diff line number Diff line change
@@ -1,68 +1,47 @@
package slvtwn.khu.toyouserver.common;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import java.time.LocalDateTime;
import lombok.Getter;

@Getter
@JsonPropertyOrder({"code", "message", "data", "pageInfo"})
public class ApiResponse<T> {
@JsonPropertyOrder({"timestamp", "code", "message", "data"})
public class ApiResponse {

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy.MM.dd HH:mm:ss", timezone = "Asia/Seoul")
@JsonProperty("timestamp")
private LocalDateTime timestamp = LocalDateTime.now();

private final String code;

private final String message;

@JsonInclude(Include.NON_NULL)
private final T data;

private final PageInfoResponse pageInfo;
private final Object data;

ApiResponse(String code, String message) {
this(code, message, null, null);
}

ApiResponse(String code, String message, T data) {
this(code, message, data, null);
this(code, message, null);
}

ApiResponse(String code, String message, T data, PageInfoResponse pageInfo) {
ApiResponse(String code, String message, Object data) {
this.code = code;
this.message = message;
this.data = data;
this.pageInfo = pageInfo;
}

public static <T> ApiResponse<T> success(SuccessType successType) {
return new ApiResponse<>(successType.getCode(), successType.getMessage());
}

public static <T> ApiResponse<T> success(SuccessType successType, T data) {
return new ApiResponse<>(successType.getCode(), successType.getMessage(), data);
}

public static <T> ApiResponse<T> success(SuccessType successType, T data, PageInfoResponse pageInfo) {
return new ApiResponse<>(successType.getCode(), successType.getMessage(), data, pageInfo);
public static ApiResponse success(SuccessType successType, Object data) {
return new ApiResponse(successType.getCode(), successType.getMessage(), data);
}

public static ApiResponse<?> error(ErrorType errorType) {
return new ApiResponse<>(errorType.getCode(), errorType.getMessage());
public static ApiResponse error(ErrorType errorType) {
return new ApiResponse(errorType.code(), errorType.message());
}

public static <T> ApiResponse<T> error(ErrorType errorType, T data) {
return new ApiResponse<>(errorType.getCode(), errorType.getMessage(), data);
public static ApiResponse error(ErrorType errorType, Object data) {
return new ApiResponse(errorType.code(), errorType.message(), data);
}

public static ApiResponse<?> error(ErrorType errorType, String message) {
return new ApiResponse<>(errorType.getCode(), message);
}

public static <T> ApiResponse<T> error(ErrorType errorType, String message, T data) {
return new ApiResponse<>(errorType.getCode(), message, data);
}

public static <T> ApiResponse<Exception> error(ErrorType errorType, Exception e) {
return new ApiResponse<>(errorType.getCode(), errorType.getMessage(), e);
}

}
47 changes: 47 additions & 0 deletions src/main/java/slvtwn/khu/toyouserver/common/ApiResponseAdvice.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package slvtwn.khu.toyouserver.common;


import jakarta.servlet.http.HttpServletResponse;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

@RestControllerAdvice(basePackages = "slvtwn.khu.toyouserver")
public class ApiResponseAdvice implements ResponseBodyAdvice<Object> {

@Override
public boolean supports(MethodParameter returnType, Class converterType) {
return true;
}

@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
HttpServletResponse servletResponse =
((ServletServerHttpResponse) response).getServletResponse();

HttpStatus resolve = HttpStatus.resolve(servletResponse.getStatus());

if (!(body instanceof ApiResponse)) {
assert resolve != null;
return createResponseByHttpStatus(resolve, body);
}
return body;
}

private Object createResponseByHttpStatus(HttpStatus status, Object body) {
if (status.is2xxSuccessful()) {
return ApiResponse.success(SuccessType.OK, body);
} else if (status.is4xxClientError()) {
return ApiResponse.error(ErrorType.BAD_REQUEST);
} else if (status.is5xxServerError()) {
return ApiResponse.error(ErrorType.INTERNAL_SERVER_ERROR);
}
return ApiResponse.error(ErrorType.RESPONSE_FORMAT_ERROR, body);
}
}
41 changes: 33 additions & 8 deletions src/main/java/slvtwn/khu/toyouserver/common/ErrorType.java
Original file line number Diff line number Diff line change
@@ -1,17 +1,42 @@
package slvtwn.khu.toyouserver.common;

import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;

@Getter
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public enum ErrorType {

// 404 (Not Found)
NOT_FOUND("TYU404", "Not Found");
// 400 ~ 499 (요청 오류)
RESPONSE_FORMAT_ERROR(HttpStatus.BAD_REQUEST, "TYU-400", "응답 형식 오류"),
BAD_REQUEST(HttpStatus.BAD_REQUEST, "TYU-400", "잘못된 요청입니다."),
NOT_FOUND(HttpStatus.NOT_FOUND, "TYU-404", "요청한 자원을 찾을 수 없습니다."),

private final String code;
// 500 ~ 599 (서버 오류)
INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "TYU-500", "서버 내부 오류");

ErrorType(
final HttpStatusCode httpStatusCode,
final String code,
final String message
) {
this.httpStatusCode = httpStatusCode;
this.code = code;
this.message = message;
}

private final HttpStatusCode httpStatusCode;
private final String code;
private final String message;

public HttpStatusCode httpStatusCode() {
return httpStatusCode;
}

public String code() {
return code;
}

public String message() {
return message;
}

}
5 changes: 4 additions & 1 deletion src/main/java/slvtwn/khu/toyouserver/common/SuccessType.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;

@Getter
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public enum SuccessType {

// 200 (OK)
OK("TYU200", "success");
OK(HttpStatus.OK, "TYU-200", "응답 성공");

private final HttpStatusCode httpStatusCode;
private final String code;
private final String message;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package slvtwn.khu.toyouserver.response;

import slvtwn.khu.toyouserver.common.PageInfoResponse;

public record PageableDto(
Long id,
String name,
PageInfoResponse pageInfo) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package slvtwn.khu.toyouserver.response;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RequestMapping("/test")
@RestController
public class ResponseController {

private final ResponseService responseService;

public ResponseController(ResponseService responseService) {this.responseService = responseService;}

@GetMapping
public PageableDto health() {
return responseService.health();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package slvtwn.khu.toyouserver.response;

import static org.mockito.BDDMockito.given;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.web.servlet.MockMvc;
import slvtwn.khu.toyouserver.common.PageInfoResponse;

@AutoConfigureMockMvc
@SpringBootTest
public class ResponseControllerTest {

@Autowired
private MockMvc mockMvc;

@MockBean
private ResponseService responseService;

@DisplayName("서버 응답 테스트를 실행한다.")
@Test
public void 응답_테스트() throws Exception {
PageableDto expect = new PageableDto(1L, "test", PageInfoResponse.of(1, 1, 1));
given(responseService.health())
.willReturn(expect);
mockMvc.perform(get("/test"))
.andExpect(status().isOk())
.andExpect(result -> {
String contentAsString = result.getResponse().getContentAsString();
System.out.println(contentAsString);
});
}
}
13 changes: 13 additions & 0 deletions src/test/java/slvtwn/khu/toyouserver/response/ResponseService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package slvtwn.khu.toyouserver.response;

import org.springframework.stereotype.Service;
import slvtwn.khu.toyouserver.common.PageInfoResponse;

@Service
public class ResponseService {

public PageableDto health() {
PageInfoResponse pageInfo = PageInfoResponse.of(1, 1, 1);
return new PageableDto(1L, "health", pageInfo);
}
}