브릿지(Bridge)란?웹앱에서 브릿지는 서로 다른 실행 컨텍스트 간의 통신을 추상화한 레이어입니다.
postMessage나 네이티브 API는 저수준(low-level)이라 메시지 타입 관리, 요청-응답 매핑, 에러 처리를 직접 구현해야 합니다.브릿지 패턴을 적용하면 이를 추상화하여 RPC(Remote Procedure Call)처럼 쉽게 사용할 수 있습니다.
브릿지가 필요한 대표적인 상황
상황
통신 대상
iframe 내부 ↔ 부모 페이지
다른 origin의 window
웹뷰(WebView) ↔ 네이티브 앱
React Native, iOS, Android
Web Worker ↔ 메인 스레드
백그라운드 스레드
브라우저 확장(Extension) ↔ 웹 페이지
확장 콘텐츠 스크립트
1. 기본 브릿지 패턴 (iframe ↔ 부모)요청마다 고유한 id를 부여하고, 응답이 오면 해당 id의 Promise를 resolve하는 방식입니다.
// bridge.js (부모 페이지와 iframe 양쪽에서 공유)class IframeBridge { constructor(targetWindow, targetOrigin) { this.targetWindow = targetWindow; this.targetOrigin = targetOrigin; this.pendingRequests = new Map(); this.handlers = new Map(); window.addEventListener('message', (event) => { if (event.origin !== this.targetOrigin) return; this._handleMessage(event.data); }); } // 메시지 전송 후 응답을 Promise로 반환 request(type, payload) { return new Promise((resolve, reject) => { const id = crypto.randomUUID(); this.pendingRequests.set(id, { resolve, reject }); this.targetWindow.postMessage( { id, type, payload }, this.targetOrigin ); // 타임아웃 처리 (5초) setTimeout(() => { if (this.pendingRequests.has(id)) { this.pendingRequests.delete(id); reject(new Error(`Request timeout: ${type}`)); } }, 5000); }); } // 특정 메시지 타입에 핸들러 등록 on(type, handler) { this.handlers.set(type, handler); } _handleMessage(message) { const { id, type, payload, error, isResponse } = message; // 응답 메시지 처리 if (isResponse && this.pendingRequests.has(id)) { const { resolve, reject } = this.pendingRequests.get(id); this.pendingRequests.delete(id); error ? reject(new Error(error)) : resolve(payload); return; } // 요청 메시지 처리 → 핸들러 실행 후 응답 전송 const handler = this.handlers.get(type); if (handler) { Promise.resolve() .then(() => handler(payload)) .then((result) => { this.targetWindow.postMessage( { id, type, payload: result, isResponse: true }, this.targetOrigin ); }) .catch((err) => { this.targetWindow.postMessage( { id, type, error: err.message, isResponse: true }, this.targetOrigin ); }); } }}
부모 페이지에서 사용