Hook
파일 하나가 346KB, 7,600줄이 되었습니다. HTML, CSS, JavaScript가 전부 한 파일 안에 있습니다. Git diff는 읽을 수 없고, AI 에이전트는 매 편집마다 이 거대한 파일을 통째로 읽어야 하고, 캐싱은 당연히 효율이 없습니다.
AI 디자인 도구는 기본적으로 모놀리식 단일 파일을 생성하는 경향이 있습니다. 이 글에서는 빌드 도구를 도입하지 않고 4-Phase 점진적 분할로 이 문제를 해결한 과정을 공유합니다.
TL;DR
- AI 도구의 모놀리식 경향이 기술 부채의 원인: 단일 파일은 머지 충돌, 리뷰 어려움, 캐싱 비효율, AI 컨텍스트 폭발을 유발합니다
- 4-Phase 점진적 분할로 해결: 정적 데이터 → CSS 분리 → core 로직 → screens 분할
- 빌드 도구는 도입하지 않습니다: 순수 파일 분할만으로 AI 컨텍스트를 절약합니다
- 매 Phase마다 회귀 검증 스크립트로 안전하게 마이그레이션합니다
Background: 왜 단일 파일이 문제인가
모놀리식 HTML이 만드는 4가지 부채
| 부채 | 설명 | 영향 |
|---|---|---|
| 머지 충돌 | 여러 변경이 같은 파일에 집중 | 병합 지연, 수동 해결 |
| 리뷰 어려움 | PR diff가 수백 줄 | 코드 리뷰 품질 저하 |
| 캐싱 비효율 | 전체 파일을 재다운로드 | 성능 저하 |
| AI 컨텍스트 폭발 | AI가 전체 파일을 읽어야 함 | 응답 품질 저하, 토큰 낭비 |
AI 컨텍스트 문제가 가장 심각합니다
단일 파일 (Before):
AI가 "운동 저장 버튼 수정" 요청을 받음
→ 346KB / 7,600줄 파일 전체를 읽음
→ 컨텍스트 대부분을 소비
→ 정작 수정할 부분의 맥락이 흐려짐
→ 잘못된 수정 발생
분할 후 (After):
AI가 같은 요청을 받음
→ workout-form.js (200줄)만 읽음
→ 전체 컨텍스트를 이 파일에 집중
→ 정확한 수정, 빠른 응답
Solution: 4-Phase 점진적 분할
핵심 원칙
빌드 도구를 도입하지 않습니다. 순수 파일 분할만으로 해결합니다. 이는 “단일 파일 정적 PWA”라는 원래 아키텍처 결정의 정신을 유지하면서, 발생한 부채만 해결하는 것입니다.
Phase별 마이그레이션
Phase 1: 정적 데이터 분할 ✅
│ 운동 정의, 설정값, 상수를 별도 JS 파일로 추출
│
Phase 2: CSS 분리 ✅
│ 인라인 <style>을 별도 .css 파일로 추출
│
Phase 3: core 로직 분할 ✅
│ 비즈니스 로직(오버로드 계산, 상태 관리)을 별도 모듈로
│
Phase 4: screens 분할 (진행 중)
각 화면(루틴, 운동, 통계, 설정)을 독립 파일로
4가지 엄격 규칙
분할 과정에서 지켜야 할 규칙입니다:
| 규칙 | 금지 | 유일한 예외 |
|---|---|---|
| 인라인 CSS | <style>, style="" | 크리티컬 CSS (압축 ≤14KB) |
| 인라인 JS | <script> 코드 바디 | 환경 변수 주입 (window.__ENV__) |
| 인라인 이벤트 | onclick, onload | 없음, addEventListener 사용 |
| CSS-in-JS | 인라인 style 객체 | 없음, CSS Modules 사용 |
관심사 분리 구조
┌─────────────┐ 구조 & 시맨틱 ┌──────────────┐
│ .html 파일 │ │ 마크업만 │
└─────────────┘ └──────────────┘
┌─────────────┐ 표현 & 레이아웃 ┌──────────────┐
│ .css 파일 │ │ 1컴포넌트당 │
└─────────────┘ │ 1 module.css │
└─────────────┘ └──────────────┘
┌─────────────┐ 동작 & 상호작용 ┌──────────────┐
│ .js 파일 │ │ 기능별 모듈 │
└─────────────┘ └──────────────┘
네이밍 컨벤션
| 대상 | 규칙 | 예시 |
|---|---|---|
| CSS 클래스 | BEM | .flow-card, .flow-card__header, .flow-card--active |
| JS 모듈 | kebab-case | flow-builder.js |
| 컴포넌트 | PascalCase | FlowBuilder |
| 변수 | camelCase | calculateProgressiveOverload |
| 타입/인터페이스 | PascalCase | FlowStep |
| 상수 | UPPER_SNAKE_CASE | MAX_SETS |
점진적 마이그레이션 정책 (기존 코드베이스)
새 프로젝트라면 처음부터 규칙을 적용하면 됩니다. 하지만 이미 모놀리식인 코드베이스에는 점진적 접근이 필요합니다:
- 패턴 동결: 채택일 이후 새 인라인 코드 금지
- 점진적 추출: 다른 이유로 파일 수정 시 해당 부분만 추출
- “빅뱅” 리팩터 금지: 한 번에 전체 재작성은 부채를 숨길 뿐
- 부채 추적: 미추출 부분에 주석 마킹
<!-- FIXME: extract inline styles -->
<div style="display:flex;gap:8px">...</div>
회귀 검증
매 Phase 완료 후 검증 스크립트(tools/check.js)를 실행합니다:
검증 스크립트 9개 카테고리
| # | 카테고리 | 검증 내용 |
|---|---|---|
| 1 | 파일 크기 | 개별 파일 400KB 하드 실패 |
| 2 | manifest 스키마 | 필드 누락/타입 오류 |
| 3 | HTML 구조 | 필수 요소 존재 여부 |
| 4 | 데드 버튼 | 빈 onclick/href=”#” 정적 탐지 |
| 5 | 목 데이터 | mock/dummy/fake/sample 변수 탐지 |
| 6 | SW precache | 모든 자산이 캐시 대상에 포함 |
| 7 | 회귀 검증 | 기능별 핵심 데이터 구조 보존 |
| 8 | i18n 구조 | 번역 파일 구조 정합성 |
| 9 | 사용자 데이터 | LocalStorage 키 보존 |
이 스크립트는 의존성 없는 Node 표준 모듈만 사용합니다.
npm install없이 CI/로컬/hook 3곳에서 동일하게 실행됩니다.
Result: 분할 전후 비교
| 지표 | Before (단일 파일) | After (분할) |
|---|---|---|
| 최대 파일 크기 | 346KB / 7,600줄 | 개별 파일 평균 300줄 |
| AI 컨텍스트 소비 | 전체 파일 읽기 | 관련 파일만 (평균 80% 절약) |
| Git diff 가독성 | 수백 줄 변경 | 기능별로 분산 |
| 캐싱 | 전체 재다운로드 | 변경된 파일만 (immutable 캐싱) |
| 머지 충돌 | 빈번 | 드묾 |
마치며
파일 하나가 346KB, 7,600줄이 되었을 때 처음 한 생각은 “단일 파일 PWA니까 어쩔 수 없지”였습니다. 단일 파일은 오프라인 보장과 무료 호스팅이라는 장점이 있으니, 파일 크기가 큰 것은 그 아키텍처의 대가라고 받아들였습니다. 하지만 이것은 의도적 단일 파일과 비의도적 모놀리식을 혼동한 것이었습니다. 빌드 도구 없이도 파일을 분할할 수 있다는 사실을 깨닫기까지 꽤 오래 걸렸고, 분할 후에도 원래 아키텍처의 이점을 그대로 유지할 수 있다는 것을 확인했을 때 비로소 “아, 파일 크기와 아키텍처는 별개의 문제였구나”라고 말할 수 있었습니다.
분할 작업에서 가장 의미 있었던 발견은 파일이 작아졌을 때 가장 큰 수혜자가 사람이 아니라 AI 에이전트 자체라는 점입니다. 346KB 파일을 매번 통째로 읽던 AI는 컨텍스트를 거의 다 소진해버려 수정 품질이 떨어질 수밖에 없었습니다. 반면 200줄짜리 파일만 읽는 AI는 컨텍스트에 여유가 남고, 그 여유가 곧 출력 품질로 직결됩니다. “AI 컨텍스트 절약”을 파일 분할의 1순위 동기로 삼게 된 결정적인 이유였습니다.
점진적 마이그레이션을 선택한 것도 현명한 판단이었습니다. 한 번에 전체를 재작성하면 반드시 무언가 망가지지만, “다른 이유로 파일을 수정할 때 그 부분만 추출한다”는 전략은 느려 보이면서도 재작업이 없어서 결과적으로 더 빠릅니다. AI가 생성하는 코드량이 앞으로 더 늘어날수록, AI가 효율적으로 읽고 수정할 수 있는 파일 구조를 유지하는 일은 단순한 정리가 아니라 개발 생산성의 핵심이 될 것입니다.
| ← 이전 | 시리즈: AI 주도 개발 거버넌스 | 다음 → |
|---|---|---|
| (2) 4단계 디자인 게이트 | (3) 모놀리식 HTML 분할 | (4) gettext 원문 키 |
게이트웨이 On-promise 제품 팀에서 시스템 모니터링 및 관리를 쉽게 다가갈 수 있도록 하기 위한 업무를 하고 있습니다.
Contact: lhjnano@gmail.com