바닐라 자바스크립트로 프로젝트를 진행하던 중 상태를 전역으로 관리해야 하는 부분이 생겨 옵저버 패턴에 대해 알아보고 정리해보았다.
옵저버 패턴은 객체의 상태 변화를 관찰하는 옵저버들(감시자들)의 목록을 객체에 등록하여 상태 변화가 있을 때마다 메서드 등을 통해 객체가 직접 각각의 옵저버들에게 통지하도록 하는 디자인 패턴이다.
Observer는 Subscriber 또는 Listener라고도 불리며, 발행/구독 모델로도 알려져 있다.
⇒ 한 주제 객체(Subject)의 상태가 바뀌면 다른 구독 객체들(Subscribers)에게 상태와 변경을 알린다.
관찰 대상 ⇒ 주제 객체(Subject)
관찰자 ⇒ 구독 객체(Subscriber)
옵저버 패턴은 우리의 생활 속에서도 찾아볼 수 있다.
다음의 예시들은 모두 어떠한 이벤트가 발생했을 때 옵저버들이 바로 반응하는 패턴이다.
옵저버 패턴은 모델-뷰-컨트롤러(Model-View-controller, MVC) 패러다임과 자주 결합된다. 옵저버 패턴은 MVC에서 모델과 뷰 사이를 느슨히 연결하기 위해 사용된다. 대표적으로 모델에서 일어나는 이벤트를 통보받는 옵저버는 뷰의 내용을 바꾸는 스위치를 작동시킨다.
주로 분산 이벤트 핸들링 시스템을 구현하는데 사용된다.
만약 옵저버 패턴을 가지지 않는다면 이벤트를 체크해야 하는 오브젝트들은 해당 이벤트가 일어나는지 확인하기 위해 주기적으로 계속 확인해야 하는데, 이러한 방법을 폴링(polling)이라고 한다.
이런 경우 폴링을 사용하게 되면 필요없는 리소스 단계가 생기기도 하고, 지정해둔 시간 내에 이벤트가 생겼다가 사라지면 이벤트 발생 여부도 알 수가 없다.
ex) 손님들이 마트에 브로콜리가 재입고 되기를 기다리고 있는 상황
✔️ 폴링(polling) 마트 주인에게 손님들이 시간마다 전화해서 브로콜리 재입고 됐나요? 브로콜리 재입고? 브로콜리? 물어본다. . ✔️ 옵저버 패턴(observer pattern) 마트 주인이 손님들에게 연락처를 받아 목록에 올려놓고, 브로콜리가 재입고 되면 단체 문자를 보낸다.
위의 예시를 보면 옵저버 패턴이 왜 필요한지 단번에 이해할 수 있을 것이다.
관찰 대상이 되는 객체(⇒ Subject, 이벤트를 발생시키는 주체)에 하나 이상의 옵저버 객체를 등록시키면, 각각의 옵저버들은 관찰 대상인 객체가 발생시키는 이벤트를 받아서 처리한다.
이벤트가 발생하면 각 옵저버는 콜백(callback)을 받는다. notify
함수는 관찰 대상이 발행한 메시지 이외에, 옵저버 자신이 생성한 인자값을 전달할 수도 있다.
각각의 파생 옵저버는 이벤트가 발생했을 때 처리해야 할 각자의 동작을 notify
함수 안에 정의하면 된다.
class Subject {
constructor() {
this.observers = [];
}
getObserversList() {
return this.observers;
}
subscribe(observer) {
this.observers.push(observer);
}
unsubscribe(observer) {
this.observers.filter((obs) => obs !== observer);
}
notifyAll() {
this.observers.forEach((subscriber) => {
try {
subscriber.update(this.constructor.name);
} catch (err) {
console.error("error", err);
}
}
}
}
class Observer {
constructor(name) {
this.name = name;
}
update(subject) {
console.log(`${this.name}: notified from ${subject} class!`);
}
}
const subj = new Subject();
const obsA = new Observer("A");
const obsB = new Observer("B");
const obsC = new Observer("c");
subj.subscribe(obsA);
subj.subscribe(obsB);
subj.subscribe(obsC);
console.log(subj.getObserverList());
/*
[
Observer { name: 'A' },
Observer { name: 'B' },
Observer { name: 'C' },
]
*/
subj.unsubscribe(obsB);
subj.notifyAll();
/*
A: notified from Subject class!
C: notified from Subject class!
*/
참고 자료
위키백과 옵서버 패턴
youtube 코딩문
Vanilla Javascript로 상태관리 시스템 만들기