정구리의 우주정복
[Spring] @Builder 파헤치기 본문
항상 습관처럼 @Builder 와 @NoArgsConstructor ,@AllArgsConstructor 를 썼는데 왜 둘을 같이 써야하는지, 어떤 원리로 동작하는지도 모르고있었다
나는 바보 ! lombok 개발자 !!
알아보도록 하자
틀리거나 문제가 있다면 댓글로 알려주세요 감사합니다땡큐
@Builder 의 역할
- 생성자를 메서드 체인을 사용해서 호출할 수 있는 빌더 클래스를 생성해준다
- 순서도 상관없고, 값을 넣지 않은 경우 default 값이 들어가게 된다
@Builder 장점
- 쉽게 빌더 패턴을 사용할 수 있음 (객체 생성 후 값을 변경되지 않게 할 수 있다)
- 유연한 객체 생성 (필드 순서에 상관없음, 필요한 필드만 생성 가능)
- 옵션 값 처리 (필수 값만 넣어주고 나머지는 기본값을 사용가능함)
@Builder 사용법
사용법은 어렵지 않음
@Builder 붙여주기 + 생성자 가 있으면 된다
Recommend.java
@Builder
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class Recommend {
private final int id; // primitive type
private final String title; // wrapper class
private final boolean content;
@Override
public String toString() {
return "Recommend{" +
"id='" + id + '\'' +
", title='" + title + '\'' +
", content='" + content + '\'' +
'}';
}
}
생성자는 @AllArgsConstructor(access = AccessLevel.PRIVATE) 로 만들었다
내용 잘 보여줄라고 toString 하나정도 넣었음 !
TestController.java
@GetMapping("/test")
public String test() {
Recommend recommend = Recommend.builder()
.id(1)
.title("title")
.content(true)
.build();
return recommend.toString();
}
실행시 결과 값
Recommend{id='1', title='title', content='true'}
값을 넣지 않으면 어떻게 될까 ?
@GetMapping("/test")
public String test() {
Recommend recommend = Recommend.builder()
.build();
return recommend.toString();
}
원시타입은 default 값이, 래퍼클래스는 null 이 들어가는 것을 확인할 수 있다
Recommend{id='0', title='null', content='false'}
@Builder 원리
builder 를 비슷하게 만들어보며 원리를 공부해보자 !
@Builder
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class Recommend {
private final int id; // primitive type
private final String title; // wrapper class
private final Boolean content;
@Override
public String toString() {
return "Recommend{" +
"id='" + id + '\'' +
", title='" + title + '\'' +
", content='" + content + '\'' +
'}';
}
이녀석을 어노테이션 없는 상태로 만들어보자
public class Recommend {
private final int id;
private final String title;
private final boolean content;
// @AllArgsConstructor(access = AccessLevel.PRIVATE)
private Recommend(int id, String title, boolean content) {
this.id = id;
this.title = title;
this.content = content;
}
// @Builder
public static class Builder {
private int id;
private String title;
private boolean content;
public Builder id(int id) {
this.id = id;
return this;
}
public Builder title(String title) {
this.title = title;
return this;
}
public Builder content(boolean content) {
this.content = content;
return this;
}
public Recommend build() {
return new Recommend(id, title, content);
}
}
// .builder() 로 사용하기 위함
public static Builder builder() {
return new Builder();
}
@Override
public String toString() {
return "Recommend{" +
"id='" + id + '\'' +
", title='" + title + '\'' +
", content='" + content + '\'' +
'}';
}
}
생성자를 private 로 만든것은 public 으로 하면
@GetMapping("/test")
public String test() {
Recommend recommend = Recommend.builder()
.build();
Recommend recommend1 = new Recommend(1,"seo",false);
return recommend.toString();
}
builder 를 사용하지 않고 new . . . 이런식으로 생성이 가능하기 때문에 막아주려고 private 를 사용했다
builder().id().content().build()
이런식으로 사용하기 위해서
public static Builder builder() {
return new Builder();
}
를 넣어줬다
이거 없으면
@GetMapping("/test")
public String test() {
Recommend recommend = Recommend.builder()
.build();
Recommend.Builder builder = new Recommend.Builder();
Recommend recommend1 = builder.id(1).content(false).build();
return recommend.toString();
}
이렇게 만들어줘야함
@Builder
- @NoArgsConstructor, @RequiredArgsConstructor 어노테이션을 사용하고 @AllArgsConstructor를 함께 사용하지 않으면, 컴파일러 오류가 발생할 수 있다. 그 이유는 모든 인수 생성자가 있다고 가정하고 이를 사용하는 코드를 생성하므로 이 생성자가 없으면 오류가 발생하는 것이다. (출처)
지식이 늘었다
Setter 를 사용하면 언제든 값을 바꿀 수 있는 문제가 있는데
Builder 를 사용해서 값을 변경할 수 없도록 하면서 유연하게 객체 생성이 가능해지게 되었다 !
심지어 가독성도 좋아졌다 ! 굉장히 좋군 !