크롬 확장 프로젝트 구조 정리하기: popup, options 폴더 분리
크롬 확장 루트 디렉토리에 뒤섞인 파일들을 기능별 폴더로 정리했습니다. manifest.json 경로 업데이트, git mv로 히스토리 보존, 트러블슈팅 팁까지 리팩토링 전 과정을 공유합니다.
1. 문제 상황
증상: 뒤섞인 파일들
크롬 확장 개발 초기에는 모든 파일을 루트 디렉토리에 두었습니다.
extension/
background.js
manifest.json
popup.html # 팝업 관련
popup.css # 팝업 관련
popup.js # 팝업 관련
options.html # 옵션 관련
options.css # 옵션 관련
options.js # 옵션 관련
images/
문제점:
- 파일 수가 늘어날수록 어떤 파일이 어떤 기능인지 파악 어려움
- 관련 파일을 찾으려면 목록에서 일일이 검색
- 새로운 기능(onboarding, manager 등) 추가 시 더 혼란
- 협업 시 파일 위치 설명에 시간 소요
목표: 기능별 폴더 분리
extension/
background.js
manifest.json
popup/ # 팝업 관련 파일 모음
popup.html
popup.css
popup.js
options/ # 옵션 관련 파일 모음
options.html
options.css
options.js
images/
2. Manifest V3 경로 규칙
상대 경로 기준
manifest.json의 모든 경로는 manifest.json 위치 기준 상대 경로입니다.
extension/
manifest.json <- 기준점
popup/
popup.html <- "popup/popup.html"
options/
options.html <- "options/options.html"
지원되는 경로 형식
{
"action": {
"default_popup": "popup/popup.html"
},
"background": {
"service_worker": "background.js"
},
"options_page": "options/options.html"
}
주의: 절대 경로(/popup/popup.html)는 지원되지 않습니다.
3. 리팩토링 단계
Step 1: 폴더 생성
cd extension
mkdir popup options
Step 2: 파일 이동
# popup 관련 파일 이동
mv popup.html popup/
mv popup.css popup/
mv popup.js popup/
# options 관련 파일 이동
mv options.html options/
mv options.css options/
mv options.js options/
또는 Git 명령어로 히스토리 보존:
# Git에서 이동 추적
git mv popup.html popup/
git mv popup.css popup/
git mv popup.js popup/
git mv options.html options/
git mv options.css options/
git mv options.js options/
Step 3: manifest.json 업데이트
// Before
{
"manifest_version": 3,
"name": "My Extension",
"version": "1.0.0",
"action": {
"default_popup": "popup.html"
},
"options_page": "options.html",
"background": {
"service_worker": "background.js"
}
}
// After
{
"manifest_version": 3,
"name": "My Extension",
"version": "1.0.0",
"action": {
"default_popup": "popup/popup.html"
},
"options_page": "options/options.html",
"background": {
"service_worker": "background.js"
}
}
Step 4: HTML 내부 참조 확인
파일 위치가 바뀌었으므로 HTML 내의 상대 경로 참조도 확인합니다.
<!-- popup/popup.html -->
<!-- 같은 폴더 내 파일 참조 - 변경 없음 -->
<link rel="stylesheet" href="popup.css">
<script src="popup.js"></script>
<!-- 상위 폴더의 파일 참조 - 변경 필요 -->
<!-- Before (루트에 있을 때) -->
<link rel="stylesheet" href="shared/common.css">
<!-- After (popup/ 안에 있을 때) -->
<link rel="stylesheet" href="../shared/common.css">
Step 5: 확장 리로드 및 테스트
chrome://extensions열기- 개발자 모드 활성화
- "리로드" 버튼 클릭
- 팝업 열어서 정상 동작 확인
- 옵션 페이지 열어서 확인
4. 권장 폴더 구조
기본 구조
extension/
manifest.json # 확장 설정
background.js # Service Worker
popup/ # 브라우저 액션 팝업
popup.html
popup.css
popup.js
options/ # 설정 페이지
options.html
options.css
options.js
images/ # 아이콘, 이미지
icon-16.png
icon-48.png
icon-128.png
shared/ # 공유 리소스
common.css
utils.js
확장된 구조 (대규모 프로젝트)
extension/
manifest.json
background.js
popup/
popup.html
popup.css
popup.js
components/ # 팝업 전용 컴포넌트
list-item.js
options/
options.html
options.css
options.js
onboarding/ # 온보딩 페이지
onboarding.html
onboarding.css
onboarding.js
manager/ # 관리 페이지 (확장 페이지)
manager.html
manager.css
manager.js
content/ # Content Script
content.js
content.css
images/
icons/
icon-16.png
icon-48.png
icon-128.png
screenshots/
shared/
common.css
utils.js
constants.js
_locales/ # 다국어 지원
en/
messages.json
ko/
messages.json
5. 주요 고려사항
Service Worker (background.js) 위치
{
"background": {
"service_worker": "background.js"
}
}
Service Worker는 import 경로 해석 문제로 루트에 두는 것이 일반적입니다.
하위 폴더에 둘 경우:
{
"background": {
"service_worker": "background/service-worker.js"
}
}
그러나 ES Modules 사용 시 경로 복잡성이 증가하므로 루트 권장.
Content Script 경로
{
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content/content.js"],
"css": ["content/content.css"]
}
]
}
모든 경로는 manifest.json 기준.
Web Accessible Resources
{
"web_accessible_resources": [
{
"resources": [
"images/icon-48.png",
"shared/injected.js"
],
"matches": ["<all_urls>"]
}
]
}
Content Script에서 확장 리소스에 접근할 때 사용.
6. 실제 변경 예시
Before: 변경 전 manifest.json
{
"manifest_version": 3,
"name": "Secret Guard",
"version": "0.1.0",
"description": "Incognito session restore and secret bookmarks.",
"incognito": "split",
"permissions": ["tabs", "bookmarks", "sessions", "storage", "contextMenus"],
"action": {
"default_title": "Secret Guard",
"default_popup": "popup.html"
},
"background": {
"service_worker": "background.js"
},
"options_page": "options.html"
}
After: 변경 후 manifest.json
{
"manifest_version": 3,
"name": "Secret Guard",
"version": "0.1.0",
"description": "Incognito session restore and secret bookmarks.",
"incognito": "split",
"permissions": ["tabs", "bookmarks", "sessions", "storage", "contextMenus"],
"action": {
"default_title": "Secret Guard",
"default_popup": "popup/popup.html"
},
"background": {
"service_worker": "background.js"
},
"options_page": "options/options.html"
}
변경 사항:
popup.html->popup/popup.htmloptions.html->options/options.htmlbackground.js는 루트에 유지
7. Git으로 히스토리 보존하기
git mv 사용
# 파일 이동 + Git 추적
git mv popup.html popup/popup.html
git mv popup.css popup/popup.css
git mv popup.js popup/popup.js
# 커밋
git commit -m "refactor(extension): reorganize popup files into subdirectory"
git mv의 장점
- 파일 히스토리가 보존됨 (
git log --follow popup/popup.html) - 삭제 + 추가가 아닌 이동으로 인식됨
- 코드 리뷰 시 변경 내용 명확
한 번에 여러 파일 이동
# 폴더 먼저 생성
mkdir -p popup options
# 파일 이동
git mv popup.html popup/
git mv popup.css popup/
git mv popup.js popup/
git mv options.html options/
git mv options.css options/
git mv options.js options/
# 커밋
git commit -m "refactor(extension): reorganize popup and options into subdirectories
- popup.html/css/js를 popup/ 하위 폴더로 이동
- options.html/css/js를 options/ 하위 폴더로 이동
- manifest.json 경로 참조 업데이트
- 유지보수성 향상을 위한 파일 구조 개선"
8. 트러블슈팅
문제 1: 팝업이 열리지 않음
증상: 확장 아이콘 클릭 시 빈 화면 또는 에러
원인: manifest.json 경로 오타
해결:
// 확인 사항
{
"action": {
"default_popup": "popup/popup.html" // 슬래시 방향, 오타 확인
}
}
개발자 도구 > 오류 확인:
Error: Could not load manifest. 'action.default_popup' not found at path: popap/popup.html
문제 2: CSS/JS 로드 실패
증상: HTML은 열리지만 스타일이 적용 안 됨
원인: HTML 내 상대 경로 잘못됨
해결:
<!-- popup/popup.html -->
<!-- 같은 폴더 내 참조: 경로 그대로 -->
<link rel="stylesheet" href="popup.css">
<!-- 상위 폴더 참조: ../ 추가 -->
<link rel="stylesheet" href="../shared/common.css">
문제 3: Chrome Extension Reload 안 됨
증상: 파일 변경 후에도 이전 상태 유지
해결:
chrome://extensions에서 "리로드" 버튼 클릭- 또는 확장 ID 클릭 > "업데이트" 버튼
- 그래도 안 되면: 확장 제거 > 다시 로드
문제 4: 옵션 페이지 접근 안 됨
증상: 확장 > 옵션 클릭 시 404 또는 빈 화면
원인: options_page 경로 오류
해결:
{
"options_page": "options/options.html"
}
또는 chrome_url_overrides 사용 시:
{
"options_ui": {
"page": "options/options.html",
"open_in_tab": true
}
}
9. 핵심 개념 정리
경로 참조 정리
| 위치 | 참조 대상 | 경로 |
|---|---|---|
| manifest.json | popup.html | popup/popup.html |
| manifest.json | options.html | options/options.html |
| popup/popup.html | popup.css | popup.css |
| popup/popup.html | shared/common.css | ../shared/common.css |
| background.js | - | 루트에 유지 권장 |
폴더 분리 체크리스트
- [ ] 폴더 생성 (
mkdir popup options) - [ ] 파일 이동 (
git mv권장) - [ ] manifest.json 경로 업데이트
- [ ] HTML 내부 참조 확인
- [ ] 확장 리로드 및 테스트
- [ ] 팝업 동작 확인
- [ ] 옵션 페이지 동작 확인
10. 베스트 프랙티스
DO (해야 할 것)
- [x] 기능별 폴더 분리 (popup/, options/, content/)
- [x] 공통 리소스는 shared/ 폴더에
- [x] git mv로 이동하여 히스토리 보존
- [x] 이동 후 즉시 테스트
- [x] 명확한 커밋 메시지
DON'T (하지 말아야 할 것)
- [ ] 모든 파일을 루트에 방치
- [ ] 깊은 중첩 폴더 (2단계 이상 지양)
- [ ] 파일 삭제 후 재생성 (히스토리 손실)
- [ ] manifest.json 수정 없이 파일만 이동
명명 규칙 권장
popup/
popup.html # 진입점: 폴더명 반복 OK
popup.css
popup.js
# 또는
popup/
index.html # 진입점: index 사용
styles.css
script.js
폴더명을 반복하는 방식이 명확하고 검색하기 쉬움.
11. FAQ
Q: Service Worker도 폴더로 옮길 수 있나요?
A: 가능하지만 권장하지 않습니다. ES Modules 사용 시 import 경로 해석이 복잡해질 수 있어 루트에 두는 것이 일반적입니다.
// 가능하지만 권장하지 않음
{
"background": {
"service_worker": "background/service-worker.js"
}
}
Q: 아이콘 경로는 어떻게 되나요?
A: manifest.json 기준 상대 경로:
{
"action": {
"default_icon": {
"16": "images/icon-16.png",
"48": "images/icon-48.png",
"128": "images/icon-128.png"
}
}
}
Q: TypeScript/번들러 사용 시에도 동일한가요?
A: 번들러 출력 디렉토리 기준으로 동일하게 적용됩니다. 보통 dist/ 폴더가 extension 역할을 하며, manifest.json도 그 안에 위치합니다.
src/ # 소스 코드
popup/
popup.ts
dist/ # 번들 출력 (= extension)
manifest.json
popup/
popup.html
popup.js
Q: Content Script도 폴더로 분리하나요?
A: 네, 권장합니다:
content/
content.js
content.css
injected/
injected.js
{
"content_scripts": [{
"matches": ["<all_urls>"],
"js": ["content/content.js"],
"css": ["content/content.css"]
}]
}
Q: 다국어 지원 시 구조는?
A: Chrome 확장의 i18n은 _locales 폴더를 사용합니다:
_locales/
en/
messages.json
ko/
messages.json
manifest.json에서:
{
"default_locale": "en"
}
12. 참고 자료
- Chrome Extension Architecture
- Chrome Extension Files Structure
- Manifest V3 Migration Guide
- Git mv Documentation
13. 다음 단계
이 글에서는 크롬 확장의 파일 구조 정리를 다뤘습니다. 이 시리즈의 다른 글들도 확인해보세요.
시리즈 목차:
- JavaScript URL 비교와 정규화
- Web Crypto API로 안전한 해싱 구현하기
- CSS 변수와 다크 모드 구현하기
- 크롬 확장 프로젝트 구조 정리하기 ← 현재 글