웹 서비스를 이용할 때 사용자가 여러 브라우저 탭을 열어두고 한쪽에서 로그아웃을 하거나 장바구니 품목을 변경했는데, 다른 탭에는 기존 정보가 그대로 남아있어 오작동하는 문제를 경험해봤을 것이다.
다만 많은 개발자들이 탭 간 실시간 데이터 동기화를 어떻게 깔끔하게 구현해야 하는지 정확한 방법과 원리를 모른 채 복잡한 WebSockets나 localStorage 이벤트 꼼수에 의존하곤 한다.
이번에는 브라우저에서 공식 지원하는 BroadcastChannel API가 정확히 무엇이고, 왜 필요하며, 실무에서 어떻게 적용하는지 완벽하게 정리해서 소개하겠다.
BroadcastChannel API는 동일한 출처(Same-Origin: 프로토콜, 도메인, 포트가 동일한 환경)를 공유하는 브라우저 맥락(탭, 창, iframe, Web Worker) 간에 간단하게 메시지를 주고받을 수 있도록 지원하는 웹 표준 API다.
과거에는 탭 간 통신을 위해 `localStorage`에 임시 값을 쓰고 `storage` 이벤트를 감지하거나, 서버를 거치는 WebSocket을 사용해야 했다. 하지만 BroadcastChannel을 사용하면 복잡한 백엔드 서버나 파일 스토리지 I/O 없이 순수 클라이언트 단에서 매우 빠른 속도로 데이터를 브로드캐스팅할 수 있다.
| 비교 항목 | BroadcastChannel API | LocalStorage Event | WebSocket / SSE |
|---|---|---|---|
| 통신 대상 | 동일 출처의 모든 탭/Worker | 동일 출처의 모든 탭 | 서버와 연결된 모든 클라이언트 |
| 부하 및 속도 | 매우 빠름 (메모리 통신) | 보통 (디스크/스토리지 I/O) | 서버 네트워크 트래픽 발생 |
| 구현 난이도 | 매우 간결함 (단 몇 줄) | 이벤트 파싱 필요 | 서버 및 클라이언트 양쪽 구축 필요 |
| 주요 용도 | 탭간 상태 동기화 (로그아웃, 테마) | 데이터 지속 저장 및 보조 이벤트 | 실시간 데이터 연동 (채팅, 알림) |
BroadcastChannel 사용법은 매우 단순하다. 채널 이름을 지정하여 객체를 생성한 뒤, 메시지를 전송하거나 이벤트 리스너를 통해 수신하면 된다.
전송하려는 페이지에서 다음과 같이 동일한 채널명으로 인스턴스를 생성하고 `postMessage()` 메서드를 호출한다.
// 'user_session'이라는 이름의 채널 생성
const authChannel = new BroadcastChannel('user_session');
// 데이터 전송 (객체, 문자열, 배열 등 전송 가능)
authChannel.postMessage({
type: 'LOGOUT',
userId: 'user123',
timestamp: Date.now()
});
다른 탭에서는 동일한 채널 이름을 생성한 뒤 `onmessage` 이벤트 리스너를 등록하여 전달된 데이터를 받아 처리한다. 더 이상 통신이 필요 없거나 컴포넌트가 언마운트될 때는 반드시 `close()`를 호출해 메모리 누수를 방지해야 한다.
const authChannel = new BroadcastChannel('user_session');
// 메시지 수신 이벤트 핸들러
authChannel.onmessage = (event) => {
console.log('수신된 데이터:', event.data);
if (event.data.type === 'LOGOUT') {
alert('다른 탭에서 로그아웃 되었습니다.');
window.location.href = '/login';
}
};
// 사용이 끝난 후 메모리 해제
// authChannel.close();
실무에서 가장 자주 발생하는 문제인 '한 탭에서 로그아웃 시 다른 탭 자동 세션 처리' 예제를 작성해보자.
✗ Storage 이벤트를 이용하는 폴백(Fallback) 방식은 데이터 저장소를 거치므로 불필요한 스토리지 접근이 발생하며, 동일한 값을 연속으로 세팅할 때는 이벤트가 감지되지 않는 치명적인 단점이 있다.
// ✗ 잘못된 예: localStorage 감지 방식
window.addEventListener('storage', (event) => {
// 같은 키에 동일한 값을 다시 저장하면 이벤트를 놓칠 수 있음
if (event.key === 'logout_event') {
location.reload();
}
});
function triggerLogout() {
// 디스크 I/O 발생 및 타임스탬프 꼼수가 필요함
localStorage.setItem('logout_event', Date.now().toString());
}
✓ BroadcastChannel을 활용하면 메모리상에서 객체 데이터를 직접 전송할 수 있어 깔끔하고 신뢰성 높은 이벤트 전달이 가능하다.
// ✓ 올바른 예: BroadcastChannel 모듈화 구현
class SessionSync {
constructor() {
this.channel = new BroadcastChannel('app_session_sync');
this.initListener();
}
// 메시지 수신 대기
initListener() {
this.channel.onmessage = (event) => {
const { action, payload } = event.data;
switch (action) {
case 'LOGOUT':
this.handleLogout();
break;
case 'DARK_MODE_TOGGLE':
document.body.classList.toggle('dark-theme', payload.isDark);
break;
}
};
}
// 이벤트 전송 메서드
notifyLogout() {
this.channel.postMessage({ action: 'LOGOUT' });
}
notifyThemeChange(isDark) {
this.channel.postMessage({
action: 'DARK_MODE_TOGGLE',
payload: { isDark }
});
}
handleLogout() {
alert('다른 브라우저 탭에서 로그아웃 되었습니다.');
window.location.reload();
}
// 리소스 해제
destroy() {
this.channel.close();
}
}
// 사용 예시
const sessionManager = new SessionSync();
// 로그아웃 버튼 클릭 시
document.getElementById('logout-btn').addEventListener('click', () => {
// 1. 현재 탭 로그아웃 처리
// 2. 다른 탭들에 알림 전송
sessionManager.notifyLogout();
});
1. A 탭에서 [로그아웃] 버튼을 클릭한다.
2. `sessionManager.notifyLogout()`이 실행되면서 BroadcastChannel로 `{ action: 'LOGOUT' }` 메시지가 브로드캐스팅된다.
3. 동일한 웹사이트가 열려있는 B, C 탭에서 즉시 `onmessage` 이벤트가 수신된다.
4. B, C 탭에 알림창이 뜨며 페이지가 새로고침되어 동기화가 완료된다.
BroadcastChannel을 사용할 때 개발자들이 흔히 범하는 실수와 주의점은 다음과 같다.
BroadcastChannel은 동일 출처에서만 작동한다. 예를 들어 `https://example.com`과 `https://sub.example.com` 또는 `http://`와 `https://` 간에는 메시지를 주고받을 수 없다. 서브도메인 간 통신이 필요한 경우에는 `window.postMessage`를 활용해야 한다.
React나 Vue 같은 SPA 환경에서 컴포넌트가 언마운트될 때 인스턴스를 닫지 않으면 메모리 누수나 중복 이벤트 수신 문제가 발생할 수 있다.
✗ 잘못된 예: `useEffect` 내에서 cleanup 함수 없이 채널 생성
✓ 올바른 예: `return () => channel.close();` 구문을 추가하여 컴포넌트 해제 시 채널을 반드시 닫는다.
최신 브라우저는 모두 지원하지만, 구형 환경이나 특수 브라우저를 대비해 가드 코드를 작성하는 것이 안전하다.
if ('BroadcastChannel' in window) {
const channel = new BroadcastChannel('my_channel');
} else {
// Fallback: localStorage 이벤트 방식 사용
}
브라우저 탭 간 상태 불일치 문제는 서비스의 신뢰성과 직결되는 중요한 요소다. 단순한 로그아웃 처리부터 장바구니 변경, 실시간 테마 변경 등 다양한 UX 요소에 BroadcastChannel API를 적극 활용할 수 있다.
작은 최적화와 올바른 API 활용 습관이 모여 서비스 전체의 완성도를 크게 높인다는 점을 잊지 말자. 이 글의 실전 예제 코드를 참고하여 프로젝트에 탭 간 동기화 로직을 적용해보면, 사용자에게 훨씬 깔끔하고 부드러운 웹 경험을 제공할 수 있을 것이다.