Menu
Coddy logo textTech

옵저버 패턴

Coddy PHP 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 91개 중 73번째.

옵저버 패턴(Observer Pattern)은 객체 간의 일대다 관계를 설정하는 행동 디자인 패턴입니다. 하나의 객체(주체)의 상태가 변경되면, 모든 의존 객체들(옵저버)이 자동으로 알림을 받고 업데이트됩니다.

뉴스레터 구독을 생각해 보세요. 뉴스레터는 주체(subject)이며, 구독자는 관찰자(observer)입니다. 새로운 호가 발행되면, 뉴스레터가 각 구독자의 세부 정보를 알 필요 없이 모든 구독자가 자동으로 이를 받게 됩니다.

다음은 PHP로 구현한 기본적인 예제입니다:

<?php
interface Observer {
    public function update(string $message): void;
}

class Newsletter {
    private array $subscribers = [];
    
    public function subscribe(Observer $observer): void {
        $this->subscribers[] = $observer;
    }
    
    public function publish(string $content): void {
        foreach ($this->subscribers as $subscriber) {
            $subscriber->update($content);
        }
    }
}

class EmailSubscriber implements Observer {
    public function __construct(private string $email) {}
    
    public function update(string $message): void {
        echo "Sending to {$this->email}: $message\n";
    }
}

$newsletter = new Newsletter();
$newsletter->subscribe(new EmailSubscriber("alice@example.com"));
$newsletter->subscribe(new EmailSubscriber("bob@example.com"));
$newsletter->publish("New article released!");

출력:

Sending to alice@example.com: New article released!
Sending to bob@example.com: New article released!

이 패턴은 주체(subject)를 관찰자(observer)들로부터 분리합니다. Newsletter 클래스는 이메일 구독자, SMS 구독자 또는 푸시 알림 서비스 중 무엇에 알림을 보내는지 알 필요가 없습니다. 단지 Observer 인터페이스를 구현하는 모든 객체에 대해 update()를 호출할 뿐입니다. 이를 통해 기존 코드를 수정하지 않고도 새로운 관찰자 유형을 손쉽게 추가할 수 있습니다.

challenge icon

챌린지

쉬움

옵저버 패턴(Observer Pattern)을 사용하여 주가 알림 시스템을 구축해 보겠습니다. 주식 가격이 변동될 때 등록된 모든 투자자에게 자동으로 알림이 전송되도록 하는 것은 이 패턴의 완벽한 실제 적용 사례입니다.

코드는 다음 네 개의 파일로 구성됩니다:

  • Observer.php — 단일 메서드 update(string $stockName, float $price): void를 가진 Observer 인터페이스를 정의합니다. 이것은 모든 옵저버가 알림을 받기 위해 따라야 하는 규약입니다.
  • Investors.php — Observer 파일을 포함하고 인터페이스를 구현하는 두 개의 클래스를 생성합니다:
    • EmailInvestor — 생성자에서 이메일 주소를 받습니다 (생성자 프로모션(constructor promotion)을 사용하세요). update() 메서드는 "Email to {$this->email}: {$stockName} is now \${$price}\n" 형식을 출력합니다.
    • SmsInvestor — 생성자에서 전화번호를 받습니다. update() 메서드는 "SMS to {$this->phone}: {$stockName} is now \${$price}\n" 형식을 출력합니다.
  • Stock.php — Observer 파일을 포함하고 주체(subject) 역할을 하는 Stock 클래스를 생성합니다. 주식 클래스는 다음을 수행해야 합니다:
    • 주식 이름과 현재 가격을 저장합니다 (이름에는 생성자 프로모션을 사용하고, 가격은 0.0으로 초기화합니다).
    • 옵저버들을 담을 private 배열을 유지합니다.
    • 옵저버를 추가하기 위한 subscribe(Observer $observer): void 메서드를 가집니다.
    • 가격을 업데이트하고 모든 옵저버에게 알림을 보내는 setPrice(float $price): void 메서드를 가집니다.
    옵저버에게 알림을 보낼 때, 각 옵저버의 update() 메서드를 주식 이름과 새로운 가격과 함께 호출합니다.
  • main.php — Investors 및 Stock 파일을 포함합니다. 주식 이름, 이메일 주소, 전화번호의 세 가지 입력을 받습니다.

    주어진 이름으로 Stock 객체를 생성합니다. 해당 이메일 주소를 가진 EmailInvestor와 해당 전화번호를 가진 SmsInvestor를 구독(subscribe)시킵니다.

    주식 가격을 150.50으로 설정하여 모든 구독자에게 알림이 가도록 트리거합니다.

옵저버 패턴은 Stock 클래스를 구체적인 알림 방법으로부터 분리(decoupled)된 상태로 유지해 줍니다. 나중에 Stock 클래스를 전혀 수정하지 않고도 PushInvestorSlackInvestor를 쉽게 추가할 수 있습니다.

치트 시트

옵저버 패턴(Observer Pattern)은 주제(subject)의 상태가 변경될 때 모든 관찰자(observer)에게 자동으로 알림을 보내는 일대다 관계를 설정합니다.

주요 구성 요소:

  • 옵저버 인터페이스(Observer Interface)update() 메서드를 통해 옵저버를 위한 규약을 정의합니다
  • 주제(Subject) — 옵저버 목록을 유지하고 상태 변경 시 이들에게 알림을 보냅니다
  • 구체적인 옵저버(Concrete Observers) — 알림을 받기 위해 옵저버 인터페이스를 구현합니다

기본 구현:

<?php
interface Observer {
    public function update(string $message): void;
}

class Subject {
    private array $observers = [];
    
    public function subscribe(Observer $observer): void {
        $this->observers[] = $observer;
    }
    
    public function notify(string $data): void {
        foreach ($this->observers as $observer) {
            $observer->update($data);
        }
    }
}

class ConcreteObserver implements Observer {
    public function __construct(private string $name) {}
    
    public function update(string $message): void {
        echo "Notifying {$this->name}: $message\n";
    }
}

$subject = new Subject();
$subject->subscribe(new ConcreteObserver("Observer1"));
$subject->subscribe(new ConcreteObserver("Observer2"));
$subject->notify("State changed!");

장점:

  • 주제와 옵저버 간의 결합도를 낮춥니다
  • 기존 코드를 수정하지 않고도 새로운 옵저버 유형을 쉽게 추가할 수 있습니다
  • 동적 구독/구독 해지를 지원합니다

직접 해보기

<?php
require_once 'Investors.php';
require_once 'Stock.php';

// 입력 읽기
$stockName = trim(fgets(STDIN));
$email = trim(fgets(STDIN));
$phone = trim(fgets(STDIN));

// TODO: 주어진 이름으로 Stock(주식) 생성

// TODO: 이메일 주소로 EmailInvestor 구독 설정

// TODO: 전화번호로 SmsInvestor 구독 설정

// TODO: 알림을 트리거하기 위해 주식 가격을 150.50으로 설정
?>
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨