ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • iframe communication quick start
    JS & TS 2023. 2. 24. 23:44

    목차

    • 아이프레임이란 무엇인가요?
    • 아이프레임으로 무엇을 할 수 있을까요?
    • TODO
    • 전략
    • 실전
    • 결과 보기

    아이프레임이란 무엇인가요?

    An iframe (short for "inline frame") is an HTML element that allows you to embed another HTML document or webpage within your current webpage. It creates a frame or window within your webpage that can display content from a different source, such as a different website, another webpage on your own site, or even a separate part of the same webpage.

     

    아이프레임은 HTML 엘리먼트로 다른 HTML 다큐먼트 또는 웹페이지를 당신의 현재 웹페이지에 임베드할 수 있도록 만들어줍니다. 아이프레임은 프레임 또는 윈도우를 만들어 다른 웹사이트 또는 당신이 소유한 다른 웹사이트 심지어 같은 웹페이지의 다른 부분까지 화면에 표시할 수 있도록 해줍니다.

    아이프레임으로 무엇을 할 수 있을까요?

    Here are some things you can do with an iframe:

    1. Embedding content from other websites: You can use an iframe to embed content from another website, such as a video, map, or social media post, directly into your own webpage.
    2. Displaying a portion of another webpage: You can use an iframe to display a portion of another webpage, such as a specific section or article.
    3. Creating an inline image gallery: You can use an iframe to create an image gallery that displays a set of images within your webpage.
    4. Creating a website preview: You can use an iframe to create a website preview widget that allows users to preview a website before clicking on a link.
    5. Embedding external applications: You can use an iframe to embed external applications, such as a chat widget or a payment gateway, within your website.

    아이프레임으로 할 수 있는 일들이 있습니다

    1. 다른 웹사이트로부터의 컨텐츠를 임베딩할 수 있습니다
    2. 다른 웹페이지의 일부분을 표기하기
    3. 인라인 이미지 갤러리 만들기
    4. 웹사이트 프리뷰 만들기
    5. 외부 어플리케이션 임베딩하기

    TODO

    두 가지 웹사이트를 아이프레임을 통해 소통할 수 있도록 만들기

    1. localhost:3000 (부모 웹 사이트)
    2. localhost:3001 (자식 웹 사이트)

    전략

    1. 부모 웹 사이트(3000)에서 자식 웹 사이트(3001)를 아이프레임을 통해 임베딩합니다
    2. 부모 웹 사이트(3000)에서 자식 웹 사이트(3001)에 특정 함수를 통해 값 또는 함수를 전달합니다
    3. 자식 웹 사이트(3001)에서 부모(3000)의 메시지를 받아 상태를 업데이트합니다
    4. 자식 웹 사이트(3001)에서의 업데이트된 상태 값을 다시 부모(3000)에게 전달합니다
    5. 부모 웹 사이트(3000)에서 자식 웹 사이트(3001)로부터 온 상태 값을 화면에 렌더링 합니다

    부모가 자식에게 값 전달하기

    부모 웹 사이트가 자식 웹사이트에게 아이프레임을 통해 값을 전달하기 위해서 우리는 postMessage라는 메서드를 사용해야 합니다.

    const iframe = document.getElementById('my-iframe');
    iframe.contentWindow.postMessage('Hello from parent!', '*');

    자식에서 부모의 값 받기

    아이프레임은 부모로부터 전달된 postMessage를 window.addEvenListener(’message’) 이벤트를 통해 전달받을 수 있습니다.

    window.addEventListener('message', function(event:  MessageEvent) {
      if (event.origin !== '<https://parentpage.com>') return; 
      console.log('Received message in iframe:', event.data);
    });
    

    실전

    아이프레임을 통해 값을 전달하더라도 리액트는 리렌더링이 발생하지 않습니다. 리렌더링을 하기 위해서는 상태 값을 변경해줘야 합니다. 이를 확인하기 위해 직접 코드를 구현해 보도록 하겠습니다.

    (A) child component - iframe embeded (localhost:3001/test)

    $ npx create-next-app child --typescript
    
    import React, { useEffect, useState } from 'react';
    
    const Test = () => {
      const [counter, setCounter] = useState(0);
    
      useEffect(() => {
        window.addEventListener('message', handleMessage);
        return () => {
          window.removeEventListener('message', handleMessage);
        };
      }, []);
    
      const handleMessage = (event) => {
        if (event.data.type === 'plusCounter') {
          setCounter((prev) => prev + event.data.payload);
        } else if (event.data.type === 'minusCounter') {
          setCounter((prev) => prev - event.data.payload);
        }
      };
    
      const doSomething = () => {
        window.parent.postMessage(counter, '*');
      };
    
      return (
        <>
          <span style={{ marginRight: 10 }}>Counter: {counter}</span>
    
          <button type='button' onClick={doSomething}>
            submit current state
          </button>
        </>
      );
    };
    
    export default Test;
    
    1. useState를 통해 counter, setCounter를 초기화합니다
    2. useEffect 함수를 통해 처음 마운트될 때 ‘message’ 이벤트를 읽을 수 있도록 구독처리합니다
    3. handleMessage는 ‘message’ 이벤트가 발생했을 때 호출할 함수입니다
    4. 부모 컴포넌트에서 event.data를 값으로 간단히 보내는 대신 객체로 보내 type에 따라 counter를 업데이트할 수 있도록 만듭니다
    5. type은 총 두 가지입니다
      1. updateCounter - 이전 counter 변수에 event.data.payload를 더합니다
      2. minusCounter - 이전 counter 변수에 event.data.payload를 뺍니다
    6. 버튼을 클릭할 경우 doSomething 함수를 호출합니다
      1. doSomething 함수는 저장하고 있는 최신의 counter state를 부모에게 전달하는 역할을 합니다
      2. 부모 컴포넌트에게 메시지를 전달하는 방법은 window.parent.postMessage() 메서드를 사용합니다
    targetWindow.postMessage(message, targetOrigin, [transfer]);
    

    targetWindow

     

    메세지를 전달받을 window의 참조. 참조를 취득할 방법으로는 다음과 같은 것이 있습니다:

     

    Window.open (새 창을 만들고 새 창을 참조할 때)

    Window.opener (새 창을 만든 window를 참조할 때)

    HTMLIFrameElement.contentWindow (부모 window에서 임베디드 된 <iframe>을 참조할 때)

    Window.parent (임베디드 된 <iframe>에서 부모 window를 참조할 때)

    (B) parent component that use ifrmae (localhost:3000)

    $ npx create-next-app parent --typescript
    
    import React, { useCallback, useEffect, useRef, useState } from 'react';
    
    const Parent = () => {
      const [parentCounter, setParentCounter] = useState(0);
      const iframeRef = useRef<HTMLIFrameElement>(null);
    
      const receiveMessage = useCallback((event: MessageEvent<any>) => {
        if (!event.origin.includes('<http://localhost:3001>')) return;
    
        setParentCounter(event.data);
      }, []);
    
      const handleButtonClick = () => {
        if (!iframeRef.current || !iframeRef.current.contentWindow) return;
    
        const message = {
          type: 'plusCounter',
          payload: 1,
        };
        iframeRef.current.contentWindow.postMessage(message, '*');
      };
    
      const handleMinusButtonClick = () => {
        if (!iframeRef.current || !iframeRef.current.contentWindow) return;
    
        const message = {
          type: 'minusCounter',
          payload: 1,
        };
    
        iframeRef.current.contentWindow.postMessage(message, '*');
      };
    
      useEffect(() => {
        window.addEventListener('message', receiveMessage, false);
    
        return () => {
          window.removeEventListener('message', receiveMessage, false);
        };
      }, [receiveMessage]);
    
      return (
        <div>
          <button type='button' onClick={handleButtonClick}>
            +
          </button>
    
          <button type='button' onClick={handleMinusButtonClick}>
            -
          </button>
    
          <h1>Editor</h1>
          <iframe
            ref={iframeRef}
            src='<http://localhost:3001/test>'
            style={{
              margin: 0,
              border: '2px solid',
              width: '100%',
              height: '80vh',
              boxSizing: 'border-box',
            }}
          />
    
          {parentCounter ? <span>Parent counter is {parentCounter}</span> : null}
        </div>
      );
    };
    
    export default Parent;
    
    1. 자식에게 전달받은 counter 상태값을 저장할 useState를 초기화합니다 (parentCounter)
    2. 아이프레임의 DOM에 직접 접근하기 위해 useRef를 초기화합니다 (iframeRef)
    3. 자식으로부터 message를 넘겨받을 때 호출할 함수를 정의합니다 (receiveMessage)
    4. 부모에서 자식으로 보낼 postMessage를 정의합니다 (handleButtonClick, handleMinusButtonClick)
    5. 마운트 이후 message 이벤트를 받을 수 있도록 useEffect 내부에 window.addEventListener를 선언합니다
    6. 각 버튼에 선언한 버튼을 연결합니다 (+), (-)
    7. 아이프레임에 useRef 및 자식 컴포넌트인 자식 페이지를 연동합니다
    8. parentCounter 가 falsy 값이 아닐 경우, 화면에 렌더링 합니다 (parentCounter)

    결과 보기

    댓글

Designed by Tistory.