diff --git a/DATA_MODELS.md b/DATA_MODELS.md
index 5bd7ddf..fb2b4d2 100644
--- a/DATA_MODELS.md
+++ b/DATA_MODELS.md
@@ -655,6 +655,7 @@ interface MonitoringData extends WritingStats {
displayName: string; // 표시 이름
speedHistory: SpeedDataPoint[]; // 속도 히스토리 (최근 10개)
currentSpeed: number; // 현재 속도 (글자/분)
+ isActive: boolean; // 활성 상태 (false면 나간 상태)
preview?: string; // 미리보기 (선택적)
}
```
@@ -696,6 +697,7 @@ interface MonitoringData extends WritingStats {
lastUpdated: 1731398400000,
displayName: "철수",
currentSpeed: 420, // 글자/분 (클라이언트 계산)
+ isActive: true, // 활성 상태 (30초 타임아웃 체크)
speedHistory: [ // 최근 10개 (클라이언트 계산)
{ speed: 480, timestamp: 1731398370000 },
{ speed: 420, timestamp: 1731398375000 },
@@ -708,6 +710,21 @@ interface MonitoringData extends WritingStats {
}
```
+**활성 상태 판단 로직**:
+```typescript
+// 1. Firebase에서 데이터 수신 → isActive: true
+// 2. 30초 타임아웃 체크
+const timeSinceUpdate = Date.now() - lastUpdated;
+if (timeSinceUpdate > 30000) {
+ isActive = false; // 30초 이상 업데이트 없음 → 나감
+}
+
+// 3. Firebase에서 삭제되어도 마지막 통계 유지
+if (!newData[userId] && prevData[userId]) {
+ keep prevData[userId] with isActive: false;
+}
+```
+
#### 9.2. previewRequests (미리보기 요청)
**경로**: `previewRequests/{userId}/{requestId}`
diff --git a/PROJECT_STRUCTURE.md b/PROJECT_STRUCTURE.md
index 1ba9e90..a7acfe9 100644
--- a/PROJECT_STRUCTURE.md
+++ b/PROJECT_STRUCTURE.md
@@ -1,23 +1,42 @@
# 라온누리 - 프로젝트 구조
-> 최종 업데이트: 2025-11-12 (실시간 글쓰기 모니터링 시스템)
+> 최종 업데이트: 2025-11-13 (다국어 지원 시스템)
초등학생을 위한 창작 글쓰기 교육 플랫폼
+**최신 업데이트** (2025-11-13):
+- 🌏 **다국어 지원 시스템 (i18n)**
+ - next-intl 라이브러리 기반
+ - 한국어(ko), 영어(en) 지원
+ - **[locale] 라우팅**: URL에 언어 코드 표시 (`/ko/home`, `/en/home`)
+ - **브라우저 언어 자동 감지**: `Accept-Language` 헤더 기반 자동 리다이렉트
+ - **언어 전환 버튼**: LocaleSwitcher 컴포넌트 (Navbar 우측)
+ - **번역 파일**: `messages/ko.json`, `messages/en.json`
+ - **완성된 페이지**: Navbar, Landing 페이지, Home 페이지 전체 번역 완료
+ - **타입 안전**: useTranslations 훅으로 타입 체크
+ - **쿠키 저장**: 사용자가 선택한 언어 기억 (`NEXT_LOCALE`)
+ - **site.ts 텍스트 이동**: 사이트 메타데이터도 번역 파일로 관리
+
**최신 업데이트** (2025-11-12 PM):
- 📡 **실시간 글쓰기 모니터링 시스템**
- Firebase Realtime Database 기반 실시간 통신 (Redis Pub/Sub 방식)
- - 팀 주제 선택 시 해당 주제로 작성 중인 학생 실시간 표시
+ - **모든 팀 멤버 표시** (getUsersByTeam)
+ - **3가지 상태 관리 및 자동 정렬**:
+ - 🟢 작성 중 (isActive: true, lastUpdated < 30초) - 초록 배지, 핑크 테두리
+ - 🟠 나감 (isActive: false, 마지막 통계 유지) - 주황 배지, 주황 테두리
+ - ⚪ 대기 중 (한 번도 작성 안 함) - 회색 배지, 투명도 60%
- 5초 주기 자동 업데이트 (글자 수, 단어 수, lastUpdated 타임스탬프)
- - **작성 속도 계산** (클라이언트 측, 글자/분 단위)
- - **Sparkline 그래프** (Area Chart, 최근 10개 데이터 포인트)
- - **인터랙티브 툴팁** (마우스 오버 시 속도 + 몇 초 전 데이터인지 표시)
- - 미리보기 요청-응답 시스템 (관리자 클릭 → 학생 응답 → Dialog 표시)
- - onDisconnect() 자동 정리 (페이지 이탈 시 세션 자동 삭제)
- - LiveWritingMonitor 컴포넌트 (주제 Select, 실시간 카드 그리드)
+ - **작성 속도 계산** (클라이언트 측, charDiff * 12 = 글자/분)
+ - **Sparkline 그래프** (Area Chart, 최근 10개 데이터 포인트, 0 표시)
+ - **인터랙티브 툴팁** (마우스 오버 시 속도 + 몇 초 전 데이터)
+ - 미리보기 요청-응답 (관리자 클릭 → 학생 응답 → Dialog, 작성 중만)
+ - **30초 타임아웃**: 업데이트 없으면 "나감" 처리
+ - **마지막 통계 유지**: Firebase 삭제되어도 클라이언트 상태 유지
+ - onDisconnect() 자동 정리 (페이지 이탈 시 세션 삭제)
+ - LiveWritingMonitor 컴포넌트 (주제 Select, StudentMonitorCard)
- WritingSessionManager (Realtime DB 작업, 상세 디버그 로그)
- 완전 무료 (동시 접속 100명까지, 1GB/day)
- - Security Rules로 권한 관리 (본인 쓰기, 인증된 사용자 읽기)
+ - Security Rules (본인 쓰기, 인증된 사용자 읽기)
**최신 업데이트** (2025-11-12 AM):
- ✅ **Writing API 구현 완료**
@@ -113,25 +132,39 @@
## 페이지 구조
+### 🌏 다국어 라우팅 (2025-11-13)
+
+모든 페이지는 `[locale]` 세그먼트를 통해 다국어를 지원합니다:
+- **한국어**: `/ko/*` (기본값)
+- **영어**: `/en/*`
+- **자동 감지**: 브라우저 언어 설정에 따라 첫 방문 시 자동 리다이렉트
+- **언어 전환**: Navbar의 지구본 버튼(🌐 KO/EN) 클릭
+
+**URL 예시**:
+- 랜딩: `/ko`, `/en`
+- 홈: `/ko/home`, `/en/home`
+- 글쓰기: `/ko/write`, `/en/write`
+- 팀: `/ko/team`, `/en/team`
+
### ✅ 구현 완료
-| 페이지 | 경로 | 설명 | 주요 기능 |
-|-------|------|------|---------|
-| **랜딩 페이지** | `/` | 서비스 소개 및 홍보 (비로그인 전용) | Hero, Features, How It Works, CTA, Footer
로그인 시 `/home`으로 자동 리다이렉트 |
-| **유저 홈** | `/home` | 인증된 사용자 대시보드 | 환영 메시지, 빠른 시작 대시보드, 최근 활동
비로그인 시 `/`로 자동 리다이렉트
정식 계정은 "내 팀" 카드 추가 표시 |
-| **글쓰기** | `/write` | Tiptap 기반 순수 텍스트 에디터 + 주제 선택 | 주제 선택 (자유 주제/개인 주제/팀 주제)
🆕 **주제 변경 경고** (작성 중 내용 초기화 알림, 임시 저장 안내)
제목 입력 (Editable), 순수 텍스트 에디터 (포맷팅 없음)
🆕 **다중 글조각 관리** (최대 10개), "새 글쓰기" / "저장된 글조각" 버튼
🆕 **강화된 자동 저장** (2초 debounce, 저장 상태 표시: 저장 중/저장됨)
템플릿 미리채우기 (제목/내용), Firestore 저장
비로그인도 접근 가능 (저장 시 로그인 유도) |
-| **테스트** | `/test` | 팀 코드 시스템 테스트 페이지 | 팀 코드 생성/검증 테스트
팀/학생 생성 테스트
학생 로그인 테스트
authStore 상태 확인 |
-| **팀 목록** | `/team` | 내가 만든 팀 목록 (정식 계정 전용) | 팀 카드 그리드, 팀 정보 (코드, 멤버 수, 보안 설정)
"새 팀 만들기" 버튼 |
-| **팀 생성** | `/team/create` | 새 팀 만들기 (정식 계정 전용) | 팀 이름 입력, 팀 코드 자동 생성
🆕 **5단계 보안 레벨 선택** (RadioCard, 애니메이션)
🆕 **명단 관리 (Level 2/4)**: TagsInput으로 Enter/쉼표 입력
생성 후 `/team/[teamId]`로 이동 |
-| **팀 멤버 페이지** | `/team/[teamId]` | 팀 멤버용 페이지 (멤버/소유자 모두 접근) | 팀 정보, 팀 코드 복사, 멤버 목록
소유자는 "팀 관리" 버튼 표시
팀 코드 로그인 후 기본 이동 페이지 |
-| **팀 관리** | `/team/[teamId]/manage` | 팀 관리 페이지 (소유자 전용) | 팀 정보 및 코드, 보안 설정 표시
**팀 주제 관리 (생성/삭제)**
🆕 **주제별 학생 분석** (TopicMemberAnalysisSection)
멤버 목록 및 관리
🆕 **멤버 메뉴 - 팀 내 글 분석** (by-team)
"멤버 페이지 보기" 버튼 |
+| 페이지 | 경로 | 설명 | 주요 기능 | 다국어 |
+|-------|------|------|---------|--------|
+| **랜딩 페이지** | `/[locale]` | 서비스 소개 및 홍보 (비로그인 전용) | Hero, Features, How It Works, CTA, Footer
로그인 시 `/home`으로 자동 리다이렉트
🆕 **전체 번역 완료** (사이트명, 태그라인, 모든 섹션) | ✅ 완료 |
+| **유저 홈** | `/[locale]/home` | 인증된 사용자 대시보드 | 환영 메시지, 빠른 시작 대시보드, 최근 활동
비로그인 시 `/`로 자동 리다이렉트
정식 계정은 "내 팀" 카드 추가 표시
🆕 **전체 번역 완료** (웰컴 메시지, 모든 액션 카드) | ✅ 완료 |
+| **글쓰기** | `/[locale]/write` | Tiptap 기반 순수 텍스트 에디터 + 주제 선택 | 주제 선택 (자유 주제/개인 주제/팀 주제)
🆕 **주제 변경 경고** (작성 중 내용 초기화 알림, 임시 저장 안내)
제목 입력 (Editable), 순수 텍스트 에디터 (포맷팅 없음)
🆕 **다중 글조각 관리** (최대 10개), "새 글쓰기" / "저장된 글조각" 버튼
🆕 **강화된 자동 저장** (2초 debounce, 저장 상태 표시: 저장 중/저장됨)
템플릿 미리채우기 (제목/내용), Firestore 저장
비로그인도 접근 가능 (저장 시 로그인 유도) | 🔜 예정 |
+| **테스트** | `/[locale]/test` | 팀 코드 시스템 테스트 페이지 | 팀 코드 생성/검증 테스트
팀/학생 생성 테스트
학생 로그인 테스트
authStore 상태 확인 | 🔜 예정 |
+| **팀 목록** | `/[locale]/team` | 내가 만든 팀 목록 (정식 계정 전용) | 팀 카드 그리드, 팀 정보 (코드, 멤버 수, 보안 설정)
"새 팀 만들기" 버튼 | 🔜 예정 |
+| **팀 생성** | `/[locale]/team/create` | 새 팀 만들기 (정식 계정 전용) | 팀 이름 입력, 팀 코드 자동 생성
🆕 **5단계 보안 레벨 선택** (RadioCard, 애니메이션)
🆕 **명단 관리 (Level 2/4)**: TagsInput으로 Enter/쉼표 입력
생성 후 `/team/[teamId]`로 이동 | 🔜 예정 |
+| **팀 멤버 페이지** | `/[locale]/team/[teamId]` | 팀 멤버용 페이지 (멤버/소유자 모두 접근) | 팀 정보, 팀 코드 복사, 멤버 목록
소유자는 "팀 관리" 버튼 표시
팀 코드 로그인 후 기본 이동 페이지 | 🔜 예정 |
+| **팀 관리** | `/[locale]/team/[teamId]/manage` | 팀 관리 페이지 (소유자 전용) | 팀 정보 및 코드, 보안 설정 표시
**팀 주제 관리 (생성/삭제)**
🆕 **주제별 학생 분석** (TopicMemberAnalysisSection)
멤버 목록 및 관리
🆕 **멤버 메뉴 - 팀 내 글 분석** (by-team)
"멤버 페이지 보기" 버튼 | 🔜 예정 |
### 🚧 구현 예정
| 페이지 | 경로 | 설명 | 상태 |
|-------|------|------|------|
-| **학습하기** | `/learn` | 레슨/코스 학습 | ❌ 미구현 (Navbar 링크만 존재) |
-| **스티커** | `/stickers` | 스티커 컬렉션 | ❌ 미구현 (Navbar 링크만 존재) |
+| **학습하기** | `/[locale]/learn` | 레슨/코스 학습 | ❌ 미구현 (Navbar 링크만 존재) |
+| **스티커** | `/[locale]/stickers` | 스티커 컬렉션 | ❌ 미구현 (Navbar 링크만 존재) |
| **마이페이지** | Dialog (별도 페이지 없음) | 사용자 대시보드 다이얼로그 | 🔜 계획 중 (UserProfileButton에서 열림) |
---
@@ -185,15 +218,21 @@
| 컴포넌트 | 파일명 | 설명 | 상태 |
|---------|--------|------|------|
-| **Navbar** | `Navbar.tsx` | 상단 네비게이션 바 | ✅ 완료 |
+| **Navbar** | `Navbar.tsx` | 상단 네비게이션 바 (다국어 지원) | ✅ 완료 |
+| **LocaleSwitcher** | `LocaleSwitcher.tsx` | 🆕 **언어 전환 버튼** (KO ↔ EN, 지구본 아이콘) | ✅ 완료 |
-**네비게이션 링크**:
-- 홈 (`/` 또는 `/home`) - 인증 상태에 따라 동적 변경
- - 비로그인: `/` (랜딩 페이지)
- - 로그인: `/home` (유저 대시보드)
-- 글쓰기 (`/write`) - 미구현
-- 학습하기 (`/learn`) - 미구현
-- 스티커 (`/stickers`) - 미구현
+**네비게이션 링크** (다국어 지원):
+- 홈 (`/[locale]` 또는 `/[locale]/home`) - 인증 상태에 따라 동적 변경
+ - 비로그인: `/[locale]` (랜딩 페이지)
+ - 로그인: `/[locale]/home` (유저 대시보드)
+- 글쓰기 (`/[locale]/write`) - 미구현
+- 학습하기 (`/[locale]/learn`) - 미구현
+- 스티커 (`/[locale]/stickers`) - 미구현
+
+**주요 기능** (2025-11-13):
+- ✅ next-intl 타입 안전 Link 사용
+- ✅ useTranslations 훅으로 번역 텍스트 관리
+- ✅ LocaleSwitcher로 실시간 언어 전환
---
@@ -310,6 +349,11 @@
**주요 기능**:
- ✅ **LiveWritingMonitor** (2025-11-12):
- 주제 선택 드롭다운 (Chakra UI Select)
+ - **모든 팀 멤버 표시** (getUsersByTeam)
+ - **3가지 상태 관리 및 정렬**:
+ - 🟢 작성 중 (isActive: true, lastUpdated < 30초)
+ - 🟠 나감 (isActive: false, 마지막 통계 유지)
+ - ⚪ 대기 중 (한 번도 작성 안 함)
- StudentMonitorCard 컴포넌트 (개별 학생 카드)
- 실시간 통계: 글자 수, 단어 수, 마지막 업데이트 시간 ("N초 전")
- **작성 속도 계산** (클라이언트 측, charDiff * 12 = 글자/분)
@@ -320,9 +364,11 @@
- **인터랙티브 툴팁** (Recharts Tooltip)
- 속도 값 ("N자/분")
- 시간 정보 ("N초 전")
- - 미리보기 버튼 (Dialog로 현재 작성 중인 글 표시)
+ - 미리보기 버튼 (작성 중인 학생만)
+ - **30초 타임아웃**: 업데이트 없으면 "나감" 처리
+ - **마지막 통계 유지**: 나간 학생도 통계 표시
- 실시간 구독/구독 해제 (useEffect cleanup)
- - 빈 상태 UI (작성 중인 학생 없을 때)
+ - 시각적 구분 (색상별 테두리, 아바타, 배지)
- ✅ SecurityLevelSelector: RadioCard 기반, 선택 시 추가 UI 애니메이션으로 표시
- ✅ AllowListManager: Level 2/4용 명단 관리, Enter/쉼표로 구분 입력
- ✅ 그라데이션 배경 (RadioCard와 자연스럽게 연결)
@@ -514,35 +560,45 @@
project_w/
├── database.rules.json # 🆕 Firebase Realtime DB Security Rules
│
+├── messages/ # 🆕 다국어 번역 파일
+│ ├── ko.json # 한국어 번역
+│ └── en.json # 영어 번역
+│
├── src/
+├── i18n/ # 🆕 다국어 설정
+│ ├── routing.ts # 라우팅 설정 (locales, defaultLocale)
+│ └── request.ts # 번역 메시지 로더
+│
+├── middleware.ts # 🆕 next-intl 미들웨어 (언어 감지, 리다이렉트)
+│
├── app/ # Next.js App Router
-│ ├── layout.tsx # 루트 레이아웃
-│ ├── page.tsx # 랜딩 페이지 (/) - 로그인 시 /home 리다이렉트
-│ ├── home/
-│ │ └── page.tsx # ✅ 유저 홈 페이지 (/home)
-│ ├── write/
-│ │ └── page.tsx # ✅ 글쓰기 페이지 (/write) - 🆕 실시간 모니터링 전송
-│ ├── test/
-│ │ └── page.tsx # ✅ 테스트 페이지 (/test) - 팀 코드 시스템 테스트
+│ ├── layout.tsx # 루트 레이아웃 (메타데이터만)
+│ ├── [locale]/ # 🆕 다국어 라우팅
+│ │ ├── layout.tsx # locale별 레이아웃 (Provider, Navbar, Auth)
+│ │ ├── page.tsx # ✅ 랜딩 페이지 (/[locale]) - 🆕 전체 번역 완료
+│ │ ├── home/
+│ │ │ └── page.tsx # ✅ 유저 홈 페이지 (/[locale]/home) - 🆕 전체 번역 완료
+│ │ ├── write/
+│ │ │ └── page.tsx # ✅ 글쓰기 페이지 (/[locale]/write) - 🆕 실시간 모니터링
+│ │ ├── test/
+│ │ │ └── page.tsx # ✅ 테스트 페이지 (/[locale]/test)
+│ │ └── team/ # ✅ 팀 관리 페이지들
+│ │ ├── page.tsx # ✅ 팀 목록 (/[locale]/team)
+│ │ ├── create/
+│ │ │ └── page.tsx # ✅ 팀 생성 (/[locale]/team/create)
+│ │ └── [teamId]/
+│ │ ├── page.tsx # ✅ 팀 멤버 페이지 (/[locale]/team/[teamId])
+│ │ └── manage/
+│ │ └── page.tsx # ✅ 팀 관리 페이지 (/[locale]/team/[teamId]/manage)
│ ├── globals.css
-│ ├── favicon.ico
-│ ├── team/ # ✅ 팀 관리 페이지들
-│ │ ├── page.tsx # ✅ 팀 목록 (/team)
-│ │ ├── create/
-│ │ │ └── page.tsx # ✅ 팀 생성 (/team/create)
-│ │ └── [teamId]/
-│ │ ├── page.tsx # ✅ 팀 멤버 페이지 (/team/[teamId])
-│ │ └── manage/
-│ │ └── page.tsx # ✅ 팀 관리 페이지 (/team/[teamId]/manage)
-│ └── [미구현 페이지들]
-│ ├── learn/ # 학습 페이지
-│ ├── stickers/ # 스티커 페이지
-│ └── admin/ # 관리자
+│ └── favicon.ico
│
├── components/ # React 컴포넌트
│ ├── auth/ # ✅ 인증 (로그인, 프로필, 팀 코드)
-│ ├── landing/ # ✅ 랜딩 페이지 카드들
-│ ├── navigation/ # ✅ 네비게이션 바
+│ ├── landing/ # ✅ 랜딩 페이지 카드들 (다국어 지원)
+│ ├── navigation/ # ✅ 네비게이션 바 (다국어 지원)
+│ │ ├── Navbar.tsx # 다국어 링크 텍스트
+│ │ └── LocaleSwitcher.tsx # 🆕 언어 전환 버튼
│ ├── seo/ # ✅ SEO 컴포넌트
│ ├── ui/ # ✅ Chakra UI 기본
│ ├── writing/ # ✅ 글쓰기 에디터
diff --git a/ROADMAP.md b/ROADMAP.md
index 5fc7923..33d1bc0 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -1,6 +1,6 @@
# 라온누리 - 개발 로드맵
-> 최종 업데이트: 2025-11-12 (실시간 글쓰기 모니터링 시스템)
+> 최종 업데이트: 2025-11-13 (다국어 지원 시스템)
초등학생을 위한 창작 글쓰기 교육 플랫폼 개발 계획
@@ -83,7 +83,8 @@
| **Writing API 구현** | **POST /api/writing (글 생성), GET/PUT/DELETE /api/writing/[id] (조회/수정/삭제), POST /api/writing/user (목록), POST /api/writing/recent (최근 글), src/lib/server/writing.ts (Firestore CRUD), 권한 체크 (작성자만), 텍스트 통계 자동 계산** | **2025-11-12** |
| **패턴 분석 - 팀 소유자 기능** | **3가지 분석 타입 (self/by-team/by-topic), API 권한 체크, 팀 주제 필터링, WritingPatternDialog Props 확장, TopicMemberAnalysisSection (UI만), 팀 관리 페이지에 멤버 분석 메뉴** | **2025-11-12** |
| **Content Hash 기반 3단계 캐싱** | **글 목록 해시 생성 (id+updatedAt), L1: localStorage (영구, LRU 10개), L2: Firestore patternAnalyses (영구), L3: Server in-memory (5분, 50개), 변경 자동 감지, AI 비용 절감 (전체 사용자 기준 1회 분석), contentHash.ts + patternCacheManager.ts + patternAnalysis.ts** | **2025-11-12** |
-| **실시간 글쓰기 모니터링** | **Firebase Realtime Database 기반 실시간 통신 (Redis Pub/Sub 방식), WritingSessionManager (5초 주기 통계 전송, 미리보기 요청-응답, 상세 디버그 로그), LiveWritingMonitor 컴포넌트 (주제 Select, 실시간 카드 그리드, StudentMonitorCard), 글쓰기 페이지 모니터링 전송 (contentRef로 최적화), onDisconnect() 자동 정리, 작성 속도 계산 (클라이언트 측, 글자/분), Sparkline 그래프 (Area Chart, 최근 10개 히스토리, 0도 표시), 인터랙티브 툴팁 (속도 + 몇 초 전 데이터), 마지막 업데이트 시간 표시, database.rules.json (topicId 레벨 읽기 권한), @chakra-ui/charts + recharts 패키지, 완전 무료 (100명까지)** | **2025-11-12** |
+| **실시간 글쓰기 모니터링** | **Firebase Realtime Database 기반 실시간 통신 (Redis Pub/Sub 방식), WritingSessionManager (5초 주기 통계 전송, 미리보기 요청-응답, 상세 디버그 로그), LiveWritingMonitor (주제 Select, 모든 팀 멤버 표시, StudentMonitorCard), 3가지 상태 관리 (작성 중/나감/대기 중, 정렬 순서, 색상별 시각화), 글쓰기 페이지 모니터링 (contentRef 최적화), onDisconnect() 자동 정리, 작성 속도 계산 (클라이언트, 글자/분, charDiff*12), Sparkline 그래프 (Area Chart, 최근 10개, 0 표시), 인터랙티브 툴팁 (속도+시간), 30초 타임아웃 체크 (lastUpdated), 마지막 통계 유지 (Firebase 삭제되어도 유지), database.rules.json (topicId 레벨 읽기), @chakra-ui/charts+recharts, 완전 무료 (100명)** | **2025-11-12** |
+| **다국어 지원 시스템 (i18n)** | **next-intl 라이브러리 설치 및 설정, [locale] 라우팅 구조 변경 (/ko/*, /en/*), middleware.ts 생성 (브라우저 언어 자동 감지, Accept-Language 헤더), 번역 파일 생성 (messages/ko.json, messages/en.json), Navbar 번역 적용 (useTranslations 훅), LocaleSwitcher 컴포넌트 (지구본 아이콘 + KO/EN 토글), Landing 페이지 전체 번역 (Hero, Features, Steps, CTA, Footer), Home 페이지 전체 번역 (Welcome, QuickStart 9개 액션 카드, RecentActivity), site.ts 텍스트를 번역 파일로 이동 (사이트명, 태그라인, 저작권), localeDetection: true 설정, NEXT_LOCALE 쿠키 저장, next.config.ts에 next-intl 플러그인 추가, i18n/routing.ts + i18n/request.ts 설정, 타입 체크 통과** | **2025-11-13** |
### 🚧 진행 중
diff --git a/TECH_STACK.md b/TECH_STACK.md
index 9567cb9..9613e61 100644
--- a/TECH_STACK.md
+++ b/TECH_STACK.md
@@ -1,6 +1,6 @@
# 라온누리 - 기술 스택 및 개발 환경
-> 최종 업데이트: 2025-11-12 (실시간 글쓰기 모니터링 시스템)
+> 최종 업데이트: 2025-11-13 (다국어 지원 시스템)
---
@@ -25,6 +25,7 @@
| **Framer Motion** | 12.23.24 | 애니메이션 라이브러리 |
| **React Icons** | 5.5.0 | 아이콘 세트 |
| **Tiptap** | latest | 리치 텍스트 에디터 |
+| **next-intl** | latest | 🆕 **다국어 지원 (i18n)** |
### Backend & Database
@@ -526,12 +527,20 @@ LiveWritingMonitor 컴포넌트
- **LiveWritingMonitor** (`src/components/team/LiveWritingMonitor.tsx`):
- 주제 선택 Select 컴포넌트 (Chakra UI Select)
- - 실시간 학생 카드 그리드 (StudentMonitorCard)
+ - **모든 팀 멤버 표시** (getUsersByTeam)
+ - **3가지 상태 관리**:
+ - 🟢 작성 중 (isActive: true, lastUpdated < 30초) - 초록 배지, 핑크 테두리
+ - 🟠 나감 (isActive: false, 마지막 통계 유지) - 주황 배지, 주황 테두리
+ - ⚪ 대기 중 (한 번도 작성 안 함) - 회색 배지, 투명도 60%
+ - **정렬 순서**: 작성 중 → 나감 → 대기 중
+ - StudentMonitorCard 컴포넌트 (개별 학생 카드)
- 유저 정보와 통계 자동 결합 (userManager 활용)
- **작성 속도 실시간 계산** (클라이언트 측, 글자/분)
- **Sparkline 그래프** (Area Chart, 최근 10개 히스토리)
- **인터랙티브 툴팁** (속도 값 + 몇 초 전 데이터)
- - 미리보기 Dialog
+ - 미리보기 Dialog (작성 중인 학생만)
+ - **30초 타임아웃**: 업데이트 없으면 "나감" 처리
+ - **마지막 통계 유지**: Firebase 삭제되어도 클라이언트 상태 유지
- 마지막 업데이트 시간 표시 ("N초 전")
**Realtime DB 구조**:
@@ -1162,6 +1171,257 @@ interface Draft {
---
+### 12. 다국어 지원 시스템 (i18n)
+
+#### 핵심 개념
+
+**목적**: 한국어/영어 사용자 모두 접근 가능한 글로벌 플랫폼 구축
+
+**라이브러리**: next-intl (Next.js App Router 표준 i18n 라이브러리)
+
+**지원 언어**:
+- 한국어 (ko) - 기본값
+- 영어 (en)
+
+#### 아키텍처
+
+```
+브라우저 요청 (/)
+ ↓
+Middleware (src/middleware.ts)
+ ├─> Accept-Language 헤더 확인
+ ├─> NEXT_LOCALE 쿠키 확인
+ └─> 적절한 locale로 리다이렉트
+ ├─> 한국어 우선: /ko
+ └─> 영어 우선: /en
+ ↓
+[locale] 라우팅 (src/app/[locale]/*)
+ ├─> layout.tsx (locale별 레이아웃)
+ │ ├─> NextIntlClientProvider (번역 메시지 주입)
+ │ ├─> Provider (Chakra UI)
+ │ ├─> Navbar (다국어 메뉴)
+ │ └─> AuthInitializer
+ │
+ └─> page.tsx (각 페이지)
+ └─> useTranslations('namespace') 훅 사용
+ └─> {t('key')} 형태로 번역 표시
+```
+
+#### 번역 파일 구조
+
+**파일 위치**: `messages/{locale}.json`
+
+```json
+// messages/ko.json
+{
+ "site": {
+ "name": "라온누리",
+ "tagline": "재미있게 글쓰기를 배워보자!",
+ "subtitle": "친구들과 함께 신나는 글쓰기 모험을 떠나요"
+ },
+ "navbar": {
+ "home": "홈",
+ "write": "글쓰기",
+ "learn": "학습하기",
+ "stickers": "스티커"
+ },
+ "landing": {
+ "hero": {
+ "cta": "지금 시작하기",
+ "teamCode": "팀 코드로 참여"
+ },
+ "features": {...},
+ "howItWorks": {...}
+ },
+ "home": {
+ "hero": {
+ "welcome": "환영합니다, {name}님!", // 파라미터 지원
+ "subtitle": "오늘도 멋진 글쓰기를 시작해볼까요?"
+ },
+ "quickStart": {...}
+ }
+}
+```
+
+#### 설정 파일
+
+**i18n/routing.ts** - 라우팅 설정:
+```typescript
+export const routing = defineRouting({
+ locales: ['ko', 'en'],
+ defaultLocale: 'ko',
+ localePrefix: 'always', // URL에 항상 표시 (/ko/*, /en/*)
+ localeDetection: true // 브라우저 언어 자동 감지
+});
+
+// next-intl 타입 안전 내비게이션 API
+export const {Link, redirect, usePathname, useRouter} = createNavigation(routing);
+```
+
+**i18n/request.ts** - 번역 메시지 로더:
+```typescript
+export default getRequestConfig(async ({requestLocale}) => {
+ let locale = await requestLocale;
+
+ if (!locale || !routing.locales.includes(locale)) {
+ locale = routing.defaultLocale;
+ }
+
+ return {
+ locale,
+ messages: (await import(`../../messages/${locale}.json`)).default
+ };
+});
+```
+
+**middleware.ts** - 자동 언어 감지:
+```typescript
+import createMiddleware from 'next-intl/middleware';
+import {routing} from './i18n/routing';
+
+export default createMiddleware(routing);
+
+export const config = {
+ matcher: ['/', '/(ko|en)/:path*']
+};
+```
+
+#### 컴포넌트 사용 패턴
+
+**Server Component** (기본):
+```typescript
+import {useTranslations} from 'next-intl';
+
+export default function Page() {
+ const t = useTranslations('namespace');
+
+ return
{t('key')}
; +} +``` + +**파라미터가 있는 번역**: +```typescript +const t = useTranslations('home'); + +// messages/ko.json: "welcome": "환영합니다, {name}님!" +