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;
    // 인증 토큰 사용 로직 실행
  }
});

  1. 자식 웹 → 부모 웹: 자식 웹에서 인증 토큰을 요청하는 메시지를 보냅니다.
  2. 부모 웹 → 자식 웹: 부모 웹은 메시지를 받고 세션 스토리지에서 토큰을 꺼내 자식 웹에 전달합니다.
  3. 자식 웹: 받은 토큰으로 인증 관련 작업을 진행합니다.

🚨

postMessage 사용 시 보안 주의사항

  • 메시지를 받을 때는 반드시 event.origin을 확인해 신뢰할 수 있는 출처인지 검증해야 합니다.
  • 민감한 데이터를 다룰 경우 targetOrigin은 반드시 명시적으로 지정하여 데이터가 잘못된 사이트로 전달되지 않도록 방지하세요.

부모 웹과 자식 웹 간의 동작 방식을 이해할 수 있도록 예제를 구성하였습니다.

아래 input에 iframe으로 삽입할 자식 웹의 URL을 입력하면, 기본값으로 설정된 예제 페이지가 iframe에 로드되고, 부모-자식 간 메시지를 주고받는 과정을 실시간으로 확인할 수 있습니다.


부모 웹

🔗 [Github] 부모 웹 소스코드


부모 웹에서는 자식 웹으로부터 전달되는 메시지를 수신하고 처리하는 역할을 합니다.

  • allowedOrigins 배열에 명시된 출처(origin)에서 온 메시지만 처리하도록 필터링합니다.
  • 자식 웹에서 아래와 같은 메시지를 보내면 다음과 같이 동작합니다:
    • SCROLL_TOP: 부모 웹 페이지를 최상단으로 스크롤
    • GO_BACK: 브라우저의 뒤로 가기(history.back()) 실행

이처럼 자식 웹에서 사용자 액션을 트리거하고, 실제 동작은 부모 웹이 수행하도록 구성하여 도메인이 다른 웹 간에도 안전하게 협업할 수 있는 구조로 구현하였습니다.

자식 웹

🔗 [CodeSandbox] 자식 웹 소스코드


자식 웹에서는 버튼 클릭 이벤트를 통해 부모 웹에 메시지를 전송하고, 동시에 부모로부터 전달된 메시지도 수신할 수 있도록 구현되어 있습니다.

  • postToParent 함수는 사용자가 입력한 부모 웹의 origin 값을 기반으로 메시지를 전송합니다.
    • 입력된 origin이 allowedOrigins에 포함되어 있는 경우에만 메시지를 전송합니다.
  • 부모 웹 스크롤 최상단 이동 버튼 클릭 시 SCROLL_TOP 메시지를 전송합니다.
  • 부모 웹 뒤로가기 버튼 클릭 시 GO_BACK 메시지를 전송합니다.
  • "message" 타입 메시지를 수신하면 해당 내용을 화면에 출력합니다.

이를 통해 서로 다른 출처 간에도 명확한 메시지 구조와 보안 검증 로직을 갖추고 통신하는 방식을 확인할 수 있습니다.