BroadcastChannel API란? 브라우저 탭 간 상태를 동기화하는 방법

이명진·2026년 8월 17일

TIL

목록 보기
28/29

프론트엔드 개발을 하다 보면 여러 브라우저 탭 사이에서 상태를 동기화해야 하는 상황이 생긴다.

예를 들어 쇼핑몰에서 다음과 같은 상황을 생각해볼 수 있다.

Tab 1
└── 상품 상세 페이지
    └── 상품 좋아요 변경

Tab 2
└── 홈 페이지
    └── 해당 상품의 좋아요 상태 표시

Detail 페이지에서 상품의 좋아요 상태를 변경했을 때 다른 탭의 Home에서도 변경된 상태를 반영해야 한다.

처음에는 전역 상태 관리 라이브러리인 Zustand를 생각할 수 있다.

Detail
  ↓
Zustand 상태 변경
  ↓
Home

하지만 여기에는 한 가지 문제가 있다.

Zustand는 기본적으로 같은 JavaScript 실행 환경 안에서 상태를 공유하는 도구다.

브라우저 탭이 분리되어 있다면 각 탭은 서로 다른 JavaScript 실행 환경을 가지고 있기 때문에 하나의 Zustand 상태를 자동으로 공유하지 않는다.

┌───────────────┐       ┌───────────────┐
│    Tab 1      │       │    Tab 2      │
│               │       │               │
│    Detail     │       │     Home      │
│               │       │               │
│   Zustand     │       │   Zustand     │
└───────────────┘       └───────────────┘

이때 사용할 수 있는 Web API가 BroadcastChannel API다.


1. BroadcastChannel API란?

BroadcastChannel은 같은 Origin을 사용하는 서로 다른 브라우징 컨텍스트 사이에서 메시지를 전달할 수 있도록 해주는 Web API다.

쉽게 말하면 다음과 같다.

같은 웹사이트의 다른 탭이나 창에 메시지를 보내는 브라우저 기능이다.

구조는 다음과 같다.

┌───────────────┐
│     Tab 1     │
│               │
│    Detail     │
└───────┬───────┘
        │
        │ postMessage()
        ▼
┌──────────────────────┐
│   BroadcastChannel   │
└──────────────────────┘
        │
        │ message
        ▼
┌───────────────┐
│     Tab 2     │
│               │
│     Home      │
└───────────────┘

별도의 서버를 구축하지 않고도 브라우저 내부에서 탭 간 통신을 구현할 수 있다는 것이 특징이다.


2. 기본적인 사용 방법

BroadcastChannel을 생성하는 방법은 간단하다.

const channel = new BroadcastChannel('my-channel');

여기서 'my-channel'은 통신에 사용할 채널 이름이다.

같은 이름의 BroadcastChannel을 생성한 브라우징 컨텍스트끼리 메시지를 주고받을 수 있다.

메시지 보내기

channel.postMessage({
  type: 'PRODUCT_UPDATED',
  productId: 123,
});

메시지 받기

channel.onmessage = (event) => {
  console.log(event.data);
};

또는 addEventListener를 사용할 수도 있다.

channel.addEventListener('message', (event) => {
  console.log(event.data);
});

사용이 끝났다면 채널을 닫아주는 것이 좋다.

channel.close();

3. 실제로 어떻게 사용할 수 있을까?

쇼핑몰을 예시로 들어보겠다.

현재 브라우저에서 두 개의 탭을 열어두었다고 가정한다.

Tab 1
└── 상품 상세 페이지
    └── 상품 좋아요 변경

Tab 2
└── 홈 페이지
    └── 해당 상품의 좋아요 상태 표시

Detail에서 좋아요를 변경하면 Home에서도 변경된 상태를 반영해야 한다.

Detail에서는 다음과 같이 메시지를 전송할 수 있다.

const channel = new BroadcastChannel('product');

function updateLike(productId: number) {
  // 서버에 좋아요 변경 요청
  updateProductLike(productId);

  // 다른 탭에 변경 사실 전달
  channel.postMessage({
    type: 'LIKE_UPDATED',
    productId,
  });
}

Home에서는 메시지를 수신한다.

const channel = new BroadcastChannel('product');

channel.addEventListener('message', (event) => {
  if (event.data.type === 'LIKE_UPDATED') {
    const { productId } = event.data;

    // 해당 상품 데이터 다시 조회
    refetchProduct(productId);
  }
});

전체적인 흐름은 다음과 같다.

             Detail Tab
                 │
                 │ 서버에 상태 변경
                 ▼
              Server
                 │
                 │
                 │
Detail Tab ──────┼──── BroadcastChannel
                 │
                 ▼
              Home Tab
                 │
                 │ refetch
                 ▼
              Server
                 │
                 ▼
              최신 데이터

여기서 중요한 점이 있다.

BroadcastChannel 자체가 서버의 상태를 동기화해주는 것은 아니다.

BroadcastChannel은 단순히

"다른 탭에서 이런 일이 발생했다."

라는 이벤트를 전달하는 역할을 한다.

실제 데이터의 정합성을 맞추는 것은 서버 API나 데이터 조회 로직이 담당한다.


4. BroadcastChannel은 전역 상태 관리 라이브러리인가?

여기서 Zustand와 헷갈릴 수 있다.

둘은 비슷해 보이지만 목적이 다르다.

Zustand는 애플리케이션의 상태를 관리하기 위한 도구다.

Component A
     │
     ▼
┌──────────────┐
│    Zustand   │
│              │
│ user         │
│ cart         │
│ modal        │
└──────────────┘
     ▲
     │
Component B

예를 들어 다음과 같은 상태를 관리할 수 있다.

const useStore = create((set) => ({
  count: 0,

  increment: () =>
    set((state) => ({
      count: state.count + 1,
    })),
}));

같은 애플리케이션 안의 여러 컴포넌트가 하나의 상태를 공유할 수 있다.


5. 그런데 브라우저 탭이 다르면?

다음과 같은 상황을 생각해보자.

Chrome Tab 1
┌─────────────────────┐
│ React Application   │
│                     │
│ Zustand Store A     │
└─────────────────────┘


Chrome Tab 2
┌─────────────────────┐
│ React Application   │
│                     │
│ Zustand Store B     │
└─────────────────────┘

Tab 1의 Zustand Store와 Tab 2의 Zustand Store는 서로 다른 메모리 공간에 존재한다.

따라서 Tab 1에서 Zustand의 상태를 변경했다고 해서 Tab 2의 Zustand 상태가 자동으로 변경되는 것은 아니다.

이때 BroadcastChannel을 추가할 수 있다.

Tab 1
┌───────────────┐
│    Zustand    │
└───────┬───────┘
        │
        │ postMessage()
        ▼
┌──────────────────────┐
│   BroadcastChannel   │
└──────────┬───────────┘
           │
           ▼
Tab 2
┌───────────────┐
│    Zustand    │
└───────────────┘

즉,

Zustand = 상태를 관리한다.

BroadcastChannel = 다른 브라우징 컨텍스트에 이벤트를 전달한다.

라고 구분하면 이해하기 쉽다.


6. 그렇다면 WebSocket과는 무엇이 다를까?

BroadcastChannel을 공부하다 보면 WebSocket과도 비슷해 보인다.

둘 다 메시지를 주고받을 수 있기 때문이다.

하지만 통신 대상이 완전히 다르다.

BroadcastChannel

Browser
│
├── Tab A
│
├── Tab B
│
└── Tab C

Tab A ── BroadcastChannel ──> Tab B
                       └────> Tab C

브라우저 내부의 다른 브라우징 컨텍스트와 통신한다.


WebSocket

Client A ──────┐
               │
Client B ──────┼── WebSocket Server
               │
Client C ──────┘

WebSocket은 클라이언트와 서버 사이의 실시간 양방향 통신을 위한 기술이다.

따라서 사용 목적도 다르다.

구분BroadcastChannelWebSocket
통신 대상같은 Origin의 브라우저 컨텍스트클라이언트 ↔ 서버
서버 필요❌⭕
다른 탭 통신⭕⭕
서버와 실시간 통신❌⭕
대표적인 용도탭 간 상태/이벤트 전달실시간 데이터
채팅부적합적합
실시간 주식 가격부적합적합
로그인 상태 변경 전달적합가능하지만 과함
여러 탭 간 UI 상태 동기화적합과함

7. WebSocket으로 탭 간 상태를 동기화하면 안 될까?

물론 가능하다.

예를 들어 모든 탭이 하나의 WebSocket 서버에 연결되어 있다고 생각해보자.

Tab A ─────┐
           │
Tab B ─────┼──── WebSocket Server
           │
Tab C ─────┘

Tab A에서 데이터를 변경하면 서버가 Tab B와 Tab C에 변경 이벤트를 전송할 수 있다.

Tab A
 │
 │ update
 ▼
Server
 │
 ├────────> Tab B
 │
 └────────> Tab C

하지만 단순히 같은 브라우저 안에서 탭끼리 이벤트를 전달하려는 목적이라면 WebSocket은 상당히 무거운 해결책이 될 수 있다.

서버 연결을 유지해야 하고,

  • WebSocket 연결 관리
  • 재연결
  • 서버 구현
  • 인증
  • 연결 종료 처리

등을 추가로 고려해야 하기 때문이다.

단순히

"다른 탭에서 데이터가 변경됐다는 사실만 알려주고 싶다."

라면 BroadcastChannel이 훨씬 단순하다.


8. BroadcastChannel vs Zustand vs WebSocket

세 기술을 역할 관점에서 비교하면 더 쉽게 이해할 수 있다.

                    ┌─────────────────┐
                    │   상태 관리      │
                    │     Zustand     │
                    └─────────────────┘
                            │
                            ▼
                    "현재 상태가 무엇인가?"


                    ┌─────────────────┐
                    │   탭 간 통신      │
                    │ BroadcastChannel│
                    └─────────────────┘
                            │
                            ▼
                    "다른 탭에 알려준다."


                    ┌─────────────────┐
                    │   서버 통신      │
                    │    WebSocket    │
                    └─────────────────┘
                            │
                            ▼
                    "서버와 실시간으로 통신한다."

따라서 이 셋은 서로 경쟁하는 관계라기보다는 서로 다른 문제를 해결하는 도구라고 보는 것이 좋다.


9. 실제 프로젝트에서는 조합해서 사용할 수도 있다

실제 React 애플리케이션에서는 BroadcastChannel과 Zustand를 함께 사용할 수도 있다.

예를 들어 인증 상태를 생각해보자.

Tab A에서 로그아웃했다고 가정한다.

Tab A
   │
   │ logout
   ▼
Server
   │
   ▼
BroadcastChannel
   │
   ├──────────────> Tab B
   │
   └──────────────> Tab C

Tab B와 Tab C에서는 메시지를 받는다.

channel.addEventListener('message', (event) => {
  if (event.data.type === 'LOGOUT') {
    useAuthStore.getState().logout();
  }
});

그러면 역할이 명확해진다.

BroadcastChannel
        │
        │ 이벤트 전달
        ▼
      Zustand
        │
        │ 상태 변경
        ▼
     React UI

이런 구조라면 각각의 도구가 자신의 역할에 집중할 수 있다.


10. BroadcastChannel을 사용할 때 주의할 점

10-1. 데이터 저장소가 아니다

BroadcastChannel은 상태를 저장하지 않는다.

channel.postMessage({
  user: 'kim',
});

이 메시지를 보내고 나서 나중에 채널에 들어왔다고 해서 과거 메시지를 받을 수 있는 것은 아니다.

즉,

BroadcastChannel
       │
       ├── 메시지 전달
       │
       └── 상태 저장 ❌

상태가 필요하다면 Zustand, 서버, IndexedDB 등의 별도 저장소가 필요하다.


10-2. 이벤트 전달과 상태 동기화를 구분해야 한다

이 부분이 가장 중요하다.

예를 들어 Detail에서 다음과 같이 메시지를 보냈다고 해보자.

channel.postMessage({
  type: 'PRODUCT_UPDATED',
});

이 메시지는

"상품이 변경됐다."

라는 이벤트다.

하지만 Home이 이 메시지만 가지고 실제 최신 상품 데이터를 알 수 있는 것은 아니다.

따라서 보통은 다음과 같이 사용한다.

Detail
  │
  │ 상태 변경
  ▼
Server
  │
  │ 성공
  ▼
BroadcastChannel
  │
  │ PRODUCT_UPDATED
  ▼
Home
  │
  │ refetch
  ▼
Server
  │
  ▼
최신 데이터

이렇게 하면 서버를 Source of Truth로 유지하면서 탭 간 변경 사실만 빠르게 전달할 수 있다.


11. BroadcastChannel을 사용하면 좋은 상황

다음과 같은 상황에서 특히 유용하다.

① 여러 탭의 로그아웃 상태 동기화

Tab A에서 로그아웃
       ↓
BroadcastChannel
       ↓
Tab B도 로그아웃 처리

② 여러 탭의 데이터 변경 알림

Detail에서 상품 변경
       ↓
BroadcastChannel
       ↓
Home에서 해당 데이터 refetch

③ 관리자 페이지와 사용자 페이지 간 상태 전달

Admin Tab
   ↓
상품 상태 변경
   ↓
BroadcastChannel
   ↓
User Tab

④ 여러 탭에서 같은 작업을 수행하는 경우

예를 들어 한 탭에서 특정 작업을 완료했다는 사실을 다른 탭에 전달할 때 사용할 수 있다.


12. 반대로 BroadcastChannel이 적합하지 않은 경우

다음과 같은 상황에서는 다른 기술을 사용하는 것이 좋다.

실시간 서버 데이터

주식 가격
채팅
게임 상태
실시간 알림

→ WebSocket 등의 서버 기반 실시간 통신이 적합하다.

애플리케이션 내부의 상태 관리

모달
사용자 설정
장바구니 UI 상태
필터
현재 선택된 메뉴

→ Zustand 같은 상태 관리 도구가 적합하다.

영속적인 데이터 저장

새로고침 후에도 유지해야 하는 데이터

→ 서버 DB, localStorage, IndexedDB 등의 저장소를 고려해야 한다.


13. 정리

BroadcastChannel API를 처음 접하면 Zustand나 WebSocket과 비슷해 보일 수 있다.

하지만 각각의 역할을 구분하면 어렵지 않다.

┌──────────────────────┐
│      Zustand         │
│                      │
│  애플리케이션 상태 관리 │
└──────────────────────┘


┌──────────────────────┐
│  BroadcastChannel    │
│                      │
│  브라우저 컨텍스트 간   │
│      메시지 전달       │
└──────────────────────┘


┌──────────────────────┐
│      WebSocket       │
│                      │
│  클라이언트 ↔ 서버     │
│      실시간 통신       │
└──────────────────────┘

한 문장으로 정리하면 다음과 같다.

Zustand는 "상태를 관리하기 위한 도구", BroadcastChannel은 "다른 브라우저 컨텍스트에 이벤트를 전달하기 위한 도구", WebSocket은 "서버와 실시간으로 통신하기 위한 도구"다.

그리고 실제 서비스에서는 이들을 서로 대체하기보다는 조합해서 사용할 수 있다.

예를 들어 다음과 같은 구조를 생각할 수 있다.

             ┌──────────────┐
             │    Server    │
             │ Source of    │
             │    Truth     │
             └──────┬───────┘
                    │
          ┌─────────┴─────────┐
          │                   │
       Detail               Home
          │                   ▲
          │                   │
          │ BroadcastChannel  │
          └───────────────────┘
                    │
                  refetch

서버가 실제 상태를 관리하고, BroadcastChannel은 탭 간 변경 사실을 전달하며, 각 애플리케이션은 Zustand나 TanStack Query 등을 이용해 자신의 상태를 갱신하는 방식이다.

결국 중요한 것은 특정 기술을 선택하는 것이 아니라 각 기술이 해결하려는 문제를 구분하는 것이다.

  • 애플리케이션 내부 상태가 필요하다 → Zustand
  • 다른 탭에 변경 사실을 알려야 한다 → BroadcastChannel
  • 서버와 실시간으로 데이터를 주고받아야 한다 → WebSocket

이렇게 역할을 나누면 불필요하게 WebSocket을 도입하지 않고도 브라우저 탭 간 상태 동기화 문제를 간단하게 해결할 수 있다.

profile
프론트엔드 개발자 초보에서 고수까지!

0개의 댓글