-
다른 탭에 변경을 알렸는데 새로 연 탭은 모르는 이유Programming 2026. 10. 4. 00:49728x90반응형

열린 탭은 알림 뒤 재조회하고 새 탭은 시작할 때 원본을 읽는 구조입니다. 설명용 설계도입니다. 설정 화면 A에서 테마를 바꾸자 열려 있던 B도 어두운 화면으로 바뀝니다. 그런데 그다음에 연 C는 예전 테마를 보여 줍니다. BroadcastChannel에 변경 알림을 보낸 것과 현재 설정을 저장한 것은 서로 다른 작업이기 때문입니다.
BroadcastChannel은 참여 중인 탭에 메시지를 전달합니다. 나중에 열린 탭이 이전 알림을 다시 받는 이력 저장소는 제공하지 않습니다. 새 탭은 저장된 현재 상태를 먼저 읽고, 열린 탭은 알림을 받으면 그 상태를 다시 읽게 만드세요. 이 글에서는 서버에 설정을 저장하는 가상 앱으로 그 순서를 따라갑니다. 실제 서비스를 실행한 기록은 아닙니다.
A와 B가 맞았는데 C만 틀린 까닭
다음 코드는 다른 탭에 신호를 보내는 데는 충분합니다.
const channel = new BroadcastChannel( "settings" ); channel.postMessage({ type: "settings-changed", });하지만 메시지에는 보관 기한이나 재생 위치가 없습니다. 그 시점에 같은 채널로 연결된 수신자가 메시지를 처리할 수 있을 뿐입니다.
postMessage()가 반환돼도 B의 화면 갱신이 끝났다는 응답을 받은 것은 아닙니다. 보낸 객체 자신에게 같은 메시지가 돌아오는 것도 아닙니다. 메시지 전달 동작C가 처음 열릴 때 빈 화면 상태를 만들고 알림만 기다리면, 이미 지나간 변경을 알 길이 없습니다. 브라우저를 재시작한 경우에도 같습니다. 이때 필요한 질문은 “알림을 어디에 더 보내야 하나?”보다 “현재 설정은 어디에 기록돼 있나?”입니다.
이 예제에서는 서버의
/api/settings응답을 원본으로 삼습니다. A는 저장 요청이 성공한 뒤 자기 화면을 갱신하고 B에게 알립니다. B는 알림을 받은 뒤 서버를 다시 조회합니다. C도 시작할 때 같은 주소를 조회합니다. 세 탭이 같은 상태에 도달하는 근거가 각자의 메시지 수신 이력에서 서버의 현재 기록으로 바뀝니다.초기 조회도 알림 처리와 같은 함수로 묶습니다
아래 코드는 조회 순서를 보여 주는 구성 예시입니다.
renderSettings()와showError()는 앱에서 제공해야 하는 화면 함수입니다. API는 성공한 조회에 JSON을 반환하며, 설정 응답을 오래된 캐시에서 가져오지 않는다는 전제입니다.const channel = new BroadcastChannel( "settings" ); let newestRequest = 0; async function reloadSettings() { const requestId = ++newestRequest; try { const response = await fetch( "/api/settings", { cache: "no-store" } ); if (!response.ok) { throw new Error( "설정을 읽지 못했습니다." ); } const settings = await response.json(); if ( requestId === newestRequest ) { renderSettings(settings); } } catch (error) { if ( requestId === newestRequest ) { showError(error.message); } } } channel.onmessage = ({ data }) => { if ( data?.type === "settings-changed" ) { reloadSettings(); } }; reloadSettings();수신 함수를 먼저 연결한 뒤 최초 조회를 시작합니다. 조회 중에 변경 알림이 도착해도 같은 함수가 다시 실행됩니다. 요청 번호는 먼저 시작한 조회가 늦게 끝나 새 조회 결과를 덮는 상황을 막기 위한 앱 쪽 장치입니다.
다만 이 번호는 서버 데이터의 버전이 아닙니다. 분산 서버의 늦은 복제본을 읽거나 최신 요청이 실패하는 문제까지 해결하지는 않습니다. 마지막 조회가 실패하면 예제는 오류를 보여 주며, 재시도 버튼이나 복귀 시 재조회는 제품 동작에 맞게 추가해야 합니다. 서버가 단조 증가하는 버전을 제공한다면 그 버전으로 적용 순서를 판단하는 설계도 가능합니다.
저장 성공 다음에 알립니다
A에서 클릭하자마자 알림을 보내면 B의 조회가 저장보다 먼저 끝날 수 있습니다. B는 예전 값을 잘 읽어 놓고도 최신 상태라고 생각하게 됩니다. 예제의 저장 처리는 다음 순서를 지킵니다.
async function saveSettings( nextSettings ) { const response = await fetch( "/api/settings", { method: "PUT", headers: { "Content-Type": "application/json", }, body: JSON.stringify( nextSettings ), } ); if (!response.ok) { throw new Error( "설정을 저장하지 못했습니다." ); } channel.postMessage({ type: "settings-changed", }); await reloadSettings(); }저장 API의 성공은 다른 탭이 곧바로 그 값을 조회할 수 있는 시점을 뜻해야 합니다. 실제 API가 비동기 처리 접수만 반환한다면 완료 확인을 더 넣어야 합니다. 호출자는 저장 오류를 사용자에게 표시해야 하고, 서버의 인증·인가도 별도로 유지해야 합니다. 채널 메시지 자체를 접근 권한으로 사용하지 않습니다.
여기서는 설정 전체 대신 “다시 읽으라”는 신호만 보냅니다. 메시지를 받은 탭이 자신의 권한으로 원본을 조회하게 하면 전달할 데이터도 줄어듭니다. 이미 열려 있던 탭이 오랫동안 쉬었다 돌아오는 경우에는 알림 수신만 믿지 말고 현재 설정을 다시 확인하도록 정하세요.
같은 주소여도 채널이 이어지지 않는 경우
BroadcastChannel은 같은 origin뿐 아니라 같은 저장 파티션 범위에서 동작합니다.
a.example안의b.exampleiframe과 최상위b.example탭은 이 조건이 달라 통신하지 못할 수 있습니다. 다른 기기까지 동기화하려면 서버 알림 등 별도 경로가 필요합니다. 통신 범위와 저장 파티션컴포넌트가 채널을 소유한다면 종료할 때
close()로 정리하고, 다시 만들 때 초기 조회도 다시 연결합니다. 지원하지 않는 브라우저를 대상으로 한다면 생성 전에 기능 유무를 검사하고 조회·새로고침 경로를 남겨야 합니다.수정한 뒤에는 A와 B가 열린 상태에서 저장하는 경우, 저장 후 C를 여는 경우, 조회 두 개가 역순으로 끝나는 경우를 각각 확인하세요. 특히 C를 새로 열어도 현재 설정이 나오는지 보면 상태 저장과 알림 전달을 제대로 나눴는지 바로 드러납니다.
문서 확인: 2026년 9월 28일, MDN Broadcast Channel API. 코드는 앱에 통합할 흐름을 설명하며 서버 구현이나 기기 간 동기화를 포함하지 않습니다.
728x90반응형'Programming' 카테고리의 다른 글
웹앱을 배포했는데 열린 탭만 옛날 버전인 이유 (0) 2026.10.05 오프라인 파일 하나가 404인데 왜 설치 전체가 실패할까 (0) 2026.10.04 탭 두 개가 같은 작업을 동시에 할 때: Web Locks의 범위 (0) 2026.10.04 복사 버튼은 눌렀는데 클립보드가 안 바뀌는 이유 (0) 2026.10.04 자동재생을 켰는데 비디오가 멈춰 있는 이유 (0) 2026.10.04