"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 는 센서.