같은 사이트를 여러 탭에서 열었을 때, 한 탭에서 로그아웃하면 다른 탭에서도 반영되어야 합니다. SharedWorker와 BroadcastChannel은 이런 탭 간 통신 문제를 해결하는 API입니다.

BroadcastChannel — 가장 간단한 탭 간 통신

같은 출처(origin)의 모든 탭/윈도우/iframe 간에 메시지를 주고받을 수 있습니다.

JS
// 채널 생성 (같은 이름이면 같은 채널)
const channel = new BroadcastChannel("auth");

// 메시지 전송
channel.postMessage({
  type: "logout",
  timestamp: Date.now(),
});

// 메시지 수신 (같은 탭에서 보낸 건 수신하지 않음)
channel.addEventListener("message", (event) => {
  const { type } = event.data;

  if (type === "logout") {
    // 다른 탭에서 로그아웃됨 → 현재 탭도 로그아웃 처리
    clearSession();
    window.location.href = "/login";
  }
});

// 채널 닫기
channel.close();

실전 활용 — 테마 동기화

JS
const themeChannel = new BroadcastChannel("theme");

// 테마 변경 시 다른 탭에도 알림
function changeTheme(newTheme) {
  document.documentElement.dataset.theme = newTheme;
  localStorage.setItem("theme", newTheme);
  themeChannel.postMessage({ theme: newTheme });
}

// 다른 탭에서 테마 변경 수신
themeChannel.addEventListener("message", (event) => {
  document.documentElement.dataset.theme = event.data.theme;
});

실전 활용 — 장바구니 동기화

JS
const cartChannel = new BroadcastChannel("cart");

function addToCart(item) {
  cart.push(item);
  updateCartUI();
  cartChannel.postMessage({ type: "cartUpdate", cart });
}

cartChannel.addEventListener("message", (event) => {
  if (event.data.type === "cartUpdate") {
    cart = event.data.cart;
    updateCartUI();
  }
});

SharedWorker — 탭 간 공유 워커

여러 탭이 ** 하나의 워커 인스턴스 **를 공유합니다. WebSocket 연결을 하나만 유지하면서 여러 탭에 데이터를 분배하는 데 유용합니다.

워커 파일 (shared-worker.js)

JS
// shared-worker.js
const ports = new Set();

// 새 탭이 연결될 때
self.addEventListener("connect", (event) => {
  const port = event.ports[0];
  ports.add(port);

  port.addEventListener("message", (e) => {
    const { type, data } = e.data;

    if (type === "broadcast") {
      // 모든 탭에 메시지 전달
      ports.forEach((p) => {
        if (p !== port) { // 보낸 탭 제외
          p.postMessage({ type: "message", data });
        }
      });
    }
  });

  port.start();

  // 연결된 탭 수 알림
  ports.forEach((p) => {
    p.postMessage({ type: "tabCount", data: ports.size });
  });
});

메인 스레드

JS
// SharedWorker에 연결
const worker = new SharedWorker("shared-worker.js");
const port = worker.port;

port.start();

// 메시지 전송
port.postMessage({
  type: "broadcast",
  data: { user: "정훈", action: "commented" },
});

// 메시지 수신
port.addEventListener("message", (event) => {
  const { type, data } = event.data;

  if (type === "message") {
    console.log("다른 탭에서:", data);
  } else if (type === "tabCount") {
    console.log(`현재 ${data}개 탭 연결 중`);
  }
});

SharedWorker로 WebSocket 공유

JS
// shared-worker.js
let ws = null;
const ports = new Set();

function connectWebSocket() {
  ws = new WebSocket("wss://api.example.com/ws");

  ws.addEventListener("message", (e) => {
    const data = JSON.parse(e.data);
    // 모든 탭에 전달
    ports.forEach((port) => {
      port.postMessage({ type: "ws-message", data });
    });
  });

  ws.addEventListener("close", () => {
    setTimeout(connectWebSocket, 3000);
  });
}

self.addEventListener("connect", (event) => {
  const port = event.ports[0];
  ports.add(port);

  // 첫 연결 시 WebSocket 시작
  if (!ws || ws.readyState === WebSocket.CLOSED) {
    connectWebSocket();
  }

  port.addEventListener("message", (e) => {
    if (e.data.type === "ws-send" && ws.readyState === WebSocket.OPEN) {
      ws.send(JSON.stringify(e.data.data));
    }
  });

  port.start();
});

다른 탭 간 통신 방법들

localStorage 이벤트

JS
// storage 이벤트는 다른 탭에서 변경 시에만 발생
window.addEventListener("storage", (event) => {
  if (event.key === "auth-token" && !event.newValue) {
    // 다른 탭에서 로그아웃됨
    handleLogout();
  }
});

// 이벤트 발생시키기
localStorage.setItem("auth-token", ""); // 다른 탭에서 감지됨

MessageChannel

JS
// iframe과의 통신
const channel = new MessageChannel();
const port1 = channel.port1;
const port2 = channel.port2;

// iframe에 포트 전달
iframe.contentWindow.postMessage("init", "*", [port2]);

// 메시지 주고받기
port1.addEventListener("message", (e) => {
  console.log("iframe에서:", e.data);
});
port1.start();
port1.postMessage("부모에서 보냄");

방법별 비교

방법사용 편의성데이터 타입양방향지속성
BroadcastChannel매우 쉬움구조화 복제OX
SharedWorker중간구조화 복제OO
localStorage 이벤트쉬움문자열만XO
postMessage쉬움구조화 복제OX

주의사항

JS
// 1. BroadcastChannel은 같은 출처에서만 동작
// http://a.com ↔ http://b.com 통신 불가

// 2. SharedWorker는 브라우저 지원 확인 필요
if (typeof SharedWorker !== "undefined") {
  // SharedWorker 사용
} else {
  // 폴백: BroadcastChannel 또는 localStorage
}

// 3. 탭 닫힐 때 정리
window.addEventListener("beforeunload", () => {
  channel.close();
  port.close();
});

**기억하기 **: 간단한 탭 간 메시지 전달에는 BroadcastChannel이 가장 쉽습니다. WebSocket을 탭 간에 공유하려면 SharedWorker를 사용합니다. 로그아웃 동기화 같은 간단한 경우에는 localStorage 이벤트도 충분합니다.

댓글 로딩 중...