diff --git a/PROJECT_STRUCTURE.md b/PROJECT_STRUCTURE.md index 50404d4..3c6f256 100644 --- a/PROJECT_STRUCTURE.md +++ b/PROJECT_STRUCTURE.md @@ -526,7 +526,7 @@ | **글 상세보기** | `/[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, 저장 상태 표시: 저장 중/저장됨)
🆕 **저장 플로우 개편** (저장 → 백그라운드 분석 → `/imageUpload` 리다이렉트)
🆕 **GenerateImageDialog 제거** (새 플로우로 대체)
템플릿 미리채우기 (제목/내용), Firestore 저장
비로그인도 접근 가능 (저장 시 로그인 유도) | ✅ 완료 | | **이미지 업로드/선택** | `/[locale]/imageUpload` | 🆕 **이미지 소스 선택 페이지 (글 저장 후 자동 이동)** | 🆕 **2가지 선택지**: AI 생성 (장면 추출 + 선택 + 생성) / 직접 업로드
🆕 **드래그앤드롭 파일 업로드** (미리보기, 5MB 제한, JPEG/PNG/WebP)
🆕 **Canvas API 클라이언트 사이드 리사이즈** (1920x1080 최대, 85% 품질)
🆕 **Firebase Storage 업로드** (WritingManager.uploadUserImage)
🆕 **이미 이미지 있으면 자동으로 `/interaction` 리다이렉트**
🆕 **다국어 지원** (imageUpload namespace, ko/en/ja) | ✅ 완료 | -| **인터랙션 편집** | `/[locale]/interaction` | 🆕 **이미지 왜곡 편집 페이지 (이미지 생성/업로드 후 이동)** | 🆕 **이미지 없으면 `/imageUpload` 자동 리다이렉트**
왜곡 영역 편집 (EditorCanvas, DistortionArea)
모션 프리셋 선택 (horizontal, vertical, rotate, pulse 등)
물리 설정 (stiffness, damping, mass)
에디터/인터랙션 모드 전환 (Switch)
저장 시 Writing.distortionAreas 업데이트 | ✅ 완료 | +| **인터랙션 편집** | `/[locale]/interaction` | 🆕 **이미지 왜곡 편집 페이지 (이미지 생성/업로드 후 이동)** | 🆕 **7:3 그리드 레이아웃** (이미지 70%, 컨트롤러 30%)
🆕 **Sticky Header** (InteractionHeader, 스크롤 시 상단 고정)
🆕 **framer-motion 애니메이션** (모드 전환, 영역 선택 부드러운 전환)
🆕 **영역 목록 일반 모드 표시** (CustomAreaList, 모든 모드에서 접근 가능)
🆕 **에디터 버튼 우측 배치** (추가/삭제/숨기기, 가로 3개)
🆕 **컨트롤러 내부 스크롤** (커스텀 스크롤바, 높이 80dvh)
🆕 **반응형 이미지** (aspectRatio 유지, 찌그러짐 해결)
왜곡 영역 편집 (EditorCanvas, DistortionArea)
모션 프리셋 선택 (horizontal, vertical, rotate, pulse 등)
물리 설정 (stiffness, damping, mass)
에디터/인터랙션 모드 전환 (Switch)
저장 시 Writing.distortionAreas 업데이트 | ✅ 완료 | | **테스트** | `/[locale]/test` | 팀 코드 시스템 테스트 페이지 | 팀 코드 생성/검증 테스트
팀/학생 생성 테스트
학생 로그인 테스트
authStore 상태 확인 | 🔜 예정 | | **공개 팀 목록** | `/[locale]/team/all` | 🆕 **공개 팀 둘러보기** | 🆕 **공개된 팀 목록 표시** (isPublic=true)
🆕 **TeamCard 그리드** (글래스모피즘)
🆕 **페이지네이션** (커서 기반)
🆕 **Navbar "공개 팀" 메뉴** | ✅ 완료 | | **내 팀 목록** | `/[locale]/team` | 내가 만든/참여한 팀 목록 (정식 계정 전용) | 팀 카드 그리드, 팀 정보 (코드, 멤버 수, 보안 설정)
"새 팀 만들기" 버튼 | ✅ 완료 | @@ -770,10 +770,19 @@ | 컴포넌트 | 파일명 | 설명 | 상태 | |---------|--------|------|------| +| **InteractionHeader** | `InteractionHeader.tsx` | 🆕 **인터랙션 페이지 Sticky Header** (IntersectionObserver + Sentinel 패턴, glassmorphism, 반응형 제목/버튼) | ✅ 완료 | | **AnalysisNeededBanner** | `AnalysisNeededBanner.tsx` | 분석 필요 알림 배너 (AI 분석 요청 유도) | ✅ 완료 | | **AreaUnlockBadge** | `AreaUnlockBadge.tsx` | 영역 잠금 해제 배지 (점수 달성 시 표시) | ✅ 완료 | | **ImprovementHint** | `ImprovementHint.tsx` | 개선 힌트 표시 (점수가 낮을 때 조언 제공) | ✅ 완료 | | **ScoreBadge** | `ScoreBadge.tsx` | 점수 배지 (오감/감정/대화/의성어 점수 표시) | ✅ 완료 | +| **EditorModeSwitch** | `EditorModeSwitch.tsx` | 기본/고급 모드 전환 스위치 | ✅ 완료 | +| **SimpleMotionSelector** | `SimpleMotionSelector.tsx` | 간소화된 모션 선택기 (기본 모드용) | ✅ 완료 | +| **SimpleEasingSelector** | `SimpleEasingSelector.tsx` | 간소화된 이징 선택기 | ✅ 완료 | +| **SimpleSpeedSelector** | `SimpleSpeedSelector.tsx` | 간소화된 속도 선택기 | ✅ 완료 | +| **SimpleStrengthSelector** | `SimpleStrengthSelector.tsx` | 간소화된 강도 선택기 | ✅ 완료 | +| **PhysicsPresetSelector** | `PhysicsPresetSelector.tsx` | 물리 프리셋 선택기 | ✅ 완료 | +| **CustomAreaList** | `CustomAreaList.tsx` | 🆕 **왜곡 영역 목록 (일반/고급 모드 공통 표시)** | ✅ 완료 | +| **CustomParameterPanel** | `CustomParameterPanel.tsx` | 커스텀 파라미터 패널 (고급 모드 전용) | ✅ 완료 | --- diff --git a/ROADMAP.md b/ROADMAP.md index 39fb726..474e5ca 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -1,6 +1,6 @@ # 라온누리 - 개발 로드맵 -> 최종 업데이트: 2025-12-23 (이미지 4:3 비율 표준화) +> 최종 업데이트: 2025-12-24 (인터랙션 페이지 UX 개선) 초등학생을 위한 창작 글쓰기 교육 플랫폼 개발 계획 @@ -160,6 +160,7 @@ | **글 상세 페이지 Sticky Header** | **WritingDetailHeader.tsx 컴포넌트 추가, 스크롤 시 상단에 고정되는 헤더 기능, 글 제목/작성자 정보/이미지 포함 여부 표시, WritingOwnerActions 컴포넌트 재활용, 기존 헤더 관련 코드 제거 및 구조 변경** | **2025-12-19** | | **AI 이미지 생성 에러 처리 개선** | **에러 코드별 토스트 메시지 처리 로직 개선, plan_not_supported/limit_exceeded/ai_disabled 등 에러 코드 메시지 추가, 에러 발생 시 'selecting' 단계로 되돌아가는 로직 유지, 다국어 지원 (ko/en/ja)** | **2025-12-19** | | **의존성 업데이트** | **@ark-ui/react 및 @zag-js 패키지 버전 업데이트 (v1.29.1 → v1.31.1)** | **2025-12-19** | +| **인터랙션 페이지 UX 대폭 개선** | **7:3 그리드 레이아웃 (이미지 70%, 컨트롤러 30%), 한 화면에서 이미지 보면서 편집 가능, 컨트롤러 내부 스크롤 (커스텀 스크롤바), InteractionHeader 컴포넌트 (IntersectionObserver + Sentinel 패턴, sticky header, glassmorphism 효과, 제목 크기 동적 변경 2xl→xl, 버튼 텍스트 숨김), framer-motion 애니메이션 (AnimatePresence, MotionBox/MotionVStack, 모드 전환/영역 선택 부드러운 전환, 0.2~0.3초 ease curve), 영역 목록을 일반 모드에서도 표시 (CustomAreaList, 고급 모드에서만 CustomParameterPanel), 에디터 버튼 우측 패널 상단 이동 (추가/삭제/숨기기, 가로 배치 flex:1), Container maxW 확장 (1400px→95vw), 이미지 반응형 수정 (InteractiveImageViewer width:100%, VStack maxH 제거, aspectRatio 충돌 해결), 높이 제한 80dvh (우측만 스크롤), 타입 체크 통과** | **2025-12-24** | ### 🚧 진행 중 diff --git a/TECH_STACK.md b/TECH_STACK.md index b276284..108f7fc 100644 --- a/TECH_STACK.md +++ b/TECH_STACK.md @@ -1,6 +1,6 @@ # 라온누리 - 기술 스택 및 개발 환경 -> 최종 업데이트: 2025-12-19 (Curriculum Fork Model + 의존성 업데이트) +> 최종 업데이트: 2025-12-23 (이미지 4:3 비율 표준화) --- @@ -27,6 +27,13 @@ | **Tiptap** | v3.9.1 | 리치 텍스트 에디터 | | **next-intl** | v4.5.2 | 🆕 **다국어 지원 (i18n)** | +### Image Processing + +| 기술 | 버전 | 용도 | +|-----|------|------| +| **react-cropper** | ^2.3.3 | 🆕 **이미지 크롭 React 래퍼** (4:3 비율 크롭) | +| **cropperjs** | ^1.6.1 | 🆕 **이미지 크롭 라이브러리** (회전, 확대/축소, 리셋) | + ### Backend & Database | 기술 | 버전 | 용도 | @@ -2546,6 +2553,257 @@ src/hooks/useShakeAnimation.ts (+45줄) - Shake 애니메이션 훅 --- +### 25. 인터랙션 페이지 UX 패턴 (Sticky Header + 7:3 그리드 + 애니메이션) + +#### 핵심 개념 + +**목적**: 이미지를 보면서 실시간으로 인터랙션을 편집할 수 있는 최적화된 레이아웃 + +#### Sticky Header 패턴 (IntersectionObserver + Sentinel) + +**기술**: `WritingDetailHeader.tsx`와 동일한 패턴 재사용 + +**구조**: +```tsx +// InteractionHeader.tsx +<> + {/* Sentinel - 스크롤 감지용 */} + + + {/* Sticky Header */} + + + + {/* 왼쪽: 뒤로가기 + 제목 */} + + + + {title} + + + + {/* 오른쪽: 배지 + 모드 전환 + 이미지 변경 */} + + + + + + + + + + +``` + +**IntersectionObserver 로직**: +```typescript +const [isSticky, setIsSticky] = useState(false); +const sentinelRef = useRef(null); + +useEffect(() => { + const sentinel = sentinelRef.current; + if (!sentinel) return; + + const observer = new IntersectionObserver( + ([entry]) => { + setIsSticky(!entry.isIntersecting); // sentinel이 안 보이면 sticky + }, + { threshold: 0 } + ); + + observer.observe(sentinel); + return () => observer.disconnect(); +}, []); +``` + +**glassmorphism 효과**: +- 반투명 배경 (`bg/80`) +- 블러 효과 (`blur(12px)`) +- 부드러운 전환 (0.2s ease) + +#### 7:3 그리드 레이아웃 + +**구조**: +```tsx + + {/* 왼쪽: 이미지 영역 */} + + + + + {/* 오른쪽: 컨트롤러 영역 (스크롤 가능) */} + + + {/* 모드 전환 스위치 */} + {/* 에디터 버튼 (추가/삭제/숨기기) */} + {/* 영역 목록 (일반/고급 모드 공통) */} + {/* 파라미터 패널 (고급 모드 전용) */} + {/* 선택된 영역 설정 */} + + + +``` + +**주요 특징**: +- ✅ **한 화면 편집**: 이미지 보면서 인터랙션 수정 (스크롤 불필요) +- ✅ **독립 스크롤**: 컨트롤러만 내부 스크롤 (80dvh 제한) +- ✅ **반응형 이미지**: `width="100%"` + `aspectRatio` 유지 +- ✅ **Container 확장**: `maxW="95vw"` (화면 최대 활용) + +#### framer-motion 애니메이션 패턴 + +**MotionBox 생성**: +```typescript +import {motion, AnimatePresence} from "framer-motion"; + +const MotionBox = motion.create(Box); +const MotionVStack = motion.create(VStack); +``` + +**AnimatePresence 활용**: +```tsx +{/* 고급 모드 에디터 컨트롤 패널 */} + + {showEditor && isAdvanced && ( + + {/* 영역 목록 + 파라미터 패널 */} + + )} + + +{/* 선택된 영역 설정 */} + + {selectedArea && ( + + {/* 모션/이징/속도/강도 선택기 */} + + )} + + +{/* 조건부 UI (모션 있을 때만 표시) */} + + {selectedArea.movement.preset !== 'none' && ( + + + + )} + +``` + +**애니메이션 특징**: +- ✅ **Easing Curve**: `[0.22, 1, 0.36, 1]` (자연스러운 감속) +- ✅ **Duration**: 0.2~0.3초 (빠르면서도 부드러운) +- ✅ **mode="wait"**: 퇴장 완료 후 등장 시작 +- ✅ **key 전략**: 고정 key로 영역 변경 시 재마운트 방지 + +#### 반응형 이미지 처리 (aspectRatio 충돌 해결) + +**문제**: VStack `maxH` + Box `aspectRatio` 충돌 → 이미지 찌그러짐 + +**해결**: +```tsx +{/* VStack에서 maxH 제거 */} + + {/* aspectRatio가 자유롭게 작동 */} + +``` + +**InteractiveImageViewer 개선**: +```tsx +// 에디터 모드 + + + +``` + +**라이브러리 동작**: +- EditorCanvas: `width: 100%, height: 100%` (부모 크기 따름) +- ImageDistortion: `width: 100%, height: 100%` (부모 크기 따름) +- ResizeObserver로 실제 렌더링 크기 측정 + +#### z-index 계층 구조 + +``` +Navbar (z-index: 1000) + └─ Sticky Headers (z-index: 100) + ├─ WritingDetailHeader + └─ InteractionHeader + └─ 하단 저장 버튼 바 (z-index: 10) +``` + +#### 주요 컴포넌트 + +| 컴포넌트 | 파일 | 역할 | +|---------|------|------| +| **InteractionHeader** | `src/components/interaction/InteractionHeader.tsx` | Sticky header (제목, 배지, 모드 전환, 이미지 변경) | +| **CustomAreaList** | `src/components/interaction/CustomAreaList.tsx` | 왜곡 영역 목록 (일반/고급 모드 공통) | +| **CustomParameterPanel** | `src/components/interaction/CustomParameterPanel.tsx` | 파라미터 패널 (고급 모드 전용) | + +#### 성능 최적화 + +- ✅ **IntersectionObserver**: scroll 이벤트 대비 성능 우수 +- ✅ **ResizeObserver**: Canvas 크기 동적 조정 +- ✅ **AnimatePresence**: DOM 제거 시에도 애니메이션 +- ✅ **key 전략**: 불필요한 재마운트 방지 + +--- + ## 참고 문서 - [PROJECT_STRUCTURE.md](./PROJECT_STRUCTURE.md) - 프로젝트 구조