본문 바로가기
시작/TIL(Today I Learned)

브라우저 탭간 통신하기 : BroadCast Channel API ( feat. Next.js)

by 백씨네 2024. 1. 16.
728x90

 

브라우저 탭 사이에 데이터 공유하기

프론트 작업을 하면서 A태그의 target:_blank를 이용해서 새 탭을 열어서 작업을 하는 과정이 있었다.
기존에 열고 있던 페이지가 (A) 새로운 탭이 (B)라고 했을 때, A와 B는 동일한 오리진으로 로컬 스토리지를 공유할 수 있었다.

현재 로컬 스토리지에서 Access Token을 관리하는 로직으로 프로젝트를 만들고 있다.
그래서 처음 진입을 하는 과정에서는 액세스 토큰을 사용할 때 문제가 없었다. 하지만 B에서 작업을 하던 중 Access Token의 만료로 다시 재발급을 받아 로컬 스토리지를 바꿨을 때 A에서는 아직 기존에 있는 Access Token을 들고 있었고 어떠한 작업을 할 때 Access Token이 필요하지만 과거의 Access Token을 가지고 있었기 때문에 에러를 발생하는 문제가 발생했다.

이를 해결하기 위해서 B페이지에서 새로운 token으로 갱신을 할 때 기존에 있는 페이지에서도 바꿔주면 되지 않을까?라는 생각을 했고 그러기 위해서 탭 간 통신을 할 수 있는 과정이 필요했다.

위의 상황은 내가 겪을 상황이지만 정리하자면

내 애플리케이션의 상태를 탭 사이에 공유해야 한다.

방법

    1. Local Storage Event
      • 기본적으로 로컬 스토리지가 변하는 것에 대해서 이벤트를 감지할 수 있다고 있다. 만약에 단순히 로컬 스토리지만 관리한다고 했을 때는 이것을 써도 좋을 것 같다.
    1. window.postMessage
      • window 객체 안에 있는 postMessage를 이용하는 방법도 있다. 다른 탭이나 윈도우, ifame 간에 메시지를 전달할 수 있다.
      • 서로 다른 origin에서도 가능
    1. service worker
      • 웹의 백그라운드 작업을 처리하는 데 사용되고, 푸시 알림, 네트워크 요청 캐시 등의 기능을 제공
      • 탭 간의 통신보다는 오프라인 경험, 데이터 캐싱, 백그라운드 동기화에 적합
      • serviceworker.js를 만들어 관리해야 한다.
    2. BroadCast Channel API
      • 같은 Origin을 가진 다양한 브라우저 컨텍스트 사이에서 간단하게 데이터 교환을 할 수 있는 API
      • 동일한 Origin 내에서 데이터 공유 및 실시간 커뮤니케이션에 이상적이다.

선택

  1. 단순 로컬 스토리지뿐 아니라 메시지를 주고받을 수 있다, 추후 어떠한 상황이 있을지 아직 모른다는 점 (확장성)
  2. 브라우저 탭 간에 통신이 되고 그 데이터를 송수신한다는 점
  3. 같은 오리진이라는 점
  4. 간단하게 사용할 수 있어야 한다는 점

위의 세 가지를 고려해서 broadcast channel API를 선택하게 되었다.

BroadCast Channel API

BroadCast Channel API는 같은 오리진에서 사용할 수 있다는 점으로 송수신자를 별도로 표기하지 않아도 된다. 마지막까지 봤었던 window.postMessage는 다른 오리진간에도 가능하기 때문에 보안을 위해서 메시지를 보낸 출처를 확인하는 과정이 있었다.

postMessage 예시

// 송신자 
const childWindow = window.open('https://example.com/child-page.html'); 

// 메시지 전송 
childWindow.postMessage('데이터', 'https://example.com');



//수신자

// 메시지 수신 리스너 설정 
window.addEventListener('message', (event) => { 
// 보안을 위해 메시지를 보낸 출처 확인 
    if (event.origin !== 'https://example.com') return;
    console.log('Received message:', event.data); 
});

반면에 broadcast channel API는 동일 오리진이기 때문에 다른 검증과정은 없고 인스턴스를 통해서 송수신을 했다.

Broadcast Channel API 예시

//송신자

const channel = new BroadcastChannel('채널이름')
channel.postMessage('데이터')

//수신자

const channel = new BroadcastChannel('채널이름'); 
// 메시지 수신 리스너 설정 
channel.onmessage = (event) => { console.log('Received message:', event.data); };

조금 더 간단하게 쓸 수 있었고 다른 여러 창이 열려있는 상황에서도 동기화를 시킬 수 있다고 생각했고, 그래서 BroadCast Channel API를 이용하기로 했다.

BroadCast Channel API는 브라우저 기반 API이기 때문에 클라이언트 사이드에서만 사용할 수 있다.

어떻게 사용했는가?

브라우저 기반 API이므로 클라이언트 사이드에서 써야 한다. 우리의 프로젝트에서 어디에서나 작동하는 client side 컴포넌트는 root에 react-qeury(tanstack-query)를 세팅한 부분이다.

그래서 react-query를 세팅한 컴포넌트에서 수신을 하면 recoil을 통해서 로컬스토리지를 변경하는 로직을 추가하였다.

//rootwrap.tsx


const channel = new BroadcastChannel('bc')

channel.onmessage = (e) => {
    if (e.data) {
        setAccessToken(e.data) // recoil
    }
}

기본적으로 우리 프로젝트에서 토큰이 만료되면 토큰이 만료됐다는 에러를 던지고 그걸 브라우저에서 감지하게 되면 재발급을 한 후 새로운 토큰을 이용해서 재요청을 진행한다.
그래서 토큰을 재발급받는 과정 뒤에 postMessage를 사용하였다.

const sendMessage = (message: string) => {
    if (typeof window !== 'undefined') {
        const channel = new BroadcastChannel('bc')        
        channel.postMessage(message)
    }
}


// 토큰 발급 받은 후
sendMessage(accesstoken)

이러한 과정을 통해서 동일한 오리진의 다른 탭에서 토큰을 재발급받더라도 토큰 받을 후 로직에 sendMessage를 통해서 모든 탭의 토큰에 최신화를 시켜줄 수 있는 과정을 만들었다.

 

Broadcast Channel API 원리

 

출처 : MDN web Docs - Broadcast Channel API

 

 

일반적인 BroadcastChannel의 원리는 크게 3단계이다.

  1. 채널 생성 및 관리
  2. 메시지 큐잉 및 전달
  3. 이벤트 전파

1. 채널 생성 및 관리

  • 브라우저는 각각의 BroadcastChannel 인스턴스에 대한 참조를 관리한다. 주로 브라우저의 메모리 내에서 채널 이름을 키로 해서 관리된다.
  • 채널을 동일한 이름으로 생성된 모든 BroadcastChannel 인스턴스에 자동으로 연결된다.

2. 메시지 큐잉 및 전달

  • 메시지가 postMessage()를 통해서 전송되면, 브라우저는 해당 메시지를 내부적인 메세지 큐에 넣는다.
  • 브라우저는 동일한 채널에 가입된 모든 인스턴스들에게 메시지를 전달한다. 비동기적으로 수행되며, 발신 컨텍스트는 수신 컨텍스트의 응답을 기다리지 않는다.

3. 이벤트 전파

  • 메시지가 큐에 삽입되면, 브라우저는 해당 채널에 모든 컨텍스트에 대해 이벤트를 발생시킨다.
  • 각 컨텍스트는 자신의 이벤트 루프 내에서 message 이벤트를 수신하며, 등록된 onMessage 이벤트 핸들러가 메시지를 처리한다.

Broadcast Channel API 특징

1. 동기화와 동시성 제어

  • 브라우저는 각 BroadcastChannel인스턴스 간의 동기화를 담당한다. 다른 탭이나 윈도우가 동시에 동일한 채널에 접근하더라도 메시지가 올바르게 전달되도록 보장한다.
  • 메시지는 순차적으로, 전송된 순서대로 처리되며, 이를 통해서 동시성 문제를 방지한다.

2. 보안 및 격리

  • BroadcastChannel동일 출처 정책을 준수한다. 채널이 출처가 다른 탭이나 윈도우 간에 격리되어 있음을 의미한다.
  • 내부적으로 브라우저는 출처 정보를 사용하여 각 채널 범위를 결정하고, 오직 같은 출처의 컨텍스트만 메시지를 수신할 수 있도록 한다.
반응형

댓글