Programming/Javascript, Typescript

JavaScript 디자인 패턴

리버김 2025. 7. 6.
책 <자바스크립트 + 리액트 디자인 패턴> (한빛미디어)를 읽고 19개의 패턴의 간단한 정의와 예시들을 정리해본다.

사진: Unsplash 의 Hardingferrent

생성 패턴

객체를 생성하는 방법을 다루는 패턴

 

생성자 패턴

*ES6의 class 문법을 사용

 

객체 지향 프로그래밍을 위한 문법적 설탕(syntactic sugar)으로, constructor 메서드를 통해 객체 초기화를 수행하고 new 키워드를 사용하여 인스턴스를 생성하는 구조이다. class 내부에서 속성과 메서드를 정의하며, 상속 및 캡슐화 같은 OOP 개념도 자연스럽게 표현할 수 있다.

class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }

    sayHello() {
        console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
    }
}

const person1 = new Person('John', 30);
person1.sayHello(); // Hello, my name is John and I am 30 years old.

const person2 = new Person('Jane', 25);
person2.sayHello(); // Hello, my name is Jane and I am 25 years old.

 

장점

  • 코드 재사용성 up
  • 캡슐화 용이

 

모듈 패턴

변수와 함수를 캡슐화하고 공개(public) 또는 비공개(private) 인터페이스를 정의하기 위한 디자인 패턴이다. 간단히 말해 데이터 은닉과 네임스페이스 충돌 방지를 위한 구조다.

 

const CounterModule = (function () {
  // private 변수
  let count = 0;

  // private 함수
  function logCount() {
    console.log("Current count:", count);
  }

  // public API
  return {
    increment: function () {
      count++;
      logCount();
    },
    reset: function () {
      count = 0;
      logCount();
    }
  };
})();

// 사용
CounterModule.increment(); // Current count: 1
CounterModule.increment(); // Current count: 2
CounterModule.reset();     // Current count: 0

 

 

클로저를 사용하는 위 방식은 옛날 방식이고, ES6부터 도입된 ES Module이 현재 널리 사용되는 방식이다. import와 export를 사용하고, 파일이 스코프라서 export를 사용하면 자동 은닉된다. 비동기 실행, 트리 셰이킹 등도 가능하다.

 

장점

  • 은닉화
  • 공개 메서드만 노출해 API 처럼 사용

 

싱글톤 패턴

클래스의 인스턴스가 오직 하나만 존재하도록 제한하는 패턴이다. 전역에서 접근 및 공유해야 하는 단 하나의 객체가 필요할 때 유용하다.

 

class Singleton {
  constructor() {
    if (Singleton.instance) {
      return Singleton.instance;
    }

    this.timestamp = Date.now(); // 예시로 현재 시간 저장
    Singleton.instance = this;
  }

  getTime() {
    return this.timestamp;
  }
}

// 사용 예시
const a = new Singleton();
const b = new Singleton();

console.log(a === b); // true (같은 인스턴스)
console.log(a.getTime()); // 동일한 timestamp
console.log(b.getTime()); // 동일한 timestamp

 

여러 번 호출해도 처음 만들어진 인스턴스를 계속 반환하므로 생성자는 두 번째부터는 실행되지 않는다.

자바스크립트에서 싱글톤이 필요하다는 건 설계를 다시 생각해봐야 한다는 신호일 수도 있는데, 객체를 생성하기 위해 클래스를 정의해야 하는 C++나 자바와 달리, 자바스크립트는 객체를 직접적으로 생성할 수 있기 때문이다.

 

장점

싱글톤 패턴은 애플리케이션 전역에서 동일한 인스턴스를 하나만 유지해 상태·설정의 단일 소스를 보장하고, 어디서든 같은 객체에 접근할 수 있어 호출이 단순해진다는 장점이 있다. 또, 초기화를 지연하여 인스턴스가 사용될 때 비로소 생성할 수 있다는 장점도 있다.

 

프로토타입 패턴

프로토타입 패턴은 자바스크립트 언어의 대표적인 특징이자 자바스크립트에만 존재하는 독특한 패턴이다.

 

미리 만들어 둔 원형(프로토타입) 객체를 복제하여 새 객체를 만들고 필요한 속성만 덮어쓰는 생성 방식이다. 초기화 비용이 큰 객체를 반복 생성하지 않아 성능과 메모리 측면에서 이점이 있다. 깊은/얕은 복사 규칙과 순환 참조를 관리해야 한다는 점은 주의가 필요하다.

 

그럼 자바스크립트는 왜 혼자서 이런 패턴을 쓰는 걸까?

 

자바스크립트가 프로토타입 방식을 쓰는 이유는 언어의 출발과 웹 환경의 요구가 맞물렸기 때문이다. 1995년에 매우 짧은 기간에 설계되면서, 클래스를 먼저 설계·컴파일·인스턴스화하는 전통적 OOP보다 객체를 곧바로 만들고 다른 객체를 원형으로 삼아 위임하는 방식이 더 단순하고 유연하다고 판단했기 때문이다. ES6의 class 문법도 근본적으로는 이 프로토타입 위임 모델에 대한 문법 설탕일 뿐이라, 자바스크립트의 상속과 재사용의 핵심은 여전히 프로토타입 기반 위임에 있다고 보면 된다.

 

// 원형(프로토타입)을 정의한다.
const ButtonProto = {
  type: 'primary',
  radius: 8,
  style: { padding: 12, color: '#fff', bg: '#2563eb' },
  render()
};

// 복제하여 필요한 부분만 덮어쓴다.
const dangerBtn = Object.create(ButtonProto);
dangerBtn.type = 'danger';
dangerBtn.style = { ...ButtonProto.style, bg: '#dc2626' };

const ghostBtn = Object.create(ButtonProto);
ghostBtn.type = 'ghost';
ghostBtn.style = { ...ButtonProto.style, color: '#2563eb', bg: 'transparent' };

 

JS 프로토타입과 전통 OOP의 차이

 

JS(프로토타입): 인스턴스 만든 뒤에도 원형에 메서드를 추가하면 기존 인스턴스가 곧바로 쓴다.

전통 OOP: 클래스를 실행 중에 바꿀 수 없음(재컴파일/재배포 필요).

const Animal = { speak(){ console.log(`${this.name} makes a sound`); } };

const a1 = Object.create(Animal); a1.name = '멍멍이';
const a2 = Object.create(Animal); a2.name = '야옹이';

Animal.run = function(){ console.log(`${this.name} runs`); }; // 인스턴스 만든 뒤 추가
a1.run(); // 멍멍이 runs
a2.run(); // 야옹이 runs

 

 

 

댓글