@Builder 어노테이션
Spring Boot와 Lombok 라이브러리를 활용할 때 @Builder 어노테이션은 객체 생성 과정을 간소화하고, 더 유연하고 안정적인 코드를 작성할 수 있도록 돕는다. 이 어노테이션은 크게 두 가지 방식으로 사용될 수 있다: 클래스 전체에 적용하는 방식과 클래스 내부의 특정 메서드에 적용하는 방식이다. 이 두 방식은 객체 생성의 유연성과 사용 목적에서 차이를 보인다.
클래스 전체에 @Builder 선언
클래스 레벨에 @Builder를 선언하면, 해당 클래스의 모든 필드를 포함하는 빌더 클래스가 자동으로 생성된다. 이 방식은 클래스의 모든 필드를 빌더 패턴을 통해 설정할 수 있도록 하며, 불변 객체 패턴(Immutable Object Pattern)을 구현하기 위해 주로 사용된다. 클래스 레벨의 @Builder는 가장 일반적인 사용 방식으로, 객체 생성 시 필요한 모든 필드를 체인 방식으로 설정할 수 있는 편리한 API를 제공한다.
💡 불변 객체 패턴(Immutable Object Pattern): 객체가 한 번 생성되면 그 상태를 변경할 수 없게 하는 디자인 패턴이다. 이 패턴을 사용하는 객체는 생성 시점에 모든 필드값이 설정되고, 이후에는 그 상태가 변하지 않는다.
예시 코드
- Builder 어노테이션 선언
import lombok.Builder;
import lombok.Getter;
@Getter
@Builder
public class Person {
private final String name;
private final int age;
}
- Person 객체 생성
public class Main {
public static void main(String[] args) {
Person person = Person.builder()
.name("John Doe")
.age(30)
.build();
System.out.println("Name: " + person.getName() + ", Age: " + person.getAge());
}
}
이 예시에서 Person 클래스에 @Builder 어노테이션이 클래스 레벨에 적용되어 있다. 이를 통해 Person.builder() 메서드를 사용하여 빌더 객체를 생성하고, .name("John Doe").age(30).build()와 같이 체인 방식으로 필드를 설정한 후, .build() 메서드를 호출하여 최종 객체를 생성할 수 있다.
클래스 내부의 특정 메서드에 @Builder 선언
반면, 특정 메서드에 @Builder를 적용하는 방식은 해당 부분만을 대상으로 빌더 패턴을 적용하고자 할 때 사용된다. 이는 객체의 일부분만 특별히 생성하고자 하거나, 특정 생성 패턴을 클라이언트에 제공하고자 할 때 유용하다.
"특정 생성 패턴을 클라이언트에 제공하고자 할 때"란, 객체를 생성하는 과정에서 특정한 조건이나 규칙을 적용하고 싶은 경우를 말한다.
예를 들어, 객체 생성 시 필수적으로 설정해야 하는 필드가 있거나, 특정 필드들의 조합으로만 객체를 생성하고 싶은 경우 등이 이에 해당한다. 이럴 때, 빌더 패턴을 활용하여 클라이언트 코드가 이러한 규칙을 자연스럽게 따르도록 유도할 수 있다.
예시 코드
- Builder 어노테이션 선언
import lombok.Builder;
import lombok.Getter;
@Getter
public class Person {
private String name;
private Integer age;
private String email;
// name만으로 Person 객체를 생성하는 팩토리 메서드
@Builder(builderMethodName = "personWithName", builderMethodName = "build")
public static Person createWithName(String name) {
Person person = new Person();
person.name = name;
return person;
}
// name, age, email로 Person 객체를 생성하는 팩토리 메서드
@Builder(builderMethodName = "personWithDetails")
public static Person createWithDetails(String name, Integer age, String email) {
Person person = new Person();
person.name = name;
person.age = age;
person.email = email;
return person;
}
}
- Person 객체 생성
public class Main {
public static void main(String[] args) {
Person person1 = Person.personWithName()
.name("John Doe")
.build();
System.out.println("Name: " + person1.getName() + ", Age: " + person1.getAge()); // Age는 초기화되지 않았기 때문에 기본값인 0을 출력
Person person2 = Person.personWithDetails()
.name("Jane King")
.age(20)
.email("jane@email.com")
.build();
System.out.println("Name: " + person2.getName() + ", Age: " + person2.getAge() + ", Email: " + person2.getEmail());
}
}
이 예시에서는 Person 클래스 내에 createWithName 메서드와 createWithDetails 메서드에 @Builder를 적용하고 있다.
createWithName 메서드의 경우, Person.personWithName().name("John Doe").build();와 같이 사용하여 name 필드만 초기화된 Person 객체를 생성할 수 있다.
personWithDetails 메서드의 경우, Person.personWithDetails().name("John Doe").age(20).email("jane@email.com")build();와 같이 사용하여 초기화된 Person 객체를 생성할 수 있다.
builderMethodName과 buildMethodName을 사용해 메서드 이름을 직접 지정할 수 있으며, 이를 통해 빌더의 사용법을 좀 더 유연하게 조절할 수 있다.
builderMethodName , buildMethodName
@Builder 어노테이션에서 builderMethodName과 buildMethodName 옵션은 빌더 패턴을 구현할 때 사용되는 메서드의 이름을 사용자 정의할 수 있게 해주는 속성이다. 각각의 역할은 다음과 같다:
builderMethodName
이 옵션은 빌더 패턴을 시작하기 위해 호출하는 정적 팩토리 메서드의 이름을 정의한다. 즉, 이 메서드를 호출하면 빌더 객체가 생성되고 반환된다. @Builder 어노테이션을 사용하는 메서드나 클래스에 이 이름을 지정함으로써, 사용자가 빌더 패턴을 시작할 때 사용할 메서드 이름을 커스터마이징 할 수 있다.
buildMethodName
이 옵션은 빌더 패턴을 통해 모든 필요한 설정을 마친 후, 최종 객체를 생성하여 반환하는 메서드의 이름을 정의한다. 기본적으로 build라는 이름이 사용되지만, 이 속성을 통해 다른 이름으로 변경할 수 있다. 즉, 빌더 패턴의 마지막 단계에서 호출되는 메서드의 이름을 사용자가 정의할 수 있게 해 준다.
이렇게 이름을 사용자 정의할 수 있게 함으로써, 코드의 가독성을 높이고, 빌더 패턴의 사용을 더 명확하게 할 수 있다.
결론
- 클래스 레벨의 @Builder: 객체의 모든 필드를 빌더를 통해 설정할 수 있는 가장 일반적인 방법으로, 전체적인 객체 생성에 유용하다.
- 메서드 레벨의 @Builder: 객체의 일부분만 특별히 생성하고자 할 때 사용되며, 특정 생성 패턴을 제공하고자 할 때 적합하다.
두 방식은 서로 다른 상황과 요구 사항에 따라 선택하여 사용할 수 있으며, 때로는 한 클래스 내에서도 이 두 방식을 조합하여 사용하기도 한다.
'Spring > Spring Boot' 카테고리의 다른 글
객체 간 매핑을 도와주는 MapStruct 라이브러리(2) - 추가 어노테이션 (0) | 2024.04.09 |
---|---|
객체 간 매핑을 도와주는 MapStruct 라이브러리(1) - 기본 어노테이션 (0) | 2024.04.02 |
RequestDto에서 MultipartFile 필드 사용 방법: 객체 바인딩 방법부터 테스트 코드까지 (3) | 2024.01.13 |
[Spring Boot] 스프링 부트 3에 레디스 적용하기 (1) | 2024.01.09 |
스프링 부트 3 버전에서 AWS SNS 클라이언트 여러 개 사용하기 (2) | 2023.12.29 |