스프링 부트 성공 응답 전역처리

췌누의 개발·2024년 9월 1일
post-thumbnail

행운복권 프로젝트를 진행하면서 api 통신을 할 때 api마다 응답을 따로 생성했었다. 프로젝트 특성상 효율적이지 않을 것이라고 판단했다. 응답을 공통적으로 한 번에 처리하고자 한다.


SuccessResponse

{
    "success": true,
    "status": 200,
    "data": { },
    "timeStamp": "2024-09-01T22:27:27.142128"
}

우리 프로젝트의 성공 응답 스펙이다. 기존에는 Controller에서 객체를 만들어서 return 하는 형식으로 코드를 작성했다. 이것을 전역적으로 처리할 수 있도록 구현하고자 한다.


ResponseBodyAdvice

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

profile
아샷추를 좋아합니다

0개의 댓글