はじめまして、Arganoのジェソンです。 postMessageを利用する機会があったので、パフォーマンスについて調べて見ました。


postMessage란?

postMessage는 크로스 도큐먼트 메세징(Cross Document Messaging)이라고도 불립니다. 일반적으로 스크립트는 same-origin policy에 의해 protocol, domain, port 중 하나라도 다른 경우엔 다른 윈도우와 통신할 수 없습니다. 하지만 이름에서도 알 수 있듯이 postMessage()를 사용하면 origin이 다른 윈도우와 통신할 수 있습니다.

사용방법은 아래와 같습니다.

송신방법

window.postMessage(data, targetOrigin, [transfer]);
  • data: 다른 스크립트에 전달할 메세지(data는 string형태로만 전달된다.)
  • targetOrigin: 타겟 도메인, 즉 메세지를 수신받는 도메인을 지정한다. 대상이 특정 도메인이 아니라면 "*"료 지정한다.
  • transfer 객체를 전송할 수 있다.

수신방법

window.addEventListener("message", () => {
    console.log(e.data);
});

postMessage를 어떻게 활용할 수 있을까?

윈도우간의 데이터 송수신이 필요한 경우가 있습니다. iframe경우를 대표적인 예로 들 수 있겠습니다. 이때 postMessage를 사용하면 손쉽게 데이터를 송수신 할 수 있습니다.

iframe을 사용하는 경우 부모 윈도우에서 유저 이벤트가 일어났을때 자식 윈도우의 width를 조절해보겠습니다.

부모 window(송신 측)

const button = document.getElementById("button");
button.addEventListener("click", () => {
    const child = document.getElementById("child");
    child.contenWindow.postMessage("100px", "*");
});

자식 window(수신 측)

window.addEventListener("message", (ev: MessageEvent) => {
    const width = ev.data;
    const content = document.getElementById("content");
    content.style.width = width;
});

반대로 자식 윈도우에서 유저 이벤트가 일어났을때 부모 윈도우의 배경화면을 변경해보겠습니다.

자식 window(송신 측)

const button = document.getElementById("button");
const backGroundImgUrl = "/public/background.img";
button.addEventListener("click", () => {
    window.parent.postMessage(backGroundImgUrl, "*");
});

부모 window(수신 측)

window.addEventListener("message", (ev: MessageEvent) => {
    const backgroundImgUrl = ev.data;
    const content = document.getElementById("content");
    content.style.backgroundImage = `url(${backgroundImgUrl})`;
});

이런 식으로 구현이 가능합니다.


postMessage는 새탭으로 데이터를 전송할 수 있을까?

postMessage를 이용해 팝업 등의 새로운 탭으로 데이터를 보내는 것은 가능할까 실험해보았습니다. nowWindow, targetWindow 로 명칭하여 코드를 적어 보도록 하겠습니다.

nowWindow(송신 측)

const targetWindow = window.open("url");
targetWindow.postMessage("hellow newTab", "*");

newTab(수신 측)

window.addEventListener("message", (ev: MessageEvent) => {
    const data = ev.data;
    console.log(ev.data);
});

결과는 성공입니다. 팝업창 등에 데이터를 건낼때 유용하게 쓰일것같습니다.


postMessage의 취약점

postMessage 사용 시 주의해야할 점은 메세지를 보내는 대상이 웹 문서가 아닌 윈도우라는 점입니다. 윈도우의 경우는 다른 윈도우에서 postMessage()를 전달하고 받을 수 있는 가능성이 있기때문에 이 상태에서 메시지를 보내게 되면 의도하지 않은 origin에 메세지가 유출될 수 있습니다. 그래서 postMessage의 두 번째 인자인 targetOrigin에 대상 origin을 상세히 명시하는 것으로 이 문제를 해결 할 수 있습니다.

window.addEventListener("message", (ev: MessageEvent) => {
    if (ev.origin !== "https://example.org") {
        return;
    }
});

postMessage의 브라우저별 속도

그럼 postMessage의 브라우저별 송신 속도는 어떤지 알아보겠습니다.

자식 window에서 data에 현재 시각을 millisecond로 담아서 송신 후 수신 측에서 받은데이터와 도착한 시간을 비교해보겠습니다.

자식 window(송신 측)

window.parent.postMessage(Date.now(), "*");

부모 window(수신 측)

window.addEventListener("message", (ev: MessageEvent) => {
    const startTime = Number(ev.data);
    const end = Date.now();
    const diff = ((end - ev.data)/1000)/60;
    console.log("diff : ", diff);
});

각 브라우저별 50회씩 실시하고 가장 평균 값에 소수점 이하 다섯자리까지 표시

ChromeFirefoxMicrosoft Edge
0.000010.000010.00001
  • 결과 : 브라우저별로 유의미한 속도차이는 나지 않았습니다.

postMessage 데이터의 송수신량의 한계는 있을까?

string형태로만 주고받을 수 있는 postMessage의 문자열 송수신량의 한계가 있는지 알아보고 싶어졌습니다. 아주 긴 문자열을 전달하는 단순한 테스트로 진행하였습니다.

// largeString.length = 536,870,887;
const largeString = "...";
const child = document.getElementById("child");
child.contenWindow.postMessage(largeString, "*");
window.addEventListener("message", (ev: MessageEvent) => {
    const data = ev.data;
    console.log(data);
});

// 여기 수정

  • 결과: string 길이가 536,870,887자리 이후에는 Uncaught RangeError: Invalid string length 에러가 발생했습니다. (Chrome기준)
  • 브라우저별로 배열의 최대크기 수용량이 이번 테스트에서는 1-5자리 정도의 차이가 있어서 브라우저가 다운되지 않도록 536,870,880자리의 문자열 데이터로 실험해보았습니다.

브라우저별 테스트때 혹시 데이터가 적어서 송신속도에 유의미한 차이가 나지않았던 것은 아닐까라는 생각이 들어 최대 송신량으로 다시 테스트 해 보았습니다.

ChromeFirefoxMicrosoft Edge
0.53s1.25s0.44s

여기에선 유의미한 차이가 나는 것을 확인할 수 있었습니다. 의외로 edge가 가장 빠른 성능을 보여주었습니다.

이번엔 최대 송신량 크기로 새 탭으로 전송해 보도록 하겠습니다.

ChromeFirefoxMicrosoft Edge
0.63s1.13s0.45s

새 탭에서도 결과는 비슷하였습니다. Firefox가 1.10s ~ 1.40s 사이로 가장 느린 속도를 보여주었습니다.


마무리

이상으로 postMessage사용법과 여러가지 테스트를 적어보았습니다. 읽어 주셔서 감사합니다.