Hollywood Principle

Hollywood Principle 은 객체지향 설계 및 프레임워크 설계에서 널리 쓰이는 원칙으로, “Don’t call us, we’ll call you” 라는 문구로 대표된다. 이 원칙은 저수준 (구현) 모듈이 고수준 (프레임워크, 추상화) 모듈을 직접 호출하는 것이 아니라, 고수준 모듈이 저수준 모듈을 필요할 때 호출하도록 구조를 설계한다. 이를 통해 의존성 부패 (Dependency Rot) 를 방지하고, 시스템의 유연성, 확장성, 테스트 용이성을 높인다. 대표적으로 Inversion of Control, Dependency Injection, Observer, Template Method, Strategy 패턴 등에서 적용된다.

핵심 개념

Hollywood Principle 은 **“Don’t call us, we’ll call you”**라는 문구로 요약되며, 저수준 컴포넌트는 고수준 컴포넌트의 호출을 기다리는 방식으로 제어 흐름을 관리한다. 느슨한 결합, 의존성 부패 방지, 확장성 및 테스트 용이성 확보를 목적으로 하며 프레임워크 설계, 이벤트 기반 시스템, 디자인 패턴 (Observer, Template Method, Strategy 등) 에 적용된다.

  1. 제어의 역전 (Inversion of Control, IoC)
    • 프로그램의 제어 흐름이 애플리케이션 코드가 아닌 외부 프레임워크에서 결정되는 원칙
    • 전통적인 “call” 방식에서 “callback” 방식으로의 패러다임 전환
  2. 콜백 메커니즘 (Callback Mechanism)
    • 특정 이벤트나 조건 발생 시 자동으로 호출되는 함수나 메서드
    • 비동기 프로그래밍과 이벤트 처리의 핵심 메커니즘
  3. 의존성 주입 (Dependency Injection, DI)
    • 객체가 필요한 의존성을 생성하지 않고 외부에서 제공받는 방식
    • 객체 생성과 사용의 책임을 분리하여 결합도를 낮춤
  4. 이벤트 기반 아키텍처 (Event-driven Architecture)
    • 이벤트의 발생과 처리를 중심으로 시스템을 설계하는 방식
    • Publisher-Subscriber 패턴의 기반이 되는 아키텍처 스타일
  5. 느슨한 결합 (Loose Coupling)
    • 컴포넌트 간 의존성을 최소화하여 변경에 대한 영향을 줄이는 설계 방식
    • 높은 응집도 (High Cohesion) 와 함께 소프트웨어 품질의 핵심 지표
  6. 프레임워크 vs 라이브러리 구분
    • 프레임워크: 애플리케이션 코드를 호출하는 주체
    • 라이브러리: 애플리케이션 코드에 의해 호출되는 대상
  7. Publisher-Subscriber 패턴
    • 이벤트 발행자와 구독자 간의 비동기 통신을 지원하는 패턴
    • 일대다 통신과 느슨한 결합을 동시에 실현

배경 및 필요성

Hollywood Principle 은 1980 년대 후반 객체지향 프로그래밍의 발전과 함께 등장했다. Martin Fowler 가 1988 년으로 그 기원을 추적하였으며, 할리우드 오디션에서 흔히 듣는 “Don’t call us, we’ll call you” 문구에서 이름이 유래되었다.

필요성:

  • 코드의 재사용성 향상
  • 컴포넌트 간 결합도 감소
  • 테스트 가능성 증대
  • 확장성과 유지보수성 개선
  • 관심사의 분리 실현

주요 기능 및 역할

  1. 제어 흐름 관리: 프레임워크가 애플리케이션의 실행 흐름을 제어
  2. 의존성 관리: 객체 간 의존성을 외부에서 주입하여 관리
  3. 이벤트 처리: 비동기적 이벤트 발생과 처리를 조율
  4. 생명주기 관리: 객체의 생성, 초기화, 소멸을 프레임워크가 관리
  5. 확장점 제공: 플러그인이나 확장 모듈을 위한 훅 (Hook) 제공

특징

  • 비침입적 (Non-intrusive): 비즈니스 로직에 프레임워크 코드가 침투하지 않음
  • 선언적 (Declarative): 설정이나 어노테이션을 통한 선언적 프로그래밍
  • 확장가능 (Extensible): 새로운 구현체를 쉽게 추가할 수 있는 구조
  • 테스트 친화적: Mock 객체를 쉽게 주입하여 단위 테스트 용이

핵심 원칙

  1. “Don’t call us, we’ll call you”: 능동적 호출에서 수동적 대기로의 전환
  2. 제어권 위임: 애플리케이션 코드가 제어권을 프레임워크에 위임
  3. 인터페이스 기반 설계: 구체 클래스 대신 인터페이스에 의존
  4. 관심사 분리: 비즈니스 로직과 인프라스트럭처 코드의 분리

작동 원리

  • 저수준 컴포넌트 (플러그인, 리스너 등) 는 고수준 컴포넌트에 등록만 한다.
  • 고수준 컴포넌트 (프레임워크, 이벤트 디스패처 등) 가 필요할 때 저수준 컴포넌트를 호출한다.
sequenceDiagram
    participant Client as Client Code
    participant Framework as Framework
    participant Callback as Callback Handler
    
    Note over Client,Callback: 1. 등록 단계 (Registration Phase)
    Client->>Framework: register(callback)
    Framework->>Framework: store callback in registry
    
    Note over Framework: 2. 대기 단계 (Waiting Phase)
    Note over Framework: Framework waits for events/conditions
    
    Note over Framework: 3. 트리거 단계 (Triggering Phase)
    Framework->>Framework: Internal event occurs
    
    Note over Framework,Callback: 4. 호출 단계 (Invocation Phase)
    Framework->>Callback: "Don't call us, we'll call you"
    
    Note over Callback: 5. 실행 단계 (Execution Phase)
    Callback->>Callback: Execute business logic
    
    Note over Framework: 6. 반환 단계 (Return Phase)
    Callback->>Framework: Return control/result
    Framework->>Client: Optional: notify completion

Hollywood Principle 의 작동 원리는 다음과 같은 단계로 구성된다:

  1. 등록 단계 (Registration): 클라이언트 코드가 프레임워크에 콜백이나 핸들러를 등록
  2. 대기 단계 (Waiting): 클라이언트는 능동적으로 호출하지 않고 대기
  3. 트리거 단계 (Triggering): 프레임워크 내부에서 특정 이벤트나 조건 발생
  4. 호출 단계 (Invocation): 프레임워크가 등록된 콜백을 호출
  5. 실행 단계 (Execution): 클라이언트의 비즈니스 로직 실행
  6. 반환 단계 (Return): 제어권이 프레임워크로 돌아감

구조 및 아키텍처

Hollywood Principle 은 고수준 모듈이 제어 흐름을 담당하고 저수준 모듈은 콜백, 이벤트 등록, 추상 클래스 상속 등을 통해 호출될 수 있는 상태를 유지하는 구조이다.

graph TB
    subgraph "Hollywood Principle 전체 아키텍처"
        Framework[Framework/Container]
        
        subgraph "필수 구성요소 (Essential Components)"
            Registry[Callback Registry]
            EventManager[Event Manager]
            LifecycleManager[Lifecycle Manager]
        end
        
        subgraph "선택 구성요소 (Optional Components)"
            ConfigManager[Configuration Manager]
            SecurityManager[Security Manager]
            MonitoringManager[Monitoring Manager]
        end
        
        subgraph "클라이언트 구성요소 (Client Components)"
            ClientCode[Client Code]
            CallbackHandlers[Callback Handlers]
            EventListeners[Event Listeners]
        end
        
        Framework --> Registry
        Framework --> EventManager
        Framework --> LifecycleManager
        Framework -.-> ConfigManager
        Framework -.-> SecurityManager
        Framework -.-> MonitoringManager
        
        ClientCode --> CallbackHandlers
        ClientCode --> EventListeners
        
        Registry --> CallbackHandlers
        EventManager --> EventListeners
        LifecycleManager --> ClientCode
        
        style Framework fill:#e8f5e8
        style Registry fill:#e3f2fd
        style EventManager fill:#e3f2fd
        style LifecycleManager fill:#e3f2fd
        style ConfigManager fill:#fff3e0
        style SecurityManager fill:#fff3e0
        style MonitoringManager fill:#fff3e0
        style ClientCode fill:#fce4ec
        style CallbackHandlers fill:#f3e5f5
        style EventListeners fill:#f3e5f5
    end

구성요소

구분구성요소기능 설명역할 및 특징
필수Framework / Container(프레임워크 / 컨테이너)시스템 전체 제어 흐름 담당콜백 실행, 생명주기 전반 제어중앙 제어권 보유, 실행 흐름 결정
Callback Registry(콜백 레지스트리)콜백 함수 등록/해제/관리동적 콜백 관리로 유연성 제공
Event Manager(이벤트 매니저)이벤트 발생/전파/처리이벤트 큐 및 리스너 관리비동기 이벤트 처리 지원
Lifecycle Manager(생명주기 매니저)객체 생성~소멸까지 관리의존성 주입 시점 통제리소스/의존성 관리, 전체 생명주기 제어
선택Configuration Manager(설정 매니저)설정 파일 로딩 및 설정 값 관리런타임 설정 변경 지원
Security Manager(보안 매니저)인증/인가, 접근 제어 수행**AOP(관심사 분리)**에 적합
Monitoring Manager(모니터링 매니저)성능 지표 수집, 로깅, 시스템 상태 추적실시간 모니터링 및 경보 시스템 연동

구현 기법

구분패턴 / 기법개념 요약제어 흐름목적대표 기술 / 예시
의존성 주입Dependency Injection (DI)의존 객체 생성을 외부에서 주입객체 생성 제어권 → 컨테이너결합도 감소, 테스트 용이성 향상Spring, NestJS, Angular, Guice
관찰자 패턴Observer Pattern상태 변화 발생 시 등록된 리스너에 자동 통보알림 제어권 → Subject상태 변화 감지, 다대일 연동EventEmitter, addEventListener
템플릿 메서드Template Method Pattern알고리즘의 뼈대를 상위 클래스가 정의하고, 하위 클래스가 구현알고리즘 순서 제어권 → 추상 클래스알고리즘 재사용성, 확장성Spring JdbcTemplate, React Lifecycle
이벤트 기반Event-driven Programming이벤트가 발생하면 리스너 또는 핸들러가 비동기적으로 반응흐름 제어권 → 이벤트 루프/큐느슨한 결합, 비동기 흐름 처리Node.js, Kafka, AWS EventBridge
전략 패턴Strategy Pattern실행 알고리즘을 객체로 분리하고 유연하게 교체실행 제어권 → 외부 전략 객체런타임 유연성, 조건별 실행 방식 분리Array.prototype.sort(callback)
서비스 등록소Service Locator Pattern객체를 전역 등록소에서 가져다 씀 (DI 의 대안적 방식)의존성 해결 제어권 → Locator의존성 중앙 관리Unity Container, InversifyJS
이벤트 버스Event Bus / Message Bus메시지 중심으로 컴포넌트 간 통신메시지 흐름 제어권 → 중앙 버스컴포넌트 간 결합도 제거Vue EventBus, RabbitMQ, EventBridge
콜백 등록Callback Registry콜백 함수를 동적으로 등록 및 해제 가능호출 시점 제어권 → 외부 등록자유연한 이벤트 대응 구조jQuery .on(), Node.js callbacks
생명주기 관리Lifecycle Management객체 생성~소멸 주기를 일관되게 관리자원 생명 제어권 → 컨테이너자원 누수 방지, 예측 가능한 상태 전환React, Angular, Spring Context

Dependency Injection (의존성 주입)

  • 제어 역전: 객체 생성과 의존성 주입을 컨테이너가 담당
  • 핵심: " 객체를 직접 생성하지 말고, 주입받아 사용하세요 "
graph TD
    subgraph "Dependency Injection Pattern"
        DI_Container[DI Container]
        DI_Service[Service Interface]
        DI_ServiceImpl[Service Implementation]
        DI_Client[Client Class]
        
        DI_Container -->|creates & injects| DI_ServiceImpl
        DI_Container -->|creates & injects| DI_Client
        DI_Client -.->|depends on| DI_Service
        DI_ServiceImpl -.->|implements| DI_Service
        
        style DI_Container fill:#e8f5e8
        style DI_Service fill:#e3f2fd
        style DI_ServiceImpl fill:#fff3e0
        style DI_Client fill:#fce4ec
    end

정의: 객체가 필요한 의존성을 생성하지 않고 외부에서 주입받는 기법
구성:

  • Injector (주입자): 의존성을 제공하는 주체
  • Service (서비스): 주입될 의존성 객체
  • Client (클라이언트): 의존성을 필요로 하는 객체

목적: 객체 생성과 사용의 책임 분리, 결합도 감소
실제 예시:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// Spring Framework 예시
@Service
public class OrderService {
    private final PaymentService paymentService;
    
    // 생성자 주입
    public OrderService(PaymentService paymentService) {
        this.paymentService = paymentService;
    }
}

Observer Pattern (관찰자 패턴)

  • 제어 역전: 상태 변화 알림을 Subject 가 주도
  • 핵심: " 변화를 감지하면 우리가 알려드리겠습니다 "
graph TD
    subgraph "Observer Pattern"
        Subject[Subject]
        Observer1[Observer 1]
        Observer2[Observer 2]
        ObserverN[Observer N]
        
        Subject -->|"notifyObservers()"| Observer1
        Subject -->|"notifyObservers()"| Observer2
        Subject -->|"notifyObservers()"| ObserverN
        
        Observer1 -.->|subscribe| Subject
        Observer2 -.->|subscribe| Subject
        ObserverN -.->|subscribe| Subject
        
        style Subject fill:#e8f5e8
        style Observer1 fill:#e3f2fd
        style Observer2 fill:#e3f2fd
        style ObserverN fill:#e3f2fd
    end

정의: 객체의 상태 변화를 여러 관찰자에게 자동으로 알리는 패턴
구성:

  • Subject (주제): 상태 변화의 주체
  • Observer (관찰자): 변화를 감지하고 처리하는 객체

목적: 일대다 의존성 정의, 느슨한 결합 실현
실제 예시:

1
2
3
4
5
6
7
8
9
// JavaScript EventEmitter 예시
const EventEmitter = require('events');
const emitter = new EventEmitter();

emitter.on('dataChanged', (data) => {
    console.log('Data updated:', data);
});

emitter.emit('dataChanged', { id: 1, value: 'new data' });

Template Method Pattern (템플릿 메서드 패턴)

  • 제어 역전: 알고리즘 실행 순서를 상위 클래스가 제어
  • 핵심: " 전체 흐름은 우리가 관리하고, 세부사항만 구현하세요 "
graph TD
    subgraph "Template Method Pattern"
        AbstractClass[Abstract Template Class]
        ConcreteClass1[Concrete Class 1]
        ConcreteClass2[Concrete Class 2]
        
        AbstractClass -->|calls| Hook1["primitiveOperation1()"]
        AbstractClass -->|calls| Hook2["primitiveOperation2()"]
        
        ConcreteClass1 -.->|implements| Hook1
        ConcreteClass2 -.->|implements| Hook2
        
        ConcreteClass1 -->|extends| AbstractClass
        ConcreteClass2 -->|extends| AbstractClass
        
        style AbstractClass fill:#e8f5e8
        style ConcreteClass1 fill:#e3f2fd
        style ConcreteClass2 fill:#e3f2fd
        style Hook1 fill:#fff3e0
        style Hook2 fill:#fff3e0
    end

정의: 알고리즘의 골격을 정의하고 하위 클래스에서 세부 단계를 구현하는 패턴
구성:

  • Abstract Class (추상 클래스): 템플릿 메서드 정의
  • Concrete Class (구체 클래스): 세부 단계 구현

목적: 알고리즘 구조 고정, 세부 구현의 변경 허용
실제 예시:

1
2
3
4
5
6
7
8
9
// Spring JdbcTemplate 예시
public abstract class JdbcTemplate {
    public final List<T> query(String sql, RowMapper<T> mapper) {
        // 1. 연결 획득
        // 2. SQL 실행
        // 3. 결과 처리 (mapper 콜백 호출)
        // 4. 연결 해제
    }
}

Event-driven Programming (이벤트 기반 프로그래밍)

  • 제어 역전: 이벤트 발생과 처리를 이벤트 버스가 조율
  • 핵심: " 이벤트가 발생하면 우리가 처리하겠습니다 "
graph TD
    subgraph "Event-Driven Pattern"
        EventProducer[Event Producer]
        EventBus[Event Bus]
        EventConsumer1[Event Consumer 1]
        EventConsumer2[Event Consumer 2]
        EventQueue[Event Queue]
        
        EventProducer -->|publish| EventBus
        EventBus -->|enqueue| EventQueue
        EventQueue -->|dispatch| EventConsumer1
        EventQueue -->|dispatch| EventConsumer2
        
        EventConsumer1 -.->|subscribe| EventBus
        EventConsumer2 -.->|subscribe| EventBus
        
        style EventProducer fill:#e8f5e8
        style EventBus fill:#e3f2fd
        style EventQueue fill:#fff3e0
        style EventConsumer1 fill:#fce4ec
        style EventConsumer2 fill:#fce4ec
    end

정의: 이벤트 발생에 따라 프로그램 흐름이 결정되는 프로그래밍 방식
구성:

  • Event Producer (이벤트 생산자): 이벤트를 발생시키는 주체
  • Event Bus (이벤트 버스): 이벤트 전달 매개체
  • Event Consumer (이벤트 소비자): 이벤트를 처리하는 주체

목적: 비동기적 상호작용 처리, 시스템 간 결합도 감소
실제 예시:

1
2
3
4
5
6
7
// Node.js 이벤트 루프 예시
const fs = require('fs');

fs.readFile('data.txt', (err, data) => {
    if (err) throw err;
    console.log(data); // 콜백이 호출됨
});

장점과 단점

구분항목설명
✅ 장점느슨한 결합 (Low Coupling)컴포넌트 간 직접적인 의존성이 줄어들어 변경에 유연하게 대응 가능
높은 응집도 (High Cohesion)각 모듈이 자신이 맡은 역할에 집중할 수 있도록 구조화됨
확장성 (Extensibility)새로운 구현체 또는 기능 추가 시 기존 코드 변경 없이 가능
재사용성 (Reusability)공통 인터페이스 및 컴포넌트 재사용률 증가
관심사의 분리 (Separation of Concerns)도메인 로직, 인프라 로직, UI 등 계층별 책임 분리가 용이
테스트 용이성 (Testability)의존 객체를 Mock 등으로 주입하여 단위 테스트가 쉬워짐
유지보수성 향상 (Maintainability)변경이 국소화되어 코드 이해 및 수정이 용이
설계 유연성 확보전략 패턴, DI, 이벤트 기반 처리 등 다양한 설계 방식과 연계 가능
⚠ 단점구조적 복잡성 증가객체 간 연결 관계가 분산되어 코드 구조를 직관적으로 파악하기 어려움
디버깅의 어려움런타임에 의존성 주입, 이벤트 전파 등으로 인해 호출 추적이 복잡
성능 오버헤드DI 컨테이너, 리플렉션, 프록시 객체 사용 등으로 인한 실행 비용 증가 가능
프레임워크 종속성 증가특정 IoC/DI 프레임워크 (Spring, Angular 등) 에 대한 강한 의존 위험
학습 곡선 (Learning Curve)초보 개발자에게는 DI, 이벤트 전파, 추상화 개념이 어려울 수 있음
오용 시 설계 취약점 노출과도한 추상화 또는 불필요한 인터페이스 설계는 오히려 복잡도 유발
초기 설정 부담의존성 매핑, 설정파일 구성 등 초기 진입 비용 존재
동적 흐름으로 인한 예측 어려움실행 흐름이 런타임에 결정되므로 정적 분석이 어렵고 예외 발생 위치 추적이 어려움
  • 장점은 시스템의 유연성, 테스트성, 재사용성, 구조적 품질을 대폭 향상시킴
  • 단점은 시스템의 추상화 수준 증가, 학습 난이도, 디버깅/성능 관리 부담
  • **현대 프레임워크 (Spring, NestJS, Angular 등)**는 이러한 장단점을 구조적으로 흡수할 수 있도록 다양한 도구 (DevTools, AOP, 트레이싱 등) 를 함께 제공

도전 과제

도전 과제설명해결책
1. 코드 복잡도 증가간접 호출과 추상화가 많아지며, 전반적인 코드 흐름이 복잡해짐명확한 인터페이스 설계, 계층적 책임 분리, 포괄적 문서화 및 코드 리뷰 강화
2. 성능 저하 가능성리플렉션, 프록시, 동적 주입 등의 사용으로 런타임 오버헤드 발생캐싱 전략, 컴파일 타임 주입 (Dagger, NestJS), 지연 로딩 (Lazy Loading) 적용
3. 디버깅 난이도실행 흐름이 이벤트나 DI 컨테이너에 의해 결정되어 추적이 어려움구조화된 로깅 (SLF4J, Winston), APM (New Relic, Datadog), 단위별 테스트 도입
4. 호출 흐름 가시성 부족이벤트 기반 설계나 메시지 버스 사용 시 흐름이 암시적으로 전개되어 가독성이 저하됨AOP 기반 트레이싱, 이벤트 로그 시각화 도구 활용 (OpenTelemetry, Zipkin 등)
5. 학습 곡선DI, IoC, 이벤트 기반 패턴에 익숙하지 않은 개발자에게 진입장벽이 존재페어프로그래밍, 주석 중심 설계 문서 제공, 온보딩용 샘플 프로젝트 제공
6. 의존성 과잉 분리 문제작은 단위로 지나치게 나눈 컴포넌트는 오히려 관리 포인트를 증가시키고 오버엔지니어링 유발의미 단위의 기능 그룹화, 모듈 간 책임 최소화, 필요 시 단순화 전략 적용
7. 프레임워크 의존성 증가DI/IoC 가 프레임워크에 묶여 구현될 경우 전환 비용이 커질 수 있음도메인 로직을 프레임워크와 분리하고, 어댑터 패턴 등으로 종속성 차단
8. 테스트 복잡성 증가DI 및 콜백 기반 설계로 인해 테스트 환경 구성 시 모킹과 설정이 복잡해질 수 있음테스트 컨테이너 활용 (Spring TestContext, Jest Mocks), 의존성 명시적 구성
  • IoC 기반 설계는 유연성과 확장성의 장점이 크지만, 동시에 복잡성과 학습 비용, 디버깅 어려움이라는 단점이 동반된다.
  • 이러한 도전 과제를 해결하기 위해선 설계 명확화, 도구 활용, 문서화, 교육 체계화가 필수이다.
  • 특히 로깅/모니터링/트레이싱 도구는 실무에서 IoC 설계의 가시성과 유지보수성을 확보하는 핵심 도구이다.

분류에 따른 종류 및 유형

분류 기준유형설명적용 사례
설계 패턴 기반Observer이벤트 기반 구조에서 상태 변화 시 알림을 구독자에게 전달하는 패턴EventEmitter, RxJS, DOM Events
Template Method알고리즘의 구조는 상위 클래스가 정의하고, 구체 구현은 하위 클래스에 위임Spring JdbcTemplate, React render()
제어 구조 방식Callback-based IoC프레임워크가 콜백 함수를 등록하고 필요 시 실행Express middleware, setTimeout
Event-driven IoC이벤트 발생 → 핸들러 등록 기반 흐름 제어Node.js 이벤트 루프, NestJS
Injection-based IoC외부에서 의존성을 생성하고 객체에 주입Spring DI, NestJS Provider
Template-based IoC일정한 실행 순서를 정한 후 사용자 구현을 삽입하는 방식Java Template 패턴
제어 주체 유형Flow Inversion실행 흐름 제어가 프레임워크에 의해 전환됨React 렌더링, Spring DispatcherServlet
Interface Inversion객체가 인터페이스에 의존하고 구현체는 나중에 주입DIP (Dependency Inversion Principle)
의존성 주입 방법Constructor-based IoC생성자를 통한 의존성 주입@Autowired 생성자 주입
Setter-based IoC설정자 (setter) 를 통해 의존성 주입Spring XML DI
Interface-based IoC명시적인 인터페이스 구현을 통한 주입Guice, Spring
Annotation-driven IoC어노테이션으로 의존성 및 실행 흐름 제어Spring Boot, Jakarta CDI
적용 범위 기준Component-level IoC단일 컴포넌트 혹은 클래스 단위에서의 제어 역전React 컴포넌트, DI 단위 서비스
Framework-level IoC프레임워크가 전체 실행 제어권을 가짐Spring, Angular, NestJS
System-level IoC아키텍처 전반에서 이벤트와 제어 흐름을 위임EDA(Event-Driven Architecture), Kubernetes Operator

실무 적용 예시

분야/기술적용 방식구체적 예시
프론트엔드컴포넌트 간 메시지 전달, 라이프사이클 자동 제어Vue 의 이벤트 버스, React useEffect, Angular ngOnInit()
웹 개발 (JS)이벤트 핸들러 등록에 의한 흐름 위임addEventListener, EventEmitter, async/await
모바일 개발시스템 호출 기반 생명주기 관리Android onCreate, onPause, iOS AppDelegate 메서드
게임 개발엔진이 호출하는 구조 기반 흐름 제어Unity Start(), Update() 등 MonoBehaviour 메서드
백엔드 (Java)의존성 주입 (DI), 이벤트 리스닝Spring @Autowired, @Component, @EventListener, @Async
Node.js이벤트 루프 기반 비동기 처리EventEmitter, 비동기 콜백, Promise, async/await
마이크로서비스이벤트 기반 아키텍처로 서비스 간 제어 분리Kafka, RabbitMQ 등 메시지 브로커를 통한 서비스 간 간접 통신
ASP.NET페이지/컨트롤러 생명주기 자동 호출Page_Load, Page_Init, MVC 컨트롤러의 OnActionExecuting
Angular의존성 주입 및 모듈 자동 등록@Injectable(), 의존성 프로바이더 설정, 컴포넌트 주입
Express.js미들웨어 체인 구조에 따른 흐름 위임app.use(), next() 를 활용한 요청 처리 흐름 제어

활용 사례

사례 1: 주문 처리 시스템에서 이메일 알림 처리

시나리오: 주문 처리 시스템에서 이메일 알림 처리

시스템 구성:

  • OrderService: 주문 처리 및 이벤트 발행
  • EmailNotificationService: 이벤트 리스너로 동작하여 메일 발송
  • EventDispatcher: 이벤트 관리 및 분배

Workflow 다이어그램:

sequenceDiagram
  participant OrderService
  participant EventDispatcher
  participant EmailService

  OrderService->>EventDispatcher: emit(OrderCreated)
  EventDispatcher->>EmailService: on(OrderCreated)
  EmailService->>EmailService: sendEmail()

Workflow:

  1. 주문 생성 요청 → OrderService
  2. 이벤트 발행: OrderCreated
  3. EmailService 가 해당 이벤트 수신 후 이메일 발송

역할 요약:

컴포넌트역할
OrderService주문 처리 및 이벤트 발행
EventDispatcher이벤트 라우팅 및 디스패치
EmailNotificationService후속 처리 (이메일 발송)

사례 2: 온라인 쇼핑몰 주문 처리 시스템

시나리오: 대규모 온라인 쇼핑몰에서 주문 처리 시 여러 서비스가 협력하여 작업을 수행하는 시스템
시스템 구성 요소:

  1. OrderService (주문 서비스): 주문 생성 및 관리
  2. EventPublisher (이벤트 발행자): 시스템 이벤트 발행
  3. PaymentService (결제 서비스): 결제 처리
  4. InventoryService (재고 서비스): 재고 관리
  5. EmailService (이메일 서비스): 알림 이메일 발송
  6. EventBus (이벤트 버스): 이벤트 중개 및 라우팅
    시스템 구성:
graph TB

    %% Frontend Layer
    subgraph "👤 Frontend Layer"
        User[사용자]
    end

    %% Service Layer
    subgraph "⚙️ Service Layer"
        OrderController[주문 컨트롤러]
        OrderService[주문 서비스]
        EventPublisher[이벤트 발행자]
        EventBus[이벤트 버스]
        PaymentService[결제 서비스]
        InventoryService[재고 서비스]
        EmailService[이메일 서비스]
        LoggingService[로깅 서비스]
        ShippingService[배송 서비스]
        NotificationService[알림 서비스]
    end

    %% Infra Layer (간접적 표현)
    subgraph "💾 Infra Layer"
        Database[(DB 또는 큐 시스템)]
    end

    %% 흐름 정의
    User -->|"1.주문 요청"| OrderController
    OrderController -->|"2.주문 처리"| OrderService
    OrderService -->|"3.이벤트 발행"| EventPublisher
    EventPublisher -->|"4.주문 생성 이벤트"| EventBus

    EventBus -->|"5.결제 처리"| PaymentService
    EventBus -->|"6.재고 차감"| InventoryService
    EventBus -->|"7.이메일 발송"| EmailService
    EventBus -->|"8.로깅"| LoggingService

    PaymentService -->|결제 완료 이벤트| EventBus
    InventoryService -->|재고 차감 이벤트| EventBus
    EventBus -->|배송 준비 이벤트| ShippingService
    ShippingService -->|배송 시작 이벤트| EventBus
    EventBus -->|알림 발송| NotificationService

    %% Infra 연계 표현
    PaymentService --> Database
    InventoryService --> Database
    LoggingService --> Database

Hollywood Principle 의 역할:

  1. 이벤트 기반 아키텍처: 각 서비스는 직접 호출하지 않고 이벤트를 통해 통신
  2. 느슨한 결합: 서비스 간 직접적인 의존성 없이 독립적 운영
  3. 확장성: 새로운 서비스 추가 시 기존 코드 변경 없이 이벤트 구독만 추가
  4. 비동기 처리: 블로킹 없이 여러 작업을 동시에 처리

Workflow:

  1. 사용자가 주문 요청을 보냄
  2. 주문 컨트롤러가 요청을 받아 주문 서비스에 전달
  3. 주문 서비스가 " 주문 생성 " 이벤트를 이벤트 버스에 발행
  4. Hollywood Principle 적용: 이벤트 버스가 등록된 서비스들을 호출
  5. 각 서비스가 독립적으로 작업 수행 (결제, 재고 차감, 이메일 발송)
  6. 작업 완료 후 결과를 다시 이벤트로 알림
  7. 최종적으로 사용자에게 주문 완료 응답 전송
sequenceDiagram
    participant User as 사용자
    participant Controller as 주문 컨트롤러
    participant OrderSvc as 주문 서비스
    participant EventBus as 이벤트 버스
    participant PaymentSvc as 결제 서비스
    participant InventorySvc as 재고 서비스
    participant EmailSvc as 이메일 서비스
    participant ShippingSvc as 배송 서비스
    
    Note over User,ShippingSvc: 주문 처리 워크플로우
    
    User->>Controller: 1. 주문 요청 (POST /orders)
    Controller->>OrderSvc: 2. 주문 생성 요청
    OrderSvc->>OrderSvc: 3. 주문 데이터 검증 및 저장
    OrderSvc->>EventBus: 4. "OrderCreated" 이벤트 발행
    
    Note over EventBus: Hollywood Principle 적용점:<br/>"우리가 필요한 서비스들을 호출할게요!"
    
    par 병렬 처리 (Parallel Processing)
        EventBus->>PaymentSvc: 5a. 결제 처리 콜백 호출
        PaymentSvc->>PaymentSvc: 결제 API 호출
        PaymentSvc->>EventBus: "PaymentCompleted" 이벤트
    and
        EventBus->>InventorySvc: 5b. 재고 차감 콜백 호출
        InventorySvc->>InventorySvc: 재고 확인 및 차감
        InventorySvc->>EventBus: "InventoryUpdated" 이벤트
    and
        EventBus->>EmailSvc: 5c. 주문 확인 이메일 콜백 호출
        EmailSvc->>EmailSvc: 이메일 템플릿 생성 및 발송
        EmailSvc->>EventBus: "EmailSent" 이벤트
    end
    
    Note over EventBus: 모든 이벤트 완료 확인
    EventBus->>ShippingSvc: 6. 배송 준비 콜백 호출
    ShippingSvc->>ShippingSvc: 배송 라벨 생성
    ShippingSvc->>EventBus: "ShippingPrepared" 이벤트
    
    EventBus->>Controller: 7. 주문 처리 완료 알림
    Controller->>User: 8. 주문 완료 응답 (200 OK)
    
    Note over User,ShippingSvc: 전체 프로세스에서 Hollywood Principle:<br/>각 서비스는 직접 호출하지 않고<br/>이벤트 버스가 필요할 때 호출

실무에서 효과적으로 적용하기 위한 고려사항 및 주의할 점

구분고려사항설명권장사항
설계 원칙적절한 추상화 수준 유지과도한 추상화는 복잡도만 높이고, 추상화 부족은 재사용성을 낮춤핵심 도메인만 추상화, 구체 구현은 필요시 계층화 적용
인터페이스명확한 계약 정의소비자와 제공자 간의 인터페이스는 역할에 맞게 분리되어야 함단일 책임 원칙 (SRP), 인터페이스 분리 원칙 (ISP) 적용
구현 구조IoC/DI 구조의 일관성프레임워크/컨테이너에 따라 설계 표준과 구현 패턴이 달라질 수 있음코드 스타일 가이드 문서화, 코드 리뷰 프로세스 강화
컴포넌트 책임모듈 간 책임 명확화이벤트/콜백/의존성 흐름이 명확하지 않으면 추적과 유지보수가 어려움이벤트 네이밍 컨벤션 정립, 도메인 중심 핸들러 설계
테스트 전략테스트 용이성 확보DI 를 적용해도 테스트 전략이 미흡하면 결합도를 낮추는 이점이 사라짐Mock/Stub/Fake 등 Test Double 전략 활용
에러 처리장애 전파 방지느슨한 결합 구조에서는 한 컴포넌트 실패가 전체 장애로 이어질 수 있음Circuit Breaker, Retry, Timeout 설정 적용
흐름 추적이벤트 및 제어 흐름 가시성 확보비동기 구조에서는 흐름 파악이 어려워 디버깅, 운영에 어려움 발생APM, 구조화 로깅, 시퀀스 다이어그램 기반 문서화
문서화호출 구조 및 의사결정 기록DI 구조에서는 흐름이 코드로 드러나지 않으므로 시각적 문서화 필요아키텍처 다이어그램, 이벤트 카탈로그, 기술 의사결정 기록 (ADR) 관리
모니터링시스템 관측 가능성 (Observability)문제 발생 원인을 빠르게 식별할 수 있는 설계 필요로그 집계 (ELK), 분산 추적 (Jaeger, OpenTelemetry), 메트릭 수집 (Prometheus)

최적화하기 위한 고려사항

카테고리고려사항설명권장사항
이벤트 처리이벤트 병목 방지하나의 이벤트에 과도한 리스너가 연결될 경우 처리 지연 발생 가능이벤트 큐, 메시지 브로커, 비동기 배치 처리 도입
이벤트 순서 보장이벤트 핸들러가 비동기일 경우 순서가 꼬이는 문제 발생Kafka Partition Key, Event Sourcing 시간 정렬 사용
불필요한 이벤트 제한남용 시 코드 복잡도 및 자원 낭비 유발필요 이벤트만 정의, Consumer 수 제한, 이벤트 카탈로그 관리
콜백/비동기콜백 체인 최적화깊은 콜백 체인은 디버깅/유지보수 어려움Promise/async-await, 콜백 분리, 체인 길이 제한
비동기 처리 최적화비동기 병렬 처리가 오히려 성능 저하로 이어질 수 있음스레드 풀 크기 조정, 논블로킹 I/O (예: Netty, Reactor)
DI 구성 요소DI 오버헤드 관리DI 컨테이너 자체의 초기화 비용, 리플렉션 성능 이슈 존재JIT 대신 AOT 컴파일 사용 (Spring Native 등), 런타임 캐싱
불필요한 추상화 제거과도한 추상화는 불필요한 계층 및 호출 비용 유발실제 변경 가능성이 있는 부분만 추상화
의존성 범위 최소화DI 범위가 넓을수록 초기화 시간과 메모리 사용 증가DI 타겟을 단일 책임 단위로 분리, 프로토타입보다 싱글톤 활용
메모리 관리GC 친화적 구조 설계이벤트 리스너 및 DI 객체의 참조 누락 시 GC 대상 제외WeakReference 사용, 리스너 해제 필수, @PreDestroy 등으로 정리 수행
리플렉션리플렉션 최소화DI 프레임워크의 리플렉션 사용은 런타임 성능 저하 원인메타프로그래밍 최소화, 런타임 코드 생성 회피, final 타입 활용
직렬화/캐싱효율적인 데이터 직렬화느린 JSON 직렬화는 응답 지연 유발Protobuf, Avro 등 이진 직렬화 적용, GZIP 압축
런타임/컴파일 캐싱동일 의존성/이벤트 반복 생성 시 오버헤드 발생CDI 캐시, Context 객체 재사용, 인스턴스 풀링 적용
모니터링/분석성능 병목 지점 파악병목 발생 시 위치 추적이 어려운 구조APM (New Relic, Datadog), 분산 추적 (OpenTelemetry, Jaeger) 도입

주제와 관련하여 주목할 내용

분류항목설명
핵심 원칙Hollywood Principle“Don’t call us, we’ll call you”—프레임워크가 제어 흐름을 관리
의존성 역전 원칙 (DIP)고수준 모듈이 저수준 모듈에 의존하지 않고 추상화에 의존하도록 설계
단일 책임 원칙 (SRP)컴포넌트가 하나의 책임만 가지도록 하여 유지보수성과 확장성 확보
디자인 패턴Strategy Pattern실행 알고리즘을 동적으로 변경 가능하게 하여 유연한 구조 제공
Command Pattern요청을 객체로 캡슐화해 실행을 지연하거나 큐잉/로깅 등 가능
Observer Pattern이벤트 발생 시 리스너에게 자동 알림, 느슨한 결합 구현
Template Method Pattern알고리즘의 틀은 상위 클래스에서, 세부 로직은 하위 클래스에 위임
아키텍처마이크로서비스 아키텍처서비스 간 통신을 이벤트 기반 메시징으로 처리하여 IoC 구현
육각형 아키텍처 (Hexagonal)포트 - 어댑터 구조로 외부와의 의존성 방향을 명확히 분리
프레임워크Spring IoC Container의존성 주입 및 생명주기 제어를 지원하는 Java 기반 IoC 프레임워크
Angular DI계층적 주입 트리 기반의 DI 시스템을 제공하는 프론트엔드 프레임워크
기술/언어AOP (Aspect-Oriented Programming)횡단 관심사 분리로 로깅/보안/트랜잭션 등의 IoC 기반 처리 가능
Reactive Programming비동기 스트림 기반으로 이벤트/데이터 흐름을 조율하는 패러다임
JavaScript Event Loop비동기 콜백 기반 IoC 처리 메커니즘 (단일 스레드 환경에서의 제어 역전 예시)
C# Delegates / Events형식 안전한 콜백 구조로 이벤트 기반 프로그래밍 가능
설계 목적느슨한 결합객체 간 독립성 강화로 유지보수성/확장성 확보
의존성 부패 방지 (Dependency Rot)과도하게 얽힌 의존성 구조를 추상화와 제어 분리로 해소

하위 주제별 추가 학습 필요 내용

카테고리주제간략 설명
디자인 패턴Observer Pattern이벤트 알림 구조로 느슨한 결합 구현
Template Method Pattern알고리즘 골격은 상위에서, 세부는 하위 클래스에 위임
Strategy Pattern런타임 알고리즘 교체 가능, 유연한 설계 구조
IoC / DIIoC (Inversion of Control)제어 흐름을 프레임워크가 담당, Hollywood Principle 의 핵심
DI (Dependency Injection)외부에서 의존 객체를 주입, 테스트/유지보수 용이
설계 원칙SOLID 원칙 (특히 DIP, SRP)의존성 역전 원칙, 단일 책임 원칙 등 IoC 와 밀접한 설계 원칙
Law of Demeter (최소 지식 원칙)컴포넌트 간 결합도 최소화, 간접 호출 지양
아키텍처 패턴이벤트 소싱 (Event Sourcing)모든 상태 변경을 이벤트로 저장, 감사/복원 가능
CQRS (Command Query Responsibility Segregation)읽기/쓰기 분리로 시스템 확장성과 최적화 구현
플러그인/확장성 설계플러그인 아키텍처동적으로 기능 추가 가능한 구조, 인터페이스 기반 확장
이벤트 시스템도메인 이벤트를 중심으로 비동기/확장 가능한 시스템 구축
테스트 전략Mock / Stub / SpyIoC 환경에서의 의존성 대체 및 단위 테스트 구현 기법
동시성 모델Actor Model메시지를 주고받는 방식으로 동시성 제어, 고립성 유지
CSP (Communicating Sequential Processes)채널 기반 통신 (Go, Kotlin Coroutine 등에서 활용)
함수형 프로그래밍고차 함수 (Higher-Order Functions)함수를 매개변수/리턴값으로 사용하는 추상화 기법
Reactive Extensions (Rx, Reactor 등)비동기 데이터 스트림과 옵저버블 기반 이벤트 처리 모델

관련 분야별 추가 학습 내용

카테고리주제설명
웹 개발Webhook 아키텍처외부 시스템과의 통합 시 HTTP 기반 이벤트 푸시 방식
Server-Sent Events (SSE)서버에서 클라이언트로 단방향 실시간 데이터 전송 방식
클라우드Serverless 아키텍처이벤트 기반 함수 실행 (ex. AWS Lambda), 자동 확장 및 비용 최적화에 효과적
AWS 기반 이벤트 아키텍처EventBridge, Lambda, SQS 조합으로 서버리스 이벤트 처리 구현
데이터베이스Database TriggersINSERT/UPDATE/DELETE 시 자동 실행되는 DB 수준의 콜백 기능
Change Data Capture (CDC)데이터 변경을 추적하여 외부 시스템으로 이벤트 발행 (ex. Debezium, Kafka Connect)
모바일 개발iOS Delegation Pattern델리게이트를 활용한 ViewController 간의 통신 구조 (Swift/Objective-C)
Android Lifecycle Callbacks생명주기 이벤트에 따른 콜백 메서드 활용 (onCreate, onResume 등)
게임 개발Entity-Component-System (ECS)데이터 중심, 컴포넌트 기반 아키텍처로 유연하고 확장성 높은 게임 로직 구성
Event Bus Pattern객체 간 메시지를 느슨하게 전달하는 게임 이벤트 처리 방식
DevOpsGitOps WorkflowGit 저장소의 이벤트 기반으로 자동화된 CI/CD 파이프라인 실행
Infrastructure as Code (IaC)Terraform, Pulumi 등으로 선언적으로 인프라를 관리하고 자동화
소프트웨어 설계레이어드 아키텍처표현, 애플리케이션, 도메인, 인프라 계층으로 구성된 구조에서 IoC 적용 지점 분석
결합도와 응집도컴포넌트 간 의존성 최소화 (Loose Coupling) 와 책임 집중 (High Cohesion) 설계 원칙
코드 품질의존성 분석의존성 그래프 시각화 및 모듈간 결합도 분석 (ex. SonarQube, CodeScene)
성능 최적화DI 컨테이너 성능 최적화DI 사용에 따른 메모리/속도 이슈 관리 (AOT 컴파일, Lazy Loading 등)

용어 정리

핵심 원칙 및 설계 철학

용어설명
Hollywood Principle (헐리우드 원칙)“Don’t call us, we’ll call you”—제어 흐름을 프레임워크가 담당.
Inversion of Control (IoC, 제어의 역전)실행 흐름 제어권을 프레임워크 등 상위 컴포넌트로 전환하는 설계 원칙
Dependency Injection (DI, 의존성 주입)의존성을 외부에서 주입하여 결합도를 낮추는 IoC 구현 방식
느슨한 결합 (Loose Coupling)컴포넌트 간 의존성이 낮아 변경에 강한 구조
높은 응집도 (High Cohesion)한 모듈이 명확하고 관련된 책임만 갖도록 하는 설계
Law of Demeter (최소 지식 원칙)다른 객체 내부 구조를 몰라도 작동하는 설계

디자인 패턴 및 구조 전략

용어설명
Observer / Template / Strategy 패턴제어 흐름 역전이 적용되는 대표적인 디자인 패턴
CQRSCommand 와 Query 를 분리해 읽기/쓰기 책임을 분리하는 아키텍처 패턴
Event Sourcing상태 저장 대신 이벤트 시퀀스로 시스템 상태를 재구성하는 방식
Choreography서비스 간 이벤트 기반 비중앙 통합 방식
Orchestration중앙 조정자가 서비스 호출을 순서대로 관리하는 통합 방식
Service Locator의존 객체를 찾기 위한 전역 접근점 제공 패턴 (IoC 대안이나 결합도 높음)

비동기/이벤트 기반 프로그래밍

용어설명
Callback Hell중첩된 콜백 구조로 인해 가독성 및 유지보수성이 낮아지는 문제
Event LoopJavaScript 등 단일 스레드 환경에서 비동기 작업을 처리하는 메커니즘
Reactive Streams비동기 데이터 흐름을 처리하기 위한 스트림 표준
Backpressure생산자와 소비자 간 속도 불균형을 조절하는 흐름 제어 메커니즘
Fluent Interface메서드 체이닝 기반의 유창한 API 설계 방식

실패 내성 및 안정성 설계

용어설명
Circuit Breaker연속 실패 시 호출 차단으로 시스템을 보호하는 패턴
Retry실패한 작업을 일정 횟수 재시도하는 실패 복구 전략
보상 트랜잭션 (Compensating Transaction)실패한 작업의 반대 작업을 통해 데이터 일관성을 복구하는 전략
Idempotency동일 작업을 여러 번 실행해도 결과가 동일한 특성 (중복 처리 방지)
DLQ (Dead Letter Queue)실패 메시지를 별도로 격리하여 후속 처리용으로 보관하는 큐

IoC 인프라 및 실무 관련 개념

용어설명
IoC Container객체 생성, 의존성 주입, 생명주기 관리 등을 담당하는 컨테이너 컴포넌트
Aspect WeavingAOP(관점 지향 프로그래밍) 에서 관심사를 핵심 로직에 삽입하는 처리 과정
의존성 부패 (Dependency Rot)과도한 의존성 확장 및 모듈 간 결합 증가로 구조가 무너지는 현상

참고 및 출처

핵심 개념 (Hollywood Principle / IoC / DI)

프레임워크 / 이벤트 시스템 실무 예시

클라우드 / 메시징 / 아키텍처

기타 참고 아티클 및 커뮤니티 자료