한 객체(발행자, Publisher)의 상태가 바뀌면 그것을 구독한 객체(구독자, Subscriber)들에게 자동으로 알리는 패턴. 발행자는 구독자가 누구인지 구체적으로 몰라도 된다.
function createStore<T>(initial: T) {
let state = initial
const listeners = new Set<(s: T) => void>()
return {
get: () => state,
set(next: T) { state = next; listeners.forEach(l => l(state)) },
subscribe(l: (s: T) => void) { listeners.add(l); return () => listeners.delete(l) },
}
}- 이벤트 리스너, UI 데이터 바인딩, 상태 관리 라이브러리의 구독이 모두 이 구조다. 외부 스토어를 React에 연결하는 useSyncExternalStore가 정확히
subscribe와get을 요구한다 - DOM 이벤트의
addEventListener도 옵저버다(이벤트 버블링·캡처링과 위임) - 엄밀히는 발행자가 구독자 목록을 직접 들고 있으면 옵저버, 중간에 메시지 브로커(Message Broker)를 두고 서로를 전혀 모르면 발행-구독(Publish-Subscribe)이라 구분하기도 한다. 시스템 사이의 발행-구독은 Kafka 같은 메시지 큐가 맡는다
주의할 점
- 구독 해제를 잊으면 메모리 누수(Memory Leak)와 이미 사라진 화면의 갱신이 생긴다. 위 예처럼
subscribe가 해제 함수를 돌려주게 하고 정리 시점에 부른다(Effect 정리 함수) - 알림이 또 다른 알림을 부르는 연쇄·순환이 생기면 흐름을 추적하기 어렵다
- 알림 순서에 기대는 코드를 만들지 않는다
행동 패턴 전체는 행동 패턴.