본문 바로가기
개발/React

리액트 - 크롬 확장 프로그램에서 현재 페이지의 URL과 HTML 가져오기 (Retrieve URL and HTML of the Current Page in a Chrome Extension)

by 피로물든딸기 2025. 1. 10.
반응형

리액트 전체 링크

 

참고

- 크롬 확장 프로그램 사이드 패널 만들기 (React Chrome Extension Side Panel Example)

 

이전 글에 이어 버튼을 클릭할 경우, 다음과 같이 현재 페이지의 URLHTML을 출력해 보자.


manifest.json 수정

 

크롬 확장 프로그램이 특정 페이지에서 동작하도록 권한을 추가한다. 

아래 옵션은 모든 URL에 접근이 가능하다는 뜻이 된다.

"host_permissions": ["<all_urls>"]

 

그리고 permissions에서 activeTab을 추가한다.

  "permissions": ["storage", "scripting", "tabs", "sidePanel", "notifications", "activeTab"],

eslintrc 파일 추가

 

chrome API를 사용하기 위해 루트 폴더에 .eslintrc.js을 추가한다.

 

.eslintrc.js는 다음과 같다.

module.exports = {
  env: {
    browser: true, // 브라우저 환경에서 사용 가능한 전역 객체 (window, document 등) 허용
    webextensions: true, // chrome을 global 객체로 설정
    es2021: true, // ES2021 문법 지원
  },
  parserOptions: {
    ecmaVersion: 12, // 최신 ECMAScript 기능 지원
    sourceType: "module", // ES6 모듈 지원
    ecmaFeatures: {
      jsx: true, // JSX 문법 지원
    },
  },
};

 

크롬 확장 프로그램 개발 중 chrome API를 호출하는 경우, Strict Mode에 의해 문제가 발생할 수 있다.

따라서 index.js에서 React.StrictMode를 제거한다.

import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

background.js 수정 + contentScript.js 추가

 

backgroud.js에서 contentScript.js를 실행하는 코드를 추가한다.

chrome.action.onClicked.addListener((tab) => {
  if (tab.id) {
    chrome.scripting.executeScript({
      target: { tabId: tab.id },
      files: ["contentScript.js"],
    });
  }
});

 

public/contentScript.js는 다음과 같다.

(() => {
  console.log("start content script!!");
  const pageUrl = window.location.href;
  const pageHtml = document.documentElement.outerHTML;

  console.log("Page URL:", pageUrl);
  console.log("Page HTML:", pageHtml);

  // HTML과 URL을 백그라운드로 전달
  chrome.runtime.sendMessage({
    type: "PAGE_INFO",
    url: pageUrl,
    html: pageHtml,
  });
})();

URL과 HTML 가져오기

 

이제 다음 함수를 이용해서 URL HTML을 다음과 같이 가져올 수 있다.

실제 실행 코드는 전체 코드의 App.js를 참고하자.

  const getPageInfo = async () => {
    return new Promise((resolve, reject) => {
      // 현재 활성 탭 정보 가져오기
      chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
        if (tabs[0]?.id) {
          // content script 실행
          chrome.scripting.executeScript(
            {
              target: { tabId: tabs[0].id },
              files: ["contentScript.js"],
            },
            () => {
              console.log("Content script executed!");
            }
          );
        }
      });

      // content script에서 메시지 수신
      chrome.runtime.onMessage.addListener((message) => {
        console.log("here", message);
        if (message.type === "PAGE_INFO") {
          console.log("Page URL:", message.url);
          console.log("Page HTML:", message.html);

          resolve({
            url: message.url,
            html: message.html,
          });
        }
      });
    });
  };

 

참고로 확장 프로그램 콘솔 로그확장 프로그램에서 마우스 오른쪽 버튼 → 검사 탭에서 확인할 수 있다.


전체 코드는 다음과 같다.

 

App.js

import React from "react";

// import "./App.css";

function App() {
  const getPageInfo = async () => {
    return new Promise((resolve, reject) => {
      // 현재 활성 탭 정보 가져오기
      chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
        if (tabs[0]?.id) {
          // content script 실행
          chrome.scripting.executeScript(
            {
              target: { tabId: tabs[0].id },
              files: ["contentScript.js"],
            },
            () => {
              console.log("Content script executed!");
            }
          );
        }
      });

      // content script에서 메시지 수신
      chrome.runtime.onMessage.addListener((message) => {
        if (message.type === "PAGE_INFO") {
          resolve({
            url: message.url,
            html: message.html,
          });
        }
      });
    });
  };

  const handleButtonClick = async () => {
    try {
      const result = await getPageInfo();
      console.log("Received URL:", result.url);
      console.log("Received HTML:", result.html);
    } catch (error) {
      console.error("Error:", error);
    }
  };

  return (
    <div>
      <div className="App">
        <h1>Get Tab Info</h1>
        <button onClick={handleButtonClick}>URL & HTML</button>
      </div>
    </div>
  );
}

export default App;

 

index.js

import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

 

.eslintrc.js

module.exports = {
  env: {
    browser: true, // 브라우저 환경에서 사용 가능한 전역 객체 (window, document 등) 허용
    webextensions: true, // chrome을 global 객체로 설정
    es2021: true, // ES2021 문법 지원
  },
  parserOptions: {
    ecmaVersion: 12, // 최신 ECMAScript 기능 지원
    sourceType: "module", // ES6 모듈 지원
    ecmaFeatures: {
      jsx: true, // JSX 문법 지원
    },
  },
};

 

public/background.js

// 확장 프로그램 클릭 시, 사이드 패널 Open
chrome.sidePanel
  .setPanelBehavior({ openPanelOnActionClick: true })
  .catch((error) => console.error(error));

// 설치 시 알림 및 초기 설정
chrome.runtime.onInstalled.addListener(() => {
  console.log("Extension installed.");

  // 알림 생성
  chrome.notifications.create(
    "welcome-notification",
    {
      type: "basic",
      iconUrl: "icon.png",
      title: "Welcome!",
      message: "Click the extension icon to open the side panel.",
    },
    () => {
      if (chrome.runtime.lastError) {
        console.error("Notification error:", chrome.runtime.lastError.message);
      }
    }
  );
});

chrome.action.onClicked.addListener((tab) => {
  if (tab.id) {
    chrome.scripting.executeScript({
      target: { tabId: tab.id },
      files: ["contentScript.js"],
    });
  }
});

 

public/contentScript.js

(() => {
  console.log("start content script!!");
  const pageUrl = window.location.href;
  const pageHtml = document.documentElement.outerHTML;

  console.log("Page URL:", pageUrl);
  console.log("Page HTML:", pageHtml);

  // HTML과 URL을 백그라운드로 전달
  chrome.runtime.sendMessage({
    type: "PAGE_INFO",
    url: pageUrl,
    html: pageHtml,
  });
})();

 

public/manifest.json

{
  "manifest_version": 3,
  "name": "React Side Panel Extension",
  "version": "1.0",
  "description": "React app in Chrome Side Panel",
  "permissions": ["storage", "scripting", "tabs", "sidePanel", "notifications", "activeTab"],
  "host_permissions": ["<all_urls>"],
  "action": {
    "default_title": "Click to open panel"
  },
  "side_panel": {
    "default_path": "index.html"
  },
  "background": {
    "service_worker": "background.js"
  },
  "web_accessible_resources": [
    {
      "resources": [
        "index.html",
        "static/*"
      ],
      "matches": [
        "<all_urls>"
      ]
    }
  ]
}
반응형