자바스크립트 환경에서 객체 생성의 복잡성을 우아하게 해결하는 빌더 패턴을 알아보자. 단순히 코드를 깔끔하게 만드는 것을 넘어, 유지보수성이 높은 아키텍처를 설계하는 데 도움이 된다.
햄버거를 주문한다고 생각해보자.
참깨빵 → 순쇠고기 → 특별한 소스 → 양상추 → 치즈 → 피클 → 양파 → 진동벨"양파는 빼줘" → "피클도 빼줘" → "치즈 두 장으로"원하는 조합만 골라서 완성
이처럼 원하는 내용만 합쳐서 만들어내는 패턴이다.
소프트웨어가 커질수록 객체는 점점 더 많은 설정값을 갖게 된다. 이때 생성자 함수만을 사용하면 여러 문제에 봉착한다.
점증적 생성자의 혼란
인자가 5개, 10개로 늘어나면 다음과 같은 코드가 탄생한다.
new User('Jay', 28, null, null, 'Seoul', true...)
각 인자가 무엇을 의미하는지 파악하려면 끊임없이 클래스 정의를 확인해야 한다.
유연성 결여
특정 시나리오에서는 A, B 속성만 필요하고, 다른 시나리오에서는 C, D 속성만 필요할 수 있다.
특정 시나리오: 이름 + 나이만 필요다른 시나리오: 이메일 + 전화번호만 필요→ 모든 조합을 위한 생성자 10개 필요?
특정 시나리오: 아이디 + 비밀번호다른 시나리오: 아이디 + 비밀번호 + 닉네임
생성자 방식은 모든 조합에 대응하기 위해 수많은 조건문 분기가 필요할 수 있다.
폼 검증의 반복
const validateUser = (user) => {if (!user.name) errors.name = '필수';if (user.name.length > 50) errors.name =
빌더 패턴은 객체의 구성 로직을 실제 표현에서 분리한다. 자바스크립트에서는 클래스 내부에 내부 빌더를 두거나, 별도의 빌더 클래스를 만들어 메서드 체이닝을 통해 구현하는 것이 일반적이다.
예시: 스마트폰 제조 시스템
class Smartphone {constructor() {this.cpu = 'Default CPU';this.ram = '8GB';this.storage = '128GB';this.camera = 'Standard'
class SmartphoneBuilder {constructor() {// 내부에 실제 객체 생성this.phone = new Smartphone();}// 각 설정 메서드: 값 세팅 후 this 반환setCPU(cpu) {
// 실사용 코드const myProPhone = new SmartphoneBuilder().setCPU('Snapdragon Gen 3').setRAM('16GB').setStorage('512GB').useHighEndCamera().enable5G
예시: 폼 검증 빌더
class FormValidator {constructor() {this.rules = []; // 검증 규칙 누적 배열}// required: 빈 값 체크 (trim으로 공백 무시)required(msg = '필수 입력입니다') {this
// 폼별 특화 빌더class UserFormValidator extends FormValidator {// 이름 규칙 한 번에 설정withNameRules() {return this.required('이름 필수').maxLength(50);}
// 사용const validator = new UserFormValidator().withNameRules().withEmailRules().build();const result = validator.validate('test@example.com')
빌더 패턴은 모든 곳에 쓰기보다는 다음과 같은 특정 맥락에서 도입할 때 효과가 극대화된다.
생성자 인자가 '임계점'을 넘었을 때
보통 인자가 4개 이상이거나, 선택적 매개변수가 필수 매개변수보다 많아질 때 고려한다.
객체 생성 과정이 단계적일 때
데이터베이스 연결 설정처럼 보안 설정 -> 호스트 설정 -> 커넥션 풀 설정 등 순차적인 단계가 필요할 때 유용하다.
불변성이 중요할 때
빌더를 통해 모든 설정을 마치고 build()를 누르는 순간에만 실제 객체를 생성하게 함으로써, 생성 이후 수정이 불가능한 'Read-only' 객체를 안전하게 만들 수 있다.
| 가독성 | • 코드가 마치 문장처럼 읽힌다. • setRAM('16GB')는 단순한 값 16보다 훨씬 명확한 의도를 전달한다. |
| 단일 책임 원칙 준수 | • 복잡한 생성 로직을 별도의 빌더 클래스에 격리하여, 메인 비즈니스 로직(Smartphone 클래스)을 단순하게 유지할 수 있다. |
| 확장성 | • 체이닝 방식으로 규칙을 유연하게 추가할 수 있다. • 새로운 검증 규칙 추가가 간단하다. |
| 코드량 증가 | • 클래스마다 빌더를 만들어야 하므로 코드 베이스가 커진다. • 아주 단순한 객체에는 오히려 오버엔지니어링이 될 수 있다. |
| 자바스크립트의 대안 존재 | • 자바스크립트는 객체 리터럴과 구조 분해 할당이 매우 강력하다. • 단순히 이름 있는 인자가 필요한 정도라면 constructor({ cpu, ram, storage }) 형태의 Option Object 패턴이 더 효율적일 수 있다. |
단순히 "값"을 세팅하는 용도라면 객체 리터럴을 활용한 생성을 권장하며, 각 설정 단계에서 추가적인 로직(계산, 검증, 부수 효과)이 수반되어야 한다면 빌더 패턴이 유용하다.
빌더 패턴을 이용하여 객체를 생성하면, 객체 생성 코드의 가독성과 유지보수성이 높아진다. 또한, 빌더 클래스를 이용하여 다양한 객체를 생성할 수 있으며, 객체 생성 과정을 단계적으로 구현할 수 있다. 필요한 속성만 설정할 수 있기 때문에, 객체 생성 시 불필요한 속성을 설정할 필요가 없다.