Files
2nd/10_Wiki/Topics/Coding/Web_BroadcastChannel_SharedWorker.md
T
2026-05-10 22:08:15 +09:00

7.8 KiB

id, title, category, status, source_trust_level, verification_status, created_at, updated_at, tags, tech_stack, applied_in, aliases
id title category status source_trust_level verification_status created_at updated_at tags tech_stack applied_in aliases
web-broadcastchannel-sharedworker BroadcastChannel / SharedWorker — tab 간 통신 Coding draft B conceptual 2026-05-09 2026-05-09
web
worker
vibe-coding
language applicable_to
TS
Frontend
BroadcastChannel
SharedWorker
MessageChannel
postMessage
tab sync
cross-tab

BroadcastChannel / SharedWorker

Tab / iframe / worker 간 message passing. BroadcastChannel (simple), SharedWorker (logic 공유), MessageChannel (port pair). Same-origin 만.

📖 핵심 개념

  • 같은 origin 의 다른 tab.
  • BroadcastChannel: pub/sub.
  • SharedWorker: 공유 worker.
  • MessageChannel: 1-1 port.

💻 코드 패턴

BroadcastChannel

const channel = new BroadcastChannel('updates');

// Sender (tab A)
channel.postMessage({ type: 'config-changed', value: 'dark' });

// Receiver (tab B)
channel.onmessage = (e) => {
  console.log(e.data);  // { type: 'config-changed', value: 'dark' }
};

// Cleanup
channel.close();

→ Same-origin 의 모든 tab 가 receive.

Use case

- Theme toggle: 1 tab 에서 바꾸면 모든 tab.
- Logout: 1 tab 에서 logout = 모두.
- Cache invalidation: data 변경 알림.
- Real-time sync: collaborative edit.

SharedWorker

// shared-worker.ts (worker file)
const ports: MessagePort[] = [];

self.onconnect = (e) => {
  const port = e.ports[0];
  ports.push(port);
  
  port.onmessage = (e) => {
    // 모든 tab 에 broadcast
    ports.forEach(p => p.postMessage(e.data));
  };
};
// main thread
const worker = new SharedWorker('shared-worker.ts', { type: 'module' });
worker.port.start();

worker.port.postMessage({ type: 'hello' });
worker.port.onmessage = (e) => console.log(e.data);

→ 모든 tab 가 1 worker instance 공유.

vs BroadcastChannel

BroadcastChannel:
- Simple.
- 매 tab 가 자체 logic.

SharedWorker:
- 공유 state (1 instance).
- Logic 가 worker 안.
- WebSocket / DB connection 가 1 곳.

MessageChannel (port pair)

const channel = new MessageChannel();
const { port1, port2 } = channel;

port1.onmessage = (e) => console.log('p1:', e.data);
port2.onmessage = (e) => console.log('p2:', e.data);

port1.postMessage('hello');
// → p2: hello

port2.postMessage('world');
// → p1: world

port1.start();
port2.start();

→ 1-1 통신. iframe / worker 친화.

iframe 와 통신

// Parent
const iframe = document.querySelector('iframe')!;
const channel = new MessageChannel();
iframe.contentWindow!.postMessage({ port: channel.port2 }, '*', [channel.port2]);

channel.port1.postMessage('hello');
channel.port1.onmessage = (e) => console.log(e.data);
// iframe
window.addEventListener('message', (e) => {
  const port = e.data.port;
  port.onmessage = (e) => console.log('parent:', e.data);
  port.postMessage('hi from iframe');
});

→ Parent ↔ iframe 의 secure channel.

Use case 비교

BroadcastChannel:
- 매 tab 가 같은 logic.
- 알림 / sync.

SharedWorker:
- 공유 connection (WebSocket).
- 공유 cache.
- 공유 computation.

MessageChannel:
- iframe 와 통신.
- Worker 와 sub-channel.

Web Locks:
- Tab 간 mutex.

SharedWorker 의 함정

- iOS Safari 가 안 (no support).
- Chrome incognito 가 안 (privacy).
- Firefox 가 OK.

→ 가벼운 use case 만.
대부분 = BroadcastChannel + Web Locks.

Web_Web_Locks_API.

Single WebSocket pattern

// SharedWorker 가 1 WS.
self.onconnect = (e) => {
  const port = e.ports[0];
  
  if (!ws) {
    ws = new WebSocket('wss://...');
    ws.onmessage = (msg) => {
      // 모든 connected tab 에 broadcast
      ports.forEach(p => p.postMessage({ type: 'message', data: msg.data }));
    };
  }
  
  ports.push(port);
  port.onmessage = (e) => ws.send(e.data.payload);
};

→ N tab → 1 WS connection.

postMessage (window)

// Parent → iframe
iframe.contentWindow.postMessage({ type: 'init' }, 'https://child.com');

// iframe
window.addEventListener('message', (e) => {
  if (e.origin !== 'https://parent.com') return;  // 검증
  console.log(e.data);
});

→ Cross-origin (BroadcastChannel 가 안 됨).

postMessage 의 함정

- Origin 검증 필수 (모든 origin = 위험).
- Object 가 structured clone (function 안 됨).
- 큰 object = 느림 → transferable.

Transferable

const buf = new ArrayBuffer(1_000_000);
worker.postMessage({ buf }, [buf]);
// → buf 가 worker 로 transfer (main thread 에서 사용 X).

→ Zero-copy.

MessageChannel 의 transferable port

const { port1, port2 } = new MessageChannel();
worker.postMessage({ port: port2 }, [port2]);
// → port2 가 worker 로 transfer.

React 통합

// Theme sync across tab
useEffect(() => {
  const ch = new BroadcastChannel('theme');
  ch.onmessage = (e) => setTheme(e.data);
  return () => ch.close();
}, []);

const updateTheme = (t: Theme) => {
  setTheme(t);
  new BroadcastChannel('theme').postMessage(t);
};

Service Worker + BroadcastChannel

// Service worker
self.addEventListener('fetch', async (event) => {
  // ...
  const ch = new BroadcastChannel('cache');
  ch.postMessage({ type: 'fetched', url: event.request.url });
});

// Page
const ch = new BroadcastChannel('cache');
ch.onmessage = (e) => console.log(e.data);

Browser support

BroadcastChannel: 모든 modern (Safari 15.4+).
SharedWorker: Chrome / FF / Edge. Safari iOS 안.
MessageChannel: 모든.
postMessage: 모든.

Polyfill (BroadcastChannel)

// localStorage 식 (옛 browser)
window.addEventListener('storage', (e) => {
  if (e.key === 'channel:updates') {
    const data = JSON.parse(e.newValue!);
    // ...
  }
});

localStorage.setItem('channel:updates', JSON.stringify({ ... }));
localStorage.removeItem('channel:updates');

→ 옛 hack. Modern = native.

When 안 사용?

- Cross-origin: postMessage + origin check.
- Same-tab worker: regular worker.
- Server → multi-tab: SSE / WebSocket + BroadcastChannel.
- Persistent: IndexedDB.

함정

- BroadcastChannel 가 same origin 만.
- Worker close 시 message lost.
- iOS Safari 가 SharedWorker X.
- 큰 object = serialize cost.
- Origin 검증 안 함: XSS.

Use case 패턴

1. Tab sync:
   BroadcastChannel.

2. Single WS:
   SharedWorker (Chrome/FF) + BroadcastChannel fallback.

3. iframe widget:
   MessageChannel.

4. Service worker:
   BroadcastChannel.

5. Cross-origin:
   postMessage + origin.

Performance

BroadcastChannel: ms latency.
SharedWorker: 비슷.
MessageChannel: 가장 빠름 (port).
postMessage cross-origin: 비슷.

→ 모두 빠름. 큰 object 가 cost.

Tab 의 visibility

document.addEventListener('visibilitychange', () => {
  if (document.visibilityState === 'visible') {
    // Refetch state via BroadcastChannel
    bc.postMessage({ type: 'request-state' });
  }
});

→ Tab 가 hidden = sync 가 throttle.

🤔 의사결정 기준

작업 추천
Tab sync BroadcastChannel
Shared logic SharedWorker
iframe 통신 MessageChannel + postMessage
Cross-origin postMessage + origin check
Mutex Web Locks
1 WS / N tab SharedWorker
Service worker BroadcastChannel

안티패턴

  • Cross-origin 가 BroadcastChannel: 안 됨.
  • Origin 검증 X: XSS.
  • iOS Safari + SharedWorker: 깨짐.
  • 큰 object 자주 broadcast: 느림.
  • Channel close X: leak.
  • 모든 tab 가 자체 WS: 비효율.

🤖 LLM 활용 힌트

  • BroadcastChannel = simple cross-tab.
  • SharedWorker = 공유 logic (Safari iOS X).
  • MessageChannel = iframe / worker.
  • Origin check 가 cross-origin 의 보안.

🔗 관련 문서