콘텐츠로 이동

빌드와 패키징

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

graph LR
src["src/: FSD 슬라이스"]
manifest[manifest.config.ts]
vite[vite + @crxjs/vite-plugin]
dist["dist/: 로드 가능한 확장"]
zip[packages/extension.zip]
src --> vite
manifest --> vite
vite --> dist
dist --> zip

src/의 React/TS 코드와 manifest.config.ts가 함께 vite로 들어가고, pnpm run build가 dist/를 만들어 냅니다. pnpm run package는 그 dist를 zip으로 묶을 뿐입니다.

저장소 루트의 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.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 한 곳만 손대면 끝납니다.

Terminal window
pnpm run dev

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

  • popup, options: 일반 vite HMR을 씁니다. 저장하면 즉시 반영됩니다
  • content_script: 새 빌드가 만들어지면 chrome이 자동으로 reinject 합니다. 페이지 새로고침은 필요할 수 있습니다
  • background service worker: 코드가 바뀌면 worker 자체가 종료되고 다시 시작됩니다

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

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

pnpm run build는 dist/에 운영 환경용 빌드를 만들어 냅니다. manifest.json도 같이 생성됩니다. 이 상태로 chrome의 “Load unpacked”로 곧장 띄울 수 있습니다.

pnpm run package는 그 dist를 zip으로 묶어 packages/extension.zip 같은 경로에 만들어 냅니다. zip 안의 구조는 dist와 동일합니다.

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

pnpm run package로 zip이 만들어진 시점이 base 자동화의 끝입니다. 그 다음에 누구에게 보낼지, 어디에 호스팅할지, .pem 키를 어디에 둘지, Chrome Web Store에 등록할지는 도구가 관여하지 않는 영역입니다. build-and-package 스킬이 zip과 함께 만드는 DISTRIBUTION.md도 결정용 메모일 뿐, 자동으로 누군가에게 보내거나 어딘가로 올리지 않습니다.

이 경계는 의도한 것입니다. 키 파일과 스토어 콘솔과 사내 정책 서버는 사용자 자격으로 접근해야 하는 자산이고, 자동화가 그 선을 넘으면 키 유출과 검수 사고가 뒤따릅니다. 다음 절에서 세 가지 배포 경로의 장단점과 키 관리 절차를 봅니다.

다음 섹션은 distribution입니다. zip 직접 배포, Web Store 업로드, self-host .crx와 update_url 세 가지의 장단점, .pem 키 관리, 그리고 chrome이 외부 .crx를 차단하는 것을 어떻게 우회하는지를 봅니다.