본문 바로가기
공부/FE

PostMessage 알아보기

by Piva 2024. 10. 20.
  • 개발 중 브라우저 창끼리 데이터를 주고 받아야 하는 상황이 생겼다.
  • 이를 구현하기 위해 방안을 찾아보던 결과, GPT가 제시한 방안이 PostMessage를 사용하는 것이었다.
  • 사용법 및 용도에 대해 간단히 복습 겸 메모해보기로.

PostMessage란?

  • Window 창 사이 Cross-Origin 통신을 할 수 있게 해주는 메서드다.
    • 즉, 별도의 Cross Origin 설정을 하지 않더라도 안전하게 서로 다른 출처 간 통신이 가능하다!

 

개발 상황

  PostMessage를 사용하여 구현하기로 한 상황은 대충 아래와 같았다. 아래의 상황을 재현하기 위해 간략하게 구현해보기로 한다.

  • 웹 A에서 특정 메뉴를 통해 웹 B를 새 윈도우로 띄운다.
  • 이때 웹 A는 웹 B가 필요로 하는 데이터를 보내주어야 한다.
  • 웹 B는 웹 A가 보내온 데이터를 받아 필요한 작업을 수행한다.

 

웹 A (데이터를 보내는 쪽)

  웹 A는 웹 B를 새 창에서 열고, 필요한 데이터를 PostMessage를 통해 보내야 한다. 

export const PageA = () => {
  // 데이터를 전송하는 페이지
  const onClickButton = () => {
    const newWindow = window.open('/pageB', '_blank');

    setInterval(
      () => newWindow?.postMessage('sample data!', 'http://localhost:5173')
      , 3000
    );
  };

  return (
    <div>
      <h3>Page A</h3>
      <button onClick={onClickButton}>Click to Open Page B</button>
    </div>
  );
};

 

  • open 메서드를 사용해 웹 B를 새 창으로 열고, 새롭게 열린 창을 변수로 저장한다.
  • postMessage를 사용하여 새롭게 열린 창에 데이터를 보낸다.
    • setInterval을 사용하는 이유는, 딜레이를 주지 않으면 새롭게 열린 창에서 바로 보내진 데이터를 받지 못할 수도 있기 때문이다.
    • 따라서 몇 초의 시간이 지난 이후 데이터를 보냄으로써 새 창이 데이터를 확실히 받을 수 있도록 한다.
  • 위 코드에서 postMessage가 받는 인자는 아래와 같다.
    • 첫 번째 인자 (message): 전달할 데이터를 받는다.
    • 두 번째 인자(targetOrigin): PostMessage를 받을 출처를 지정한다. 즉, PostMessage를 받을 출처가 targetOrigin과 일치하지 않는다면 MessageEvent가 전송되지 않는다. 이 재현 코드에서는 Vite를 쓰고 있어서, Vite의 기본 포트 번호인 5173을 사용하는 로컬호스트로 설정되어있다.

 

웹 B (데이터를 받는 쪽)

  반대로 웹 B는 PostMessage를 통해 보내진 데이터를 받아야 한다. PostMessage 데이터를 받기 위해서는 이벤트 리스너를 등록하고, 데이터를 받았을 때 처리할 로직을 등록해두면 된다.

 

import { useEffect, useState } from "react";

export const PageB = () => {
  const [receivedMessage, setReceivedMessage] = useState<string>();

  useEffect(() => {
    const getPostMessage = (event: MessageEvent) => {
      if (event.origin === 'http://localhost:5173') {
        setReceivedMessage(event.data);
      }
    };

    window.addEventListener('message', getPostMessage);

    return () => {
      window.removeEventListener('message', getPostMessage);
    };
  }, []);

  return (
    <div>
      <h3>Page B</h3>
      <p>
        Received Data is: {receivedMessage}
      </p>
       
    </div>
  );
};

 

  • useEffect 내부에서 addEventListener를 통해 PostMessage를 처리할 로직을 등록한다.
  • 들어오는 이벤트는 MessageEvent 타입이다. 여기서 사용된 속성들은 아래와 같다.
    • origin 속성: 해당 메시지의 출처를 가리킨다. 위에서와 마찬가지로 로컬호스트로 지정되어있다.
    • data 속성: 메시지를 통해 보내진 데이터가 들어있다.
  • 이 때, PostMessage를 보낸 출처(Origin)를 확인하는 작업이 중요하다.
    • 위에서 언급했듯 PostMessage는 Cross-origin 통신이 가능하므로, 모든 출처에서 오는 데이터를 받을 수 있는 상황이라면 악용될 가능성이 있다. 따라서 origin을 확인하고, 허용할 출처에 한해서만 데이터를 받아 처리함으로써 안전성을 챙겨야 한다.
  • Origin을 확인했다면 데이터를 state에 저장하고, 화면상에 출력한다.

 

결과

Page B에서 데이터를 받아 화면 상에 띄우는 모습

 

  위에서 구현했듯 A에서 B 창을 띄우고 3초 후 PostMessage로 받은 메시지를 state에 저장한 후 띄우는 것을 확인할 수 있다.

 


  • Window 객체 사이에서 동작하기 때문에 다양한 상황에서 활용할 수 있을 것 같다.
    • 여기서는 새 창으로 했지만, 이후 요 때의 경험에 기반해서 페이지 내에 다른 페이지를 iframe으로 임베딩 한 후 PostMessage 를 추가했을 때도 작동하는 것을 확인할 수 있었다.