spring 강의를 들으면서 배운 내용으로는,
실무에서 Set 메서드를 사용하면 무분별하게 값이 변경되기 때문에 지양해야하고
필드의 개수가 늘어남에 따라 선택적으로 필드에 값을 넣기위해 @Builder를 사용한다고 했다.
이 내용에 대해 좀 더 깊이있게 다뤄보려고 한다.
@Builder 사용방법 ( With Lombok )
lombok dependency를 사용한다는 가정하에,
아래의 코드는 Builder 패턴을 구현한 대신에 롬복에서 제공하는 @Builder 를 적용한 예이다.
여기서는 email과 passwd에 대해서만 적용했는데 용도에 따라 매개변수를 추가해서 활용하면 된다.
..
public class MemberDto {
    private String email;
    private String passwd;
    private String address;
    private String tel;
    private Long age;
    @Builder // ★
    public MemberDto(String email, String passwd) {
        this.email = email;
        this.passwd = passwd;
    }
}
@Builder 를 적용한 생성자 안에 있는 필드의 데이터에 한하여, 아래와 같이 사용할 수 있다.
public class MemberService {
	..
    public void toDto(Member member) {
        MemberDto memberDto = MemberDto.builder()
                .email(member.getEmail())
                .passwd(member.getPasswd())
                .build();
    }
}
Builder Pattern ( without Lombok )
위에서는, 단순히 Lombok 의 @Builder 애노테이션 적용만으로 부가적인 코드 구현이 없었다.
Lombok 없이 Builder 패턴을 구현해보자.
public class MemberDto {
    private String email;
    private String passwd;
    private String address;
    private String tel;
    private Long age;
    
    public String getEmail() {
        return email;
    }
    public String getPasswd() {
        return passwd;
    }
    public String getAddress() {
        return address;
    }
    public String getTel() {
        return tel;
    }
    public Long getAge() {
        return age;
    }
    public MemberDto(Builder builder) {
        this.email = builder.email;
        this.passwd = builder.passwd;
        this.address = builder.address;
        this.tel = builder.tel;
        this.age = builder.age;
    }
    
    public static Builder builder() {
        return new Builder();
    }
    
    public static final class Builder{
        String email;
        String passwd;
        String address;
        String tel;
        Long age;
        private Builder(){
        }
        public Builder email(String email) {
            this.email = email;
            return this;
        }
        public Builder passwd(String passwd) {
            this.passwd = passwd;
            return this;
        }
        public Builder address(String address) {
            this.address = address;
            return this;
        }
        public Builder tel(String tel) {
            this.tel = tel;
            return this;
        }
        public Builder age(Long age) {
            this.age = age;
            return this;
        }
        public MemberDto build() {
            return new MemberDto(this);
        }
    }
}위와 같이 Builder Patten을 구현해봄으로서 좀 더 명확하게 동작에 대해 이해할 수 있다.
MemberDto에는 getter 만 있기때문에, 불변객체로 사용이 가능하며 사용방법은 @Builder와 동일하다.
        MemberDto memberDto = MemberDto.builder()
                .email("test@test.com")
                .passwd("1234")
                .address("서울")
                .tel("010-xxxx-xxxx")
                .age(20L)
                .build();
Builder 패턴의 이점
정리하자면, Builder 패턴의 이점은 다음과 같다.
1. Builder를 통해 불변 객체로 생성할 수 있다. ( 안정성 ↑ )
2. Builder를 통해 매개변수의 순서에 대한 오류를 예방할 수 있다. ( 가독성 ↑ )
1. Builder를 통해 불변 객체로 생성할 수 있다.
Setter를 사용하게 되면 어디서든 값이 변경이 가능하며, 필수 데이터 말고도 다른 데이터를 변경할 수 있다.
그렇지만 Builder를 사용하면 생성된 이후로 값을 변경할 수 없으며 필수 데이터 외에 다른 데이터에 대해 값을 지정할 수도 없다.
public class MemberService {
	..
    public void toDto(Member member) {
        // 1. setter
        MemberDto memberDto1 = new MemberDto();
        memberDto1.setEmail(member.getEmail());
        memberDto1.setPasswd(member.getPasswd());
        // memberDto1.setAge(member.getAge()); -> 가능
        // 2. builder
        MemberDto memberDto2 = MemberDto.builder()
                .email(member.getEmail())
                .passwd(member.getPasswd())
             // .age(member.getAge()) -> ★불가
                .build();
    }
}
2. Builder를 통해 매개변수의 순서에 대한 오류를 예방할 수 있다.
아래와 같이 모든 매개변수를 갖는 생성자를 통해 인스턴스를 생성하면 해당 값을 가지는 필드가 뭔지에 대해 명확히 나와있지 않기 때문에 순서에 대한 오류가 발생할 수 있다.
Builder를 사용하면 각 데이터에 대한 필드값이 명확하기 때문에 가독성 또한 좋아진다.
        MemberDto memberDto1 = new MemberDto("test@test.com","1234","서울","010-xxxx-xxxx",20L);
        
        /** Builder 적용 **/
        MemberDto memberDto2 = MemberDto.builder()
                .email("test@test.com")
                .passwd("1234")
                .address("서울")
                .tel("010-xxxx-xxxx")
                .age(20L)
                .build();
정리
사실 디자인 패턴의 Builder 패턴과 @Builder를 같다고 생각하지 못했었다.
디자인 패턴을 직접 내 코드에 적용해보고 구현해봄으로써 이해도가 높아진 것 같다.
이 외에도 다양한 디자인 패턴에 대해 공부해보자.
참고
https://johngrib.github.io/wiki/pattern/builder/
https://github.com/davidmoten/java-builder-pattern-tricks
'Web' 카테고리의 다른 글
| [Spring] 라이브러리 버전 관리하기 ( Gradle ) (0) | 2023.07.06 | 
|---|---|
| [Spring] Exception Handling 에 대해서 (3) | 2023.07.03 | 
| [Spring] @Email 과 이메일 정규식 표현 (0) | 2023.06.24 | 
| [Spring] Gradle 과 Maven 에 대해서 (0) | 2023.06.22 | 
| [Spring] HikariCP 와 Pool Size에 대해서 (0) | 2023.06.18 | 
