본문 바로가기

Spring Boot

Spring Boot의 ResponseEntity

Spring Boot에선 기본적으로 ResponseEntity<T> 라는 클래스를 제공한다. 이는 Http 요청에 대한 Response를 설정할 수 있는 클래스로, Body, Header, StatusCode를 설정할 수 있다. 

이 ResponseEntity<T> 클래스를 사용해 적절한 Response를 보낼 수 있다. 

ResponseEntity는 HttpEntity를 상속받는다.

public class HttpEntity<T> {

	private final HttpHeaders headers;

	@Nullable
	private final T body;
}
public class ResponseEntity<T> extends HttpEntity<T> {
    private final HttpStatusCode status;

    public ResponseEntity(HttpStatusCode status) {
        this((Object)null, (MultiValueMap)null, status);
    }

    public ResponseEntity(@Nullable T body, HttpStatusCode status) {
        this(body, (MultiValueMap)null, status);
    }

    public ResponseEntity(MultiValueMap<String, String> headers, HttpStatusCode status) {
        this((Object)null, headers, status);
    }

    public ResponseEntity(@Nullable T body, @Nullable MultiValueMap<String, String> headers, int rawStatus) {
        this(body, headers, HttpStatusCode.valueOf(rawStatus));
    }

    public ResponseEntity(@Nullable T body, @Nullable MultiValueMap<String, String> headers, HttpStatusCode statusCode) {
        super(body, headers);
        Assert.notNull(statusCode, "HttpStatusCode must not be null");
        this.status = statusCode;
    }
    ....
}

 

 

 

ResponseEntity는 사용자의 HttpRequest에 대한 응답 데이터를 포함하는 클래스이다. 따라서 HttpStatus, HttpHeaders, HttpBody를 포함한다. 이 ResponseEntity의 Body에 담을 데이터 클래스를 만들어보자.

 

우선, Response Body의 형식을 통일해야 한다. 프론트엔드 입장에서 통일되어 있지 않은 Response Body는 매우 불쾌하다. API 마다 형식이 다 다르다고 한번 생각해 보면 이해할 수 있을 것이다. 이는 유지보수 자원을 낭비하는 설계이다. 우선 Response Body에 담은 데이터들을 가지는 ResponseDto 클래스를 만들자.

 

 

@Getter
@AllArgsConstructor
public class ResponseDto<T> {
    private final String statusCode;
    private final String message;
    private final T data;

    public static <T> ResponseDto<T> res(final HttpStatusCode statusCode, final String message) {
        return new ResponseDto<>(String.valueOf(statusCode.value()), message, null);
    }

    public static <T> ResponseDto<T> res(final HttpStatusCode statusCode, final String message, final T data) {
        return new ResponseDto<>(String.valueOf(statusCode.value()), message, data);
    }
}

 

 

 

우리는 프론트엔드에 Response를 줄 때 다음과 statusCode, message, data 형식으로 넘겨줄 것이다. Response Body 예시는 이렇게 된다.

{
	"statusCode": 200,//응답 코드
	"message": "OK",//응답에 대한 메시지
	"data": [어쩌고저쩌고]//응답에 들어갈 데이터
}

 

 

 

코드에 대한 자세한 설명은 생략한다.

이 클래스를 ResponseEntity<T>에 담아 Response를 보낼 수 있다.

Controller에서 다음과 같이 사용해보자.


반환값(ResponseDto의 data)이 없는 경우

@DeleteMapping("/{id}")
public ResponseEntity<ResponseDto<Void>> deleteMemoById(@PathVariable("id") UUID id) {
    return this.memoService.deleteMemoById(id);
}
public ResponseEntity<ResponseDto<Void>> deleteMemoById(UUID memoId) {
    this.memoRepository.deleteById(memoId);
    return new ResponseEntity<>(ResponseDto.res(
            HttpStatus.OK,
            "Success"
    ), HttpStatus.OK);
}

 

 

반환 결과

{
	"statusCode": 200,
	"message": "Success",
	"data": null
}

반환값(ResponseDto의 data)이 있는 경우

@GetMapping("/{id}")
public ResponseEntity<ResponseDto<Memo>> getMemoById(@PathVariable UUID id) {
    return this.memoService.getMemoById(id);
}
public ResponseEntity<ResponseDto<Memo>> getMemoById(UUID id) {
    Memo memo = this.memoRepository.findById(id).orElseThrow();
    return new ResponseEntity<>(ResponseDto.res(
            HttpStatus.OK,
            "Success",
            memo
    ), HttpStatus.OK);
}

 

 

반환 결과

{
    "statusCode": 200,
    "message": "Success",
    "data": [
        {
            "id": "a5e82152-57ca-4cd5-b9f7-a89e418ec7af",
            "title": "memo1",
            "content": "hello",
            "createdAt": "2024-01-24T06:31:25.013+00:00",
            "updatedAt": "2024-01-24T06:31:25.013+00:00"
        }
    ]
}