BACKEND/JAVA & SPRING

@Builder란?

이-프 2023. 11. 1. 00:57

Lombok 라이브러리의 @Builder 어노테이션은 정말 많이 사용하는 어노테이션이다. 항상 생성자 자체를 사용하기 보단 @Builder을 사용해서 생성자를 만들곤 했다. 하지만 사용하면서도 Builder의 패턴이나 사용이유에 대해 깊게 생각하지 못하고 넘어갔던 것 같아 정리하고자 한다. 이번 기회에 공식문서 읽는 습관도 들여야겠다. 🌱

@Builder와 일반 객체 생성의 차이점

  • 객체를 생성하는 대표적인 방법은 “생성자 패턴”, “자바빈 패턴”, “빌더 패턴”등이 있다.
  • 근데 왜 우리는 “빌더패턴”을 유용하게 사용하는걸까?

Telescoping Constructor Pattern (점층적 생성자 패턴)

  • 각 생성자를 오버로딩해서 만드는 기초적인 생성자 패턴
    public class Member {
    
        private String name;
    		private String id; 
        private String password; 
    
        public Member(String name) {
            this.name = name;
        }
    
        public Member(String name, String id) {
            this.name= name;
            this.id = id;
        }
    
        public Member(String name, String id, String password) {
            this.name = name;
    				this.id = id;
            this.password = password;
        }
    }
  • 장점 : 가장 기본적인 패턴으로 쉽게 작성 가능
  • 단점 : 인자가 많아질수록 생성자가 많아짐 + 매개변수의 정보 설명 불가능

    ⇒ 코드 수정 또는 필드 추가가 복잡해진다!

Java Beans Pattern (자바 빈 패턴)

  • getter/setter을 이용해서 객체를 생성할 때, 필드를 주입하는 방식
    public class Member {
    
        private String name;
    		private String id; 
        private String password; 
    
        public Member() {
    		}
    
    		public void setName(String name) {
    		        this.name = name;
    		}
    
        public void setId(String id) {
            this.id = id;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    
    }
  • 장점 : 이전 생성자패턴보단 가독성이 좋아진다.
  • 단점 : getter/setter로 인해 코드량이 늘어남 + setter메소드는 값이 계속 변경될 수 있기에 객체일관성 깨짐

    ⇒ 한 번 객체를 생성할 때, 그 객체가 변할 여지가 있다.


정리 | 기존 생성자 패턴 ↔ 빌더 패턴

1️⃣ 생성자는 파라미터가 많을 경우 가독성이 좋지 않다.

  • City 클래스는 생성자 파라미터가 8개나 된다.
  • 여기에 파라미터가 더 추가된다면? 각 값들이 어떤 것을 의미하는지 파악하기 어렵다.
    	City city = new City("지역1", "도로A", "20만", null, null);
  • 이를 Builder 패턴으로 적용시켜보자.
    City city = City.builder()
    						.name()
    						.roadAddress()
    						.population()
    						.latitude()
    						.longitude()
    						.build();
  • Builder 패턴을 사용시, 각 값들이 어떤것을 의미하는지 파악하기 쉬워진다.
  • 즉, 가독성이 매우 편리해지고 같은 타입의 변수를 서로 바꿔 넣는 것을 방지할 수 있다.

2️⃣ 불필요한 매개변수의 값까지 지정해줘야 한다.

  • 생성자 호출을 위해 설정하길 원하지 않는 매개변수의 값까지 null로 지정해줘서 호출해야한다.
    City city = new City("지역1", "도로A", "20만", null, null);

3️⃣ 매개변수의 순서가 바뀌면 에러가 난다.

  • 생성자는 매개변수의 순서가 곧 생성자의 필드 순서와 같다.
  • 그러므로, 매개변수의 순서가 달라져서 type Error가 날 수있다.
  • 또한, 컴파일러가 해당 에러를 잡지 못하면 런타임에러로 이어질 수 있다.


@Builder란?

  • @Builder란, Lombok에서 제공하는 어노테이션으로, 생성자 인자를 메서드 체인을 통해 명시적으로 대입하여 생성자를 호출할 수 있게 클래스를 생성해준다.
  • IDE에서 Builder를 사용하게 된다면 자동완성 기능이 있어 생성자 작성보다 오기입 확률을 낮출 수 있다.
  • @Builder는 생성자, 메서드 또는 클래스 레벨에서 사용 가능하다.

Builder Pattern (빌더패턴)

  • @Builder 어노테이션을 사용해서 생성자를 편리하게 만들도록 하는 패턴
  • 디자인 패턴 중 하나로, 생성과 표현의 분리를 의미한다.
  • 즉, 복합 객체의 생성 과정과 표현 방법을 분리해 동일한 생성 절차에서 다른 표현 결과를 만들 수 있는 패턴이다.
  • 클래스를 설계하다보면, 필수로 받아야하는 인자들이 있고, 선택적으로 받아야하는 인자들이 있다.
  • 이때 생성자에서 인자들이 많을 때, 빌더패턴을 주로 사용한다.

클래스 레벨

  • 클래스 레벨에서 @Builder를 붙이면, 모든 요소를 받는 package-private 생성자가 자동으로 생성된다.
  • setter 메서드를 만들 필요 없이 모든 필드가 포함된 생성자가 생성된다.
  • 생성한 객체를 불변으로 만들 수 있다. 즉, 필드값 설정 이후 변경 불가하다.
@Getter
@Builder
public class AccommodationResponseDTO {

    private Long itineraryId;
    private String itineraryName;
    private String accommodationName;
    private String accommodationRoadAddressName;
    private String checkIn;
    private String checkOut;
}

생성자 레벨

  • 별도의 생성자를 직접 만들고, 빌더 패턴을 적용한다.
  • private 생성자를 가지는 클래스Builder라는 이름의 내부 빌더 클래스를 생성하여 빌더 패턴을 구현한다.

☑️ 클래스 레벨 ↔ 생성자 레벨

  • 클래스 레벨은 가능한 모든 필드에 대하여 빌더 메서드를 생성
  • 생성자 레벨에서는 생성자의 파라미터 필드에 대해서만 빌더 메서드를 생성
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
public class Trip {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    private LocalDate startDate;

    private LocalDate endDate;

    @Builder
    public Trip(Long id, String name, LocalDate startDate, LocalDate endDate) {
        this.id = id;
        this.name = name;
        this.startDate = startDate;
        this.endDate = endDate;
    }

메서드 레벨

  • 메서드레벨에서는 @Builder가 자주 사용되지 않는다.
  • 이는 객체 초기화 및 불변성을 목표로하는 빌더패턴의 목적에 벗어나기 때문이다.
public class Car {
    private String make;
    private String model;
    private int year;
    private int price;

    @Builder
    public static Car createCar(String make, String model, int year, int price) {
        return new Car(make, model, year, price);
    }

		private Car(String make, String model, int year, int price) {
        this.make = make;
        this.model = model;
        this.year = year;
        this.price = price;
    }
}

🔗 참고 url

https://velog.io/@park2348190/Lombok-Builder의-동작-원리

https://charliezip.tistory.com/17


Uploaded by N2T

'BACKEND > JAVA & SPRING' 카테고리의 다른 글

Dispatcher Servlet이란?  (0) 2023.11.09
JPA 상속 관계 매핑  (0) 2023.11.07
MariaDB + JPA 연동  (0) 2023.11.01
올바른 URI 설계 - Path Variable 과 Query Parameter  (1) 2023.10.31
@Controller vs @RestController  (0) 2023.10.31