웹 서비스를 이용할 때 사용자가 여러 브라우저 탭을 열어두고 한쪽에서 로그아웃을 하거나 장바구니 품목을 변경했는데, 다른 탭에는 기존 정보가 그대로 남아있어 오작동하는 문제를 경험해봤을 것이다.
다만 많은 개발자들이 탭 간 실시간 데이터 동기화를 어떻게 깔끔하게 구현해야 하는지 정확한 방법과 원리를 모른 채 복잡한 WebSockets나 localStorage 이벤트 꼼수에 의존하곤 한다.
이번에는 브라우저에서 공식 지원하는 BroadcastChannel API가 정확히 무엇이고, 왜 필요하며, 실무에서 어떻게 적용하는지 완벽하게 정리해서 소개하겠다.

 

1. BroadcastChannel API란 무엇인가?

BroadcastChannel API는 동일한 출처(Same-Origin: 프로토콜, 도메인, 포트가 동일한 환경)를 공유하는 브라우저 맥락(탭, 창, iframe, Web Worker) 간에 간단하게 메시지를 주고받을 수 있도록 지원하는 웹 표준 API다.
과거에는 탭 간 통신을 위해 `localStorage`에 임시 값을 쓰고 `storage` 이벤트를 감지하거나, 서버를 거치는 WebSocket을 사용해야 했다. 하지만 BroadcastChannel을 사용하면 복잡한 백엔드 서버나 파일 스토리지 I/O 없이 순수 클라이언트 단에서 매우 빠른 속도로 데이터를 브로드캐스팅할 수 있다.

 

BroadcastChannel과 기존 통신 방식 비교
비교 항목BroadcastChannel APILocalStorage EventWebSocket / SSE
통신 대상동일 출처의 모든 탭/Worker동일 출처의 모든 탭서버와 연결된 모든 클라이언트
부하 및 속도매우 빠름 (메모리 통신)보통 (디스크/스토리지 I/O)서버 네트워크 트래픽 발생
구현 난이도매우 간결함 (단 몇 줄)이벤트 파싱 필요서버 및 클라이언트 양쪽 구축 필요
주요 용도탭간 상태 동기화 (로그아웃, 테마)데이터 지속 저장 및 보조 이벤트실시간 데이터 연동 (채팅, 알림)

 

2. BroadcastChannel의 기본 사용 방법

BroadcastChannel 사용법은 매우 단순하다. 채널 이름을 지정하여 객체를 생성한 뒤, 메시지를 전송하거나 이벤트 리스너를 통해 수신하면 된다.

 

1) 채널 생성 및 메시지 전송

전송하려는 페이지에서 다음과 같이 동일한 채널명으로 인스턴스를 생성하고 `postMessage()` 메서드를 호출한다.

// 'user_session'이라는 이름의 채널 생성
const authChannel = new BroadcastChannel('user_session');

// 데이터 전송 (객체, 문자열, 배열 등 전송 가능)
authChannel.postMessage({
    type: 'LOGOUT',
    userId: 'user123',
    timestamp: Date.now()
});

 

2) 메시지 수신 및 채널 닫기

다른 탭에서는 동일한 채널 이름을 생성한 뒤 `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();

 

3. 실전 예제 - 탭 간 로그인/로그아웃 실시간 상태 동기화

실무에서 가장 자주 발생하는 문제인 '한 탭에서 로그아웃 시 다른 탭 자동 세션 처리' 예제를 작성해보자.

 

✗ 잘못된 코드 (localStorage를 이용한 기존 방식의 문제점)

✗ 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 API 적용)

✓ 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 탭에 알림창이 뜨며 페이지가 새로고침되어 동기화가 완료된다.

 

4. 주의사항 및 흔한 실수

BroadcastChannel을 사용할 때 개발자들이 흔히 범하는 실수와 주의점은 다음과 같다.

 

1) 동일 출처 정책 (Same-Origin Policy)

BroadcastChannel은 동일 출처에서만 작동한다. 예를 들어 `https://example.com`과 `https://sub.example.com` 또는 `http://`와 `https://` 간에는 메시지를 주고받을 수 없다. 서브도메인 간 통신이 필요한 경우에는 `window.postMessage`를 활용해야 한다.

 

2) 채널 닫기 (Memory Leak 방지)

React나 Vue 같은 SPA 환경에서 컴포넌트가 언마운트될 때 인스턴스를 닫지 않으면 메모리 누수나 중복 이벤트 수신 문제가 발생할 수 있다.

✗ 잘못된 예: `useEffect` 내에서 cleanup 함수 없이 채널 생성
✓ 올바른 예: `return () => channel.close();` 구문을 추가하여 컴포넌트 해제 시 채널을 반드시 닫는다.

 

3) 크로스 브라우저 호환성 및 Fallback 처리

최신 브라우저는 모두 지원하지만, 구형 환경이나 특수 브라우저를 대비해 가드 코드를 작성하는 것이 안전하다.

if ('BroadcastChannel' in window) {
    const channel = new BroadcastChannel('my_channel');
} else {
    // Fallback: localStorage 이벤트 방식 사용
}

 

5. 마무리 및 요약

브라우저 탭 간 상태 불일치 문제는 서비스의 신뢰성과 직결되는 중요한 요소다. 단순한 로그아웃 처리부터 장바구니 변경, 실시간 테마 변경 등 다양한 UX 요소에 BroadcastChannel API를 적극 활용할 수 있다.
작은 최적화와 올바른 API 활용 습관이 모여 서비스 전체의 완성도를 크게 높인다는 점을 잊지 말자. 이 글의 실전 예제 코드를 참고하여 프로젝트에 탭 간 동기화 로직을 적용해보면, 사용자에게 훨씬 깔끔하고 부드러운 웹 경험을 제공할 수 있을 것이다.