프로그램을 만들다 보면 어떤 객체의 상태가 바뀌었을 때, 다른 여러 곳이 그 변화를 알아야 하는 경우가 많습니다. 그런데 그때마다 관련 객체를 직접 다 호출하기 시작하면 결합도가 빠르게 높아집니다. 옵저버 패턴은 이런 알림 구조를 더 느슨하게 만들기 위한 대표적인 패턴입니다.
이 글에서는 아래 내용을 정리합니다.
- 옵저버 패턴이 무엇인지
- 왜 상태 변화 알림 구조에서 자주 쓰이는지
- 발행-구독과 어떤 점이 닮았는지
- 언제 잘 맞는지
핵심은 옵저버 패턴은 상태를 가진 주체와 그 변화를 듣는 대상들을 느슨하게 연결해서, 알림 대상이 늘어나도 중심 객체를 덜 흔들리게 만드는 구조라는 점입니다.
옵저버 패턴이란 무엇인가
옵저버 패턴은 어떤 객체의 변화가 생기면, 그 변화를 구독하고 있던 다른 객체들에게 자동으로 알리는 구조입니다.
즉:
- 주체(subject)가 있고
- 관찰자(observer)가 있으며
- 상태 변화가 생기면 관찰자들에게 통지합니다
라는 흐름으로 볼 수 있습니다.
왜 필요한가
예를 들어 주문 상태가 바뀌면:
- 알림 발송
- 로그 기록
- 통계 반영
- UI 업데이트
같은 여러 작업이 따라올 수 있습니다.
이걸 주문 객체가 직접 다 알고 호출하기 시작하면 중심 객체의 책임이 너무 커집니다. 옵저버 패턴은 이 결합을 줄이는 데 도움을 줍니다.
발행-구독과는 같은가
매우 비슷한 감각으로 이해할 수 있습니다. 다만 구현 방식과 범위는 상황에 따라 다를 수 있습니다.
입문 단계에서는:
- 어떤 변화가 발생하면
- 관심 있는 쪽이 반응한다
는 구조적 아이디어가 핵심입니다.
언제 잘 맞을까
- 상태 변화에 따라 여러 후속 동작이 필요한 경우
- 알림 대상이 늘어날 수 있는 경우
- 중심 로직이 후속 처리 세부사항을 덜 알고 싶을 때
즉, “이벤트가 생기면 여러 반응이 따라오는 구조”와 잘 맞습니다.
자주 하는 오해
1. 알림이 필요하면 무조건 옵저버 패턴이다
단순한 한두 개 연결이라면 직접 호출이 더 단순할 수도 있습니다.
2. 옵저버 패턴은 무조건 비동기다
그렇지 않습니다. 동기/비동기 여부는 구현 방식에 따라 다릅니다.
3. 이벤트 구조를 쓰면 무조건 결합도가 낮다
이벤트 종류와 흐름이 지나치게 많아지면 오히려 추적이 어려워질 수 있습니다.
Simple Example
interface Observer {
update(status: string): void;
}
class EmailNotifier implements Observer {
update(status: string): void {
console.log(`Send email: ${status}`);
}
}
class SlackNotifier implements Observer {
update(status: string): void {
console.log(`Send Slack message: ${status}`);
}
}
class Order {
private observers: Observer[] = [];
subscribe(observer: Observer): void {
this.observers.push(observer);
}
changeStatus(status: string): void {
this.observers.forEach((observer) => observer.update(status));
}
}
const order = new Order();
order.subscribe(new EmailNotifier());
order.subscribe(new SlackNotifier());
order.changeStatus('PAID');
The important point is that Order only knows the Observer contract. New reactions can be added without rewriting the central state owner.
FAQ
Q. 입문자는 어디서 옵저버 패턴을 떠올리면 좋을까
상태 변화 뒤에 여러 후속 동작이 붙는 구조에서 떠올리기 좋습니다.
Q. 콜백 구조와 같은가
일부 닮았지만, 옵저버 패턴은 구독자 목록과 상태 변화 알림 구조에 더 초점이 있습니다.
Q. 작은 프로젝트에도 필요할까
후속 반응이 여러 개로 늘어나는 순간부터 꽤 유용해질 수 있습니다.
Read Next
- 객체 연결을 바꾸는 다른 방식은 어댑터 패턴 가이드와 비교해보면 좋습니다.
- 역할을 감싸서 기능을 추가하는 구조는 데코레이터 패턴 가이드와 함께 보면 더 입체적으로 보입니다.
심사 대기 중에는 광고 대신 관련 가이드를 먼저 보여줍니다.
먼저 읽어볼 가이드
검색 유입이 많은 핵심 글부터 이어서 보세요.
- 미들웨어 트러블슈팅 가이드: Redis vs RabbitMQ vs Kafka 개발자를 위한 미들웨어 트러블슈팅 허브 글입니다. Redis, RabbitMQ, Kafka 중 어떤 증상부터 먼저 봐야 하는지와 어떤 문제 패턴이 각 시스템에 가까운지 정리합니다.
- Kubernetes CrashLoopBackOff: 먼저 볼 것들 startup failure, probe, config, resource limit 관점에서 CrashLoopBackOff를 어떻게 나눠서 봐야 하는지 정리한 가이드입니다.
- Kafka consumer lag가 계속 늘 때: 트러블슈팅 가이드 Kafka consumer lag가 계속 늘어날 때 무엇부터 봐야 하는지 정리합니다. poll 주기, 처리 속도, rebalance, consumer 설정까지 실전 기준으로 다룹니다.
- Kafka Rebalancing Too Often 가이드 Kafka consumer group에서 rebalance가 너무 자주 일어날 때 membership flapping, poll timing, protocol, assignment churn을 어떤 순서로 봐야 하는지 설명하는 실전 가이드입니다.
- Docker container가 계속 재시작될 때: 먼저 확인할 것들 exit code, command failure, environment mistake, health check 관점에서 Docker restart loop를 푸는 실전 가이드입니다.
심사 대기 중에는 광고 대신 관련 가이드를 먼저 보여줍니다.