조아마시

쓸모 있는 상세페이지 만들기

웹개발/javascript

[자바스크립트] 빌더 패턴

joamashi 2025. 3. 17. 16:20
반응형

빌더 패턴이란?

빌더 패턴(Builder Pattern)은 복잡한 객체를 단계적으로 생성할 수 있도록 도와주는 디자인 패턴이다. 즉, 객체를 생성할 때 필요한 값을 하나씩 설정하며 유연하게 만들 수 있도록 도와준다.


빌더 패턴을 왜 사용할까?

  1. 복잡한 생성자를 피할 수 있다
    • 만약 객체를 만들 때 옵션이 많다면, 생성자가 너무 길어질 수 있음.
    • 빌더 패턴을 사용하면 필요한 값만 설정하고 객체를 만들 수 있음.
  2. 코드를 더 읽기 쉽게 만들 수 있다
    • 메서드 체이닝(연속적인 메서드 호출)으로 객체를 설정할 수 있음.
    • 가독성이 좋아지고 유지보수가 쉬워짐.
  3. 불변성(Immutable) 객체를 쉽게 만들 수 있다
    • 객체를 한 번 설정한 후 변경할 필요가 없는 경우 유용함.

빌더 패턴을 쉽게 이해하는 예제

예제 1: 햄버거 만들기

햄버거를 만드는 과정을 생각해보자.
햄버거는 빵, 패티, 치즈, 야채 등 여러 가지 재료가 들어갈 수 있다.
우리는 손님이 원하는 재료만 추가하면서 햄버거를 만들 수 있도록 빌더 패턴을 적용할 수 있다.

class Burger {
  constructor(builder) {
    this.bun = builder.bun;
    this.patty = builder.patty;
    this.cheese = builder.cheese;
    this.lettuce = builder.lettuce;
    this.tomato = builder.tomato;
  }

  describe() {
    return `버거 구성: ${this.bun} 번, ${this.patty} 패티, ${this.cheese ? '치즈 있음' : '치즈 없음'}, 
    ${this.lettuce ? '상추 있음' : '상추 없음'}, ${this.tomato ? '토마토 있음' : '토마토 없음'}`;
  }

  static get Builder() {
    return class {
      setBun(bun) { this.bun = bun; return this; }
      setPatty(patty) { this.patty = patty; return this; }
      addCheese() { this.cheese = true; return this; }
      addLettuce() { this.lettuce = true; return this; }
      addTomato() { this.tomato = true; return this; }
      build() { return new Burger(this); }
    };
  }
}

// 햄버거 만들기
const myBurger = new Burger.Builder()
  .setBun("참깨")
  .setPatty("소고기")
  .addCheese()
  .addLettuce()
  .build();

console.log(myBurger.describe());

🛠 결과:

버거 구성: 참깨 번, 소고기 패티, 치즈 있음, 상추 있음, 토마토 없음
  • setBun(), setPatty() 등을 이용해 원하는 재료만 추가할 수 있음.
  • build() 메서드를 호출하면 최종적으로 Burger 객체가 생성됨.

예제 2: 자동차 만들기

class Car {
  constructor(builder) {
    this.make = builder.make;
    this.model = builder.model;
    this.year = builder.year;
  }

  describe() {
    return `${this.year}년식 ${this.make} ${this.model}`;
  }

  static get Builder() {
    return class {
      setMake(make) { this.make = make; return this; }
      setModel(model) { this.model = model; return this; }
      setYear(year) { this.year = year; return this; }
      build() { return new Car(this); }
    };
  }
}

// 자동차 객체 생성
const myCar = new Car.Builder()
  .setMake("Tesla")
  .setModel("Model Y")
  .setYear(2024)
  .build();

console.log(myCar.describe()); // "2024년식 Tesla Model Y"
  • 원하는 속성만 선택적으로 설정할 수 있음.
  • 객체를 쉽게 조립하듯 만들 수 있음.

정리

빌더 패턴은 객체를 만들 때 필요한 속성을 하나씩 설정하며 유연하게 생성할 수 있도록 도와줌.
복잡한 생성자보다 가독성이 뛰어나고 유지보수하기 쉬움.
메서드 체이닝을 사용해 직관적인 코드 작성이 가능.
옵션이 많은 객체 생성에 적합 (예: 자동차, 음식 주문, API 요청 등).


1. 클래스를 이용한 기본적인 빌더 패턴

class Car {
  constructor(builder) {
    this.make = builder.make;
    this.model = builder.model;
    this.year = builder.year;
  }

  toString() {
    return `${this.year} ${this.make} ${this.model}`;
  }

  static get Builder() {
    class Builder {
      setMake(make) { this.make = make; return this; }
      setModel(model) { this.model = model; return this; }
      setYear(year) { this.year = year; return this; }
      build() { return new Car(this); }
    }
    return Builder;
  }
}

const car = new Car.Builder()
  .setMake("Tesla")
  .setModel("Model 3")
  .setYear(2023)
  .build();

console.log(car.toString()); // "2023 Tesla Model 3"

2. 객체 리터럴을 이용한 빌더 패턴

const carBuilder = {
  make: "",
  model: "",
  year: 0,
  setMake(make) { this.make = make; return this; },
  setModel(model) { this.model = model; return this; },
  setYear(year) { this.year = year; return this; },
  build() { return { make: this.make, model: this.model, year: this.year }; }
};

const car2 = carBuilder
  .setMake("Toyota")
  .setModel("Corolla")
  .setYear(2022)
  .build();

console.log(car2); // { make: "Toyota", model: "Corolla", year: 2022 }

3. 함수형 빌더 패턴

const createCarBuilder = () => {
  let car = {};
  return {
    setMake: (make) => { car.make = make; return carBuilder; },
    setModel: (model) => { car.model = model; return carBuilder; },
    setYear: (year) => { car.year = year; return carBuilder; },
    build: () => car
  };
};

const car3 = createCarBuilder()
  .setMake("BMW")
  .setModel("X5")
  .setYear(2024)
  .build();

console.log(car3); // { make: "BMW", model: "X5", year: 2024 }

4. 클로저를 활용한 빌더 패턴

function carBuilder() {
  let car = {};
  return {
    setMake(make) { car.make = make; return this; },
    setModel(model) { car.model = model; return this; },
    setYear(year) { car.year = year; return this; },
    build() { return car; }
  };
}

const car4 = carBuilder()
  .setMake("Hyundai")
  .setModel("Sonata")
  .setYear(2021)
  .build();

console.log(car4); // { make: "Hyundai", model: "Sonata", year: 2021 }

5. 프로토타입을 이용한 빌더 패턴

function CarBuilder() {}
CarBuilder.prototype.setMake = function(make) { this.make = make; return this; };
CarBuilder.prototype.setModel = function(model) { this.model = model; return this; };
CarBuilder.prototype.setYear = function(year) { this.year = year; return this; };
CarBuilder.prototype.build = function() { return { make: this.make, model: this.model, year: this.year }; };

const car5 = new CarBuilder()
  .setMake("Honda")
  .setModel("Civic")
  .setYear(2020)
  .build();

console.log(car5); // { make: "Honda", model: "Civic", year: 2020 }

6. 커스텀 데이터 타입을 위한 빌더 패턴

class User {
  constructor(builder) {
    this.name = builder.name;
    this.age = builder.age;
    this.email = builder.email;
  }

  static get Builder() {
    return class {
      setName(name) { this.name = name; return this; }
      setAge(age) { this.age = age; return this; }
      setEmail(email) { this.email = email; return this; }
      build() { return new User(this); }
    };
  }
}

const user = new User.Builder()
  .setName("John Doe")
  .setAge(30)
  .setEmail("john@example.com")
  .build();

console.log(user); // User { name: "John Doe", age: 30, email: "john@example.com" }

7. 스프레드 연산자를 활용한 빌더 패턴

const personBuilder = (defaults = {}) => ({
  ...defaults,
  setName(name) { return personBuilder({ ...this, name }); },
  setAge(age) { return personBuilder({ ...this, age }); },
  setEmail(email) { return personBuilder({ ...this, email }); },
  build() { return this; }
});

const person = personBuilder()
  .setName("Alice")
  .setAge(25)
  .setEmail("alice@example.com")
  .build();

console.log(person); // { name: "Alice", age: 25, email: "alice@example.com" }

8. 데이터베이스 연결을 위한 빌더 패턴

class DatabaseConnection {
  constructor(builder) {
    this.host = builder.host;
    this.port = builder.port;
    this.username = builder.username;
    this.password = builder.password;
  }

  static get Builder() {
    return class {
      setHost(host) { this.host = host; return this; }
      setPort(port) { this.port = port; return this; }
      setUsername(username) { this.username = username; return this; }
      setPassword(password) { this.password = password; return this; }
      build() { return new DatabaseConnection(this); }
    };
  }
}

const db = new DatabaseConnection.Builder()
  .setHost("localhost")
  .setPort(5432)
  .setUsername("admin")
  .setPassword("secret")
  .build();

console.log(db);

9. REST API 요청 빌더 패턴

class ApiRequest {
  constructor(builder) {
    this.url = builder.url;
    this.method = builder.method;
    this.headers = builder.headers;
    this.body = builder.body;
  }

  static get Builder() {
    return class {
      setUrl(url) { this.url = url; return this; }
      setMethod(method) { this.method = method; return this; }
      setHeaders(headers) { this.headers = headers; return this; }
      setBody(body) { this.body = body; return this; }
      build() { return new ApiRequest(this); }
    };
  }
}

const request = new ApiRequest.Builder()
  .setUrl("https://api.example.com/data")
  .setMethod("POST")
  .setHeaders({ "Content-Type": "application/json" })
  .setBody(JSON.stringify({ key: "value" }))
  .build();

console.log(request);

10. WebSocket 빌더 패턴

class WebSocketBuilder {
  constructor() {
    this.url = "";
    this.protocols = [];
  }

  setUrl(url) { this.url = url; return this; }
  setProtocols(protocols) { this.protocols = protocols; return this; }
  build() { return new WebSocket(this.url, this.protocols); }
}

const ws = new WebSocketBuilder()
  .setUrl("wss://example.com/socket")
  .setProtocols(["json"])
  .build();

console.log(ws);

 

728x90
반응형