행운복권 프로젝트를 진행하면서 api 통신을 할 때 api마다 응답을 따로 생성했었다. 프로젝트 특성상 효율적이지 않을 것이라고 판단했다. 응답을 공통적으로 한 번에 처리하고자 한다.
SuccessResponse
{
"success": true,
"status": 200,
"data": { },
"timeStamp": "2024-09-01T22:27:27.142128"
}
우리 프로젝트의 성공 응답 스펙이다. 기존에는 Controller에서 객체를 만들어서 return 하는 형식으로 코드를 작성했다. 이것을 전역적으로 처리할 수 있도록 구현하고자 한다.
Spring Boot에서 ResponseBodyAdvice를 구현하여 성공적인 응답을 사용자 객체로 감싸는 기능을 제공한다. 내부를 살펴보면..
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);
}
beforeBodyWrite 메서드는 HTTP 응답 본문이 클라이언트에게 전송되기 전에 호출되고. 상태 코드에 따라서 응답 본문을 수정할 수 있다.
특정 컨트롤러 메서드의 반환 타입과 beforeBodyWrite 메서드를 적용할지 여부를 결정하는 데 사용한다.
SuccessResponse.java
@Getter
public class SuccessResponse {
private final boolean success = true;
private final int status;
private final Object data;
private final LocalDateTime timeStamp;
public SuccessResponse(int status, Object data) {
this.status = status;
this.data = data;
this.timeStamp = LocalDateTime.now();
}
}
성공 응답 스펙을 클래스로 생성했다. 상태, data, 현재 시간을 반환한다.
SuccessResponseAdvice.java
@RestControllerAdvice(basePackages = "uttugseuja.lucklotteryserver")
public class SuccessResponseAdvice implements ResponseBodyAdvice {
@Override
public Object beforeBodyWrite(
Object body,
MethodParameter returnType,
MediaType selectedContentType,
Class selectedConverterType,
ServerHttpRequest request,
ServerHttpResponse response) {
HttpServletResponse servletResponse =
((ServletServerHttpResponse) response).getServletResponse();
int status = servletResponse.getStatus();
HttpStatus resolve = HttpStatus.resolve(status);
if (resolve == null) {
return body;
}
if (resolve.is2xxSuccessful()) {
return new SuccessResponse(status, body);
}
return body;
}
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
return true;
}
}
전역으로 응답을 처리한 코드이다. 조금 자세히 들어가 보면
@RestControllerAdvice를 통해서 패키지 내부의 컨트롤러에 대해서 전역 처리를 하고자 했다.
HttpServletResponse servletResponse =
((ServletServerHttpResponse) response).getServletResponse();
int status = servletResponse.getStatus();
HttpStatus resolve = HttpStatus.resolve(status);
HttpServletResponse에서 HTTP 상태를 가져오고 HttpStatus를 반환한다.
if (resolve == null) {
return body;
}
if (resolve.is2xxSuccessful()) {
return new SuccessResponse(status, body);
}
응답 상태 코드가 2xx (성공) 범위에 해당하면, 본문을 SuccessResponse를 생성하여 반환한다.
WinningLotteryController.java
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/winning/lottery")
@Tag(name = "당첨 로또 관련 Controller", description = "당첨 로또 저장 및 조회 관련 기능 담당")
public class WinningLotteryController {
private final WinningLotteryService winningLotteryService;
@Operation(summary = "최근 회차의 당첨 로또 정보 조회(홈 페이지용)")
@GetMapping("/recent/round")
public WinningLotteryResponse recentRoundWinningLottery() {
return winningLotteryService.getRecentRoundWinningLottery();
}
}
로또 당첨번호 조회 Controller를 보면 중복을 제거하고 성공 응답을 전역적으로 처리한 것을 볼 수 있다.

우리 프로젝트의 응답 스펙에 맞게 응답을 제공하는 것을 확인할 수 있었다.
프로젝트 링크를 통해서 참고하시면 좋을 것 같습니다! 감사합니다.
도움이 되셨으면 좋겠습니다.👋🏼
행운 복권 깃허브 링크
https://github.com/Uttug-Seuja/luck-lottery-server