크롬 확장 프로그램 (웹뷰)

김병훈·2024년 7월 18일
0

포트폴리오

목록 보기
4/4

💡 아웃바운드를 위하여 후보자를 외부 사이트(ex. 링크드인)를 통해 소싱하려고 할 때, 위하이어 사이트와 소싱을 위한 외부 사이트를 번갈아가며 작업하는 것이 번거롭기에 이를 해결하기 위해 확장 프로그램을 개발하였습니다.

요약

  • iframe을 통해, 확장 프로그램을 웹뷰로써 동작하도록 합니다.
    • window의 message 이벤트를 통해 상호작용합니다.
  • 확장 프로그램의 유지보수를 위해, 타입스크립트로 전환하였습니다.

세부 내용

  • iframe을 통해, 확장 프로그램을 웹뷰로써 동작하도록 합니다.
    확장 프로그램의 재배포 과정을 거치지 않고, 웹 서비스의 업데이트를 통해 유저에게 최신 버전의 서비스를 제공할 수 있습니다.

    function createIframe(
      id: string,
      url: string,
      styles: Record<string, string>
    ): HTMLIframeElement {
      const iframe = document.createElement('iframe');
      iframe.id = id;
      iframe.setAttribute('src', url);
      Object.assign(iframe.style, styles);
      return iframe;
    }
    
    function insertIframe() {
      const wehireIframe = createIframe(
        IFRAME_ID,
        WEHIRE_URL,
        IFRAME_STYLES
      );
      wehireIframe.className = 'wehire-iframe';
      document.body.prepend(wehireIframe);
      // ...
    }
  • window의 message 이벤트를 통해 상호작용합니다.

    • 웹뷰의 picker 버튼을 클릭했을 때, 클라이언트 브라우저에 마우스 이벤트를 등록합니다.

      // 웹뷰
      <button onClick={() => {
        window.parent.window.postMessage({
          type: 'activatePicker',
          pickerType: 'name',
        }, "*");
      }}>
        Name Picker
      </button>
      // 확장 프로그램
      let pickerType = null;
      
      const onMessage = (
        event: MessageEvent<IframeEventMessage>
      ) => {
        const eventType = event.data.type;
      
        switch (eventType) {
          case 'activatePicker': {
            pickerType = event.data.pickerType;
            addMouseEvent();
          }
      
          case 'autoScraping': {
            // ...
          }
        }
      };
      
      window.addEventListener('message', onMessage);
  • 유지보수를 위해 스택을 자바스크립트에서 타입스크립트로 전환하고, 빌드를 위해 웹팩을 사용했습니다. (웹팩 설정)

    const path = require('path');
    const { CleanWebpackPlugin } =
          require('clean-webpack-plugin');
    const TerserPlugin =
          require('terser-webpack-plugin');
    
    module.exports = {
      entry: {
        background: './src/background.ts',
        script: './src/script.ts',
      },
      module: {
        rules: [
          {
            test: /\.tsx?$/,
            use: 'ts-loader',
            exclude: /node_modules/,
          },
        ],
      },
      resolve: {
        extensions: ['.tsx', '.ts', '.js'],
      },
      output: {
        filename: '[name].js',
        path: path.resolve(__dirname, 'dist'),
        publicPath: '/',
      },
      plugins: [
        // 빌드할 때마다 dist 폴더를 정리
        new CleanWebpackPlugin(),
      ],
      // 디버깅을 위하여, 소스맵 추가
      devtool: 'source-map',
      optimization: {
        minimize: true,
        minimizer: [
          // 코드 압축을 위해 TerserPlugin 추가
          new TerserPlugin({
            terserOptions: {
              compress: {
                drop_console: true,
              },
            },
          }),
        ],
      },
    };  

추가 내용

Window.postMessage()

  targetWindow.postMessage(message, targetOrigin, [transfer]);

주의사항

  • 메시지 출처 검증 및 targetOrigin 명시

    메시지를 수신할 때, 반드시 메시지의 출처(origin)를 검증해야 합니다. 이를 통해 신뢰할 수 없는 도메인에서 전송된 메시지를 처리하지 않도록 할 수 있습니다.

    메시지를 전송할 때는 postMessage의 두 번째 인자로 targetOrigin을 명시하여, 메시지가 전송될 대상의 출처를 지정합니다.
    targetOrigin의 정보와 targetWindow의 정보가 맞지 않다면 이벤트는 전송되지 않습니다.

    // 확장 프로그램
    const TARGET_ORIGIN = 'https://wehire.kr';
    
    // 메시지 수신 (메시지 출처 검증)
    window.addEventListener('message', (event) => {
      // 보안 검증: 메시지의 출처(origin) 확인하기
      if (event.origin !== TARGET_ORIGIN) {
        // 신뢰할 수 없는 출처에서 온 메시지를 무시합니다.
        return;
      }
    });
    
    // 메시지 발송 (targetOrigin 명시)
    function sendMessageToIframe(message) {
      iframe.contentWindow.postMessage(message, TARGET_ORIGIN);
    }
    // 웹뷰
    const TARGET_ORIGIN = 'chrome-extension://<EXTENSION_ID>'
    // 메시지 수신
    window.addEventListener('message', (event) => {
      if (event.origin !== TARGET_ORIGIN) {
        return;
      }
    });
    
    // 메시지 발송
    function sendMessageToExtension(message) {
      window.parent.window.postMessage(message, TARGET_ORIGIN);
    };
profile
재밌는 걸 만드는 것을 좋아하는 메이커

0개의 댓글