Window.postMessage를 활용한 iframe 간 안전한 데이터 통신
2024 회고 글에서 잠깐 언급했던, 작년 2~4분기 동안 클라이언트사 프로젝트를 진행하면서 도입했던 기술 중 하나를 이번에 좀 더 자세히 공유해보려 합니다.
당시 요구사항 중 하나는, 우리가 개발한 웹사이트(이하 자식 웹)를 클라이언트사 웹사이트(이하 부모 웹)에 iframe
형태로 삽입하는 것이었습니다. 그리고 부모 웹에서 세션 스토리지로 관리하는 사용자 인증 토큰을 자식 웹에서도 사용할 수 있어야 했습니다.
하지만 두 웹사이트의 도메인이 서로 달랐기 때문에, 브라우저의 동일 출처 정책(CORS) 으로 인해 자식 웹에서 부모 웹의 스토리지에 직접 접근할 수는 없었습니다.
이 글에서는 이런 상황에서 활용할 수 있는 웹 표준 API인 window.postMessage()
를 통해 크로스 오리진 환경에서도 안전하게 데이터를 주고받는 방법과, 실제 구현 과정에서 고려했던 포인트들을 정리해보았습니다.
- 자식 웹은 부모 웹 안에
iframe
으로 삽입되어야 함 - 자식 웹이 부모 웹의 세션 스토리지에 저장된 인증 토큰을 참조해야 함
- 자식 웹 내부에서 발생하는 액션이 부모 웹에서 특정 작업(예: 페이지 전환 등)을 수행해야 함
🔗 [MDN] Window > postMessage()
이 문제를 해결하기 위해 window.postMessage()
를 사용하는 방안을 제안했고, 논의 끝에 이 API를 활용해 안전하게 데이터를 주고받는 방식으로 결정했습니다.
window.postMessage()
는 서로 다른 오리진 간에도 메시지를 주고받을 수 있도록 지원하는 웹 표준 API입니다. 주로 iframe과 부모 페이지, 팝업 창과 부모 페이지 간의 데이터 교환에 자주 사용됩니다.
targetWindow.postMessage(message, targetOrigin, [transfer]);
targetWindow
: 메시지를 보낼 대상 window 객체message
: 전달할 데이터 (문자열, 객체 등)targetOrigin
: 메시지를 받을 쪽의 origin(스킴 + 도메인 + 포트). 보안을 위해 반드시 명시적으로 지정해야 하며, 특별한 이유가 없다면 "*" 사용은 피하는 게 좋습니다.
다음은 실제 메시지를 주고받는 예시 코드입니다:
// 1. 메시지 전송 (자식 웹) window.parent.postMessage({ type: "REQUEST_TOKEN" }, "https://parent-web.com"); // 2. 메시지 수신 및 응답 (부모 웹) window.addEventListener("message", (event) => { if (event.origin !== "https://child-web.com") return; if (event.data.type === "REQUEST_TOKEN") { const token = sessionStorage.getItem("user_token"); event.source.postMessage({ type: "TOKEN_RESPONSE", token }, event.origin); } }); // 3. 메시지 수신 후 처리 (자식 웹) window.addEventListener("message", (event) => { if (event.origin !== "https://parent-web.com") return; if (event.data.type === "TOKEN_RESPONSE") { const userToken = event.data.token; // 인증 토큰 사용 로직 실행 } });
- 자식 웹 → 부모 웹: 자식 웹에서 인증 토큰을 요청하는 메시지를 보냅니다.
- 부모 웹 → 자식 웹: 부모 웹은 메시지를 받고 세션 스토리지에서 토큰을 꺼내 자식 웹에 전달합니다.
- 자식 웹: 받은 토큰으로 인증 관련 작업을 진행합니다.
postMessage 사용 시 보안 주의사항
- 메시지를 받을 때는 반드시
event.origin
을 확인해 신뢰할 수 있는 출처인지 검증해야 합니다. - 민감한 데이터를 다룰 경우
targetOrigin
은 반드시 명시적으로 지정하여 데이터가 잘못된 사이트로 전달되지 않도록 방지하세요.
부모 웹과 자식 웹 간의 동작 방식을 이해할 수 있도록 예제를 구성하였습니다.
아래 input에 iframe으로 삽입할 자식 웹의 URL을 입력하면, 기본값으로 설정된 예제 페이지가 iframe에 로드되고, 부모-자식 간 메시지를 주고받는 과정을 실시간으로 확인할 수 있습니다.
부모 웹
🔗 [Github] 부모 웹 소스코드
부모 웹에서는 자식 웹으로부터 전달되는 메시지를 수신하고 처리하는 역할을 합니다.
allowedOrigins
배열에 명시된 출처(origin)에서 온 메시지만 처리하도록 필터링합니다.- 자식 웹에서 아래와 같은 메시지를 보내면 다음과 같이 동작합니다:
SCROLL_TOP
: 부모 웹 페이지를 최상단으로 스크롤GO_BACK
: 브라우저의 뒤로 가기(history.back()
) 실행
이처럼 자식 웹에서 사용자 액션을 트리거하고, 실제 동작은 부모 웹이 수행하도록 구성하여 도메인이 다른 웹 간에도 안전하게 협업할 수 있는 구조로 구현하였습니다.
자식 웹
🔗 [CodeSandbox] 자식 웹 소스코드
자식 웹에서는 버튼 클릭 이벤트를 통해 부모 웹에 메시지를 전송하고, 동시에 부모로부터 전달된 메시지도 수신할 수 있도록 구현되어 있습니다.
postToParent
함수는 사용자가 입력한 부모 웹의 origin 값을 기반으로 메시지를 전송합니다.- 입력된 origin이
allowedOrigins
에 포함되어 있는 경우에만 메시지를 전송합니다.
- 입력된 origin이
부모 웹 스크롤 최상단 이동
버튼 클릭 시SCROLL_TOP
메시지를 전송합니다.부모 웹 뒤로가기
버튼 클릭 시GO_BACK
메시지를 전송합니다."message"
타입 메시지를 수신하면 해당 내용을 화면에 출력합니다.
이를 통해 서로 다른 출처 간에도 명확한 메시지 구조와 보안 검증 로직을 갖추고 통신하는 방식을 확인할 수 있습니다.