diff --git a/API_SPEC.md b/API_SPEC.md
index 1ea434d..47a883e 100644
--- a/API_SPEC.md
+++ b/API_SPEC.md
@@ -861,333 +861,6 @@ await teamManager.removeMember(teamId, currentUser.uid);
---
-### 2. GET `/student/:id` - 학생 조회
-실제 URL: `GET /api/student/:id`
-
-**인증**: 선택적
-
-**Response**:
-```typescript
-{
- success: true,
- data: {
- student: Student;
- }
-}
-```
-
-**캐싱**: 클라이언트에서 5분간 캐싱
-
----
-
-### 3. POST `/student/by-uid` - Firebase UID로 학생 조회
-실제 URL: `POST /api/student/by-uid`
-
-**인증**: 필수 (Anonymous Auth)
-
-**Request**:
-```typescript
-{
- firebaseUid: string;
-}
-```
-
-**Response**:
-```typescript
-{
- success: true,
- data: {
- student: Student | null;
- }
-}
-```
-
-**용도**: 로그인 시 기존 학생 확인
-
-**캐싱**: 클라이언트에서 5분간 캐싱
-
----
-
-### 4. POST `/student/by-team` - 팀별 학생 목록
-실제 URL: `POST /api/student/by-team`
-
-**인증**: 선택적
-
-**Request**:
-```typescript
-{
- teamId: string;
-}
-```
-
-**Response**:
-```typescript
-{
- success: true,
- data: {
- students: Student[];
- }
-}
-```
-
-**캐싱**: 클라이언트에서 30초간 캐싱 (자주 변경)
-
----
-
-### 5. POST `/student/find-by-name` - 이름으로 학생 찾기
-실제 URL: `POST /api/student/find-by-name`
-
-**인증**: 선택적
-
-**Request**:
-```typescript
-{
- teamId: string;
- name: string;
-}
-```
-
-**Response**:
-```typescript
-{
- success: true,
- data: {
- student: Student | null;
- }
-}
-```
-
-**용도**: 학생 로그인 시 기존 학생 확인
-
----
-
-### 6. PUT `/student/:id` - 학생 정보 수정
-실제 URL: `PUT /api/student/:id`
-
-**인증**: 필수
-
-**Request**:
-```typescript
-{
- studentId: string;
- data: {
- name?: string;
- pinHash?: string;
- linkedUserId?: string;
- }
-}
-```
-
-**Response**:
-```typescript
-{
- success: true,
- data: {
- student: Student;
- }
-}
-```
-
-**캐시 무효화**: 해당 학생, 팀별 학생 목록
-
----
-
-### 7. POST `/student/kick` - 팀에서 학생 강퇴
-실제 URL: `POST /api/student/kick`
-
-**인증**: 필수
-
-**Request**:
-```typescript
-{
- studentId: string;
- teamId: string;
-}
-```
-
-**Response**:
-```typescript
-{
- success: true,
- data: {
- success: true;
- }
-}
-```
-
-**권한**: 팀 소유자만 강퇴 가능
-
-**부수 효과**:
-- `student.teamIds`에서 팀 제거
-- `team.studentIds`에서 학생 제거
-
-**캐시 무효화**: 해당 학생, 팀, 팀별 학생 목록
-
----
-
-### 8. POST `/student/update-pin` - PIN 변경
-실제 URL: `POST /api/student/update-pin`
-
-**인증**: 필수
-
-**Request**:
-```typescript
-{
- studentId: string;
- newPin: string; // 평문 (4자리 숫자)
-}
-```
-
-**Response**:
-```typescript
-{
- success: true,
- data: {
- success: true;
- }
-}
-```
-
-**검증**: 서버에서 PIN 형식 검증 (4자리 숫자)
-
-**보안**: PIN은 SHA-256 해시로 저장
-
-**캐시 무효화**: 해당 학생
-
----
-
-### 9. POST `/student/validate-pin` - PIN 검증
-실제 URL: `POST /api/student/validate-pin`
-
-**인증**: 선택적
-
-**Request**:
-```typescript
-{
- studentId: string;
- pin: string;
-}
-```
-
-**Response**:
-```typescript
-{
- success: true,
- data: {
- valid: boolean;
- }
-}
-```
-
-**용도**: 학생 로그인 시 PIN 확인
-
----
-
-### 10. POST `/student/link` - 정식 계정 연결
-실제 URL: `POST /api/student/link`
-
-**인증**: 필수 (정식 계정)
-
-**Request**:
-```typescript
-{
- studentId: string;
- // userId는 Authorization 헤더에서 추출
-}
-```
-
-**Response**:
-```typescript
-{
- success: true,
- data: {
- success: true;
- }
-}
-```
-
-**권한**: 현재 로그인한 사용자와 연결
-
-**검증**:
-- 학생이 이미 다른 계정과 연결되어 있으면 에러
-
-**캐시 무효화**: 해당 학생, 내 학생 목록
-
----
-
-### 11. POST `/student/unlink` - 정식 계정 연결 해제
-실제 URL: `POST /api/student/unlink`
-
-**인증**: 필수
-
-**Request**:
-```typescript
-{
- studentId: string;
-}
-```
-
-**Response**:
-```typescript
-{
- success: true,
- data: {
- success: true;
- }
-}
-```
-
-**캐시 무효화**: 해당 학생, 내 학생 목록
-
----
-
-### 12. GET `/student/my-students` - 내 학생 목록
-실제 URL: `GET /api/student/my-students`
-
-**인증**: 필수 (정식 계정)
-
-**Response**:
-```typescript
-{
- success: true,
- data: {
- students: Student[];
- }
-}
-```
-
-**권한**: 로그인한 사용자가 소유한 학생만 조회 (`linkedUserId` 기준)
-
-**캐싱**: 클라이언트에서 1분간 캐싱
-
----
-
-### 13. POST `/student/update-last-login` - 마지막 로그인 시간 업데이트
-실제 URL: `POST /api/student/update-last-login`
-
-**인증**: 선택적
-
-**Request**:
-```typescript
-{
- studentId: string;
-}
-```
-
-**Response**:
-```typescript
-{
- success: true,
- data: {
- success: true;
- }
-}
-```
-
-**참고**: 실패해도 클라이언트는 에러를 무시 (크리티컬하지 않음)
-
----
-
## Writing API
### 1. POST `/writing` - 글 생성
diff --git a/PROJECT_STRUCTURE.md b/PROJECT_STRUCTURE.md
index 4731508..61f2791 100644
--- a/PROJECT_STRUCTURE.md
+++ b/PROJECT_STRUCTURE.md
@@ -1,10 +1,27 @@
# 라온누리 - 프로젝트 구조
-> 최종 업데이트: 2025-11-21 (홈 페이지 모듈화)
+> 최종 업데이트: 2025-11-22 (Server Component 전환 + 공통 컴포넌트)
초등학생을 위한 창작 글쓰기 교육 플랫폼
-**최신 업데이트** (2025-11-21):
+**최신 업데이트** (2025-11-22):
+- 🔄 **글 상세보기 페이지 Server Component 전환**
+ - **SEO 최적화**: 서버에서 HTML 생성 (검색엔진 크롤링 가능)
+ - **SNS 공유 미리보기**: 카카오톡/페이스북 링크 미리보기 지원
+ - **서버 데이터 로딩**: Firebase Admin SDK 사용 (`src/lib/server/writing.ts`)
+ - **성능 개선**: 초기 로딩 빠름 (데이터 포함된 HTML 전송)
+ - **권한 체크**: 서버에서 처리 (클라이언트 깜빡임 없음)
+ - `getTranslations()` 서버 번역 함수 사용
+ - `params: Promise<{locale, writingId}>` 타입 사용
+- 🔙 **BackButton 공통 컴포넌트 추가** (`src/components/layout/BackButton.tsx`)
+ - **기본 동작**: `router.back()` (브라우저 히스토리 뒤로)
+ - **옵션 1**: `href` prop으로 특정 경로 이동
+ - **옵션 2**: `label` prop으로 버튼 텍스트 커스터마이징
+ - **다국어 지원**: `t('interaction.back')` 기본 라벨
+ - **확장성**: ButtonProps 상속으로 모든 Button 속성 지원
+ - **재사용성**: 모든 페이지에서 일관된 뒤로가기 UX
+
+**업데이트** (2025-11-21):
- 📦 **홈 페이지 모듈화**
- **6개 컴포넌트 분리**: HeroSection, QuickActionCard, QuickActionsGrid, ViewAllWritingsCard, EmptyStateCard, RecentActivitySection
- **코드 감소**: 580줄 → 223줄 (62% 감소)
@@ -233,6 +250,7 @@
| **랜딩 페이지** | `/[locale]` | 서비스 소개 및 홍보 (비로그인 전용) | Hero, Features, How It Works, CTA, Footer
로그인 시 `/home`으로 자동 리다이렉트
🆕 **전체 번역 완료** (사이트명, 태그라인, 모든 섹션) | ✅ 완료 |
| **유저 홈** | `/[locale]/home` | 인증된 사용자 대시보드 | 환영 메시지, 빠른 시작 대시보드, **최근 활동 (최근 글 3개 표시)**
비로그인 시 `/`로 자동 리다이렉트
정식 계정은 "내 팀" 카드 추가 표시
🆕 **WritingCard Grid, "모두 보기" 버튼**
🆕 **전체 번역 완료** (웰컴 메시지, 모든 액션 카드) | ✅ 완료 |
| **내 글 모음** | `/[locale]/writings` | 🆕 **전체 글 목록 페이지** | 🆕 **사용자의 모든 글 표시 (Grid)**
🆕 **정렬 Select (최신순/오래된순)**
🆕 **WritingCard 컴포넌트 사용**
🆕 **Empty state 처리**
🆕 **전체 번역 완료** (ko/en/ja) | ✅ 완료 |
+| **글 상세보기** | `/[locale]/writing/[writingId]` | 🆕 **Server Component 기반 상세 페이지** | 🆕 **SEO 최적화** (서버에서 HTML 생성)
🆕 **SNS 공유 미리보기** (카카오톡/페이스북)
🆕 **서버 데이터 로딩** (Firebase Admin SDK)
제목, 내용, 생성된 이미지 표시
주제 및 팀 정보 Badge
프롬프트 Collapsible
🆕 **CommentList** (Client Component)
🆕 **BackButton** 공통 컴포넌트 사용 | ✅ 완료 |
| **글쓰기** | `/[locale]/write` | Tiptap 기반 순수 텍스트 에디터 + 주제 선택 | 주제 선택 (자유 주제/개인 주제/팀 주제)
🆕 **글 수정 기능 (URL params ?id=xxx)**
🆕 **수정 모드 배지 표시**
🆕 **주제 변경 경고** (작성 중 내용 초기화 알림, 임시 저장 안내)
제목 입력 (Editable), 순수 텍스트 에디터 (포맷팅 없음)
🆕 **다중 글조각 관리** (최대 10개), "새 글쓰기" / "저장된 글조각" 버튼
🆕 **강화된 자동 저장** (2초 debounce, 저장 상태 표시: 저장 중/저장됨)
🆕 **저장 시 AI 분석** (실시간 분석 제거, 저장 버튼 클릭 시 분석 수행)
🆕 **분석 결과 DB 저장** (WritingAnalysis + spellingErrors + contentHash)
템플릿 미리채우기 (제목/내용), Firestore 저장
비로그인도 접근 가능 (저장 시 로그인 유도) | ✅ 완료 |
| **테스트** | `/[locale]/test` | 팀 코드 시스템 테스트 페이지 | 팀 코드 생성/검증 테스트
팀/학생 생성 테스트
학생 로그인 테스트
authStore 상태 확인 | 🔜 예정 |
| **팀 목록** | `/[locale]/team` | 내가 만든 팀 목록 (정식 계정 전용) | 팀 카드 그리드, 팀 정보 (코드, 멤버 수, 보안 설정)
"새 팀 만들기" 버튼 | 🔜 예정 |
@@ -319,6 +337,36 @@
---
+### 📁 `src/components/layout/` - 레이아웃 공통 컴포넌트
+
+| 컴포넌트 | 파일명 | 설명 | 상태 |
+|---------|--------|------|------|
+| **BackButton** | `BackButton.tsx` | 🆕 **뒤로가기 버튼 공통 컴포넌트** (router.back() 또는 특정 경로, 커스텀 라벨 지원) | ✅ 완료 |
+
+**주요 기능**:
+- ✅ `router.back()` 기본 동작
+- ✅ `href` prop으로 특정 경로 이동 가능
+- ✅ `label` prop으로 버튼 텍스트 커스터마이징
+- ✅ 다국어 지원 (기본 라벨: `t('interaction.back')`)
+- ✅ ButtonProps 확장으로 모든 Button 속성 지원
+
+**사용 예시**:
+```tsx
+// 기본 사용 (router.back())
+
+
+// 특정 경로로 이동
+
+
+// 커스텀 라벨
+
+
+// 추가 스타일링
+
+```
+
+---
+
### 📁 `src/components/ui/` - UI 기본 컴포넌트
| 컴포넌트 | 파일명 | 설명 | 상태 |
@@ -531,7 +579,6 @@
| **UserManager** | `UserManager.ts` | 사용자 관련 API 호출 (생성, 조회, 수정, 닉네임 관리) **[NEW]** | ✅ 완료 |
| **DraftManager** | `DraftManager.ts` | 글조각 관리 (**localStorage + Realtime DB 하이브리드**, 기기 간 동기화, 최대 10개, CRUD, syncStatus) | ✅ 완료 |
| **WritingSessionManager** | `WritingSessionManager.ts` | 🆕 **실시간 글쓰기 세션 관리** (Firebase Realtime DB 작업) | ✅ 완료 |
-| ~~**StudentManager**~~ | ~~`StudentManager.ts`~~ | ~~학생 관련 API 호출~~ | ⚠️ Deprecated (UserManager로 대체) |
| **WritingManager** | `WritingManager.ts` | 글쓰기 관련 비즈니스 로직 (CRUD, 통계) | ✅ 완료 |
| **TopicManager** | `TopicManager.ts` | 주제 관련 비즈니스 로직 (CRUD, 템플릿 처리) | ✅ 완료 |
| **index.ts** | `index.ts` | 모든 매니저 export | ✅ 완료 |
@@ -790,6 +837,11 @@ project_w/
│ │ │ └── page.tsx # ✅ 유저 홈 페이지 (/[locale]/home) - 🆕 전체 번역 완료
│ │ ├── write/
│ │ │ └── page.tsx # ✅ 글쓰기 페이지 (/[locale]/write) - 🆕 실시간 모니터링
+│ │ ├── writing/ # ✅ 글 상세보기
+│ │ │ └── [writingId]/
+│ │ │ └── page.tsx # 🆕 글 상세 페이지 (Server Component, SEO 최적화)
+│ │ ├── writings/
+│ │ │ └── page.tsx # ✅ 내 글 모음 페이지 (/[locale]/writings)
│ │ ├── test/
│ │ │ └── page.tsx # ✅ 테스트 페이지 (/[locale]/test)
│ │ └── team/ # ✅ 팀 관리 페이지들
@@ -809,6 +861,8 @@ project_w/
│ ├── navigation/ # ✅ 네비게이션 바 (다국어 지원)
│ │ ├── Navbar.tsx # 다국어 링크 텍스트
│ │ └── LocaleSwitcher.tsx # 🆕 언어 전환 버튼
+│ ├── layout/ # 🆕 레이아웃 공통 컴포넌트
+│ │ └── BackButton.tsx # 🆕 뒤로가기 버튼 (router.back/href/label 지원)
│ ├── seo/ # ✅ SEO 컴포넌트
│ ├── ui/ # ✅ Chakra UI 기본
│ ├── writing/ # ✅ 글쓰기 에디터
diff --git a/README.md b/README.md
index dc8bd2d..54bf724 100644
--- a/README.md
+++ b/README.md
@@ -79,11 +79,10 @@ src/
├── managers/ # ✅ 비즈니스 로직 (API + 캐싱)
│ ├── ManagerBase.ts # 공통 기능 (authenticatedFetch, caching)
│ ├── TeamManager.ts # 팀 관련 API 호출
-│ ├── StudentManager.ts # 학생 관련 API 호출
│ ├── WritingManager.ts # 글쓰기 API 호출
│ └── TopicManager.ts # 주제 API 호출
├── types/ # TypeScript 타입
-│ ├── team.ts, student.ts, writing.ts, topic.ts
+│ ├── team.ts, writing.ts, topic.ts
│ └── api/ # API Request/Response 타입
├── config/ # Firebase 설정
├── services/ # Firebase Auth, Firestore
diff --git a/ROADMAP.md b/ROADMAP.md
index ca37d49..fadf1db 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -1,6 +1,6 @@
# 라온누리 - 개발 로드맵
-> 최종 업데이트: 2025-11-21 (홈 페이지 모듈화)
+> 최종 업데이트: 2025-11-22 (Server Component 전환)
초등학생을 위한 창작 글쓰기 교육 플랫폼 개발 계획
@@ -106,6 +106,10 @@
| **Firebase Cloud Functions Phase 1** | **functions/ 프로젝트 초기화 (Node.js 22, v2 API), 파일 분리 구조 (index.ts export만, 로직 별도 파일), cleanupExpiredReservations (매 시간, 팀 코드 예약 정리), cleanupExpiredDrafts (매일 새벽 3시, 180일 이상 drafts 정리), onTeamDeleted (Firestore Trigger, cascade 삭제), onWritingCreated (Firestore Trigger, 추후 자동 분석), 한글 로그, asia-northeast1 리전, firebase.json predeploy 수정 (lint 제거), 4개 함수 배포 완료** | **2025-11-19** |
| **주제 생성 Dialog 통합** | **CreateTeamTopicDialog 삭제 (674줄), CreateTopicDialog로 통합 (개인/팀 공용), TopicFormData export (데이터 반환 방식), onSubmit 콜백 패턴 (부모가 topicManager 호출), team.manage.teamTopics.dialog 번역 키 제거 (30개), topicSelector.createSuccess 추가, 652줄 코드 감소, 관심사 분리 (Dialog는 UI만, 비즈니스 로직은 부모)** | **2025-11-21** |
| **홈 페이지 모듈화** | **HeroSection/QuickActionCard/QuickActionsGrid/ViewAllWritingsCard/EmptyStateCard/RecentActivitySection 컴포넌트 분리 (src/components/home/), 580줄 → 223줄 (62% 감소), Semantic token 적극 활용 (bg/fg/border), Conditional style 객체 최소화, 재사용성 및 유지보수성 향상, JSX 주석 한글화** | **2025-11-21** |
+| **글 상세보기 Server Component 전환** | **src/app/[locale]/writing/[writingId]/page.tsx를 Server Component로 전환, SEO 최적화 (서버에서 HTML 생성), SNS 공유 미리보기 (카카오톡/페이스북 링크), Firebase Admin SDK 사용 (src/lib/server/writing.ts, getTopic, getTeam), getTranslations() 서버 번역 함수, params: Promise<{locale, writingId}> 타입, 초기 로딩 성능 개선 (데이터 포함된 HTML 전송), 서버 권한 체크 (클라이언트 깜빡임 없음)** | **2025-11-22** |
+| **BackButton 공통 컴포넌트** | **src/components/layout/BackButton.tsx 생성, router.back() 기본 동작, href prop으로 특정 경로 이동, label prop으로 텍스트 커스터마이징, ButtonProps 확장으로 모든 Button 속성 지원, 다국어 지원 (t('interaction.back')), 재사용성 극대화 (모든 페이지에서 일관된 뒤로가기 UX)** | **2025-11-22** |
+| **Firebase Storage CORS 설정** | **firebase-storage-cors.json 생성 (origin: *, method: GET/HEAD, maxAge: 3600), gsutil cors set 명령어로 적용, Canvas 이미지 조작 허용, 캐시 문제 (CDN 1시간 캐시로 즉시 반영 안 됨, ?t=timestamp 쿼리 파라미터로 해결), TextureLoader.setCrossOrigin("anonymous") 확인** | **2025-11-23** |
+| **인터랙티브 이미지 비율 유지** | **InteractiveImage 컴포넌트 개선 (responsive-image-canvas 라이브러리), useEffect로 이미지 원본 크기 측정 (new Image, crossOrigin, onload), Box에 aspectRatio 속성 적용 (width/height 비율), Canvas 찌그러짐 방지, 로딩 상태 UI 추가, 캐시 무효화 (cacheBustedSrc ?t=Date.now())** | **2025-11-23** |
### 🚧 진행 중
diff --git a/TECH_STACK.md b/TECH_STACK.md
index 7ba99c2..eb9d3b5 100644
--- a/TECH_STACK.md
+++ b/TECH_STACK.md
@@ -243,7 +243,6 @@ NEXT_PUBLIC_API_URL=/api
securityMode: 'simple' | 'normal' | 'open';
requirePin: boolean;
allowAnonymousJoin: boolean;
- studentIds: string[]; // students 컬렉션 참조
createdAt: Timestamp;
updatedAt: Timestamp;
isActive: boolean;
@@ -828,7 +827,6 @@ writings/{writingId}
**참고 파일**:
- `src/managers/TeamManager.ts` - 팀 관련 API 호출 + 캐싱
-- `src/managers/StudentManager.ts` - 학생 관련 API 호출 + 캐싱
- `src/managers/ManagerBase.ts` - API 호출 및 캐싱 공통 로직
- `src/services/firebaseAuth.ts:125-316` - 학생 로그인 로직
- `src/store/authStore.ts` - currentStudent 중심 상태 관리