はじめまして、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회씩 실시하고 가장 평균 값에 소수점 이하 다섯자리까지 표시
Chrome | Firefox | Microsoft Edge |
---|---|---|
0.00001 | 0.00001 | 0.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자리의 문자열 데이터로 실험해보았습니다.
브라우저별 테스트때 혹시 데이터가 적어서 송신속도에 유의미한 차이가 나지않았던 것은 아닐까라는 생각이 들어 최대 송신량으로 다시 테스트 해 보았습니다.
Chrome | Firefox | Microsoft Edge |
---|---|---|
0.53s | 1.25s | 0.44s |
여기에선 유의미한 차이가 나는 것을 확인할 수 있었습니다. 의외로 edge가 가장 빠른 성능을 보여주었습니다.
이번엔 최대 송신량 크기로 새 탭으로 전송해 보도록 하겠습니다.
Chrome | Firefox | Microsoft Edge |
---|---|---|
0.63s | 1.13s | 0.45s |
새 탭에서도 결과는 비슷하였습니다. Firefox가 1.10s ~ 1.40s 사이로 가장 느린 속도를 보여주었습니다.
마무리
이상으로 postMessage사용법과 여러가지 테스트를 적어보았습니다. 읽어 주셔서 감사합니다.