안녕하세요 여러분~!
김송아입니다.
여러분 잘 오셨습니다.
오늘은 빌더에 대해 직접! 구현해보는 시간을 가지려 합니다.
맨날 말로만 들었지 빌더 빌더....😇
사실 스프링에서 빌더 쓰고 싶으면, 보통 롬복으로 그냥 @builder 이렇게 만들어달라고 하곤 하잖아요?
💡롬복
자바 세상에서 정말 많이 사용되고 있는 라이브러리로, 우리 대신 주로 코드 생성을 해주는 친구입니다.
우리는 어노테이션으로 명령할거예요.
예를 들어 @Setter 이렇게만 적으면, setter 메소드들을 알아서 다 만들어주는 기가 막힌 친구랍니다.
그래서~ 이게 어떻게 돌아가는 코드인지는 알아야! 의미 있을 것 같아 준비해보았습니다.
그럼 시작할게요?
Chapter 1. 생성자 오버로딩으론 해결되지 않는.. (feat. 핸드폰 없는 사람 아닌 사람)
자바 예시를 들 때 가장 좋은 예시죠. 사람 객체를 만들기 위한 클래스 Person을 한번 준비해보겠습니다.
사람은 기본 필드로 아래 세가지를 가진다고 가정하겠습니다.
- 이름
- 나이
- 핸드폰번호
public class Person {
private String name;
private int age;
private String phone;
public Person(String name, int age, String phone) {
this.name = name;
this.age = age;
this.phone = phone;
}
}
자 그럼, 이 클래스를 활용해서 사람 객체를 한번 만들어볼까요?
Demo라는 실행용 클래스를 별도로 만들어서, 객체를 생성하도록 하겠습니다.
class Demo {
public static void main(String[] args) {
Person songa = new Person("songa", 20, "010-1234-5678");
}
}
여기까진 아주 쉽게 잘 오셨을 것 같아요.
좋습니다! 그럼 이제 슬슬 하나씩 깊게 들어가보겠습니다.
만약 핸드폰이 없다면 이라는 가정을 해보도록 하겠습니다.
혹시 핸드폰이 없다면.. 사람이 아닌가요..?
우린 지금 생성자가 하나밖에 없기 때문에 객체를 만들 수가 없어yo...
시켜줘 사람..
자 그럼 핸드폰이 없어도 사람이 될 수 있도록 도와주는 방법으로,
우리는 크게 2가지 방법을 생각할 수 있습니다.
1️⃣ 생성자에 맞게 "" 이렇게 빈 값을 넣어줄 수도 있겠죠.
Person songa = new Person("songa", 20, "");
2️⃣ 아니면 핸드폰이 없어도 생성될 수 있게 생성자 오버로딩을 해줄 수도 있겠습니다.
public class Person {
private String name;
private int age;
private String phone;
public Person(String name, int age, String phone) {
this.name = name;
this.age = age;
this.phone = phone;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
오 쉽네?
싶으시다면, 아주 잘 오셨습니다.
이제! 극단적으로 상황을 몰고 가봅니다.
어떤 기술의 장점이 생각나지 않을 때 우린 극단적으로 생각하면 그 장점을 찾기 쉽거든요!
✔️ 예를 들어 볼게요.
사람이 가지는 정보로 이름, 나이, 핸드폰번호, 주소, 혈액형, MBTI, 좋아하는 음식, 좋아하는 가수 등등 한 뭐 필드가 100개 있다고 생각해보죠.
그럼 여러분들은 혹시 저 중 하나라도 빠지면 사람을 만들 수 없으니까...
모~든 경우의 수를 오버로딩으로 다 만들어주실 건가요?
정말 극단적이죠? (만-족)
자, 드디어! 우리는 이때 빌더를 사용하여 빛을 발할 수 있습니다⭐️
등장합니다.
Chapter 2. 소리질러 갓.빌.더🤘🏻
2-1. 나에게.. 빌더란..?
빌 to 더
빌더란, ✅ 문법적인 장점으로 얘기하자면 생성자 오버로딩을 하지 않고, 필요한 데이터만 설정하여 객체를 생성할 수 있는 방법입니다.
무한~~한 (사실은 유한한) 경우의 수를 모두 고려하여 오버로딩하지 않아도 우리가 원하는 객체를 생성할 수 있다는 겁니다!!
✅ 개념적인 장점은 뭐냐구요?
바로. 빌더 라는 겁니다.
빌더. 말 그대로 빌드(build)를 해주는 -er(것)이라는 겁니다.
무엇을 빌드 해준다는 걸까요?
그렇죠. 객체를 대신 빌드 즉, 생성해주겠다는 녀석입니다🔥
어떻게 구현하냐구요?
사실 여러분들이 이미 알고 있는 방법인데.. 아주 조금.. 아주 조금! 멋지게 표현한 것 뿐입니다.
그러니, 겁 먹지 마시고! 하나씩 가보겠습니다🔥
2-2. 빌더에게 객체 생성 책임 전가하기🔥🔥
Person 대신 Person 객체를 생성해줄 클래스를 하나 등장시켜봅니다.
이름하여 PersonBuilder 입니다. ㅋㅋㅋㅋㅋㅋ 직관적인 네이밍
class PersonBuilder {
}
Person 객체를 대신 만들어줘야 한다면,
이 친구가 new Person 생성자를 호출해줘야한다는 뜻이겠죠?
자, 그럼 일단 틀을 만들어봅니다.
build라는 메소드를 만들어서 Person 객체를 생성한 다음, 반환해주는 메소드를 만들어보자구요!
class PersonBuilder {
public Person build() {
return new Person();
}
}
자, 그렇다면 지금부터 Person 객체를 생성하기 위해선
PersonBuilder 객체의 build() 메소드를 호출해야하는 겁니다. 책임을 위임했네요!
즉, 실행용 클래스 Demo에서 이렇게 new Person() 을 직접 호출하는 것이 아니라는 거고!
class Demo {
public static void main(String[] args) {
Person songa = new Person("songa", 20, "010-1234-5678");
}
}
PersonBuilder를 통해서 Person을 만들라는 거겠죠.
그럼 우린 이렇게 PersonBuilder 객체를 생성한 다음, build() 메소드를 호출해주면 되겠습니다.
class Demo {
public static void main(String[] args) {
Person songa = new PersonBuilder().build();
// Person songa = new Person("songa", 20, "010-1234-5678");
}
}
여기까지 되셨나요?
여기까지는 PersonBuilder가 Person 객체를 대신 생성해주는 책임만 가져갔어요.
2-3. 빌더로 객체에 값 넣어보기
이제 한단계 더 가봅니다🔥🔥🔥
Person 객체 필드에 값을 넣어볼게요.
지금 생각으로는.. 그냥 build 메소드가 이렇게 매개변수로 name, age, phone 값을 받아서
Person 객체 생성할 때 넣어주기만 하면 될 것 같은데..
public Person build(String name, int age, String phone) {
return new Person(name, age, phone);
}
그럼 굉장히 아쉬운 건 뭐냐면..
빌더나.. 생성자나.. 실행용 클래스에서 사용할 땐 뭐.. 똑같다는 겁니다😇
class Demo {
public static void main(String[] args) {
Person songa = new PersonBuilder().build("songa", 20, "010-1234-5678");
심지어 아까 생성자와 마찬가지로, 여전히.. 핸드폰 번호 하나 없으면 객체 생성이 안 되겠는걸요...?
그럼 어떻게 하냐구요?
자 지금부터 본격적으로 빌더 클래스를 만들어봅니다💡
build() 메소드가 직접 매개변수를 받을 게 아니라면,
PersonBuilder의 다른 메소드에서 값을 받아서, 필드에 저장한 다음!
그 필드를 활용해야한다는 뜻이죠?
그럼 우선 아래와 같이 필드를 마련해줄게요.
class PersonBuilder {
private String name;
private int age;
private String phone;
public Person build() {
return new Person(name, age, phone);
}
}
이제 저 필드에 우리 대신 값을 받아줄 메소드를 만들어보겠습니다.
필드에 값을 받아준다라..🤔
필드에 값을 대입하는 방법은 크게 세가지가 있습니다.
1️⃣ 생성자
2️⃣ 세터(Setter)
3️⃣ 필드에 직접 대입
보시다시피 이미 습관적으로 private으로 선언된 필드는 3️⃣번 방법을 사용할 순 없을 것 같구요,
1️⃣번 방법을 쓰려고 보니.. 그럼 아래와 같이 나온다는 건데..
그럼 결국 또 phone 하나 없으면 생성이 안 되니까.. 이것도 안될 것 같습니다.
class PersonBuilder {
private String name;
private int age;
private String phone;
public PersonBuilder(String name, int age, String phone) {
this.name = name;
this.age = age;
this.phone = phone;
}
그럼 남은 방법은 바로 2️⃣ 세터(Setter) 입니다.
그냥 name, age, phone을 따로따로 필요할 때마다 받아버리는 겁니다!
필요한 값을 필드에 다 대입해둔 다음!
build() 메소드에서 가져다 쓴다면?
값이 있으면 들어갈 거고, 값이 없다면 기본값이 들어가겠죠.
바로 이렇게 구현할 수 있겠네요!!
class PersonBuilder {
private String name;
private int age;
private String phone;
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void phone(String phone) {
this.phone = phone;
}
public Person build() {
return new Person(name, age, phone);
}
}
좋습니다!
그럼 이제 실행용 클래스 Demo에서 PersonBuilder를 통해 Person을 한번 만들어볼까요?
class Demo {
public static void main(String[] args) {
PersonBuilder personBuilder = new PersonBuilder();
personBuilder.setName("songa");
personBuilder.setAge(20);
personBuilder.setPhone("010-1234-5678");
Person songa = personBuilder.build();
}
}
만약 핸드폰을 넣지 않는다고 해도?
맞아요. 아주 잘 돌아갑니다.
class Demo {
public static void main(String[] args) {
PersonBuilder personBuilder = new PersonBuilder();
personBuilder.setName("songa");
personBuilder.setAge(20);
Person songa = personBuilder.build();
}
}
사실 빌더 패턴은 이게 끝입니다!
여기까지 코드가 이해되셨다면, 여러분들은 빌더를 아주 제대로 이해하신 거예요!
Chapter 3. 여기까지 오셨다니.. 빌더 고도화💖
⚠️ 다시 한번 말씀드려요. 지금까지 코드가 이해되셨다면 빌더 패턴은 이해하신 겁니다!
지금부터는 코드를 예쁘게 만드는 것 뿐이에요.
그치만 아마 여러분들이 조금 아쉬운 부분이 있을 거예요.
아니.. Person 객체 하나 만들자고..
PersonBuilder를 이렇게 길게 구현하면;;;;
맞죠?ㅋㅋㅋㅋㅋ 자 그래서 우리는 이 코드를 좀 더 예쁘게 만들어봅니다.
3-1. PeronBuilder가 주인공도 아니고..
Person을 만드는데.. PersonBuilder 객체를 저렇게 별도로 떡하니 만들어야 한다는 게 마음에 안 듭니다.
사실 Person을 위해서 만드는 거 뿐이잖아요?
그래서 Person이 PersonBuilder를 호출하는 형태로 만들어볼게요.
Person에 빌더를 부르는 메소드 builder를 만들고, 그 안에서 PersonBuilder 객체를 생성해서 반환하도록 할게요.
public class Person {
private String name;
private int age;
private String phone;
public Person(String name, int age, String phone) {
this.name = name;
this.age = age;
this.phone = phone;
}
public PersonBuilder builder() {
return new PersonBuilder();
}
}
근데 이렇게만 만들잖아요?
Demo에서는 그럼 이렇게 불러야한다는 겁니다..
class Demo {
public static void main(String[] args) {
PersonBuilder personBuilder = new Person("songa", 20, "010-1234-5678").builder();
personBuilder.setName("songa");
personBuilder.setAge(20);
이게 뭐옄ㅋㅋㅋㅋㅋㅋ 아니 Person 객체 대신 Person 객체 만들어달랬더니.. 코드가 아주 엉맹징챙입니다.
그래서 우린 static을 사용해봅니다.
Person 클레스의 builder() 메소드에 static을 선언하면
Person 객체를 생성하지 않고도 우린 builder() 메소드를 호출할 수 있으니까요!
public class Person {
private String name;
private int age;
private String phone;
public Person(String name, int age, String phone) {
this.name = name;
this.age = age;
this.phone = phone;
}
public static PersonBuilder builder() {
return new PersonBuilder();
}
}
class Demo {
public static void main(String[] args) {
PersonBuilder personBuilder = Person.builder();
personBuilder.setName("songa");
personBuilder.setAge(20);
Person songa = personBuilder.build();
휴.. 이제 마음이 좀 편하네요
3-2. 아직도 빌더가 주인공이야.. 🤴🏻
자 그래도 조금 모자랍니다. 더 표현하고 싶어요..
PersonBuilder가 Person 때문에 나온거다.. Person 때문에 나온거다 왜 말을 못해..
그래서 이런 생각을 한번 해봅니다.
personBuilder가 필요한 만큼 set을 다 하면, Person을 딱! 생성해서 돌려주는 거죠.
그림으로 보면, 아래와 같이 그릴 수 있겠네요.
그럼 이걸 어떻게 코드로 표현할까.. 싶죠?
그니까.. 김송아말은.. 이 빨간 줄이 한번에 딱! 되었으면 좋겠다 이런 말인 것 같은데.. 그쵸?
이 때 활용할 수 있는 방법이 바로 체이닝입니다🔥🔥🔥🔥
💡체이닝이란?
메소드가 객체를 반환하게 함으로써, 메소드를 체인처럼 쭉쭉 이어서 호출할 수 있게 하는 방법을 말합니다.
ps. 필요하신 분들은 댓글 남겨주시면 다음 아티클로 좀 더 상세하게 설명해드릴게요!
자 이제 이 Setter가 일을 좀 더 해볼게요.
public void setName(String name) {
this.name = name;
}
혼자 필드에 값을 대입하고 끝내는 것이 아니라,
1️⃣ 필드에 값을 대입한 다음
2️⃣ 다른 필드도 값을 대입할 수 있게 다시 객체를 반환해주는 겁니다.
이제 set 이상의 일을 하니까, 이름도 그냥 name으로 바꿔놓도록 하겠습니다 (사실 빌드업)
다음과 같이 자기 자신 객체를 반환해줌으로써, 메소드 뒤에 메소드가 또 붙을 수 있게 해주는 거죠.
public PersonBuilder name(String name) {
this.name = name;// 필드값을 set 하고 끝! 하는 것이 아니라,
return this; // 객체 자신을 return함으로써 메소드를 체이닝(Chaining) 시켜줍니다.
}
그럼 이제, 이 체이닝 기법을 활용해서! PersonBuilder를 전체적으로 바꿔보면?
class PersonBuilder {
private String name;
private int age;
private String phone;
public PersonBuilder name(String name) {
this.name = name;// 필드값을 set 하고 끝! 하는 것이 아니라,
return this; // 객체 자신을 return함으로써 메소드를 체이닝(Chaining) 시켜줍니다.
}
public PersonBuilder age(int age) {
this.age = age;
return this;
}
public PersonBuilder phone(String phone) {
this.phone = phone;
return this;
}
public Person build() {
return new Person(name, age, phone);
}
}
이렇게 바꿀 수 있습니다🎉🎉🎉
그럼 실행용 클래스 Demo에서 어떻게 호출할 수 있는지 알아요..?
바로.. 이렇게 멋지게 바뀝니다..🫢
class Demo {
public static void main(String[] args) {
Person songa = Person.builder()
.name("songa")
.age(20)
.phone("010-1234-5678")
.build();
}
}
맞아요.
구글에 빌더 패턴 이라고 치면 나오는 코드랑 완전 똑같이 생겼죠?🕵🏻♂️
이제 우리는 모든 경우에 수를 고려한 생성자 없이도
원하는 값만 넣은 객체를 생성할 수 있게 되었습니다 야호🥳
너무 대단한 걸요 여러분?
이 글을 위에서부터 차근차근 따라오며 읽은 분들이라면, 빌더가 어떻게 생겼는지 이젠 자신있게! 구현까지 할 수 있겠네요.
빌더 패턴 너란 녀석.. 다들 아마 이렇게 날 것의 빌더 패턴을 보기는 처음이실 것 같아요!
그런 만큼, 다시 위로 올라서 꼭꼭 자기만의 코드로 다시 한번 확인해보시길 바라며!!
너무너무 고생하셨으니, 이번주는 더더 잘 보내셔야 합니다🙈
그럼 또 아티클 주제 제보 기다릴게요? 😉
여러분들에게 조금이나마 제 글이 도움이 되길 바라며,
안녕~~~~~~~
이 글은 제 블로그에 장미를 두고 가신 분과 패스트캠퍼스 백엔드 부트캠프의 멋진 수강생분들의 요청으로 제작된 글입니다💓
'Programming' 카테고리의 다른 글
2024년 D-39, 백엔드 개발자가 되기 위해 한 달 동안 뭘 하면 좋을까요? (48) | 2024.11.22 |
---|---|
백엔드 개발자 포트폴리오 작성 방법 무료 웨비나🚀 (51) | 2024.11.20 |
[공사중🚧] 이번주 포스팅은 길을 잃었습니다... (6) | 2024.11.15 |
요즘 개발자 채용 공고 트렌드 분석 [백엔드편] feat. 당근 부동산 (10) | 2024.11.12 |
API 게이트웨이와 로드밸런서 차이, 진짜 아는 지 확인해보시죠 (9) | 2024.11.09 |