포스트를 하게 된 이유
RestController에서 오류 응답을 만들어 가던 중 책임 역할 배분에 대한 고민이 생겼다.
모든 응답에 대해서 DTO 클래스를 만들 것 인지.
서버에서는 최대한 간단하게 Http status만 response하여 프론트에서 response마다 처리할 것인지
서버에서 전적으로 사용자에게 보여질 message를 response 할 것 인지에 대한 고민이었다.
개발 경험이 많지 않기 때문에 어떤 경우가 더 만족할 만한 결과를 얻을 것인지 검색 하던 중 한 의견을 보게 되었는데,
만약 요청이 웹에서만 들어오게 된다면, 위 둘중 어느 것으로 선택하더라도 크게 문제 될 것이 없을 것으로 예상되지만,
서버로 들어오는 request는 웹 뿐만 아니라 앱 또한 들어 올 수 있으므로 프론트에서 예외 응답을 전적으로 처리하게 된다면 문제가 생길 수 있다는 것이었다.
예를 들어 개발이후에 사용자에게 보여질 응답의 형태가 달라져야 하는 상황이라면
앱에서 오는 reqeust를 처리하는 경우, '앱은 강제업데이트를 해야만 사용자에게 다른 응답 결과를 보여 줄 수 있다'는 의견 이었다.
그래서 서버에서 메세지를 처리하도록 결론을 내린 후
개발 구상 중 모든 경우에 수에 대해서 응답용 DTO를 만드는 것은 비효율 적이라 느꼈고
공통화 된 제네릭을 이용해 공통된 응답을 만들자는 결론에 도달하게 되었는데 그 과정에서 Builder pattern에 대해서 알게 되어 공부하고 정리하고자 블로그를 작성했다.
시작
이론적 배경은 후술하고 먼저 Builder Pattern 이 어떻게 구성 되는 지 주석을 통해서 1차적 구조를 보고자 한다.
Lombok을 이용한 코드 생성
package com.yet.project.web.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
//data toString, hashCode, getter, setter를 만들어 주는 annotation
@Data
//argument가 없는 생성자를 만들어 주는 annotation
@NoArgsConstructor
//무슨 argument를 만들어 주는 annotation
@AllArgsConstructor
//lombok의 builder pattern을 사용하게 해주는 annotation
@Builder
public class ApiResponse<T> {
//generic을 이용한 필드, response할 데이터가 들어간다.
private T data;
//HttpStatus Code 정보를 담은 필드
private int errorCode;
//어떤 메세지를 담을 것 인지 사용하는 필드
private String errorMessage;
//data 필드와 같은 자료형이 들어올 것이지만, generic method임을 강조하기 위해 T대신 X를 사용했다.
//generic인 data를 받아 응답 ApiResponse의 instance를 반환한다. 즉 생성자와 비슷한 역할을 한다.
public static <X> ApiResponse<X> success(X data) {
/*
ApiResponseBuilder<X> builder = ApiResponse.<X>builder();
ApiResponseBuilder<X> data1 = builder.data(data);
ApiResponse<X> build = data1.build();
*/
return ApiResponse.<X>builder().data(data).build();
}
}
Lombok이 지원해주는 어노테이션을 제거하여 풀어 쓴 코드
package com.yet.project.memo;
public class ApiResponse<T> {
private T data;
private int errorCode;
private String errorMessage;
//argument가 없는 생성자 ( = @NoArgsConstructor)
public ApiResponse() {
}
//모든 argument를 사용하는 생성자 ( = @AllArgsConstructor)
public ApiResponse(T data, int errorCode, String errorMessage) {
this.data = data;
this.errorCode = errorCode;
this.errorMessage = errorMessage;
}
//ApiResponse의 inner class인 ApiResponseBuilder<X>를 생성해주는 static 메서드
public static <X> ApiResponseBuilder<X> builder() {
return new ApiResponseBuilder<>();
}
//ApiResponse의 Builder class, ApiResponse의 대부분의 기능을 담당한다
public static class ApiResponseBuilder<X> {
private X data;
private int errorCode;
private String errorMessage;
private ApiResponseBuilder() {
}
//data를 set하는 setter, method channing을 사용하기 위해 자기 자신의 instance를 return 한다.
//ex) ApiResponseBuilder<X>.data(data).errorCode(errorCode)
public ApiResponseBuilder<X> data(X data) {
this.data = data;
return this;
}
//setter method
public ApiResponseBuilder<X> errorCode(int errorCode) {
this.errorCode = errorCode;
return this;
}
//setter method
public ApiResponseBuilder<X> errorMessage(String errorMessage) {
this.errorMessage = errorMessage;
return this;
}
//inner class에서 생성자의 역할을 대신해준다.
public ApiResponse<X> build() {
return new ApiResponse<>(data, errorCode, errorMessage);
}
}
//데이터가 응답이 성공적일 경우의 메서드, inner class인 ApiResponseBuilder를 통해서 ApiResponse의 응답을 생성한다.
public static <X> ApiResponse<X> success(X data) {
return ApiResponse.<X>builder().data(data).build();
}
}
다음 포스트는 빌더 패턴 튜토리얼을 번역한 내용이 될 것이다.
[SpringBoot+React+JWT+카카오 로그인] 카카오 로그인 (4) | 2024.08.29 |
---|---|
[SringBooot][Mysql] Mysql에서 유사 Cursor 방식으로 페이징 구현 (0) | 2024.06.22 |
롬복이용해서 코드 간단하게 하기 [Lombok @Builder] [builder pattern 공부하기 - 4] (0) | 2023.06.05 |
Builder Pattern 기초 따라하기 [Lombok @Builder] [builder pattern 공부하기 - 2] (0) | 2023.05.30 |
UrlResource class를 사용한 파일 다운로드 (0) | 2023.02.14 |