C.W.K.
Stream
Lesson 01 of 06 · published

Content script 가 뭐고 왜 있는 거?

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

Level 0Extension 입덕
0 XP0/54 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 은 거실 둘 — service worker (event-driven background) 와 popup (잠깐 떴다 사라지는 toolbar UI). 둘 다 extension-owned. 둘 다 github.com 의 DOM, user 가 Wikipedia 에서 선택한 문단, Hacker News 에서 hover 중인 버튼에 직접 access 못 함.

그 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.

Page-load 시점에 DOM 에 물리적으로 존재 해야 가능한 모든 게 여기 떨어져. 다른 context (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 통해서만 대화.

이 isolation 이 content script 를 임의의 untrusted page 에서 안전하게 쓸 수 있게 해 주는 핵심. Page 가 스크립트 함수를 재정의 못 함. 스크립트가 page global 을 실수로 짓밟지 못 함. 경계가 일부러 날카로워.

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

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

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