앱을 만들다 보면 복잡도가 증가하게 되어있는데, 이때 이벤트 관리 또한 유지보수가 힘든 부분 중 하나이다.
이런 상황에서 이벤트 디스패처 패턴이 도움이 될 수 있다.
Event Dispatcher 패턴

이벤트 디스패처 패턴은 Publisher-Subscriber 구조를 기반으로 발행자와 구독자 사이에 Dispatcher를 두어 둘을 완전히 분리하는 설계 패턴을 말한다.
- Publisher: 이벤트를 발생시키는 주체
- Event Dispatcher: 이벤트를 수신하고, 등록된 리스너 목록을 보고 해당 이벤트를 전달
- Subscriber: 특정 이벤트를 구독하고, 해당 이벤트가 오면 콜백을 실행
왜 사용하는건가?
직접 A → B로 호출하는 경우 A, B 둘이 강하게 결합된다. 하지만 이벤트 디스패처 패턴을 활용하면 Publisher와 Subscriber가 서로 몰라도 되므로 코드 결합도가 낮아지고, 새 구독자를 추가할 때 기존 코드를 건드리지 않아도 된다.
활용 예시

사진과 같이 앱을 개발중인 상황,
A 컴포넌트 내부의 초록색 버튼과 C 컴포넌트 내부의 초록색 버튼을 눌렀을 때 특정 위치로 스크롤이 되어야 한다는 요구사항을 만족해야 한다고 가정하자.
커스텀 디스패처 없이 구현
// Header.tsx
function Header() {
const handleScroll = () => {
window.scrollBy({ top: 500, behavior: 'smooth' });
};
return (
<header>
<button onClick={handleScroll}>▼</button>
</header>
);
}
// C.tsx
function C() {
const handleScroll = () => {
window.scrollBy({ top: 500, behavior: 'smooth' }); // 로직 중복
};
return (
<div>
<button onClick={handleScroll}>▼</button>
</div>
);
}
따로따로 구현했을 때의 케이스다. 스크롤 동작이 바뀌면 (예: 스크롤 정도, easing, 대상 엘리먼트) 두 군데를 다 고쳐야 한다.
현재로써는 이걸로도 충분하지만 뎁스가 깊어지거나 동일 이벤트를 다른 지점에서 추가해야 하는 경우 N번의 로직 중복이 발생한다.
유지보수성이 좋다고 할 수 없다.
커스텀 디스패처를 통해 구현
// dispatcher.ts
const listeners = new Map<string, Set<Function>>();
export const dispatcher = {
on(event: string, handler: Function) {
if (!listeners.has(event)) listeners.set(event, new Set());
listeners.get(event)!.add(handler);
return () => listeners.get(event)!.delete(handler);
},
emit(event: string, payload?: any) {
listeners.get(event)?.forEach(fn => fn(payload));
},
};
먼저 전역적으로 사용할 디스패처를 구현한다.
간단하게 구독자들에 대해 이벤트를 발행하는 로직만을 가진 디스패처다.
// Header.tsx
function Header() {
return (
<button onClick={() => dispatcher.emit(SCROLL_DOWN)}>▼</button>
);
}
// C.tsx
function C() {
return (
<button onClick={() => dispatcher.emit(SCROLL_DOWN)}>▼</button>
);
}
// ScrollController.tsx
export const SCROLL_DOWN = 'scroll:down';
function ScrollController() {
useEffect(() => {
return dispatcher.on(SCROLL_DOWN, () => {
window.scrollBy({ top: 500, behavior: 'smooth' });
});
}, []);
return null;
}
이벤트 디스패처 패턴을 사용하며 코드 복잡도가 줄어들었다.
- 사용처인 컴포넌트에서는 디스패처만을 import하여 해당하는 이벤트를 발행하면 된다.
- 구독부인 ScrollController 파일에서는 이벤트 자체 로직을 정의하고, 구독시킨다.
사용할 이벤트들을 구분할 키 값으로 상수 매크로를 선언하여 export하면 유지보수성을 더욱 높일 수 있다.
'Frontend' 카테고리의 다른 글
| [패키지 매니저] npm, Yarn, pnpm에 대해 (2) | 2025.04.27 |
|---|---|
| [NextJS] 이미지 파일 관리, 최적화 (SVG, SVGO) (3) | 2025.02.09 |
| [React] 조건부 렌더링 방식 (얼리 리턴, 삼항 연산자, 논리 AND 연산자) (0) | 2025.01.10 |
| [Axios] 중복된 토큰 재발급 요청을 하나로 줄이기 (Axios Interceptor) (2) | 2024.12.08 |
| [React] useMemo 실제 활용해보기 (0) | 2024.11.28 |