크롬 익스텐션을 개발을 할때 background, popup, content script 간의 메시지 통신을 다루는 것은 필수적이다. 그런데 팝업과 사이드 패널이 아닌 웹페이지 안에 내 익스텐션의 창을 띄우고 싶다면 두 가지 방법이 있을것이다.
첫번째는 HTML 코드를 Inject하는 것이고 두번째는 iframe을 inject 하는 것이다.
그래서 나도 처음에는 HTML 코드를 inject하고 익스텐션의 기능을 구현하기 위해 chrome api와 통신을 시도했다.
하지만 addEventListener를 붙이거나 메시지를 보내도 아무런 반응이 없었다. 그래서 이를 해결하기 위해 여러 크롬 익스텐션과 Stack Overflow를 찾아보며 iframe 방식으로 해결할 수 있다는 것을 알게되었다. 그 과정에서 알게된 두 경우의 구조적 차이를 정리해보겠다.

1. 크롬 익스텐션의 구조와 메시지 통신 방식

크롬 익스텐션은 기본적으로 여러 컴포넌트로 구성된다.
  • background script: 확장 프로그램의 백그라운드에서 항상 동작
  • popup script: 브라우저 상단 확장 아이콘 클릭 시 나타나는 UI
  • content script: 웹페이지에 삽입되어 DOM을 조작하거나 이벤트를 탐지
  • injected script / HTML: content script를 통해 페이지 내부에 삽입되는 요소
이 컴포넌트들 간에는 chrome.runtime.sendMessage, chrome.runtime.onMessage.addListener, 또는 window.postMessage와 같은 API를 통해 메시지를 주고받는다.
chrome API message

2. Inject한 HTML 요소는 통신이 안되는 이유

content script에서 일반적으로 다음과 같이 DOM 요소를 삽입할 수 있다:
const div = document.createElement("div");
div.id = "html-method";
document.body.appendChild(div);
이렇게 삽입된 요소는 단순한 HTML이다. 별도의 자바스크립트 컨텍스트가 없고, 메시지를 받을 수 있는 windowaddEventListener를 가질 수 없다. 실제로 메시지를 보내려 해도 받을 수 있는 리스너가 존재하지 않는다.
html

3. iframe은 메시지 통신이 가능한 이유

반면 iframe은 다음과 같은 코드로 삽입된다:
const iframe = document.createElement("iframe");
iframe.src = chrome.runtime.getURL("iframe.html");
iframe.style = "position:fixed; bottom:0; right:0; width:300px; height:200px;";
document.body.appendChild(iframe);
iframe은 삽입되더라도 자체적인 window, document, 실행 컨텍스트를 가지므로 window.postMessage를 이용해 메시지를 주고받을 수 있다**.** 예를 들어:
iframe.contentWindow.postMessage({ type: "FROM_CONTENT" }, "*");
그리고 iframe 내부에서는 다음과 같이 메시지를 받을 수 있다:
window.addEventListener("message", (event) => {
  if (event.data.type === "FROM_CONTENT") {
    console.log("메시지 수신:", event.data);
  }
});
iframe

4. 실제 코드 예시 비교

✅ 1) div 삽입 방식

const div = document.createElement("div");
div.innerText = "Hello";
document.body.appendChild(div);

// 메시지 청취 불가 (window 없음)
div.addEventListener("message", (e) => {
  console.log(e.data); // 작동하지 않음
});

✅ 2) iframe 삽입 방식

const iframe = document.createElement("iframe");
iframe.src = chrome.runtime.getURL("iframe.html");
document.body.appendChild(iframe);

// 메시지 전송
iframe.contentWindow.postMessage({ type: "ping" }, "*");
iframe.html 내부:
<script>
  window.addEventListener("message", (e) => {
    console.log("수신됨:", e.data);
  });
</script>
코드 비교

5. 정리: 어떤 상황에서 iframe이 유리한가?

iframe은 다음과 같은 경우에 유리하다:
  • 독립된 UI 환경을 구성하고 싶을 때
  • content script와 완전히 분리된 컨텍스트에서 자바스크립트 로직을 실행하고 싶을 때
  • window.postMessage를 이용해 간편한 메시지 통신이 필요할 때
  • 익스텐션 내부 리소스를 보안상 안전하게 로드해야 할 때

6. 결론

단순히 HTML을 DOM에 삽입하는 것과 iframe을 삽입하는 것에는 본질적인 실행 환경의 차이가 있다. iframe은 자체적인 실행 컨텍스트를 갖기 때문에 메시지 수신이 가능하지만, 일반 HTML은 그렇지 않다.
  • inject된 HTML 요소: 메시지 통신 불가 (실행 컨텍스트 없음)
  • inject된 iframe: 메시지 통신 가능 (postMessage 활용)
실제 익스텐션
여러 시도 끝에 익스텐션 개발을 시작할 수가 있게되었다 완성할때까지 화이팅!