본문 바로가기
C.W.K.
Stream
Lesson 01 of 06 · published

Content script가 뭐고 왜 있는 거?

~10 min · content-script, isolated-world, dom, concepts

Level 0Extension 입덕
0 XP0/56 lessons0/13 achievements
0/100 XP to next level100 XP to go0% complete
"Service worker는 browser 안에 살고. Popup은 toolbar에 살고. Content script는 ClipDeck의 조각 중 user가 읽고 있는 페이지 안에 실제로 사는 유일한 거. Track 3가 그 방에 걸어 들어가 불 켜."

세 개의 거실

Track 2를 마칠 무렵 ClipDeck이 사는 방은 둘이었어. event를 기다리는 service worker와, 잠깐 떴다 사라지는 toolbar popup. 둘 다 extension이 소유한 자리지. 그리고 둘 다 github.com의 DOM 에도, user가 Wikipedia에서 긁어 놓은 문단에도, Hacker News에서 마우스를 올려 둔 버튼에도 손이 안 닿아.

그 access는 세 번째 방에만 있어: content script. host tab 안에서 page 자체 JavaScript와 나란히 도는, DOM read/write 권한 풀로 가진 context. Chrome extension이 자기 소유 아닌 페이지 안으로 손을 뻗는 방법.

뭘 할 수 있어?

실용 예 — content script가 가능케 하는 것들:

  • User의 선택 텍스트 read (ClipDeck의 Track 3 milestone).
  • 모든 페이지에 떠 있는 버튼이나 sidebar inject.
  • 요소 restyle — 다크 모드 없는 사이트에 다크 모드, 광고 hide, 쿠키 배너 접기.
  • 페이지 단의 click / hover / 키보드 단축키 listen.
  • 구조화 데이터 (가격표, 본문) scrape, SW로 보내 storage에 저장.
  • Form submit 전 수정 — autofill / validation / sanitization.

페이지가 뜰 때 DOM 안에 실제로 있어야 되는 일은 전부 여기 몫이야. SW도, popup도, side panel도 이걸 대신해 줄 수 없어.

Isolation 거래

Content script가 공짜는 아냐 — DOM access의 대가를 치러. 'isolated world' (Lesson 3에서 다룸) 안에 살아: 같은 DOM, page 자체와는 분리된 JavaScript heap. Host page가 정의한 window.myLib가 content script에 invisible. 스크립트의 console.log는 content-script console로 가지, page console로 안 감. 두 world가 DOM event와 message channel 통해서만 대화.

이 격리가 있어서 content script를 믿을 수 없는 아무 페이지에나 넣어도 괜찮은 거야. 페이지가 우리 함수를 몰래 바꿔치기할 수 없고, 우리 스크립트가 페이지의 전역을 실수로 뭉개지도 못해. 이 경계는 일부러 날카롭게 그어 놓은 거고.

Storage까지 닿는 법

Content script는 chrome.* API의 subset 가짐 — service worker와 대화하기엔 충분하지만 self-contained extension이 되기엔 부족. 가능한 것들:

  • 메시지 보내기: chrome.runtime.sendMessage.
  • 메시지 받기: chrome.runtime.onMessage.
  • Extension storage read/write: chrome.storage.local (SW가 read 하는 같은 storage).

등록 불가: chrome.tabs.onUpdated, 새 탭 열기, 대부분의 extension-control API. 그건 SW 영역. 실용 ClipDeck 패턴: content script가 DOM (selection, page metadata) read, package, message로 SW에 보내고, SW가 storage에 write. Storage의 system-of-record를 만지는 유일한 context는 SW. content script는 센서.

Service worker는 두뇌 (event, storage, lifecycle). Popup은 얼굴 (user interaction). Content script는 페이지 안의 손 (DOM access). 세 context, 세 책임, 한 extension.
Side panel은 뭐야? Track 4. Side panel은 popup의 큰 형제 — 같은 권한, tooltip 대신 영구 layout. Content script는 여전히 손. Side panel은 손이 잡은 걸 표시할 공간만 더 줘.

Code

content.js — DOM read 후 message로 SW에 ship·javascript
// content.js — minimum-viable content script
// Host tab 안에서 돌고; DOM access 가짐.
console.log("[ClipDeck content] hi from", location.href);

// Page 안에서 page title read
const pageTitle = document.title;
console.log("[ClipDeck content] title:", pageTitle);

// 본 걸 message 로 SW 에 보냄
chrome.runtime.sendMessage({
  type: "contentScriptHello",
  url: location.href,
  title: pageTitle,
});
manifest.json — 모든 URL에서 도는 content script 선언·json
{
  "manifest_version": 3,
  "name": "ClipDeck",
  "version": "0.4.0",
  "action": { "default_popup": "popup.html" },
  "background": { "service_worker": "background.js" },
  "permissions": ["storage", "tabs"],
  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["content.js"],
      "run_at": "document_idle"
    }
  ],
  "icons": { "16": "icons/16.png", "48": "icons/48.png", "128": "icons/128.png" }
}

External links

Exercise

첫 번째 code block의 clipdeck/content.js 생성. clipdeck/manifest.json을 두 번째 code block으로 update (version 0.4.0으로 bump, content_scripts entry 추가). chrome://extensions에서 extension reload. 실제 web page 아무거나 (wikipedia.org article 추천) 열기. Page DevTools (우클릭 → Inspect → Console) 열기. Console panel 좌상단 dropdown에서 context를 'top' 에서 'ClipDeck' 으로 전환 — [ClipDeck content] 로그가 거기 보여야 함, 'top' 에는 안 보임. 그 dropdown이 content-script 디버깅에서 가장 중요한 도구 하나.
Hint
로그가 아예 안 보이면 content.js가 실제로 load 됐는지 확인 — chrome://extensions → ClipDeck → 'Errors' 가 syntax error 알려 줘. <all_urls> match pattern은 chrome://, chrome-extension://, 새 탭 페이지엔 매칭 안 됨. 실제 http/https URL로 이동. Console dropdown에 'top' 만 보이고 'ClipDeck' 옵션 안 보이면 content script가 inject 안 된 거 — 갓 수정한 manifest에서 가장 흔한 원인은 extension reload 잊은 거. 'next page load에서 알아서 잡힐 거야' 가 아냐 — 'chrome://extensions에서 reload 버튼 먼저 클릭' 이야.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.