| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | ||
| 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 13 | 14 | 15 | 16 | 17 | 18 | 19 |
| 20 | 21 | 22 | 23 | 24 | 25 | 26 |
| 27 | 28 | 29 | 30 |
- Laravel
- SQL기초
- SQL문법
- 인덱스
- ai agent
- MySQL
- 매직메서드
- PHP객체지향
- Agent Loop
- LLM
- SQL
- ai 에이전트
- linux 권한
- mcp client
- docker network
- 디자인패턴
- PHP
- 데이터베이스
- mcp server
- 데코레이터패턴
- docker
- 라라벨
- OOP
- dhgrp
- 객체지향
- MCP
- 소프트웨어설계
- 어댑터패턴
- 객체복제
- Tool Calling
- Today
- Total
개발블로그
[디자인패턴] 옵저버 패턴 본문
개념
필요성
프로그램을 만들다 보면 어떤 객체의 상태 변화가 여러 기능에 영향을 주는 경우가 있다.
EX) 주문 상태가 결제완료로 바뀌었다고 할 때, 단순히 주문 상태만 바꾸면 끝나지 않음.
=> 상태 변화에 따라 여러 후속 작업이 필요할 수 있음
ex) 알림발송, 재고차감, 배송 준비, 포인트 적립...
def complete_order(order):
order.status = "paid"
send_notification(order)
decrease_stock(order)
prepare_delivery(order)
add_point(order)
# 상태 변화에 반응해야 하는 기능이 계속 늘어나면, 주문 로직은 점점 많은 일을 알게 됨
# ex) 나중에 쿠폰 사용 처리, 리뷰 요청 알림, 판매자 정산 요청이 추가되면, complete_order()는 계속 수정되어야 함
# 문제는, 주문 상태를 변경하는 코드가 상태 변화 이후에 실행될 모든 후속 작업까지 직접 알고 있어야 함.
옵저버 패턴은 이런 상황에서 사용할 수 있다
" 상태가 바뀌었을 때, 그 변화를 필요로 하는 객체들에게 어떻게 알릴까? "
의미
어떤 객체의 상태 변화가 발생했을 때, 그 변화를 필요로 하는 객체들에게 자동으로 알리는 패턴.
상태가 바뀌는 객체는 자신에게 관심 있는 객체들을 등록해두고, 변화가 발생하면 등록된 객체들에게 알린다.
+ 상태 변화에 반응하는 여러 작업을 느슨하게 연결하되, 실행 흐름과 실패 처리를 함께 관리해야 함
| 구분 | 역할 | 예시 |
| Subject | 상태 변화를 발생시키는 객체 => 상태 변화와 Observer 등록/알림을 관리 |
주문 |
| Observer | 상태 변화를 전달받는 객체 => 알림을 받았을 때 수행할 동작을 정의 |
알림 서비스, 재고 서비스, 배송 서비스, 포인트 서비스 |
| Notify | Subject가 Observer들에게 변화를 알리는 동작 => 실제 후속 작업을 수행 |
주문 결제 완료 사실을 전달 |
예시
EX] 주문 상태가 결제 완료로 변경되면 여러 후속 작업이 필요할 때
처음에는 주문 완료 함수 안에서 필요한 기능을 직접 호출할 수 있음
def complete_order(order):
order.status = "paid"
send_notification(order)
decrease_stock(order)
prepare_delivery(order)
add_point(order)
흐름이 단순할 때는 이해하기 쉬움.
주문 상태를 변경하고, 필요한 후속 작업을 순서대로 실행함
결제 완료 이후 해야 할 일이 늘어나면 문제가 생김
def complete_order(order):
order.status = "paid"
send_notification(order)
decrease_stock(order)
prepare_delivery(order)
add_point(order)
request_review(order)
create_settlement(order)
주문 상태 변경 뿐 아니라 알림, 재고, 배송, 포인트, 리뷰, 정산까지 직접 알고 있음.
문제는 상태 변화 이후의 후속 작업이 모두 주문 로직이 붙는다.
| 문제 | 설명 |
| 책임 증가 | 주문 완료 로직이 여러 후속 작업까지 직접 담당한다. |
| 수정 범위 증가 | 후속 작업이 추가될 때마다 주문 완료 코드를 수정해야 한다. |
| 결합도 증가 | 주문 로직이 알림, 재고, 배송, 정산 기능을 직접 알게 된다. |
| 테스트 어려움 | 주문 상태 변경만 테스트하고 싶어도 여러 후속 작업이 함께 얽힌다. |
옵저버로 알림 책임 분리하기
먼저, 상태 변화를 전달받을 옵저버의 공통 형태를 정함
class OrderObserver:
def update(self, order):
pass
각 후속 작업은 옵저버로 구현
class NotificationObserver(OrderObserver):
def update(self, order):
return "알림 발송"
class StockObserver(OrderObserver):
def update(self, order):
return "재고 차감"
class DeliveryObserver(OrderObserver):
def update(self, order):
return "배송 준비"
이제 주문 객체는 옵저버를 등록하고, 상태가 변경되었을 때 알림
class Order:
def __init__(self):
self.status = "created"
self.observers = []
def add_observer(self, observer):
self.observers.append(observer)
def notify(self):
for observer in self.observers:
observer.update(self)
def complete(self):
self.status = "paid"
self.notify()
사용하는 코드는 필요한 옵저버를 등록한다.
order = Order()
order.add_observer(NotificationObserver())
order.add_observer(StockObserver())
order.add_observer(DeliveryObserver())
order.complete()
Order은 결제 완료 이후 어떤 세부 작업이 실행되는지 직접 구현하지 않음.
등록된 옵저버들에게 상태 변화를 알린다.
새로운 후속 작업이 필요하면 옵저버를 추가하면 됨
class PointObserver(OrderObserver):
def update(self, order):
return "포인트 적립"
| 구분 | 역할 |
| Order | 상태 변경과 알림을 담당한다. |
| OrderObserver | 상태 변화를 전달받는 공통 형태를 정의한다. |
| NotificationObserver, StockObserver, DeliveryObserver | 상태 변화 이후의 실제 작업을 수행한다. |
장점
장점
| 장점 | 설명 |
| 결합도 감소 | 상태를 변경하는 객체가 후속 작업의 구체 구현을 직접 알 필요가 줄어든다. |
| 확장 용이 | 새로운 후속 작업을 옵저버로 추가할 수 있다. |
| 책임 분리 | 상태 변경 로직과 후속 처리 로직을 분리할 수 있다. |
| 재사용성 증가 | 같은 옵저버를 여러 Subject에 등록해 사용할 수 있다. |
효과적인 상황
| 상황 | 이유 |
| 하나의 상태 변화가 여러 기능에 영향을 주는 경우 | 후속 작업을 옵저버로 분리할 수 있다. |
| 후속 작업이 자주 추가되거나 변경되는 경우 | 새로운 옵저버를 추가하는 방식으로 확장할 수 있다. |
| 상태 변화 발생자와 처리자를 분리하고 싶은 경우 | Subject가 Observer의 구체 구현을 몰라도 된다. |
| 이벤트 기반 구조가 필요한 경우 | 변화가 발생했을 때 관련 작업을 실행하는 흐름을 만들 수 있다. |
주의점
주의점
| 주의점 | 설명 |
| 흐름 추적 어려움 | 어떤 옵저버가 등록되어 있는지 확인해야 전체 동작을 알 수 있다. |
| 실행 순서 문제 | 옵저버 실행 순서가 중요하다면 명확한 기준이 필요하다. |
| 예외 처리 필요 | 하나의 옵저버에서 오류가 나면 나머지 옵저버 실행에 영향을 줄 수 있다. |
| 옵저버 증가 관리 | 등록되는 옵저버가 많아지면 전체 구조를 관리하기 어려워질 수 있다. |
| 메모리 누수 가능성 | 옵저버 등록 후 해제하지 않으면 불필요한 참조가 남을 수 있다. |
ex) 옵저버 패턴은 상태 변화와 후속 처리를 분리하는 데 유용하지만, 흐름이 지나치게 분산될 수 있음
직접 호출 방식에서는 어떤 작업이 실행되는지 코드에서 바로 확인 가능
def complete_order(order):
order.status = "paid"
send_notification(order)
decrease_stock(order)
prepare_delivery(order)
하지만 옵저버 패턴을 사용하면 후속 작업이 등록된 옵저버들로 분리됨
order.add_observer(NotificationObserver())
order.add_observer(StockObserver())
order.add_observer(DeliveryObserver())
order.complete()
이 구조에서는 order.complete()만 보고는 어떤 후속 작업이 실행되는지 바로 알기 어려울 수 있음
=> 결합도는 낮아지지만, 실행 흐름을 추적하는 비용은 늘어날 수 있음
ex) 알림 발송은 실패해도 주문 완료 자체는 유지되어야 할 수 있고, 재고 차감이 실패하면 주문 완료를 되돌려야 할 수도 있음
=> 후속 작업마다 실패했을 때의 처리 방식이 다를 수 있음.
=> 단순히 알림을 보낸다에서 끝내지 않고, 실행 순서와 실패 처리 방식을 함께 설계
고려사항
| 질문 | 의미 |
| 상태 변화 이후 실행될 작업들이 독립적인가? | 각 옵저버를 분리해도 되는지 확인한다. |
| 옵저버 실행 순서가 중요한가? | 순서가 중요하다면 명시적으로 관리해야 한다. |
| 일부 옵저버가 실패하면 어떻게 할 것인가? | 예외 처리, 재시도, 보상 처리를 고민한다. |
| 어떤 옵저버가 등록되어 있는지 추적 가능한가? | 흐름이 지나치게 숨겨지지 않도록 관리한다. |
| 등록과 해제가 필요한 구조인가? | 불필요한 참조나 메모리 누수를 방지한다. |
'STUDY > CS' 카테고리의 다른 글
| [디자인패턴] 데코레이터 패턴 (0) | 2026.09.04 |
|---|---|
| [디자인패턴] 어댑터 패턴 (0) | 2026.09.04 |
| [디자인패턴] 팩토리 패턴 (1) | 2026.09.04 |
| [디자인 패턴] 전략패턴 (0) | 2026.09.04 |
| [디자인 패턴] 디자인 패턴이란? 반복되는 설계 문제를 해결하는 방법 (0) | 2026.09.03 |