1. 디자인 패턴이란?
- JS에는 문제 해결을 위한 7가지 디자인 패턴이 존재한다.
- 우리는 개발을 하면서 여러 상황에 직면하게 되고, 상황을 해결하기 위해 적지 않은 시간을 소모한다.
- 하지만 만약 이 상황을 해결하기 위한, 접근 방향을 알 수 있다면?
- 우리는 아마 해결을 위해 소모되는 시간을 줄일 수 있을 것이다.
문제 해결에 방향성을 제시하는 이론이 없을까? 디자인 패턴을 봐라!
- JS 디자인 패턴은 특정 상황을 해결하기 위해 어떤 패턴을 사용하면 좋을지에 대한 방향성을 제시한다.
- 단 유념해야할 점은 어디까지나 방향을 안내하는 역할을 하지, 구체적인 솔루션을 제공치 않는다.
- 디자인 패턴에는 Constructor, Prototype, Command, Observer, Singleton, Module, Factory 패턴이 존재하며, 이번 시간에는 이 중 옵저버 패턴(Observer)에 대해 설명한다.
2. 디자인 패턴1, 옵저버 패턴
1) 옵저버 패턴이란?
- 감시자 패턴이라고도 부르며, 크게 관찰자인 옵저버와 관찰 대상 객체로 구성되어 있다.
- 관찰 대상 객체에 옵저버(관찰자) 목록을 등록해두고, 객체의 상태가 변할 때마다 각 옵저버에게 변경 알림을 준다.
- 만약 A객체의 상태가 변할 때 B함수를 실행시키고 싶다면, 이 옵저버 패턴을 이용하면 된다.
- 옵저버 패턴의 예시로 요소의 이벤트를 감지하는
addEventListener
가 있다.
const btn = document.querySelector('.btn');
// btn에 click이벤트시 어떤 로직을 실행
btn.addEventListener("click", function() { ... })
2) 옵저버 패턴의 구성
(1) 옵저버(관찰자)
- 객체의 변화를 감지할 감시자이다.
- 객체의 상태 변화가 있을 때 동작할 메소드를 가진다. (ex.
update()
) - 여러 옵저버가 필요할 시, 옵저버 클래스를 만들고 이 클래스를 상속 받아 옵저버1, 2를 만들 수 있다.
(2) 객체(관찰대상)
- 객체는 해당 객체의 변화를 감시하는 옵저버 리스트를 저장하고 있다.
subscribe()
를 사용해 옵저버를 추가한다.- 객체 상태가 변하면(ex. 상태값 변경, 이벤트 발생)
notify()
를 사용해 옵저버에게 알려준다. - 옵저버는
notify()
를 통해 객체 상태 변화를 감지하고,update()
를 실행한다.
3) 옵저버 예시
첫째, 둘째, 셋째 자식 옵저버가 있으며, 엄마의 상태가 변했을 때 세 옵저버가 콘솔을 실행하도록 하게 하자.
(1) 옵저버 베이스 클래스 만들기
- 먼저, 옵저버 클래스를 만든다.
class Observer {
update(v) {}
}
(2) 여러 옵저버 만들기
- 그리고 첫째, 둘째, 셋째 옵저버는 Observer를 상속 받아 생성한다.
class Observer1 extends Observer {
update(isWaiting) {
if (!isWaiting) console.log('첫째는 집에 갈거야')
}
}
class Observer2 extends Observer {
update(isWaiting) {
if (!isWaiting) console.log('첫째는 집에 갈거야')
}
}
class Observer3 extends Observer {
update(isWaiting) {
if (!isWaiting) console.log('첫째는 집에 갈거야')
}
}
(3) 관찰 대상 클래스 만들기
Mom
클래스를 만든다.Mom
클래스는 자신의 변화를 감지할 옵저버 리스트(children
)을 가진다.
class Mom {
constructor() {
this.children = []; // 객체의 변화를 감지할 옵저버 리스트
this.waiting = true;
}
subscribe(child) { // 감시할 옵저버 구독하기
this.children.push(child)
}
unsubscribe(removedChild) { // 옵저버 비구독하기
this.children = this.children.filter(child => child !== removedChild)
}
notify() {
this.waiting = false; // 객체의 상태 변경
for (let child of this.children) {
child.update(this.waiting); // 객체 상태 변경에 따른 옵저버의 로직 실행
}
}
}
(4) 옵저버 구독하기
mother
객체를 관찰할 옵저버를 구독해준다.
// 관찰 대상 객체 생성
const mother = new Mom();
// 옵저버 객체 생성
const child1 = new Observer1();
const child2 = new Observer2();
const child3 = new Observer3();
// 구독
mother.subscribe(child1);
mother.subscribe(child2);
mother.subscribe(child3);
(5) 변화에 따른 로직 실행하기
- mother은
notify()
를 사용해, 관찰자에게 객체 상태 변화(waiting 변수 변경)를 알린다. - 관찰자인 옵저버는 객체 상태 변경을 감지하고, 특정 로직을 실행한다.
mother.notify(); // mom를 구독하는 모든 관찰자에게 알려줌
4) 옵저버 패턴은 어떤 상황에 유용할까?
(1) 모듈 간의 의존성을 낮출 때 유용하다.
- JS에서 코드를 설계할 때 모듈의 범위를 세부적으로 나누는 것이 중요하다.
- 또한 각 모듈간의 상호 의존성을 줄이는 게 좋다. (의존성이 크면 문제 발생시 핸들링해야 하는 규모가 커짐)
하지만 만약 A, B 모듈이 서로의 데이터가 필요한 경우에는 어떻게 해야할까?
- A, B 모듈을 합치는 건 상호 의존성을 높이며, 이는 유지 보수 측면에서 좋지 않다.
- 이 경우에는 모듈을 합치는 대신 옵저버 패턴을 이용하면 좋다.
- 먼저, A, B 모듈이 서로 필요로 하는 데이터의 변화를 감지하는 관찰자 객체(옵저버)를 만든다.
- 그리고 데이터가 변경될 때마다 각 모듈은 관찰자에게 데이터와 상태 변경을 통보한다.
- 관찰자는 데이터 변경에 따른 로직을 실행하면 된다.
(단, 모듈에서는 어떤 상태 변화에 어떤 로직을 실행할지 미리 옵저버에 등록해야 함)
(2) polling을 방지할 수 있다.
polling
이란, 상태를 주기적으로 확인하고 만약 조건을 만족할 시 자료처리를 하는 방식이다.polling
의 단점은 짧은 주기로 관찰하면 부하가 발생하며, 긴 주기로 관찰하면 실시간성이 떨어진다는 것이다.- 하지만, 옵저버 패턴을 사용하게 되면 관찰 대상의 상태가 변경됐을 때를 감지할 수 있으므로 polling을 사용하지 않아도 된다.
반응형
'개발 기술 > 개발 이야기' 카테고리의 다른 글
이중배열(2d array)에서 최고, 최저값 찾기 (0) | 2022.01.02 |
---|---|
[JS] 호이스팅(hoisting) (0) | 2021.12.21 |
클로저는 무엇일까? (feat. 외부, 내부 함수, 랙시컬 스코프) (0) | 2021.11.29 |
call, apply, bind의 차이 (0) | 2021.11.25 |
랙시컬 스코프(정적 스코프)는 무엇일까? (0) | 2021.11.22 |
댓글