[Spring] Lombok @Builder 기본값에 관하여

kshired·2022년 1월 30일
1

Spring

목록 보기
5/11
post-thumbnail

Lombok 알아보기

요즘 Spring으로 개발을 할 때는, 대부분 Lombok 라이브러리에서 제공해주는 Annotation을 사용한다.

Lombok은 Java의 Annotation을 이용하여 중복되는 코드를 생성해주는 라이브러리이다.

예시로, @Getter, @Setter와 같은 Annotation을 사용하면 클래스의 필드를 위한 Getter, Setter를 자동으로 만들어준다.

예시를 보자.

Lombok을 사용하지 않고 Getter, Setter를 만드는 경우

public class Test{
    private Integer number;
    private String str;
    
    public Intger getNumber(){
        return this.number;
    }
    
    public String getStr(){
        return this.str;
    }
    
    public void setNumber(Integer number){
        this.number = number;
    }

    public void setStr(String str){
        this.str = str;
    }
}

Lombok @Getter, @Setter를 사용하는 경우

@Getter
@Setter
public class Test{
    private Integer number;
    private String str;
}

두 개의 차이를 보면, Getter, Setter를 만들지 않아도 Lombok이 코드를 Annotation 기반으로 생성해주기 때문에 코드양에서부터 차이를 보인다.

또한, 중복되게 작성해야하는 코드를 줄일 수 있기 때문에 가독성에도 도움을 준다.

이러한 여러 장점때문에 사용하는 Lombok에서 이번 포스트에서는 @Builder Annotation에 대해 알아보려고 한다.

Lombok의 @Builder Annotation 알아보기.

Builder Pattern

빌더 패턴은 객체에 생성을 위한 방법 중 하나로, 객체 생성시 여러 필드가 존재할 때 그것의 순서에 의해 생기는 문제나 명시적이지 못한 생성자 여러개에 의해 발생하는 문제를 해결하기 위해 나온 패턴으로 아래와 같은 방식으로 사용한다.

public class Test{
    private Integer number;
    private String str;
    
    public static class Builder{
        private Integer number;
        private String str;
        
        public Builder number(Integer number){
            this.number = number;
            return number;
        }
        
        public Builder str(String str){
            this.str = str;
            return this;
        }
        
        public Test build(){
            return new Test(number, str);
        }
    }
}

public class TestMain{
   @Test
   void test1(){
       Test test = Test.builder()
                       .number(1)
                       .str("test")
                       .build();
       System.out.println("output : " + test.getNumber() + " " + test.getStr());
       
       Test test2 = Test.builder()
                       .str("test2")
                       .number(2)
                       .build();
       System.out.println("output : " + test2.getNumber() + " " + test2.getStr());
   }
}
output : 1 test
output : 2 test2

위와 같이 builder 패턴을 이용하게 되면, 생성자의 필드 순서를 알 필요 없이 필드명을 통해서 새로운 객체를 생성할 수 있고 더욱 명시적으로 객체 생성이 손쉽게 가능해진다.

하지만, 여기서 생기는 문제는 매 객체마다 builder를 만드는 것은 꽤나 수고롭고 반복적인 작업이라는 것이다.

이것을 해결할 수 있는 방법 중 하나가 Lombok의 Builder Annotation이다.

그것을 아래에서 알아보자.

@Builder Annotation 사용하기

@Builder
@Getter
public class Test{
    private Integer number;
    private String str;
}

public class TestMain{
   @Test
   void test1(){
       Test test = Test.builder()
                       .number(1)
                       .str("test")
                       .build();
       System.out.println("output : " + test.getNumber() + " " + test.getStr());
   }
}
output : 1 test

위에서 알 수 있듯이, Lombok의 @Builder Annotation은 자동으로 Builder Pattern에 맞게 builder 클래스를 생성해주고 그것을 사용할 수 있게 한다.

아까 위에서 사용했던 코드보다 코드가 훨씬 줄고, 쉽게 사용할 수 있다는 것이 느껴진다.

@Builder Annotation을 사용하여 build시 값을 넣지 않는다면?

@Builder
@Getter
public class Test{
    private Integer number;
    private String str;
}

public class TestMain{
   @Test
   void test1(){
       Test test1 = Test.builder()
                       .str("test")
                       .build();
       System.out.println("test1 output : " + test1.getNumber() + " " + test1.getStr());
       Test test2 = Test.builder()
                       .number(1)
                       .build();
       System.out.println("test2 output : " + test2.getNumber() + " " + test2.getStr());
   }
}
test1 output : 0 test
test2 output : 1 null

위와 같이 builder 를 사용하여 build를 했을 때, 특정 필드에 값을 지정해주지 않으면 0 혹은 null과 같은 값을 보이고 있다.

그러면 우리가 이 값을 지정하지 않았을 때, default로 지정하려면 어떻게 해야할까?

시도 1 - field에 기본값 초기화하기

@Builder
@Getter
public class Test{
    private Integer number = 2;
    private String str = "default";
}

public class TestMain{
   @Test
   void test1(){
       Test test1 = Test.builder()
                       .str("test")
                       .build();
       System.out.println("test1 output : " + test1.getNumber() + " " + test1.getStr());
       Test test2 = Test.builder()
                       .number(1)
                       .build();
       System.out.println("test2 output : " + test2.getNumber() + " " + test2.getStr());
   }
}
test1 output : 0 test
test2 output : 1 null

시도의 의도는 필드에 값을 미리 초기화해두면, "이 값을 쓰겠지?" 라는 의도였지만..

Builder 패턴을 사용했을 때의 결과를 보면 그렇지 않다는 것을 알 수 있다.

왜 그럴까?

우리는 그걸 알기 위해 Lombok의 문서에서 Builder에 대해 읽어보자.

@Builder.Default
If a certain field/parameter is never set during a build session, then it always gets 0 / null / false. If you've put @Builder on a class (and not a method or constructor) you can instead specify the default directly on the field, and annotate the field with @Builder.Default:
@Builder.Default private final long created = System.currentTimeMillis();

Builder에 대해 읽어보면 위와 같은 문구가 있다.

요약하자면, "builder 사용시 필드에 대한 값을 지정하지 않으면 타입에 따라 0, null, false가 초기화 될테니, @Builder.Default Annotation을 명시하고 그 변수에 값을 초기화해라." 이다.

이제 우리는 문제의 원인과 해결책을 찾았으니 시도해보자.

시도 2 - @Builder.Default 사용하기

@Builder
@Getter
public class Test{
    @Builder.Default
    private Integer number = 2;
    
    @Builder.Default
    private String str = "default";
}

public class TestMain{
   @Test
   void test1(){
       Test test1 = Test.builder()
                       .str("test")
                       .build();
       System.out.println("test1 output : " + test1.getNumber() + " " + test1.getStr());
       Test test2 = Test.builder()
                       .number(1)
                       .build();
       System.out.println("test2 output : " + test2.getNumber() + " " + test2.getStr());
   }
}
test1 output : 2 test
test2 output : 1 default

우리가 원했던 결과를 얻었다.

시도는 성공하였고, 원인도 알아냈으니 아래에서 정리해보자.

Lombok @Builder 기본 값에 대한 정리

  1. Builder를 사용하여 생성시 값을 지정하지 않으면 기본적으로는 타입에 따라 0, null, false 값이 할당된다.
  2. 그것의 기본값을 지정하기 위해, class의 field에 초기화만 하는 것은 의미가 없다.
  3. @Builder.Default Annotation을 명시하고, 초기화를 해야 기본값으로 사용할 수 있다.

... 오늘의 궁금증을 이렇게 순차적으로 해결해보았다.

평소에 Builder를 사용하면서, 아무 생각없이 사용했었는데 몰랐던 것을 많이 알아간다.

오늘의 궁금증 해결 끝..

profile
글 쓰는 개발자

0개의 댓글