본문으로 건너뛰기

빌드와 패키징

manifest.config.ts가 어떻게 manifest.json을 생성하는지, Vite와 @crxjs/vite-plugin이 dev에서 어떻게 변경 감지를 도는지, pnpm run buildpnpm run package가 각각 무엇을 떨구는지를 본다. architecture에서 잡은 FSD 트리가 여기서 manifest와 만나 chrome이 로드 가능한 산출물이 된다.

파이프라인 한 장

src/의 React/TS 코드와 manifest.config.ts가 함께 vite로 들어가고, pnpm run builddist/를 떨군다. pnpm run package는 그 dist를 zip으로 묶을 뿐이다.

manifest.config.ts

저장소 루트의 manifest.config.ts는 TypeScript 객체를 export 한다. crxjs가 제공하는 defineManifest로 감싸면 빌드 시 manifest.json으로 직렬화된다.

import { defineManifest } from '@crxjs/vite-plugin'

export default defineManifest({
manifest_version: 3,
name: '__MSG_extName__',
version: '0.1.0',
permissions: ['storage', 'activeTab'],
host_permissions: [],
action: { default_popup: 'src/app/popup/index.html' },
options_page: 'src/app/options/index.html',
background: { service_worker: 'src/app/background/index.ts' },
content_scripts: [
{ matches: ['https://*.example.com/*'], js: ['src/app/content/index.ts'] },
],
})

손볼 자리는 permissions, host_permissions, content_scripts.matches, action.default_popup, options_page, side_panel.default_path 정도. 권한을 손댈 때는 design-manifest 스킬을 통해 ADR 한 장과 같이 다듬는 편이 안전하다. 직접 손대도 동작은 한다. 그 자리가 왜 그 권한을 가졌는지가 한 달 뒤에 사라질 뿐이다.

Vite + @crxjs/vite-plugin

vite.config.ts에는 crx({ manifest })가 들어있다. plugin이 manifest를 읽어 entry point를 자동으로 결정한다. content_scripts, background.service_worker, action.default_popup, options_page가 모두 vite의 빌드 입력으로 잡힌다.

결과적으로 vite는 src의 React/TS 코드를 일반 웹 빌드처럼 컴파일하면서, 동시에 manifest가 가리키는 모든 진입점을 처리한다. 진입점 추가는 manifest 한 곳만 손대면 끝난다.

dev에서 변경 감지

pnpm run dev

vite dev server가 뜨고, crxjs HMR이 진입점별로 다른 전략을 쓴다.

  • popup, options: 일반 vite HMR. 저장하면 즉시 반영
  • content_script: 새 빌드가 떨어지면 chrome이 자동 reinject. 페이지 새로고침은 필요할 수 있음
  • background service worker: 코드가 바뀌면 worker 자체가 죽고 재기동

chrome://extensions의 "Reload" 버튼은 거의 누를 일이 없다. 권한을 새로 추가했거나 manifest 구조가 크게 바뀐 경우에만.

build와 package

pnpm run build # dist/ 에 production 빌드
pnpm run package # packages/extension.zip 으로 묶기

pnpm run builddist/에 production 빌드를 떨군다. manifest.json도 같이 생성된다. 이 상태로 chrome의 "Load unpacked"로 곧장 띄울 수 있다.

pnpm run package는 그 dist를 zip으로 묶어 packages/extension.zip 같은 자리에 떨군다. zip 안의 구조는 dist와 동일하다.

둘이 갈린 이유는 용도가 다르기 때문이다. build는 개발자가 자기 chrome에 띄울 때, package는 동료에게 zip을 건네거나 Web Store에 업로드할 때 쓴다.

다음

다음은 distribution. zip 직접 배포, Web Store 업로드, self-host .crxupdate_url 셋의 트레이드오프와, .pem 키 관리 그리고 chrome이 외부 .crx를 차단하는 자리를 어떻게 우회하는지를 본다.