BroadcastChannel API - 웹용 메시지 버스

에릭 비델만

BroadcastChannel API를 사용하면 동일한 출처 스크립트가 다른 탐색 컨텍스트로 메시지를 보낼 수 있습니다. 이는 창/탭, iframe, 웹 워커 및 서비스 워커 간에 Pub/Sub 시맨틱을 허용하는 간단한 메시지 버스로 생각할 수 있습니다.

API 기본사항

Broadcast Channel API는 탐색 컨텍스트 간에 더 쉽게 통신하도록 하는 간단한 API입니다. 즉, 창/탭, iframe, 웹 워커 및 서비스 워커 간에 통신합니다. 특정 채널에 게시되는 메시지는 해당 채널의 모든 청취자에게 전달됩니다.

BroadcastChannel 생성자는 단일 매개변수, 즉 채널 이름을 사용합니다. 이름은 채널을 식별하고 탐색 컨텍스트 전반에 걸쳐 사용됩니다.

// Connect to the channel named "my_bus".
const channel = new BroadcastChannel('my_bus');

// Send a message on "my_bus".
channel.postMessage('This is a test message.');

// Listen for messages on "my_bus".
channel.onmessage = function(e) {
    console.log('Received', e.data);
};

// Close the channel when you're done.
channel.close();

메시지 전송

메시지는 문자열이거나 구조화된 클론 알고리즘 (String, Objects, Arrays, Blobs, ArrayBuffer, Map)에서 지원하는 문자열일 수 있습니다.

- Blob 또는 File 전송

channel.postMessage(new Blob(['foo', 'bar'], {type: 'plain/text'}));

자신에게는 채널이 방송되지 않습니다. 따라서 동일한 채널의 postMessage()와 동일한 페이지에 onmessage 리스너가 있으면 message 이벤트가 실행되지 않습니다.

다른 기법과의 차이점

이 시점에서 이 방법이 WebSockets, SharedWorkers, MessageChannel API, window.postMessage()과 같은 다른 메시지 전달 기법과 어떤 관련이 있는지 궁금할 수 있습니다. Broadcast Channel API는 이러한 API를 대체하지 않습니다. 각각은 특정한 목적을 수행합니다. Broadcast Channel API는 동일한 출처에 있는 스크립트 간의 쉬운 일대다 통신을 위한 것입니다.

방송 채널의 사용 사례:

  • 다른 탭에서 사용자 작업 감지
  • 사용자가 다른 창/탭에서 계정에 로그인하면 알림
  • 작업자에게 백그라운드 작업을 실행하도록 안내
  • 서비스가 어떤 작업을 실행했을 때 이를 알 수 있습니다.
  • 사용자가 하나의 창에서 사진을 업로드하면 열려 있는 다른 페이지로 옮깁니다.

: 사용자가 언제 로그아웃하는지 알 수 있는 페이지: 동일한 사이트에 있는 다른 탭에서도 로그아웃하는 경우

<button id="logout">Logout</button>

<script>
function doLogout() {
    // update the UI login state for this page.
}

const authChannel = new BroadcastChannel('auth');

const button = document.querySelector('#logout');
button.addEventListener('click', e => {
    // A channel won't broadcast to itself so we invoke doLogout()
    // manually on this page.
    doLogout();
    authChannel.postMessage({cmd: 'logout', user: 'Eric Bidelman'});
});

authChannel.onmessage = function(e) {
    if (e.data.cmd === 'logout') {
    doLogout();
    }
};
</script>

다른 예를 들어 사용자가 앱에서 '오프라인 저장소 설정'을 변경한 후 캐시된 콘텐츠를 삭제하도록 서비스 워커에 지시한다고 가정해 보겠습니다. window.caches를 사용하여 캐시를 삭제할 수 있지만, 서비스 워커에 이미 이 작업을 수행하는 유틸리티가 포함되어 있을 수 있습니다. Broadcast Channel API를 사용하여 이 코드를 재사용할 수 있습니다. Broadcast Channel API가 없으면 서비스 워커에서 모든 클라이언트와 통신하기 위해 self.clients.matchAll()의 결과를 반복하고 각 클라이언트에서 postMessage()를 호출해야 합니다 (이러한 작업을 실행하는 실제 코드). 방송 채널을 사용하면 O(N)가 아닌 O(1)가 됩니다.

- 내부 유틸리티 메서드를 재사용하여 캐시를 삭제하도록 서비스 워커에 지시합니다.

index.html

const channel = new BroadcastChannel('app-channel');
channel.onmessage = function(e) {
    if (e.data.action === 'clearcache') {
    console.log('Cache removed:', e.data.removed);
    }
};

const messageChannel = new MessageChannel();

// Send the service worker a message to clear the cache.
// We can't use a BroadcastChannel for this because the
// service worker may need to be woken up. MessageChannels do that.
navigator.serviceWorker.controller.postMessage({
    action: 'clearcache',
    cacheName: 'v1-cache'
}, [messageChannel.port2]);

sw.js

function nukeCache(cacheName) {
    return caches.delete(cacheName).then(removed => {
    // ...do more stuff (internal) to this service worker...
    return removed;
    });
}

self.onmessage = function(e) {
    const action = e.data.action;
    const cacheName = e.data.cacheName;

    if (action === 'clearcache') {
    nukeCache(cacheName).then(removed => {
        // Send the main page a response via the BroadcastChannel API.
        // We could also use e.ports[0].postMessage(), but the benefit
        // of responding with the BroadcastChannel API is that other
        // subscribers may be listening.
        const channel = new BroadcastChannel('app-channel');
        channel.postMessage({action, removed});
    });
    }
};

postMessage()과의 차이

postMessage()와 달리 더 이상 iframe 또는 worker와 통신하기 위해 iframe 또는 worker와 통신하기 위해 참조를 유지하지 않아도 됩니다.

// Don't have to save references to window objects.
const popup = window.open('https://another-origin.com', ...);
popup.postMessage('Sup popup!', 'https://another-origin.com');

window.postMessage()를 사용하면 출처 간에 통신할 수도 있습니다. Broadcast Channel API는 same-origin입니다. 메시지는 동일한 출처에서 온 것이 보장되므로 다음과 같이 window.postMessage()를 사용할 때처럼 확인할 필요가 없습니다.

// Don't have to validate the origin of a message.
const iframe = document.querySelector('iframe');
iframe.contentWindow.onmessage = function(e) {
    if (e.origin !== 'https://expected-origin.com') {
    return;
    }
    e.source.postMessage('Ack!', e.origin);
};

특정 채널을 간단히 '구독'하여 안전한 양방향 커뮤니케이션을 할 수 있습니다.

SharedWorker와의 차이점

BroadcastChannel는 잠재적으로 여러 창/탭 또는 작업자에게 메시지를 보내야 하는 간단한 사례에 사용합니다.

잠금 관리, 공유 상태, 서버와 여러 클라이언트 간 리소스 동기화, 원격 호스트와 WebSocket 연결을 공유하는 것과 같은 고급 사용 사례의 경우 공유 작업자가 가장 적합한 솔루션입니다.

MessageChannel API와의 차이점

Channel Messaging APIBroadcastChannel의 주요 차이점은 후자가 여러 리스너 (일대다)에 메시지를 전달하는 수단이라는 점입니다. MessageChannel는 스크립트 간에 직접 일대일 통신을 위한 것입니다. 또한 더 복잡하므로 양쪽 끝에 포트를 사용하여 채널을 설정해야 합니다.

기능 감지 및 브라우저 지원

현재 Chrome 54, Firefox 38, Opera 41은 Broadcast Channel API를 지원합니다.

if ('BroadcastChannel' in self) {
    // BroadcastChannel API supported!
}

폴리필의 경우 다음과 같은 몇 가지 방법이 있습니다.

시도해 본 적이 없어서 주행 거리가 다를 수 있습니다.

자료