Observer
Observer 패턴은 객체의 상태가 바뀔 때 그 변화에 관심 있는 다른 객체들에게 자동으로 알려주는 구조입니다. 상태를 가진 쪽(Subject)과 그 변화를 지켜보는 쪽(Observer)을 분리해서, Subject는 누가 지켜보는지 몰라도 되고 Observer는 언제든 구독을 시작하거나 끊을 수 있습니다.
▶아키텍처 다이어그램
🔍 구조 다이어그램점선 애니메이션은 데이터 또는 요청의 흐름 방향을 나타냅니다
하나의 데이터가 바뀌면 화면, 로그, 캐시, 알림 등 여러 곳이 동시에 반응해야 하는 상황은 흔합니다. 이때 데이터를 가진 객체가 반응해야 할 모든 객체를 직접 호출하면, 새로운 반응이 추가될 때마다 데이터 객체의 코드를 수정해야 합니다. 반응하는 쪽도 마찬가지로 데이터 객체의 내부를 알아야 해서 양쪽이 강하게 엮입니다. 기능이 늘수록 이 결합은 연쇄적으로 퍼지고, 어디서 어떤 부수 효과가 일어나는지 추적하기 어려워집니다.
GUI 프로그래밍이 본격화되면서 하나의 데이터 모델을 여러 위젯이 동시에 보여줘야 하는 상황이 일상이 됐습니다. Smalltalk의 MVC 아키텍처가 초기에 이 문제를 정면으로 다뤘고, Model이 변하면 View가 자동으로 갱신되는 구조가 필요했습니다. 이 필요에서 Observer 패턴이 형태를 갖추었고, GoF(Gang of Four)가 1994년에 패턴으로 정리했습니다. 이후 Java의 이벤트 리스너, .NET의 이벤트 시스템, 브라우저의 addEventListener, React의 상태 관리 라이브러리까지 Observer의 변형이 곳곳에 스며들었습니다.
Observer 패턴에는 두 역할이 있습니다. Subject(발행자)는 내부에 옵저버 목록을 관리하며, subscribe()로 등록하고 unsubscribe()로 해제합니다. Subject의 상태가 바뀌면 notify() 메서드가 목록에 있는 모든 Observer의 update()를 호출합니다. 이 구조의 핵심은 Subject가 Observer의 구체 클래스를 모른다는 점입니다. update()라는 인터페이스만 알면 되기 때문에, 새로운 Observer를 추가해도 Subject 코드는 바뀌지 않습니다. 이 단방향 의존 흐름 덕분에 발행 쪽과 소비 쪽을 독립적으로 확장할 수 있습니다. 주의할 점은 Observer가 많아지면 알림 순서와 성능이 문제가 될 수 있다는 것입니다. Observer 안에서 다시 상태를 바꾸면 연쇄 알림이 발생할 수도 있으므로, 알림 경로가 순환하지 않도록 설계 단계에서 흐름을 점검해야 합니다.
Observer와 Pub/Sub(발행-구독)은 둘 다 '변경을 여러 곳에 전파한다'는 목적에서 출발합니다. 하지만 Observer는 Subject와 Observer가 서로를 직접 알고 있는 반면, Pub/Sub은 중간에 메시지 브로커나 이벤트 채널을 두어 발행자와 구독자가 서로를 전혀 모르게 만듭니다. 같은 프로세스 안에서 객체 간 상태를 동기화할 때는 Observer가 단순하고 명확합니다. 시스템이 분산되거나 발행자와 구독자의 생명주기가 다를 때는 Pub/Sub 쪽이 결합도를 더 낮출 수 있습니다. Mediator 패턴과도 비교됩니다. Mediator는 여러 객체 간의 복잡한 상호 작용을 중앙 조정자에 모으는 패턴이고, Observer는 하나의 상태 변화를 여러 곳에 일방향으로 전파하는 패턴입니다. 양방향 교류가 많으면 Mediator, 단방향 알림이 핵심이면 Observer가 맞습니다.
프론트엔드에서는 상태 관리가 Observer의 가장 흔한 적용 영역입니다. 전역 스토어의 값이 바뀌면 해당 값을 구독하는 컴포넌트만 다시 렌더링되는 구조가 Observer의 변형입니다. 백엔드에서는 도메인 이벤트 기반 아키텍처에서 자주 등장합니다. 주문이 완료되면 재고 차감, 알림 발송, 로그 기록이 각각 독립된 핸들러로 반응하는 식입니다. 도입 신호는 분명합니다. 하나의 상태 변경에 여러 곳이 반응해야 하는데, 반응하는 쪽이 앞으로 늘어날 수 있다면 Observer를 고려할 때입니다. 반대로 반응 대상이 딱 하나이거나 변하지 않는다면 직접 호출이 더 단순합니다. Observer를 남용하면 알림 경로가 암묵적이 되어 디버깅이 어려워지므로, 전파 경로를 추적할 수 있는 로깅이나 디버그 도구를 함께 마련하는 편이 좋습니다.