상세 컨텐츠

본문 제목

@Builder 붙은 코드 @Builder 없는 코드와 비교해보기 [Lombok @Builder] [builder pattern 공부하기 - 1]

WEB/스프링

by 비행학교브론즈 2023. 5. 29. 10:42

본문

포스트를 하게 된 이유

 

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();
    }

    

}

 

다음 포스트는 빌더 패턴 튜토리얼을 번역한 내용이 될 것이다.

 

관련글 더보기