Design Pattern_Behavioral Patterns_Observer

박지홍·2026년 5월 4일

DesignPattern

목록 보기
10/12

옵저버 패턴

다수의 객체가 특정 객체 상태 변화를 감지하고 알림을 받는 패턴.

예제) '구독 알림' 좋아하는 유튜버의 새동영상이 올라오면 알림이 오는 것과 같음. => Subject가 자기를 구독한 모든 Observer에게 자동으로 알림을 뿌림

before

ChatServer.java — 메시지를 주제별로 저장만 해두는 게시판

public class ChatServer {
    private Map<String, List<String>> messages;  // 주제별 메시지 목록

    public void add(String subject, String message) {
        // 메시지를 해당 주제 목록에 추가
    }

    public List<String> getMessage(String subject) {
        return messages.get(subject);  // 주제로 조회
    }
}

User.java — 유저는 메시지를 보내거나(send), 직접 가져옴(get)

public class User {
    private ChatServer chatServer;

    public void sendMessage(String subject, String message) {
        chatServer.add(subject, message);
    }

    public List<String> getMessage(String subject) {
        return chatServer.getMessage(subject);  // 직접 가져옴
    }
}

Client.java — 실행 코드

User user1 = new User(chatServer);
user1.sendMessage("디자인패턴", "이번엔 옵저버 패턴입니다.");

User user2 = new User(chatServer);
System.out.println(user2.getMessage("디자인패턴"));  // ← 직접 조회

user1.sendMessage("디자인패턴", "예제 코드 보는 중..");
System.out.println(user2.getMessage("디자인패턴"));  // ← 또 직접 조회

문제점

User1이 메시지를 새로 보냈는데, User2는 자기가 직접 getMessage()를 호출하지 않으면 새 메시지가 왔는지 모름
=> 실시간성 x, 자원낭비

after - 구독시스템

Subscriber.java — 인터페이스

public interface Subscriber {
    void handleMessage(String message);
}

Observer 패턴의 핵심. 메시지가 오면 이 메서드로 알림

User.java — Subscriber

public class User implements Subscriber {  // ← Subscriber 구현!
    private String name;

    public User(String name) {
        this.name = name;
    }

    @Override
    public void handleMessage(String message) {
        System.out.println(message);  // 알림 받으면 출력
    }
}

ChatServer.java — 핵심 발행자

public class ChatServer {
    private Map<String, List<Subscriber>> subscribers = new HashMap<>();

    // 1. 구독 등록
    public void register(String subject, Subscriber subscriber) {
        // 주제별로 구독자 목록에 추가
    }

    // 2. 구독 해지
    public void unregister(String subject, Subscriber subscriber) {
        this.subscribers.get(subject).remove(subscriber);
    }

    // 3. 메시지 발행 → 자동으로 모든 구독자에게 푸시
    public void sendMessage(User user, String subject, String message) {
        if (this.subscribers.containsKey(subject)) {
            String userMessage = user.getName() + ": " + message;
            this.subscribers.get(subject).forEach(s -> s.handleMessage(userMessage));
            //                                          ↑
            //                            구독자들에게 알아서 뿌림!
        }
    }
}

forEach(s -> s.handleMessage(userMessage)) 자동알림. 메시지가 들어오면 구독자 모두에게 한 번에 뿌림

Client.java — 사용 방식

ChatServer chatServer = new ChatServer();
User user1 = new User("keesun");
User user2 = new User("whiteship");

// 구독 등록
chatServer.register("오징어게임", user1);
chatServer.register("오징어게임", user2);
chatServer.register("디자인패턴", user1);

// 메시지 보내면 → 구독자들에게 자동으로 알림 감
chatServer.sendMessage(user1, "오징어게임", "이름이 일남이야..");
//                                           → user1, user2 자동 출력

// 구독 해지하면 → 더 이상 알림 안 옴
chatServer.unregister("디자인패턴", user2);
chatServer.sendMessage(user2, "디자인패턴", "옵저버 패턴 보는 중");
//                                           → user1만 받음 (user2는 해지됨)

User들이 미리 register()로 구독해두면, ChatServer가 메시지 올 때마다 자동으로 HandleMessage() 불러줌

장단점

  • 장점

    • 상태를 변경하는 객체와 변경을 감지하는 객체의 관계를 느슨하게 유지할 수 있다.
    • Subject의 상태 변경을 주기적으로 조회하지 않고 자동으로 감지
    • 런타임에 옵저버를 추가하거나 제거할 수 있음
  • 단점

    • 복잡도 증가
    • 다수의 Observer 등록 이후 해지 않아면 메모리 부족 발생

실제 자바 예제

Spring의 ApplicationEvent

  • 발행자
@Component
public class MyRunner implements ApplicationRunner {
    private ApplicationEventPublisher publisher;

    public MyRunner(ApplicationEventPublisher publisher) {
        this.publisher = publisher;
    }

    @Override
    public void run(ApplicationArguments args) {
        publisher.publishEvent(new MyEvent("hello spring event"));  // 이벤트 발행!
    }
}
  • 구독자
@Component
public class MyEventListener {
    @EventListener(MyEvent.class)  // ← 이 어노테이션 하나면 끝
    public void onApplicationEvent(MyEvent event) {
        System.out.println(event.getMessage());
    }
}

@EventListener : 구독 등록
회원가입 하면 환영 이메일 발송 + 통계 카운트 + 슬랙 알림

0개의 댓글