Skip to content

Commit 09afb73

Browse files
committed
simulator 변경
1 parent 523da0d commit 09afb73

61 files changed

Lines changed: 3580 additions & 19 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 382 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,382 @@
1+
# Store 상태 관리 리팩토링 계획
2+
3+
## 🎯 목표
4+
1. `needsRegistration` boolean을 `AppUser.status`로 통합
5+
2. `pageTitle`, `pageSubtitle`, `pageLanguage``PageInfo` 객체로 통합
6+
7+
상태 일관성 강화 및 확장성 개선
8+
9+
---
10+
11+
## 📋 변경 사항
12+
13+
### 1. AppUser 인터페이스 수정
14+
```typescript
15+
// Before
16+
export interface AppUser {
17+
id: string;
18+
nickname: string;
19+
role: 'user' | 'admin';
20+
oauthAccounts: OAuthAccountInfo[];
21+
}
22+
23+
// After
24+
export interface AppUser {
25+
id: string;
26+
nickname: string;
27+
role: 'user' | 'admin';
28+
oauthAccounts: OAuthAccountInfo[];
29+
status: 'new' | 'registered'; // 신규 필드
30+
}
31+
```
32+
33+
### 2. Store 수정
34+
```typescript
35+
// Before
36+
interface Store {
37+
appUser: AppUser | null;
38+
needsRegistration: boolean;
39+
setNeedsRegistration: (needs: boolean) => void;
40+
}
41+
42+
// After
43+
interface Store {
44+
appUser: AppUser | null;
45+
// needsRegistration 제거
46+
setAppUser: (user: AppUser | null) => void;
47+
}
48+
```
49+
50+
### 3. 사용 시점 변경
51+
```typescript
52+
// Before
53+
if (needsRegistration) { ... }
54+
55+
// After
56+
if (appUser?.status === 'new') { ... }
57+
```
58+
59+
---
60+
61+
## 🔍 영향 범위
62+
63+
### 수정 파일
64+
- `packages/frontend/src/stores/store.ts`
65+
- `packages/frontend/src/layouts/MainLayout.tsx`
66+
- `packages/frontend/src/components/NicknameModal.tsx`
67+
- `packages/frontend/src/router.tsx`
68+
- `packages/frontend/src/services/firebase.ts`
69+
- `packages/frontend/src/layouts/Sidebar.tsx`
70+
71+
### 백엔드 조정 필요
72+
- `/users/me` API 응답에 `status` 필드 추가
73+
74+
---
75+
76+
## ✅ 검증 항목
77+
- [ ] NicknameModal이 `appUser?.status === 'new'` 감지
78+
- [ ] 라우팅이 정상 동작
79+
- [ ] 신규 사용자 온보딩 플로우 테스트
80+
- [ ] TypeScript 컴파일 에러 없음
81+
82+
---
83+
84+
## 📈 향후 확장
85+
```typescript
86+
status: 'new' | 'registered' | 'suspended' | 'banned' | 'deleted'
87+
```
88+
추가 상태 관리 용이
89+
90+
---
91+
92+
## 🎨 UI State 리팩토링
93+
94+
### 문제점
95+
- `pageTitle`, `pageSubtitle`, `pageLanguage` 분산 관리
96+
- 실제로는 `setPageTitle()`에서 한 번에 업데이트됨
97+
- 개발자가 셋의 관계를 파악하기 어려움
98+
99+
### 변경 사항
100+
101+
#### 1. PageInfo 인터페이스 생성
102+
```typescript
103+
// Before (산재 상태)
104+
interface Store {
105+
pageTitle: string;
106+
pageSubtitle: string;
107+
pageLanguage: SupportedLanguage | null;
108+
setPageTitle: (title: string, subtitle?: string, language?: SupportedLanguage | null) => void;
109+
}
110+
111+
// After (통합 상태)
112+
export interface PageInfo {
113+
title: string;
114+
subtitle: string;
115+
language: SupportedLanguage | null;
116+
}
117+
118+
interface Store {
119+
pageInfo: PageInfo;
120+
setPageInfo: (info: Partial<PageInfo>) => void;
121+
}
122+
```
123+
124+
#### 2. 사용 시점 변경
125+
```typescript
126+
// Before
127+
const { pageTitle, pageLanguage } = useStore();
128+
129+
// After
130+
const { pageInfo } = useStore();
131+
const { title, language } = pageInfo;
132+
```
133+
134+
#### 3. TopBar 컴포넌트 수정
135+
```typescript
136+
// Before
137+
<TopBar title={pageTitle} subtitle={pageSubtitle} />
138+
139+
// After
140+
<TopBar title={pageInfo.title} subtitle={pageInfo.subtitle} />
141+
```
142+
143+
---
144+
145+
## 🔍 영향 범위 (PageInfo)
146+
147+
### 수정 파일
148+
- `packages/frontend/src/stores/store.ts`
149+
- `packages/frontend/src/components/TopBar.tsx`
150+
- `packages/frontend/src/features/**/*Page.tsx` (모든 페이지 컴포넌트)
151+
152+
### 검색 후 수정 필요
153+
```bash
154+
grep -r "pageTitle\|pageLanguage" packages/frontend/src --include="*.tsx"
155+
```
156+
157+
---
158+
159+
## ✅ 전체 검증 항목
160+
161+
### Auth State
162+
- [ ] NicknameModal이 `appUser?.status === 'new'` 감지
163+
- [ ] 라우팅이 정상 동작
164+
- [ ] 신규 사용자 온보딩 플로우 테스트
165+
166+
### UI State (PageInfo)
167+
- [ ] TopBar가 `pageInfo.title`, `pageInfo.subtitle` 정상 표시
168+
- [ ] 모든 페이지에서 setPageInfo 호출 정상 작동
169+
- [ ] 페이지 이동 시 pageInfo 정상 업데이트
170+
171+
### 전체
172+
- [ ] TypeScript 컴파일 에러 없음
173+
- [ ] `pnpm dev` 정상 동작
174+
- [ ] 브라우저 콘솔 에러 없음
175+
176+
---
177+
178+
## 🏫 API 도메인 분리: /courses 리팩토링
179+
180+
### 문제점
181+
- 콘텐츠 조회와 사용자 진행 상태가 같은 경로에 혼재
182+
- 인증 정책이 일관되지 않음 (일부는 필수, 일부는 선택적)
183+
- 사용자 개인 데이터가 글로벌 콘텐츠와 섞여 있음
184+
185+
### 현재 구조
186+
```
187+
GET /api/courses/languages ✅ 콘텐츠 (인증 불필요)
188+
GET /api/courses/chapters/:id ✅ 콘텐츠
189+
GET /api/courses/lessons/:id ✅ 콘텐츠
190+
GET /api/courses/progress ❌ 사용자 데이터 (혼재!)
191+
POST /api/courses/progress ❌ 사용자 데이터 (혼재!)
192+
GET /api/courses/chapters/:id/progress ❌ 사용자 데이터 (혼재!)
193+
```
194+
195+
### 개선된 구조
196+
197+
#### 1. 콘텐츠 API (글로벌, 캐시 가능)
198+
```typescript
199+
// 변화 없음 - 그대로 유지
200+
GET /api/courses/languages
201+
GET /api/courses/:lang/chapters
202+
GET /api/courses/chapters/:id
203+
GET /api/courses/lessons/:id
204+
GET /api/courses/:id (언어 상세)
205+
```
206+
207+
#### 2. 사용자 진행 상태 (개인 데이터, 이동 필요)
208+
```typescript
209+
// Before
210+
GET /api/courses/progress
211+
POST /api/courses/progress
212+
GET /api/courses/chapters/:id/progress
213+
214+
// After (사용자 모듈으로 이동)
215+
GET /api/users/progress
216+
POST /api/users/progress
217+
GET /api/users/chapters/:id/progress
218+
```
219+
220+
### 변경 이유
221+
1. **도메인 분리**: 콘텐츠(자료) ≠ 사용자 상태
222+
2. **인증 정책**: 콘텐츠는 공개, 진행상태는 개인
223+
3. **RESTful 원칙**: `/users/progress` (내 진행상태)가 더 자연스러움
224+
4. **캐싱 최적화**: 콘텐츠만 캐싱 가능
225+
5. **권한 관리**: 사용자 모듈에서 통합 관리
226+
227+
### 수정 파일
228+
229+
#### Frontend
230+
```bash
231+
grep -r "/courses/progress\|/courses/chapters.*progress" packages/frontend/src --include="*.tsx" --include="*.ts"
232+
```
233+
- API 호출 경로 변경: `/courses/progress``/users/progress`
234+
- 관련 훅, 서비스 파일 수정
235+
236+
#### Backend
237+
```
238+
packages/backend/src/modules/courses/routes.ts
239+
- GET /progress 제거
240+
- POST /progress 제거
241+
- GET /chapters/:id/progress 제거
242+
243+
packages/backend/src/modules/users/routes.ts
244+
- GET /progress 추가
245+
- POST /progress 추가
246+
- GET /chapters/:id/progress 추가
247+
248+
packages/backend/src/modules/courses/service.ts
249+
- 진행 상태 함수들 유지 (재사용)
250+
251+
packages/backend/src/modules/users/service.ts
252+
- 진행 상태 함수들 임포트하여 사용
253+
```
254+
255+
### 검증 항목
256+
- [ ] `/api/users/progress` GET 정상 동작
257+
- [ ] `/api/users/progress` POST 정상 동작
258+
- [ ] `/api/users/chapters/:id/progress` 정상 동작
259+
- [ ] `/api/courses/*` 콘텐츠 API 변화 없음
260+
- [ ] 진행 상태 업데이트 → 스트릭 갱신 연동 확인
261+
- [ ] Frontend API 호출 경로 모두 변경됨
262+
- [ ] TypeScript 컴파일 에러 없음
263+
264+
---
265+
266+
## 📚 레슨 콘텐츠 저장소 통합: DB vs JSON 중복 제거
267+
268+
### 문제점
269+
- **이중 로드**: DB에서 LessonContent 로드 → JSON 파일에서 다시 로드 (중복!)
270+
- **메모리 낭비**: DB 데이터 로드 후 JSON으로 덮어씀
271+
- **동기화 문제**: DB와 JSON이 불일치할 수 있음
272+
- **불완전한 데이터**: language 필드가 JSON에 없을 수 있음
273+
274+
### 현재 구조
275+
```
276+
DB (LessonContent): JSON 파일:
277+
- id ━━━━━━ -
278+
- lessonId ━━━━━━ -
279+
- code ━━━━━━━━ code (중복!)
280+
- language ━━━━━━ ?
281+
- steps (Json) ━━━━━━━━ steps (중복!)
282+
- createdAt ━━━━━━ -
283+
- updatedAt ━━━━━━ -
284+
```
285+
286+
### 로드 흐름 (비효율적)
287+
```typescript
288+
// 1. DB에서 LessonContent 로드
289+
const lesson = await prisma.lesson.findUnique({
290+
include: { content: true, quizzes: true }
291+
});
292+
293+
// 2. JSON 파일에서 다시 로드 (불필요!)
294+
const jsonContent = await lessonContentLoader.getContent(id);
295+
296+
// 3. 불필요한 병합
297+
mergedContent = {
298+
...lesson.content, // ← DB 데이터 (버려질 예정)
299+
code: jsonContent.code, // ← JSON으로 덮음 ❌
300+
steps: jsonContent.steps // ← JSON으로 덮음 ❌
301+
};
302+
```
303+
304+
### 권장 솔루션
305+
306+
**Option 1: DB만 사용 (추천) ✅**
307+
```typescript
308+
// before
309+
const lesson = await courseService.getLessonFull(id);
310+
const jsonContent = await lessonContentLoader.getContent(id);
311+
// 병합...
312+
313+
// after
314+
const lesson = await courseService.getLessonFull(id);
315+
// 그냥 lesson.content 사용 (JSON 파일 제거)
316+
```
317+
318+
**장점:**
319+
- 단일 소스 (DB만 신뢰)
320+
- DB 캐싱 자동 활용
321+
- 동기화 문제 해결
322+
- 관리 간단
323+
324+
**Option 2: JSON만 사용 (대안)**
325+
```typescript
326+
// LessonContent DB 테이블 제거
327+
// JSON 파일만 사용하여 콘텐츠 관리
328+
```
329+
330+
**장점:**
331+
- Git 버전 관리 용이
332+
- DB 마이그레이션 불필요
333+
- 가벼운 구조
334+
335+
### 추천: Option 1 (DB만 사용)
336+
337+
**이유:**
338+
1. 이미 모든 콘텐츠가 DB에 있음
339+
2. JSON 로더의 복잡성 제거
340+
3. 성능 개선 (이중 로드 제거)
341+
4. 타입 안전성 확보
342+
343+
### 변경 사항
344+
345+
#### Backend
346+
```
347+
packages/backend/src/modules/courses/routes.ts
348+
- getLessonFull() 호출만 유지
349+
- lessonContentLoader.getContent() 제거 ❌
350+
- 병합 로직 제거
351+
352+
packages/backend/src/services/lessonContentLoader.ts
353+
- 삭제 가능 (더 이상 사용 안 함)
354+
- 또는 보관 (JSON 마이그레이션 용도)
355+
```
356+
357+
#### 응답 구조 (변화 없음)
358+
```json
359+
{
360+
"id": "c-1-1",
361+
"title": "포인터 기초",
362+
"content": {
363+
"code": "...",
364+
"language": "c",
365+
"steps": [...]
366+
},
367+
"quizzes": [...]
368+
}
369+
```
370+
371+
### 검증 항목
372+
- [ ] GET `/api/courses/lessons/:id` 응답 동일
373+
- [ ] lesson.content가 완전한 데이터 포함
374+
- [ ] lessonContentLoader 제거 (또는 유지 결정)
375+
- [ ] 레슨 로드 성능 개선 측정 (이중 로드 제거)
376+
- [ ] JSON 파일 삭제 가능 확인
377+
- [ ] TypeScript 컴파일 에러 없음
378+
379+
### 성능 개선 예상
380+
- 로드 시간: ~50% 감소 (이중 로드 제거)
381+
- 메모리: DB 캐싱으로 추가 최적화
382+
- 코드 복잡성: 병합 로직 제거

0 commit comments

Comments
 (0)