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();

메시지 전송

메시지는 문자열이거나 구조화된 클론 알고리즘 (문자열, 객체, 배열, Blob, ArrayBuffer, 맵)에서 지원하는 모든 항목일 수 있습니다.

- Blob 또는 파일 전송

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 또는 작업자와 통신하기 위해 더 이상 iframe 또는 작업자에 관한 참조를 유지할 필요가 없습니다.

// 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는 동일 출처입니다. 메시지가 동일한 출처에서 오는 것이 보장되므로 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);
};

특정 채널을 '구독'하기만 하면 양방향으로 안전하게 통신할 수 있습니다.

SharedWorkers와의 차이점

여러 창/탭 또는 작업자에게 메시지를 전송해야 하는 간단한 경우에는 BroadcastChannel를 사용하세요.

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

MessageChannel API와의 차이점

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

기능 감지 및 브라우저 지원

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

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

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

제가 직접 사용해 보지는 않았으므로 결과가 다를 수 있습니다.

리소스