[Chrome Extension 개발] 프레임워크 완벽 비교: WXT vs Plasmo vs CRXJS 그리고 더 많은 선택지
들어가며
Chrome 확장 프로그램을 vanilla JavaScript로 개발하다가 프레임워크의 필요성을 느끼셨나요? 저도 같은 경험을 했습니다. 수동 빌드, 반복적인 새로고침, 복잡한 설정 파일… 이 모든 것을 해결해주는 다양한 프레임워크가 있다는 걸 알게 되었죠.
저는 Plasmo로 시작해서 WXT로 마이그레이션하는 여정을 거쳤는데, 그 과정에서 알게 된 모든 프레임워크들을 비교 분석해보겠습니다.
1. Chrome Extension 프레임워크 전체 지형도
2025년 현재 사용 가능한 프레임워크들
mindmap
root((Chrome Extension<br/>빌드 도구))
프레임워크
WXT
Extension.js
Plasmo
Bedframe
Vite 플러그인
CRXJS
직접 구성
Vite
Rollup
Parcel
프레임워크 현황 한눈에 보기
| 도구 | 시작할 때 비교할 점 |
|---|---|
| WXT | 엔트리포인트 규칙으로 manifest를 생성하며 React·Vue 등과 함께 사용 |
| Plasmo | popup.tsx 같은 파일에서 UI를 시작 |
| CRXJS | Vite 프로젝트에 플러그인을 추가하고 manifest를 직접 작성 |
| Extension.js | CLI로 시작하고 manifest에 UI 진입점을 연결 |
| Bedframe | 도입 전 필요한 기능과 유지보수 상태를 저장소에서 확인 |
CRXJS는 종료 예정이라는 이전 설명과 달리 2.x 릴리스를 공개했습니다. Vite와의 호환 여부는 CRXJS 릴리스에서 사용할 버전으로 확인하세요.
2. 각 프레임워크 심층 분석
WXT - The Next Generation Framework
1
2
3
4
5
6
7
8
9
10
// wxt.config.ts
import { defineConfig } from 'wxt';
export default defineConfig({
srcDir: 'src',
modules: ['@wxt-dev/module-react'],
manifest: {
permissions: ['storage'],
},
});
장점:
- ✅ React, Vue, Svelte, Solid 등과 통합 가능
- ✅ 뛰어난 TypeScript 지원
- ✅ 활발한 개발과 커뮤니티
- ✅ Vite의 모든 장점 활용
- ✅ 멀티 브라우저 지원 (Chrome, Firefox, Safari, Edge)
단점:
- ❌ Plasmo보다 약간 복잡한 초기 설정
- ❌ 상대적으로 적은 예제 (빠르게 개선 중)
Plasmo - The Popular Choice
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// popup.tsx
import { useState } from "react"
function IndexPopup() {
const [data, setData] = useState("")
return (
<div>
<h1>
Welcome to your Plasmo Extension!
</h1>
</div>
)
}
export default IndexPopup
장점:
- ✅ 가장 큰 커뮤니티와 많은 예제
- ✅ React 개발자에게 친숙한 구조
- ✅ 빠른 시작 가능
- ✅ Plasmo Platform 통합
단점:
- ❌ 본문의 팝업 예제는 React 기준
- ❌ 유지보수 속도 저하 우려
- ❌ 커스터마이징 제한적
- ❌ 번들 크기가 큼
CRXJS - Vite 플러그인
Vite 설정에 플러그인을 추가하고 기존 manifest.json을 넘길 수 있습니다. 아래는 @crxjs/vite-plugin을 설치한 뒤 적용하는 최소 설정입니다. CRXJS 저장소
1
2
3
4
5
6
7
8
// vite.config.ts
import { defineConfig } from 'vite'
import { crx } from '@crxjs/vite-plugin'
import manifest from './manifest.json'
export default defineConfig({
plugins: [crx({ manifest })],
})
Extension.js - The Zero-Config Solution
1
2
3
npx extension@latest create my-extension
cd my-extension
npm run dev
장점:
- ✅ Zero-config 철학
- ✅ 빠른 시작
- ✅ 모던한 개발 경험
- ✅ HMR 지원
단점:
- ❌ 아직 초기 단계
- ❌ 커뮤니티가 작음
- ❌ 고급 기능 부족
Bedframe - The Experimental One
1
2
3
4
5
6
// Early access 단계
import { defineConfig } from 'bedframe';
export default defineConfig({
// 설정 진행 중
});
현재 상황:
- Vite 기반의 실험적 프레임워크
- Early Access 프로그램 운영 중
- 프로덕션 사용 비추천
3. 실전 비교: 같은 기능, 다른 구현
Popup 구현 비교
graph LR
A[Popup 개발] --> B{프레임워크}
B --> C[WXT]
B --> D[Plasmo]
B --> E[Extension.js]
C --> C1[src/entrypoints/popup/App.tsx]
D --> D1[popup.tsx]
E --> E1[pages/popup.tsx]
style C fill:#9f9,stroke:#333,stroke-width:2px
style D fill:#99f,stroke:#333,stroke-width:2px
style E fill:#f99,stroke:#333,stroke-width:2px
WXT 방식
srcDir: 'src'와 React 모듈을 설정한 프로젝트 기준입니다. 아래 두 TSX 파일과 같은 폴더의 index.html에 다음 요소를 넣습니다. WXT 팝업 진입점
1
2
<div id="root"></div>
<script type="module" src="./main.tsx"></script>
1
2
3
4
5
6
7
8
9
10
// src/entrypoints/popup/App.tsx
export default function App() {
return <div>WXT Popup</div>
}
// src/entrypoints/popup/main.tsx
import { createRoot } from 'react-dom/client'
import App from './App'
createRoot(document.getElementById('root')!).render(<App />)
Plasmo 방식
팝업은 브라우저 툴바에서 열립니다. 사이트에 콘텐츠 스크립트를 삽입하는 matches는 팝업에 필요하지 않습니다. Plasmo 팝업 문서
1
2
3
4
// popup.tsx
export default function Popup() {
return <div>Plasmo Popup</div>
}
Extension.js 방식
React 템플릿에서 아래 컴포넌트를 렌더링하는 HTML 파일을 만든 뒤, manifest의 action.default_popup을 그 HTML 경로에 연결합니다. Extension.js의 HTML 진입점 문서
1
2
3
4
// pages/popup.tsx
export default function Popup() {
return <div>Extension.js Popup</div>
}
Content Script UI 비교
| 기능 | WXT | Plasmo | Extension.js | CRXJS |
|---|---|---|---|---|
| Shadow DOM | ✅ 네이티브 지원 | ✅ getInlineAnchor | ⚠️ 수동 | ⚠️ 수동 |
| CSS 격리 | ✅ 자동 | ✅ 자동 | ❌ 수동 | ❌ 수동 |
| React Portal | ✅ 지원 | ✅ 내장 | ⚠️ 직접 구현 | ⚠️ 직접 구현 |
| HMR | ✅ 완벽 | ⚠️ 부분적 | ✅ 지원 | ✅ 지원 |
4. 성능 비교
빌드 시간 벤치마크
같은 Node.js 버전, 브라우저, 기능을 사용한 샘플에서 첫 프로덕션 빌드와 개발 중 파일 한 개를 바꿨을 때 반영되는 시간을 따로 재세요. 팝업 변경과 콘텐츠 스크립트 변경은 각각 새로고침이 필요한지 확인합니다.
번들 크기 비교 (동일한 React 앱)
각 도구의 프로덕션 산출물을 같은 압축 방식으로 비교합니다. 소스맵과 아이콘 같은 공통 파일의 포함 여부를 맞춰야 브라우저에 실제로 배포할 ZIP 크기를 비교할 수 있습니다.
5. 마이그레이션 가이드
Plasmo → WXT 마이그레이션
저도 이 경로로 마이그레이션했습니다. 주요 변경사항:
flowchart LR
A[Plasmo 프로젝트] --> B[구조 변경]
B --> C[설정 파일 생성]
C --> D[엔트리포인트 이동]
D --> E[API 호출 수정]
E --> F[WXT 프로젝트]
style A fill:#f96,stroke:#333,stroke-width:2px
style F fill:#9f9,stroke:#333,stroke-width:2px
1. 디렉토리 구조 변경
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Plasmo
├── popup.tsx
├── contents/
├── background.ts
# WXT
├── src/
│ └── entrypoints/
│ ├── popup/
│ │ ├── index.html
│ │ ├── main.tsx
│ │ └── App.tsx
│ ├── app.content.ts
│ └── background.ts
2. 설정 파일 추가
1
2
3
4
5
6
7
8
9
10
// wxt.config.ts
import { defineConfig } from 'wxt';
export default defineConfig({
srcDir: 'src',
modules: ['@wxt-dev/module-react'],
manifest: {
// Plasmo의 package.json manifest를 여기로
}
});
3. Storage API 마이그레이션
예제의 value는 저장할 값입니다. 기존 사용자의 설정을 유지하려면 Plasmo에서 사용하던 storage 영역과 실제 저장 키를 먼저 확인하세요. 새 키에 쓰는 것만으로 기존 데이터가 옮겨지지는 않습니다. WXT 저장소 문서
1
2
3
4
5
6
7
8
// Plasmo
import { Storage } from "@plasmohq/storage"
const storage = new Storage()
await storage.set("key", value)
// WXT
import { storage } from '#imports';
await storage.setItem('local:key', value)
실제 마이그레이션 사례: ChatGPT Writer
ChatGPT Writer 개발자도 Plasmo에서 WXT로 전환한 경험을 공유했습니다. 해당 확장 프로그램의 사례이므로 내 프로젝트에서는 위와 같은 조건으로 직접 비교하세요. 개발자의 마이그레이션 기록
6. 2025년 프레임워크 선택 가이드
상황별 추천
graph TD
A[프로젝트 시작] --> B{팀 상황은?}
B --> C[React 전문팀]
B --> D[다양한 프레임워크]
B --> E[빠른 프로토타입]
B --> F[기업 프로젝트]
C --> G[Plasmo 또는 WXT]
D --> H[WXT 강력 추천]
E --> I[Extension.js]
F --> J[WXT]
style H fill:#9f9,stroke:#333,stroke-width:4px
style J fill:#9f9,stroke:#333,stroke-width:4px
최종 추천 매트릭스
| 상황 | 1순위 | 2순위 | 피해야 할 것 |
|---|---|---|---|
| 새 프로젝트 | WXT | Extension.js | - |
| React 전용 | WXT | Plasmo | - |
| Vue/Svelte | WXT | Vite 수동 설정 | Plasmo |
| 빠른 프로토타입 | Extension.js | Plasmo | - |
| 대규모 앱 | WXT | - | Extension.js |
| 기존 Vite 프로젝트 | CRXJS | WXT | - |
7. 왜 최종적으로 WXT를 선택했나?
제가 Plasmo에서 WXT로 마이그레이션한 이유:
- 프레임워크 자유도: Vue 컴포넌트를 일부 사용하고 싶었는데 Plasmo는 React만 지원
- 번들 크기: 제가 옮긴 프로젝트에서 배포 파일 크기가 줄어듦
- TypeScript 지원: WXT의 타입 지원이 훨씬 우수
- 미래 지향적: Vite 생태계와 완벽한 통합
- 활발한 개발: 이슈 대응이 빠르고 업데이트가 잦음
마무리
Chrome Extension 개발 프레임워크 선택은 프로젝트의 성공을 좌우할 수 있는 중요한 결정입니다.
제가 WXT로 옮길 때의 선택:
- 🏆 종합 우승: WXT - 유연성, 성능, 미래 지향성
- 🥈 차선책: Plasmo - React 전용이라면 여전히 좋은 선택
- 🆕 주목할 만한: Extension.js - 간단한 프로젝트에 적합
- 기존 Vite 구성을 유지할 때: CRXJS도 비교 대상
여러분의 프로젝트에 가장 적합한 프레임워크를 선택하시길 바랍니다. 저처럼 마이그레이션을 고려 중이시라면, WXT를 강력히 추천드립니다!
참고 자료
Chrome Extension 개발 경험이나 프레임워크 선택에 대한 고민이 있으시다면 댓글로 공유해주세요!
![[Chrome Extension 개발] 프레임워크 완벽 비교: WXT vs Plasmo vs CRXJS 그리고 더 많은 선택지](/assets/img/posts/chrome-extension/2025-07-18-chrome-extension-frameworks-comparison.png)