프로그래밍/알고리즘&자료구조&패턴

디자인패턴 - 옵저버 패턴

lazy_web_devloper 2025. 8. 18. 10:54
728x90
반응형

JAVA로 구현하는 알고리즘과 디자인 패턴 공부: 옵저버 패턴 (Observer)

1. 옵저버 패턴이란? 🔔

개념: 한 객체(Subject)의 상태가 변하면, 그 객체에 의존(구독)하는 다른 객체들(Observer)에게 자동으로 알림을 보내고 업데이트할 수 있게 만드는 일대다(one-to-many) 의존성 패턴입니다.

가장 완벽한 예는 유튜브 채널 구독입니다.

  • 유튜버 (Subject): 상태를 가진 주체. 새로운 영상을 올리면 '상태 변화'가 발생합니다.
  • 구독자 (Observer): 유튜버를 관찰하는 객체들.
  • 구독 (Register): 구독자가 '구독' 버튼을 눌러 자신을 유튜버의 알림 목록에 추가합니다.
  • 알림 (Notify): 유튜버가 새 영상을 올리면, 알림 목록에 있는 모든 구독자에게 알림이 갑니다.

2. 왜 사용할까요?

  • 느슨한 결합 (Loose Coupling): Subject는 자신을 관찰하는 Observer들이 구체적으로 어떤 클래스인지 알 필요가 없습니다. 오직 Observer 인터페이스를 구현했다는 사실만 알면 됩니다. 이 덕분에 Subject와 Observer는 서로에게 거의 영향을 주지 않고 독립적으로 변경하거나 재사용할 수 있습니다.
  • 동적인 관계: 런타임에 새로운 Observer를 추가하거나 제거하는 것이 자유롭습니다.
  • 브로드캐스팅: 단 한 번의 상태 변경으로 관련된 모든 객체에 정보를 효율적으로 전파할 수 있습니다.

3. Java로 구현하는 방법

시나리오: 새로운 뉴스가 발생하면 여러 뉴스 채널(Observer)에 자동으로 기사를 공급하는 뉴스 통신사(Subject) 시스템을 만들어 보겠습니다.

1단계: 서브젝트(Subject)와 옵저버(Observer) 인터페이스 정의

먼저, 상태를 가질 주체와 그 상태를 관찰할 관찰자의 기본 규약을 각각 만듭니다.

Java
 
// Observer 인터페이스
public interface Observer {
    void update(String news); // Subject로부터 새로운 소식을 전달받는 메서드
}

// Subject 인터페이스
public interface Subject {
    void registerObserver(Observer o); // Observer를 등록
    void removeObserver(Observer o);   // Observer를 제거
    void notifyObservers();          // 등록된 Observer들에게 알림
}

2단계: 구체적인 서브젝트(Concrete Subject) 클래스 구현

실제 상태 정보를 가지고, 상태가 변하면 옵저버들에게 알리는 역할을 합니다.

Java
 
import java.util.ArrayList;
import java.util.List;

public class NewsAgency implements Subject {
    // 자신을 구독한 Observer들의 리스트
    private List<Observer> observers;
    private String news;

    public NewsAgency() {
        this.observers = new ArrayList<>();
    }

    @Override
    public void registerObserver(Observer o) {
        observers.add(o);
    }

    @Override
    public void removeObserver(Observer o) {
        observers.remove(o);
    }

    @Override
    public void notifyObservers() {
        // 등록된 모든 옵저버에게 최신 뉴스를 전달
        for (Observer observer : observers) {
            observer.update(this.news);
        }
    }

    // 새로운 뉴스가 들어오면 상태를 변경하고 옵저버들에게 알린다.
    public void setNews(String news) {
        this.news = news;
        System.out.println("\n>> 새로운 뉴스 발생: " + news);
        notifyObservers();
    }
}

3단계: 구체적인 옵저버(Concrete Observer) 클래스 구현

Subject로부터 알림을 받아 특정 동작을 수행하는 실제 객체들입니다.

Java
 
// 옵저버 1: 지상파 뉴스 채널
public class TvNewsChannel implements Observer {
    private String channelName;

    public TvNewsChannel(String channelName) {
        this.channelName = channelName;
    }

    @Override
    public void update(String news) {
        System.out.println("[" + channelName + "] 속보: " + news);
    }
}

// 옵저버 2: 온라인 뉴스 채널
public class OnlineNewsChannel implements Observer {
    @Override
    public void update(String news) {
        System.out.println("[온라인 뉴스] 기사 업데이트: " + news);
    }
}

4단계: 클라이언트 코드에서 사용하기

클라이언트는 옵저버들을 생성하여 서브젝트에 등록하고, 서브젝트의 상태를 변경하여 패턴이 동작하는 것을 확인합니다.

Java
 
public class Main {
    public static void main(String[] args) {
        // 1. 상태를 가질 주체(뉴스 통신사) 생성
        NewsAgency agency = new NewsAgency();

        // 2. 소식을 전달받을 옵저버(뉴스 채널)들 생성
        Observer channelA = new TvNewsChannel("KBS");
        Observer channelB = new TvNewsChannel("MBC");
        Observer onlineNews = new OnlineNewsChannel();

        // 3. 뉴스 통신사에 채널들을 구독자로 등록
        agency.registerObserver(channelA);
        agency.registerObserver(channelB);
        agency.registerObserver(onlineNews);

        // 4. 새로운 뉴스가 발생하면, 등록된 모든 채널에 자동으로 소식이 전달된다.
        agency.setNews("대한민국, 월드컵 결승 진출!");

        // 5. 한 채널이 구독을 해지
        agency.removeObserver(channelB);
        
        agency.setNews("차세대 AI 모델 전격 공개!");
    }
}

4. 옵저버 패턴과 실제 사례

  • GUI 이벤트 리스너: Java Swing이나 Android 앱에서 버튼을 클릭했을 때(onClick) 어떤 동작을 수행하게 하는 EventListener는 옵저버 패턴의 대표적인 예입니다. 버튼이 Subject가 되고, 버튼 클릭 이벤트를 받아 처리하는 Listener 객체가 Observer가 됩니다.
  • 스프링 프레임워크 ApplicationEventPublisher: 스프링에서는 ApplicationEvent를 통해 서로 다른 빈(Bean)들 간에 느슨하게 결합된 통신을 할 수 있습니다. 특정 빈이 이벤트를 발행(publishEvent)하면, 해당 이벤트를 구독(@EventListener)하도록 설정된 다른 빈들의 메서드가 자동으로 호출됩니다. 이는 시스템의 여러 부분을 분리하는 데 매우 유용합니다.

5. 문제 제시 및 답변 💡

문제: 주식 시장 시스템을 만들려고 합니다. 특정 주식의 가격(Stock)이 변동될 때마다, 해당 주식에 관심 있는 여러 투자자(Investor)들에게 실시간으로 가격 변동 알림을 보내고 싶습니다. 옵저버 패턴을 사용하여 이 시스템을 어떻게 설계할 수 있을까요?

답변:

뉴스 통신사 예제와 거의 동일한 구조로 설계할 수 있습니다.

  1. Stock 클래스 (Subject): 주식 정보를 가지는 주체입니다. 내부에 List<Investor>를 가지고, register/remove/notify 메서드를 구현합니다. 가격이 변동되는 setPrice() 메서드가 호출되면, 내부적으로 notifyObservers()를 호출하여 모든 투자자에게 변경된 가격을 알립니다.
  2. Investor 인터페이스 (Observer): 투자자를 위한 공통 인터페이스입니다. 주식 가격 정보를 전달받을 update(String stockName, int newPrice) 같은 메서드를 가집니다.
  3. 구체적인 Investor 클래스: Investor 인터페이스를 구현한 실제 투자자 클래스입니다. update 메서드가 호출되면 "A투자자: 삼성전자 가격이 90,000원으로 변경되었습니다."와 같은 메시지를 출력하는 동작을 수행합니다.

이렇게 설계하면, Stock 클래스는 어떤 투자자들이 자신을 보고 있는지 신경 쓸 필요가 없고, 투자자들은 주식 가격이 어떻게 변하는지 계속 확인할 필요 없이 변동이 있을 때만 알림을 받을 수 있는 효율적인 시스템을 만들 수 있습니다.

728x90
반응형