본문 바로가기

front/javascript

[Javascript] 자바스크립트 디자인 패턴

728x90

 

https://www.inflearn.com/course/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4

 

항해 플러스 과제를 실행하는데 디자인 패턴이 나왔다. 들어만 봤지 실제로 해본 적은 없어서 헷갈리기도 했다.

그래서 디자인패턴은 무엇인지, 왜 사용하는지 이번 기회에 한 번 정리해보려 한다. 

 

1. Desing Pattern(디자인 패턴)

디자인 패턴은 소프트웨어 개발 과정에서 자주 발생하는 문제들을 해결하기 위한 일반적이고 재사용 가능한 해결책을 말한다.

 

자바스크립트에서 주로 사용하는 디자인 패턴에는 Module Pattern(모듈 패턴), Observer Pattern(옵저버 패턴), Factory Pattern(팩토리 패턴), Singleton Pattern(싱글톤 패턴), Facade Pattern(퍼사드 패턴), Proxy Pattern(프록시 패턴) 등이 있다.  

 

해당 포스팅에서는 모듈 패턴, 옵저버 패턴, 팩토리 패턴, 싱글톤 패턴만 우선 알아볼 예정이다.

 

 

1) Module Pattern (모듈 패턴)

모듈 패턴은 데이터를 비공개로 유지하면서 필요한 부분만 외부로 노출하는 패턴을 말한다.

자바스크립트의 클로저(closure)를 사용해 변수를 보호하면서, 외부에서는 접근할 수 없도록 설계하는 것이 핵심이다.

 

메서드와 속성을 하나의 독립된 객체로 캡슐화하는 방법이라고 생각하면 된다.

모듈 패턴은 코드의 구조화와 네임스페이스 관리에 유용하다.

 

*네임스페이스 관리 유용 : 너무 많은 변수를 선언하지 않도록 한다.

 

const Module = (function() {
    let privateVar = "I am private";  // 외부에서 접근 불가, private 변수

    function privateMethod() {
        console.log(privateVar);
    }

    return {
        publicMethod: function() {
            privateMethod();
        }
    };
})();

Module.publicMethod();  // "I am private" 출력

 

위 모듈은 즉시 실행 함수(IIFE)로 구성되어 있다. 

해당 함수는 말 그대로 호출되는 즉시 실행되며, 함수 내부에 있는 privateVar와 privateMethod가 선언된다.

이때 함수 스코프가 생성되고, 그 안에 있는 변수들과 함수들이 정의된다.

privateVar는 privateMethod의 외부 스코프에 정의된다.

 

그 후 privateMethod가 반환되고 나서도 클로저를 통해 렉시컬 스코프에 있는 변수들을 참조할 수 있게 된다.

따라서 privateVar에도 접근할 수 있게 되고 console.log("I am private")을 출력할 수 있게 된다.

 

privateVar는 외부에서 접근할 수 없지만, publicMethod를 통해서 접근할 수 있게 되는 것이다. 

 

 

 

2) Observer Pattern(옵저버 패턴)

옵저버 패턴은 객체 간의 의존 관계를 정의해 하나의 객체 상태가 변경되면,

그 객체를 관찰하는 여러 객체들이 자동으로 알림을 받고 업데이트되는 구조를 제공하는 디자인 패턴이다.

 

옵저버 패턴은 이벤트 발생과 그 반응을 효율적으로 관리할 수 있기 때문에 이벤트 기반 시스템에서 많이 사용된다.

자바스크립트에서 DOM 요소에 이벤트 리스너를 등록하는 방식이 옵저버 패턴의 전형적인 예이다.

 

// 주체 (Subject)
class Subject {
    constructor() {
        this.observers = [];  // 옵저버 리스트
    }

    // 옵저버 등록
    subscribe(observer) {
        this.observers.push(observer);
    }

    // 옵저버 제거
    unsubscribe(observer) {
        this.observers = this.observers.filter(obs => obs !== observer);
    }

    // 모든 옵저버들에게 알림
    notify(data) {
        this.observers.forEach(observer => observer.update(data));
    }
    
// 옵저버 (Observer)
class Observer {
    constructor(name) {
        this.name = name;
    }

    // 상태 변화 알림을 받으면 업데이트
    update(data) {
        console.log(`${this.name} received update: ${data}`);
    }
}

// 주체 생성
const subject = new Subject();

// 옵저버 생성
const observer1 = new Observer('Observer 1');
const observer2 = new Observer('Observer 2');

// 옵저버 등록
subject.subscribe(observer1);
subject.subscribe(observer2);

// 주체 상태 변경 및 알림 전파
subject.notify('Event 1');  // Observer 1과 Observer 2 모두 'Event 1'을 받음

// 옵저버 제거 후 다시 알림 전파
subject.unsubscribe(observer1);
subject.notify('Event 2');  // Observer 2만 'Event 2'를 받음

 

옵저버 패턴은 하나의 주체가 상태를 변경하면, 그 상태를 감시하는 다수의 옵저버가 해당 변경 사항에 반응하는 구조이다.

 

여기서 주체는 상태를 가지고 있는 개체로, 자신의 상태가 변경되었을 때 옵저버들에게 상태 변화를 알리는 역할을 한다.

옵저버들을 등록하는 관리하는 기능을 하고 있다.

 

옵저버는 주체의 상태 변화를 감시하는 객체이다.

주체가의 상태가 변하면 주체로부터 알림을 받아, 자신이 해야 할 일을 수행한다.

 

 

 

3) Factory Pattern(팩토리 패턴)

팩토리 패턴은 객체를 생성하는 과정에서 객체 생성을 담당하는 별도의 메서드 또는 클래스를 제공하는 디자인 패턴이다.

객체를 직접 생성하는 것이 아니라 팩토리라는 메서드를 통해 객체를 생성하도록 하는 것이 핵심이다.

 

객체 생성의 복작성을 숨기고, 생성 방법을 다양화하거나 동일한 인터페이스를 가진 객체를 생성해야 할 때 유용하다.

 

// 자동차 클래스
class Car {
    constructor(model) {
        this.model = model;
    }
    drive() {
        console.log(`${this.model} is driving`);
    }
}

// 자동차 팩토리
class CarFactory {
    static createCar(type) {
        if (type === 'sedan') {
            return new Car('Sedan');
        } else if (type === 'suv') {
            return new Car('SUV');
        } else {
            return null;
        }
    }
}

// 팩토리를 사용해 객체 생성
const myCar = CarFactory.createCar('sedan');
myCar.drive();  // "Sedan is driving" 출력

 

CarFactory는 createCar 메서드를 통해 Car 객체를 생성하는 역할을 한다.

CarFactory.createCar()를 통해 자동차 객체를 생성할 수 있지만, 자동차가 어떻게 생성되는지 구체적인 로직은 알 필요 없다. 

 

 

 

4) Singleton Pattern(싱글톤 패턴)

싱글톤 패턴은 오직 하나의 객체만 생성되도록 보장하는 디자인 패턴이다.

특정 클래스의 인스턴스가 단 하나만 존재해야 하며, 그 인스턴스에 전역적으로 접근할 수 있도록 하는 경우 사용된다.

 

전역 상태를 유지마녀서 객체 생성을 제어할 수 있으며, 주로 애플리케이션 설정이나 데이터베이스 연결에 사용된다.

 

싱글턴 패턴의 중요한 요소는 "유일한 인스턴스 보장"과 "전역 접근"이다. 

클래스에서 인스턴스는 오직 하나만 만들 수 있도록 해야 하며, 클래스의 인스턴스에 전역적으로 접근할 수 있어야 한다.

 

class Singleton {
    constructor() {
        if (Singleton.instance) {
            return Singleton.instance;
        }
        
        // Singleton 객체 상태
        this.data = "Initial data";

        // Singleton의 유일한 인스턴스 저장
        Singleton.instance = this;
        
        // 인스턴스 반환
        return this;
    }

    // 인스턴스 상태를 변경하는 메서드
    setData(data) {
        this.data = data;
    }

    // 인스턴스 상태를 가져오는 메서드
    getData() {
        return this.data;
    }
}

// 인스턴스 생성
const singletonA = new Singleton();
const singletonB = new Singleton();

singletonA.setData("New Data");

console.log(singletonA.getData());  // "New Data"
console.log(singletonB.getData());  // "New Data"

console.log(singletonA === singletonB);  // true

 

constructor에서 if를 이용해 Signleton.instance가 존재하는지 확인하고,

이미 존재하는 인스턴스가 있다면 새로운 인스턴스를 생성하지 않고 기존 인스턴스를 반환하도록 해준다.

 

만일 인스턴스가 처음 생성된다면, 클래스 내부의 this.data라는 상태 변수를 초기화하고 그 인스턴스를 Signleton.instance에 저장한다.

 

 


이렇게 자바스크립트 디자인 패턴에 대해 알아봤다.

디자인 패턴을 사용하면 코드의 구조화, 재사용성, 유지보수성을 크게 향상시킬 수 있다는 사실을 알게 되었다.

확실히 과제 코드를 이해하는 것도 쉬워졌다.

 

그런데 아직 실제로 사용해본 경험이 없다 보니 헷갈리는 것도 있다.

다음 과제가 나오면 해당 패턴 중 적절한 것을 선택해 적용해 보는 연습을 해봐야겠다!

728x90