From 664fe881dd8e3d4a0b09bf4f77a50b391ac7a01a Mon Sep 17 00:00:00 2001 From: gahusb Date: Mon, 27 Apr 2026 08:18:34 +0900 Subject: [PATCH] =?UTF-8?q?docs:=20Plan=201=20(Week=201=20=EC=97=94?= =?UTF-8?q?=EC=A7=84=20=EB=A7=88=EC=9D=B4=EA=B7=B8=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EC=85=98)=20=EA=B5=AC=ED=98=84=20=EA=B3=84=ED=9A=8D=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1=20(JSA-2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Granite + Apps-in-Toss + TDS 종속성을 제거하고 Vite + 순수 React로 전환하는 16개 task 구현 계획. 각 task는 2~5분 단위 step으로 분해되어 TDD 회귀 테스트, git 커밋 리듬, 명시적 코드 블록을 모두 포함. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../2026-04-27-week1-engine-migration.md | 881 ++++++++++++++++++ 1 file changed, 881 insertions(+) create mode 100644 docs/superpowers/plans/2026-04-27-week1-engine-migration.md diff --git a/docs/superpowers/plans/2026-04-27-week1-engine-migration.md b/docs/superpowers/plans/2026-04-27-week1-engine-migration.md new file mode 100644 index 0000000..e87fae4 --- /dev/null +++ b/docs/superpowers/plans/2026-04-27-week1-engine-migration.md @@ -0,0 +1,881 @@ +# Week 1 — Engine Migration Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Granite + Apps-in-Toss + TDS 종속성을 완전히 제거하고 Vite + 순수 React 위에서 모든 기존 게임 기능이 회귀 없이 동작하는 빌드를 만든다. + +**Architecture:** 게임 로직(`useGameStore`, JSON 데이터, 컴포넌트)은 변경하지 않는다. 빌드 시스템(Granite → Vite), 진입점(`_app.tsx` + `pages/index.tsx` → `main.tsx` + `App.tsx`), 외부 의존성(TDS 색상 → 로컬 shim, Toss Analytics → console adapter)만 교체한다. `src/platform/` 디렉토리를 도입하여 향후 모든 vendor 격리의 진입점으로 삼는다. + +**Tech Stack:** Vite 5.x, @vitejs/plugin-react, React 18, TypeScript 5.x, @emotion/react, zustand (모두 유지) + +**Spec reference:** `docs/superpowers/specs/2026-04-27-web-engine-pivot-design.md` 섹션 1, 2, 5.3, 5.4, 타임라인 Week 1 + +**TDD note:** 본 plan은 신규 비즈니스 로직이 거의 없는 마이그레이션 plan이다. 검증은 (1) TypeScript 컴파일, (2) Vite dev/build 통과, (3) 수동 회귀 체크리스트로 한다. 단위 테스트 프레임워크(Vitest) 도입은 Plan 3(광고 어댑터 — 신규 로직 발생)에서 진행한다. + +--- + +## File Structure + +| 작업 | 파일 | +|------|------| +| **신규** | `index.html`, `vite.config.ts`, `tsconfig.node.json`, `src/main.tsx`, `src/App.tsx`, `src/config/env.ts`, `src/styles/adaptive.ts`, `src/platform/analytics/types.ts`, `src/platform/analytics/consoleAdapter.ts`, `src/platform/analytics/index.ts` | +| **수정** | `package.json`, `tsconfig.json`, `.gitignore`, `src/styles/gameColors.ts`, `src/components/AchievementToast.tsx`, `src/components/OfflineRewardModal.tsx`, `src/components/PrestigeModal.tsx`, `src/components/screens/AchievementsScreen.tsx`, `src/components/screens/ElementsScreen.tsx`, `src/components/screens/SettingsScreen.tsx`, `src/components/screens/ShopScreen.tsx` | +| **삭제** | `babel.config.js`, `granite.config.ts`, `require.context.ts`, `global.d.ts`, `src/_app.tsx`, `pages/index.tsx`, `pages/` (디렉토리), `src/analytics.ts`, `.swc/`, `dist/` | +| **변경 없음** | `src/store/useGameStore.ts`, `src/data/*.json`, `src/hooks/*.ts`, `src/components/BottomTabBar.tsx`, `src/components/CharacterSprite.tsx`, `src/components/FloatingOverlay.tsx`, `src/components/screens/EvolutionScreen.tsx`, `src/components/screens/FusionScreen.tsx`, `src/components/tutorial/*.tsx` | + +--- + +## Task 1: 백업 git 태그 + 작업 브랜치 생성 + +**Files:** 없음 (git 작업) + +- [ ] **Step 1: 현재 master에 백업 태그 생성** + +```bash +git tag -a pre-vite-migration -m "Last Granite/Toss-based build before Vite migration" +``` + +- [ ] **Step 2: 작업 브랜치 생성** + +```bash +git switch -c feat/vite-migration +``` + +- [ ] **Step 3: 태그 + 브랜치 확인** + +Run: `git tag -l pre-vite-migration && git branch --show-current` +Expected: `pre-vite-migration` (태그 출력) + `feat/vite-migration` (브랜치 출력) + +--- + +## Task 2: package.json 의존성 교체 + +**Files:** +- Modify: `package.json` (전체) + +- [ ] **Step 1: package.json 새 내용으로 교체** + +```json +{ + "name": "archetype-firstspark", + "version": "1.0.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc --noEmit && vite build", + "preview": "vite preview", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@emotion/react": "^11.14.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "zustand": "^5.0.12" + }, + "devDependencies": { + "@types/node": "^22.7.0", + "@types/react": "^18.3.12", + "@types/react-dom": "^18.3.1", + "@vitejs/plugin-react": "^4.3.4", + "typescript": "^5.6.3", + "vite": "^5.4.11" + } +} +``` + +- [ ] **Step 2: lockfile 및 node_modules 정리** + +```bash +rm -rf node_modules package-lock.json +``` + +- [ ] **Step 3: 새 의존성 설치** + +```bash +npm install +``` + +Expected: 설치 완료, `npm warn` 정도는 OK, 에러 없음 + +- [ ] **Step 4: 커밋** + +```bash +git add package.json package-lock.json +git commit -m "chore: swap deps to Vite + React 18, remove Granite/Toss/RN" +``` + +--- + +## Task 3: Granite/Toss 빌드 산출물 + config 파일 삭제 + +**Files:** +- Delete: `babel.config.js`, `granite.config.ts`, `require.context.ts`, `global.d.ts`, `src/_app.tsx`, `pages/index.tsx`, `.swc/`, `dist/` + +- [ ] **Step 1: Granite·RN 전용 config 파일 삭제** + +```bash +rm -f babel.config.js granite.config.ts require.context.ts global.d.ts +``` + +- [ ] **Step 2: Toss/Granite 진입점 파일 삭제** + +```bash +rm -f src/_app.tsx +rm -rf pages +``` + +- [ ] **Step 3: 빌드 캐시 디렉토리 삭제** + +```bash +rm -rf .swc dist +``` + +- [ ] **Step 4: 커밋 (이 시점에 dev 부트는 아직 안 됨, 다음 task에서 복구)** + +```bash +git add -A +git commit -m "chore: remove Granite/Toss config and entry files" +``` + +--- + +## Task 4: .gitignore 업데이트 + +**Files:** +- Modify: `.gitignore` + +- [ ] **Step 1: 현재 .gitignore 확인** + +Run: `cat .gitignore` +주의: 현재 내용 그대로 두고, 아래 추가만 함. + +- [ ] **Step 2: Vite 관련 항목 추가 (이미 있다면 중복 추가하지 않음)** + +`.gitignore`에 다음 줄이 없으면 추가: + +``` +# Vite +.vite/ +dist/ +*.local + +# IDE +.vscode/ +.idea/ +``` + +- [ ] **Step 3: 커밋** + +```bash +git add .gitignore +git commit -m "chore: ignore Vite build outputs and IDE caches" +``` + +--- + +## Task 5: Vite 설정 + tsconfig 갱신 + +**Files:** +- Create: `vite.config.ts`, `tsconfig.node.json` +- Modify: `tsconfig.json` + +- [ ] **Step 1: `vite.config.ts` 생성** + +```ts +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; + +export default defineConfig({ + plugins: [ + react({ + jsxImportSource: '@emotion/react', + babel: { + plugins: ['@emotion/babel-plugin'], + }, + }), + ], + base: './', + resolve: { + alias: { + '@': '/src', + }, + }, + server: { + port: 5173, + strictPort: false, + open: true, + }, + build: { + outDir: 'dist', + sourcemap: true, + rollupOptions: { + output: { + manualChunks: { + 'react-vendor': ['react', 'react-dom'], + 'state': ['zustand'], + }, + }, + }, + }, + define: { + __APP_VERSION__: JSON.stringify(process.env.npm_package_version), + }, +}); +``` + +- [ ] **Step 2: `@emotion/babel-plugin` devDependency 추가** + +```bash +npm install -D @emotion/babel-plugin +``` + +- [ ] **Step 3: `tsconfig.node.json` 생성** (Vite config 자체의 타입체크용) + +```json +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true, + "strict": true, + "types": ["node"] + }, + "include": ["vite.config.ts"] +} +``` + +- [ ] **Step 4: `tsconfig.json` 교체** + +```json +{ + "compilerOptions": { + "target": "ES2022", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "useDefineForClassFields": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + "jsxImportSource": "@emotion/react", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "skipLibCheck": true, + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["src", "vite-env.d.ts"], + "references": [{ "path": "./tsconfig.node.json" }] +} +``` + +- [ ] **Step 5: Vite 환경변수 타입 선언 파일 생성** + +`vite-env.d.ts`: + +```ts +/// + +declare const __APP_VERSION__: string; + +interface ImportMetaEnv { + readonly VITE_PUBLIC_URL: string; + readonly VITE_AD_ADAPTER: string; + readonly VITE_GA_MEASUREMENT_ID: string; + readonly VITE_ENABLE_PUSH: string; +} + +interface ImportMeta { + readonly env: ImportMetaEnv; +} +``` + +- [ ] **Step 6: 커밋** + +```bash +git add vite.config.ts tsconfig.json tsconfig.node.json vite-env.d.ts package.json package-lock.json +git commit -m "feat: add Vite config and TypeScript settings for web build" +``` + +--- + +## Task 6: index.html 생성 (Vite 진입점) + +**Files:** +- Create: `index.html` + +- [ ] **Step 1: 루트에 `index.html` 생성** + +```html + + + + + + + + Archetype: First Spark + + + +
+ + + +``` + +- [ ] **Step 2: `app-icon-512.png`가 루트에 있는지 확인** (Vite는 루트 정적 파일을 자동 제공) + +Run: `ls app-icon-512.png` +Expected: 파일 존재. 없으면 `app-icon.png`을 사용하도록 `index.html`의 href만 수정. + +- [ ] **Step 3: 커밋** + +```bash +git add index.html +git commit -m "feat: add Vite index.html entry point" +``` + +--- + +## Task 7: TDS adaptive 색상 shim 작성 + +`@toss/tds-colors`의 `adaptive`를 라이트 모드 고정값으로 대체. `gameColors.ts`와 8개 컴포넌트가 사용하는 11개 키 모두 포함. + +**Files:** +- Create: `src/styles/adaptive.ts` + +- [ ] **Step 1: `src/styles/adaptive.ts` 생성** + +```ts +export const adaptive = { + background: '#ffffff', + greyBackground: '#f7f8fa', + grey100: '#f1f3f5', + grey200: '#e5e8eb', + grey300: '#d1d6db', + grey400: '#b0b8c1', + grey500: '#8b95a1', + grey600: '#6b7684', + grey700: '#4e5968', + grey900: '#191f28', + blue500: '#3182f6', +} as const; +``` + +- [ ] **Step 2: 커밋** + +```bash +git add src/styles/adaptive.ts +git commit -m "feat: add local adaptive color shim to replace @toss/tds-colors" +``` + +--- + +## Task 8: 8개 파일에서 adaptive import 경로 교체 + +기능 변경 0. import 경로만 `@toss/tds-colors` → `../styles/adaptive` 또는 `../../styles/adaptive` (상대 경로)로 변경. + +**Files:** +- Modify: `src/styles/gameColors.ts`, `src/components/AchievementToast.tsx`, `src/components/OfflineRewardModal.tsx`, `src/components/PrestigeModal.tsx`, `src/components/screens/AchievementsScreen.tsx`, `src/components/screens/ElementsScreen.tsx`, `src/components/screens/SettingsScreen.tsx`, `src/components/screens/ShopScreen.tsx` + +- [ ] **Step 1: `src/styles/gameColors.ts` line 1 교체** + +Find: `import { adaptive } from '@toss/tds-colors';` +Replace with: `import { adaptive } from './adaptive';` + +- [ ] **Step 2: `src/components/AchievementToast.tsx` line 3 교체** + +Find: `import { adaptive } from '@toss/tds-colors';` +Replace with: `import { adaptive } from '../styles/adaptive';` + +- [ ] **Step 3: `src/components/OfflineRewardModal.tsx` line 2 교체** + +Find: `import { adaptive } from '@toss/tds-colors';` +Replace with: `import { adaptive } from '../styles/adaptive';` + +- [ ] **Step 4: `src/components/PrestigeModal.tsx` line 3 교체** + +Find: `import { adaptive } from '@toss/tds-colors';` +Replace with: `import { adaptive } from '../styles/adaptive';` + +- [ ] **Step 5: `src/components/screens/AchievementsScreen.tsx` line 3 교체** + +Find: `import { adaptive } from '@toss/tds-colors';` +Replace with: `import { adaptive } from '../../styles/adaptive';` + +- [ ] **Step 6: `src/components/screens/ElementsScreen.tsx` line 3 교체** + +Find: `import { adaptive } from '@toss/tds-colors';` +Replace with: `import { adaptive } from '../../styles/adaptive';` + +- [ ] **Step 7: `src/components/screens/SettingsScreen.tsx` line 3 교체** + +Find: `import { adaptive } from '@toss/tds-colors';` +Replace with: `import { adaptive } from '../../styles/adaptive';` + +- [ ] **Step 8: `src/components/screens/ShopScreen.tsx` line 3 교체** + +Find: `import { adaptive } from '@toss/tds-colors';` +Replace with: `import { adaptive } from '../../styles/adaptive';` + +- [ ] **Step 9: 남아있는 `@toss/tds-colors` 임포트가 없는지 확인** + +Run: `grep -rn "@toss/tds-colors" src/ || echo OK` +Expected: `OK` (출력 없음) + +- [ ] **Step 10: 커밋** + +```bash +git add src/styles/gameColors.ts src/components +git commit -m "refactor: route adaptive colors through local shim" +``` + +--- + +## Task 9: 환경변수 단일 진입점 `src/config/env.ts` + +**Files:** +- Create: `src/config/env.ts` + +- [ ] **Step 1: `src/config/env.ts` 생성** + +```ts +export const env = { + publicUrl: import.meta.env.VITE_PUBLIC_URL ?? '', + adAdapter: import.meta.env.VITE_AD_ADAPTER ?? 'dummy', + gaMeasurementId: import.meta.env.VITE_GA_MEASUREMENT_ID ?? '', + enablePush: import.meta.env.VITE_ENABLE_PUSH === 'true', + appVersion: __APP_VERSION__, + isDev: import.meta.env.DEV, + isProd: import.meta.env.PROD, +} as const; +``` + +- [ ] **Step 2: 환경변수 예제 파일 생성** (`.env.example`) + +```bash +# Vite 빌드 환경변수 템플릿. 실제 값은 .env.local 또는 .env.production에 설정. +VITE_PUBLIC_URL= +VITE_AD_ADAPTER=dummy +VITE_GA_MEASUREMENT_ID= +VITE_ENABLE_PUSH=false +``` + +- [ ] **Step 3: `.env.local`을 `.gitignore`에 추가 (이미 있으면 skip)** + +`.gitignore`에 `.env.local` 줄이 없으면 추가. + +- [ ] **Step 4: 커밋** + +```bash +git add src/config/env.ts .env.example .gitignore +git commit -m "feat: add domain-independent env config entry point" +``` + +--- + +## Task 10: 분석 어댑터 골격 (`src/platform/analytics/`) + +기존 `src/analytics.ts`를 어댑터 인터페이스 뒤에 둔다. v1에서는 `consoleAdapter`만 동작 (실 GA4 통합은 Plan 4). + +**Files:** +- Create: `src/platform/analytics/types.ts`, `src/platform/analytics/consoleAdapter.ts`, `src/platform/analytics/index.ts` +- Delete: `src/analytics.ts` + +- [ ] **Step 1: `src/platform/analytics/types.ts` 생성** + +```ts +export type AnalyticsParams = Record; + +export interface AnalyticsAdapter { + init(): void; + track(eventName: string, params?: AnalyticsParams): void; +} +``` + +- [ ] **Step 2: `src/platform/analytics/consoleAdapter.ts` 생성** + +```ts +import type { AnalyticsAdapter, AnalyticsParams } from './types'; + +export function createConsoleAdapter(debug: boolean): AnalyticsAdapter { + return { + init() { + if (debug) console.log('[Analytics] console adapter initialized'); + }, + track(eventName: string, params: AnalyticsParams = {}) { + if (debug) console.log('[Analytics]', eventName, params); + }, + }; +} +``` + +- [ ] **Step 3: `src/platform/analytics/index.ts` 생성** (기존 호출부 호환) + +```ts +import { createConsoleAdapter } from './consoleAdapter'; +import type { AnalyticsAdapter, AnalyticsParams } from './types'; +import { env } from '../../config/env'; + +let _adapter: AnalyticsAdapter | null = null; + +export function initAnalytics(debug = env.isDev): void { + _adapter = createConsoleAdapter(debug); + _adapter.init(); +} + +export function trackGameEvent(eventName: string, params: AnalyticsParams = {}): void { + _adapter?.track(eventName, params); +} + +export type { AnalyticsAdapter, AnalyticsParams }; +``` + +- [ ] **Step 4: 기존 `src/analytics.ts` 삭제** + +```bash +rm src/analytics.ts +``` + +- [ ] **Step 5: 커밋** + +```bash +git add src/platform src/analytics.ts +git commit -m "refactor: introduce analytics adapter behind src/platform/" +``` + +--- + +## Task 11: 게임 진입점 — `src/main.tsx` + `src/App.tsx` + +기존 `pages/index.tsx`의 내용을 `src/App.tsx`로 옮기고, ReactDOM 마운트는 `src/main.tsx`에서 한다. + +**Files:** +- Create: `src/main.tsx`, `src/App.tsx` + +- [ ] **Step 1: `src/App.tsx` 생성** (기존 `pages/index.tsx` 내용 + import 경로 + analytics 경로 수정) + +```tsx +import { css } from '@emotion/react'; +import { useEffect } from 'react'; +import { BottomTabBar } from './components/BottomTabBar'; +import { OfflineRewardModal } from './components/OfflineRewardModal'; +import { AchievementToast } from './components/AchievementToast'; +import { ElementsScreen } from './components/screens/ElementsScreen'; +import { EvolutionScreen } from './components/screens/EvolutionScreen'; +import { FusionScreen } from './components/screens/FusionScreen'; +import { ShopScreen } from './components/screens/ShopScreen'; +import { AchievementsScreen } from './components/screens/AchievementsScreen'; +import { SettingsScreen } from './components/screens/SettingsScreen'; +import { TutorialOverlay } from './components/tutorial/TutorialOverlay'; +import { useIdleTick } from './hooks/useIdleTick'; +import { useGameStore } from './store/useGameStore'; +import { trackGameEvent } from './platform/analytics'; + +const rootStyle = css` + width: 100%; + height: 100vh; + display: flex; + flex-direction: column; + background-color: #f7f8fa; + overflow: hidden; + font-family: + 'Pretendard', + -apple-system, + BlinkMacSystemFont, + sans-serif; +`; + +const contentStyle = css` + flex: 1; + overflow-y: auto; + padding-bottom: 72px; +`; + +export function App() { + const { activeTab, setActiveTab, elements, gold, elementLevels } = useGameStore(); + useIdleTick(); + + useEffect(() => { + const ownedCount = Object.values(elements).filter((c) => c > 0).length; + const totalLevel = Object.values(elementLevels).reduce((sum, lv) => sum + lv, 0); + trackGameEvent('app_open', { + platform: 'web', + platform_time: new Date().toISOString(), + owned_element_count: ownedCount, + gold, + total_enhance_level: totalLevel, + }); + }, []); + + return ( +
+ + +
+ {activeTab === 'elements' && } + {activeTab === 'evolution' && } + {activeTab === 'fusion' && } + {activeTab === 'shop' && } + {activeTab === 'achievements' && } + {activeTab === 'settings' && } +
+ + +
+ ); +} +``` + +주의: 기존 `_app.tsx`의 ``를 `App.tsx` 안으로 가져왔다. Provider 래핑은 제거. + +- [ ] **Step 2: `src/main.tsx` 생성** + +```tsx +import { StrictMode } from 'react'; +import { createRoot } from 'react-dom/client'; +import { App } from './App'; +import { initAnalytics } from './platform/analytics'; +import { useGameStore, flushGameState } from './store/useGameStore'; + +initAnalytics(); + +// 탭 종료/이탈 시 즉시 저장 (throttledStorage 보호) +window.addEventListener('beforeunload', flushGameState); +window.addEventListener('pagehide', flushGameState); + +// 첫 진입 시 오프라인 보상 계산 +useGameStore.getState().initOffline(); + +const rootEl = document.getElementById('root'); +if (!rootEl) throw new Error('Root element #root not found in index.html'); + +createRoot(rootEl).render( + + + +); +``` + +- [ ] **Step 3: 커밋** + +```bash +git add src/main.tsx src/App.tsx +git commit -m "feat: add Vite entry point and root App component" +``` + +--- + +## Task 12: TypeScript 컴파일 통과 확인 + +신규 코드와 기존 코드의 타입 정합성 검증. + +**Files:** 없음 (검증) + +- [ ] **Step 1: 타입 체크 실행** + +Run: `npm run typecheck` +Expected: 에러 0개. 출력 없음 또는 `tsc -b` 성공 메시지. + +- [ ] **Step 2: 에러 발생 시 대응** + +가장 흔한 에러 후보: +- `Cannot find module '@toss/tds-colors'` → Task 8 Step 9의 grep 재확인, 누락 파일 수정 +- `Cannot find module '@apps-in-toss/...'` → 어딘가 누락된 import. grep으로 찾아 제거. +- `Property 'css' does not exist on type ...` → JSX runtime 설정 누락. `tsconfig.json`의 `jsxImportSource: "@emotion/react"` 확인. + +에러 수정 후 재실행. 모두 통과할 때까지 반복. + +- [ ] **Step 3: 성공 시 작은 마커 커밋 (선택, 작업 진행 표시용)** + +```bash +git commit --allow-empty -m "chore: typecheck passes after Vite migration" +``` + +--- + +## Task 13: Vite dev server 부팅 + 브라우저 검증 + +**Files:** 없음 (검증) + +- [ ] **Step 1: dev server 실행** + +Run: `npm run dev` +Expected: `Local: http://localhost:5173/` 또는 가용 포트로 부팅. 콘솔에 컴파일 에러 없음. + +- [ ] **Step 2: 브라우저에서 접속** + +브라우저로 `http://localhost:5173/` 열기. 게임 화면이 표시되는지 확인. + +- [ ] **Step 3: 콘솔 에러 확인** + +DevTools → Console 열고 빨간색 에러 메시지가 없는지 확인. `[Analytics] app_open ...` 같은 로그는 정상. + +- [ ] **Step 4: dev server 종료** + +터미널에서 `Ctrl+C`. + +--- + +## Task 14: 수동 회귀 테스트 체크리스트 + +dev server에서 모든 기존 기능이 회귀 없이 동작하는지 확인. 결과를 plan 문서 하단에 기록. + +**Files:** 없음 (검증) + +- [ ] **Step 1: dev server 재실행** + +Run: `npm run dev` + +- [ ] **Step 2: 다음 기능을 순서대로 검증, 각 항목 PASS/FAIL 기록** + +브라우저 localStorage를 비우고 (`localStorage.clear()` after F12 console) 새로고침 한 뒤 처음부터 시작: + +1. **튜토리얼 단계 1~5 진행** — TutorialOverlay 표시·다음 단계 진행 동작 +2. **Elements 탭** — 4원소 인벤토리 표시, 캐릭터 스프라이트 렌더링, FloatingOverlay 동작 +3. **Fusion 탭** — 두 슬롯 선택 → 합성 → 결과 원소 추가 + 골드 획득 확인 +4. **Evolution(강화) 탭** — 원소 선택 → 강화 비용 골드 차감 + 레벨 업 +5. **Shop 탭** — 부스트 구매 → activeBoosts 활성화 + tickIdle에서 2배 적용 +6. **Achievements 탭** — 합성·강화 직후 새 업적이 토스트로 표시 + 잠금해제 목록에 추가 +7. **Settings 탭** — 언어 토글(ko↔en) 저장, BGM 토글 저장 +8. **방치형 동작** — 30초 대기 → 골드/원소 자동 증가 (DevTools에서 store 상태로도 확인 가능) +9. **오프라인 보상** — 탭 닫고 1분 후 재진입 → OfflineRewardModal 표시 + 클레임 동작 +10. **프레스티지** — `creation`, `spirit` 1개씩 인벤토리에 추가 (DevTools에서 `useGameStore.setState`로 강제) → PrestigeModal 동작 + 횟수 증가 + 칭호 변경 +11. **저장/복원** — 새로고침 후에도 진행도 유지 (localStorage 키 `archetype-game-state`) +12. **F12 콘솔에 에러 없음** + +- [ ] **Step 3: 결과를 plan 문서 하단의 "Regression Test Log" 섹션에 기록** + +각 항목 옆에 `PASS` 또는 `FAIL: <이유>` 추가. FAIL이 있으면 다음 step에서 fix. + +- [ ] **Step 4: FAIL 항목이 있으면 추적 + 수정 후 재테스트** + +회귀가 발생하면 직전 commit과 비교하여 차이를 찾는다. 흔한 원인: +- import 경로 실수 +- `pages/index.tsx`에 있던 어떤 코드가 `App.tsx` 이전 시 누락 +- emotion css 설정 미스 (vite.config의 babel.plugins 확인) + +전 항목 PASS될 때까지 반복. + +--- + +## Task 15: production build 통과 확인 + +**Files:** 없음 (검증) + +- [ ] **Step 1: production build 실행** + +Run: `npm run build` +Expected: `dist/` 디렉토리 생성 + 에러 0. 경고는 허용 (예: chunk size warnings). + +- [ ] **Step 2: build 산출물 확인** + +Run: `ls dist/` +Expected: `index.html`, `assets/` 디렉토리, JS·CSS 번들 파일들 + +- [ ] **Step 3: preview server로 build 검증** + +Run: `npm run preview` +브라우저로 표시된 URL(보통 `http://localhost:4173/`) 접속. Task 14의 핵심 기능 (튜토리얼, 합성, 강화) 3개만 빠르게 재검증. + +- [ ] **Step 4: preview 종료** + +`Ctrl+C` + +- [ ] **Step 5: build 산출물은 커밋하지 않음 확인** + +Run: `git status` +Expected: `dist/` 디렉토리가 untracked로 보이지 않거나, .gitignore에 의해 무시됨. 추적되고 있다면 .gitignore 수정. + +- [ ] **Step 6: 마커 커밋** + +```bash +git commit --allow-empty -m "chore: production build verified after Vite migration" +``` + +--- + +## Task 16: 회귀 테스트 결과 기록 + Plan 1 종료 + +**Files:** +- Modify: `docs/superpowers/plans/2026-04-27-week1-engine-migration.md` (이 파일의 하단 섹션) + +- [ ] **Step 1: 이 plan 문서의 "Regression Test Log" 섹션을 채운다** + +아래 템플릿을 plan 문서 끝에 추가: + +```markdown +--- + +## Regression Test Log + +**Tested at:** YYYY-MM-DD HH:MM +**Browser:** Chrome 12X / Safari / ... +**Build:** dev / production preview + +| # | 기능 | 결과 | 비고 | +|---|------|------|------| +| 1 | 튜토리얼 1~5단계 | | | +| 2 | Elements 탭 | | | +| 3 | Fusion 탭 | | | +| 4 | Evolution 탭 | | | +| 5 | Shop 탭 | | | +| 6 | Achievements 탭 | | | +| 7 | Settings 탭 | | | +| 8 | 방치형 자동 수입 | | | +| 9 | 오프라인 보상 | | | +| 10 | 프레스티지 | | | +| 11 | localStorage 저장/복원 | | | +| 12 | 콘솔 에러 없음 | | | +``` + +- [ ] **Step 2: plan 문서 커밋** + +```bash +git add docs/superpowers/plans/2026-04-27-week1-engine-migration.md +git commit -m "docs: log regression test results for Plan 1" +``` + +- [ ] **Step 3: 작업 브랜치를 master로 머지** + +```bash +git switch master +git merge --no-ff feat/vite-migration -m "feat: complete Week 1 engine migration to Vite (JSA-2)" +``` + +- [ ] **Step 4: 최종 상태 확인** + +Run: `git log --oneline -10` +Expected: 마이그레이션 커밋들이 master에 머지되어 있음. `pre-vite-migration` 태그는 그대로 보존. + +- [ ] **Step 5: Plan 2(Week 2 PWA + Push)로 진행 준비 완료** + +Plan 1은 여기서 종료. 다음 step은 `superpowers:writing-plans`를 다시 호출하여 Plan 2 작성. + +--- + +## Definition of Done (Plan 1) + +- [ ] `git tag pre-vite-migration` 존재 (롤백 안전망) +- [ ] `package.json`에 `@granite-js/*`, `@apps-in-toss/*`, `@toss/*`, `react-native` 의존성 모두 제거됨 +- [ ] `babel.config.js`, `granite.config.ts`, `require.context.ts`, `global.d.ts`, `pages/`, `src/_app.tsx`, `src/analytics.ts` 모두 삭제됨 +- [ ] `npm run typecheck` 통과 +- [ ] `npm run dev` 부팅 성공 +- [ ] `npm run build` 성공, `dist/` 산출 +- [ ] `npm run preview` 부팅 성공 +- [ ] Regression Test Log의 12개 항목 모두 PASS +- [ ] master 브랜치에 머지 완료