๐ ๊ธฐ์กด ํ๋ก์ ํธ
ํ๋ก์ ํธ์์ ์ปค์คํ ๋ฆฌ์คํฐ์ค๋ฅผ ์ด์ฉํ์ฌ API response๋ฅผ ๋ณด๋ด์ฃผ๊ธฐ๋ก ํ์๋ค.
@GetMapping(value = "")
@Operation(summary = "ํํ ์์ธ ์ ๋ณด ์กฐํ", description = "ํํ ์์ธ ์ ๋ณด ์กฐํ API ์
๋๋ค.")
public CommonResponse<GetTeamDetailDto> getTeamDetail(@AuthUser User authUser,
@Valid @RequestParam("teamId") Long teamId) {
log.info("[api-get] ํ ์์ธ ์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ");
GetTeamDetailDto teamDetail = teamsService.getTeamDetail(authUser, teamId);
return CommonResponse.onSuccess(HttpStatus.OK.value(), teamDetail);
}
@PostMapping(value = "")
@Operation(summary = "ํํ ์์ฑ", description = "ํํ ์์ฑ API ์
๋๋ค.\n"
+ "stage๊ฐ ์์ด๋ []๋ฅผ ๋ณด๋ด์ค์ผ ํฉ๋๋ค.")
public CommonResponse<PostTeamResDto> postTeam(@AuthUser User authUser,
@Valid @RequestBody PostTeamDto postTeamDto) {
log.info("[api-post] ํ ์์ฑ");
PostTeamResDto postTeamResDto = teamsService.createTeam(authUser, postTeamDto);
return CommonResponse.onSuccess(HttpStatus.CREATED.value(), postTeamResDto);
}
Common Response ์์๋ณด๊ธฐ ↓
@Getter
public class CommonResponse<T> {
@JsonProperty("status")
private int code;
@JsonProperty("isSuccess")
private boolean success;
@JsonProperty("message")
private String message;
@JsonProperty("data")
@JsonInclude(JsonInclude.Include.NON_NULL)
private T data;
@Builder
public CommonResponse(int code, boolean success, String message, T data) {
this.code = code;
this.success = success;
this.message = message;
this.data = data;
}
public static <T> CommonResponse<T> onSuccess(int code) {
return CommonResponse.<T>builder()
.code(code)
.success(true)
.message("์์ฒญ์ ์ฑ๊ณตํ์์ต๋๋ค.")
.data(null)
.build();
}
public static <T> CommonResponse<T> onSuccess(int code, T data) {
return CommonResponse.<T>builder()
.code(code)
.success(true)
.message("์์ฒญ์ ์ฑ๊ณตํ์์ต๋๋ค.")
.data(data)
.build();
}
}
์ ์ฝ๋๋ ์ด๊ธฐ ๊ตฌํํ ์ปจํธ๋กค๋ฌ์ด๋ค.
์ฝ๋๋ฅผ ๋ณผ ๊ฒฝ์ฐ return ๋ถ๋ถ์ custom response๋ฅผ ์์ฑํ๋ ๋ถ๋ถ์ด ์ค๋ณต๋์ด์๋ค.
์ด๋ฅผ ํด๊ฒฐํ๊ณ ์ ํ๋ค.
๐ ResponseBodyAdvice ์ปค์คํ
๐ต ResponseBodyAdvice ๋?
ResponseBodyAdvice๋ controller์์ ์ฒ๋ฆฌ๋ ์๋ต์ ๊ฐ๋ก์ฑ์, ํด๋น ์๋ต์ ๋ณํํ๊ฑฐ๋ ์ถ๊ฐ์ ์ธ ์ฒ๋ฆฌ๋ฅผ ์ํํ ์ ์๋๋ก ํด์ค๋ค. ์ด๋ฅผ ํตํด ์ปจํธ๋กค๋ฌ์์ ์ฒ๋ฆฌ๋ ๋ฐ์ดํฐ๋ฅผ ์กฐ์ํ๊ณ , ํด๋ผ์ด์ธํธ์๊ฒ ๋ณด๋ด๊ธฐ ์ ์ ๋ณํํ๊ฑฐ๋, ์ถ๊ฐ์ ์ธ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ์ฝ์ ํ ์ ์๋ค.
public interface ResponseBodyAdvice<T> {
boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType);
@Nullable
T beforeBodyWrite(@Nullable T body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response);
}
์ด๋ฐ ์์ผ๋ก interface๊ฐ ๊ตฌ์ฑ๋์ด์๋๋ฐ
์ฝ๊ฒ ๋งํ๋ฉด, support๋ controller ์์ ์ด ๋๋ response๋ฅผ beforeBodyWrite ๋ฉ์๋์ ๋ณด๋ผ์ง ํ๋จํ๋ ์ญํ ์ ํ๊ณ , beforeBodyWrite ๋ฉ์๋๋ ์ค์ ์ฌ์ฉ์๊ฐ ์ํ๋ body์ ๊ฐ์ ๊ต์ฒด ๋๋ response์ ํค๋ ์ ๋ณด๋ฅผ ์ถ๊ฐํ ์ ์๋ค.
์ด๋ฅผ ์ด์ฉํ์ฌ controller ๋จ์์ ๋ฐ์ดํฐ๋ง ๋ฐํํ๋ฉด, response๊ฐ Common Response๋ก ๋ณํ๋๋๋ก ๊ตฌํํ ๊ฒ์ด๋ค.
๐ต ์ ์ฉํ๊ธฐ
๐น์ ์ฒด์ฝ๋
์ ์ฒด ์ฝ๋๋ ์ด๋ฌํ๋ค
@RestControllerAdvice
public class SuccessResponseAdvice implements ResponseBodyAdvice<Object> {
private final String[] SwaggerPatterns = {
"swagger",
"/v2/api-docs"
};
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
return true;
}
@Nullable
public Object beforeBodyWrite(
Object body,
MethodParameter returnType,
MediaType selectedContentType,
Class selectedConverterType,
ServerHttpRequest request,
ServerHttpResponse response){
HttpServletResponse servletResponse =
((ServletServerHttpResponse) response).getServletResponse();
HttpServletRequest servletRequest =
((ServletServerHttpRequest) request).getServletRequest();
HttpMessageConverter
// ์ค์จ๊ฑฐ์ผ ๊ฒฝ์ฐ ๋ฆฌ์คํฐ์ค ์ฒ๋ฆฌ ์ํ๋๋ก
for (String swaggerPattern : SwaggerPatterns) {
if (servletRequest.getRequestURI().contains(swaggerPattern))
return body;
}
HttpStatus resolve = HttpStatus.resolve(servletResponse.getStatus());
if (resolve == null)
return body;
if (resolve.is2xxSuccessful())
return CommonResponse.onSuccess(statusProvider(servletRequest.getMethod()), body);
return body;
}
private int statusProvider(String method) {
if (method.equals("POST"))
return 201;
if (method.equals("DELETE"))
return 204;
return 200;
}
}
ResponseBodyAdvice์ ๊ตฌํ์ฒด๋ก ๋ง๋ ํ, ๋ ํจ์๋ฅผ ์ค๋ฒ๋ผ์ด๋ฉ ํ๋ ๋ฐฉ์์ผ๋ก ๊ตฌํํ์๋ค.
๐นsupport
๋จผ์ support ํจ์๋ถํฐ ๋ณด๊ฒ ๋ค
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
return true;
}
์์ฒญ์ด ์ด๋ ์ปจํธ๋กค๋ฌ์์ ์ค๋๊ฐ์ ์๊ด์์ด beforeBodyWrite ๋ฉ์๋๋ฅผ ์คํ์์ผ response๋ฅผ ์ปค์คํ ํ ๊ณํ์ด๊ธฐ ๋๋ฌธ์, ๋ค๋ฅธ ๋ณ๊ฒฝ, ํํฐ๋ง ์์ด true๋ง ๋ฐํํ๋ค.
๊ทธ๋ฌ๋ฏ๋ก beforeBodyWrite๊ฐ ์คํ๋๋ค.
๐นbeforeBodyWrite
@Nullable
public Object beforeBodyWrite(
Object body,
MethodParameter returnType,
MediaType selectedContentType,
Class selectedConverterType,
ServerHttpRequest request,
ServerHttpResponse response){
HttpServletResponse servletResponse =
((ServletServerHttpResponse) response).getServletResponse();
HttpServletRequest servletRequest =
((ServletServerHttpRequest) request).getServletRequest();
// ์ค์จ๊ฑฐ์ผ ๊ฒฝ์ฐ ๋ฆฌ์คํฐ์ค ์ฒ๋ฆฌ ์ํ๋๋ก
for (String swaggerPattern : SwaggerPatterns) {
if (servletRequest.getRequestURI().contains(swaggerPattern))
return body;
}
HttpStatus resolve = HttpStatus.resolve(servletResponse.getStatus());
if (resolve == null)
return body;
if (resolve.is2xxSuccessful())
return CommonResponse.onSuccess(statusProvider(servletRequest.getMethod()), body);
return body;
}
url์ด ์ค์จ๊ฑฐ ์ฐ๊ด๋ url์ด๊ฑฐ๋, http status๊ฐ 200์ด response์ ๊ฒฝ์ฐ, custom response๋ก ๋ณํํ์ง ์๋๋ก ๋ก์ง์ ๊ตฌ์ฑํ๋ค.
๋ง์ฝ ์ด ์กฐ๊ฑด์ ํต๊ณผํ๋ค๋ฉด CommonResponse ๋ผ๋ custom response๋ก ๋ณํํ์ฌ response๊ฐ ์ ๋ฌ๋๋๋ก ๊ตฌ์ฑํ๋ค.
๐น์ค์จ๊ฑฐ url ํ๋ณ ํ response ๋ถ๋ฆฌ
์ด๊ธฐ์ ์ค์จ๊ฑฐ url์ ๋ฐ๋ก ์ฒ๋ฆฌํ์ง ์์์ ๋, ์ ์๋ฌ๊ฐ ๋ฐ์ํ๋ค.
์ ๋ก์ง์ ๋ฐ๋ฅด๋ฉด, ๋ชจ๋ success response ์ฆ, 200๋ฒ๋ response๋ common response๋ก ๋ณํ๋๋ค.
์ ํํ ์ด์ ๋ ๋ชจ๋ฅด๊ฒ ์ผ๋, ์ด ๋ถ๋ถ์์ ์๋ฌ๊ฐ ๋ฐ์ํ ๊ฒ ๊ฐ๋ค.
๊ทธ๋์ ์ค์จ๊ฑฐ url์ ๊ฐ์ง response์ผ ๊ฒฝ์ฐ, ๋ฐ๋ก ์ฒ๋ฆฌํ์ฌ body๋ฅผ return ํด์ฃผ๋ ๋ก์ง์ ์ถ๊ฐํ๋ค.
private final String[] SwaggerPatterns = {
"swagger",
"/v2/api-docs"
};
...
// ์ค์จ๊ฑฐ์ผ ๊ฒฝ์ฐ ๋ฆฌ์คํฐ์ค ์ฒ๋ฆฌ ์ํ๋๋ก
for (String swaggerPattern : SwaggerPatterns) {
if (servletRequest.getRequestURI().contains(swaggerPattern))
return body;
}
๐นHttp Method๋ก Http status code ๋ถ๋ฅํ์ฌ response ์์ฑํ๊ธฐ
์ด๊ธฐ ์ฝ๋์์ common response๋ฅผ ์์ฑํ ๊ฒฝ์ฐ, ๋ชจ๋ http status code๊ฐ 200๋ฒ์ผ๋ก ์์ฑ๋์๋ค.
ํ์ง๋ง ๊ธฐ์กด ์ฝ๋ response๋ 201, 204 ๋ฑ 200 ์ด์ธ ๋ค์ํ http status code๋ฅผ ๋ฐํํ๋ค.
๊ทธ๋์ ์๋ ์ฝ๋๋ฅผ ์ถ๊ฐํ์ฌ Http Method์ ๋ฐ๋ผ http status code๋ฅผ ๋ถ๋ฅํ์ฌ common response๋ฅผ ์์ฑํ ์ ์๋๋ก ํ๋ค.
private int statusProvider(String method) {
if (method.equals("POST"))
return 201;
if (method.equals("DELETE"))
return 204;
return 200;
}
์ด ๋ฐฉ๋ฒ์ด ์ ํํ ๋ง๋ ๋ฐฉ๋ฒ์ธ์ง๋ ์๋ฌธ์ด๋ค...
์ข์ ๋ฐฉ๋ฒ ์์๋ ๋ถ ์์ผ์๋ฉด, ๋๊ธ ๋จ๊ฒจ์ฃผ์ธ์....
'Backend' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
<Spring> Redis ๊ธฐ๋ฐ ๊ฒ์์ด ์๋ ์์ฑ ๊ธฐ๋ฅ ๊ตฌํํ๊ธฐ (1) | 2023.10.01 |
---|---|
<Spring> AOP ๊ธฐ๋ฐ ๋ถ์ฐ๋ฝ (0) | 2023.10.01 |
<Spring> ResponseEntityExceptionHandler ์ด์ฉํ ๊ณตํต ์๋ฌ ์ฒ๋ฆฌ (0) | 2023.05.07 |
<Spring> HttpServletRequest ๊ฐ ์ฌ๋ผ์ง๋ ๋ฌธ์ (1) | 2023.05.06 |
<Spring> ๊ณตํต ์๋ฌ ํธ๋ค๋ฌ ๊ตฌํํ๊ธฐ (1) | 2023.03.28 |