라온누리 (Raonnuri)
초등학생을 위한 창의적 글쓰기 교육 플랫폼
프로젝트 소개
라온누리는 초등학생들이 재미있게 글쓰기를 배울 수 있는 한국어 교육 플랫폼입니다.
주요 특징
- 팀 코드 시스템: 초등 저학년도 쉽게 로그인 ("춤추는 파란 사자")
- 개인 맞춤 주제: 자유 주제, 그룹 주제, 개인 주제 지원
- 글쓰기 에디터: 초등학생 친화적인 순수 텍스트 에디터
- 🔜 레벨업 시스템: 경험치와 스티커 보상 (예정)
- 🔜 학습 커리큘럼: 체계적인 레슨 시스템 (예정)
기술 스택
- Framework: Next.js 16 (App Router) with React 19
- 언어: TypeScript
- UI Library: Chakra UI v3
- 인증: Firebase Authentication
- Email/Password, Google OAuth (정식 계정)
- Anonymous Auth (학생 팀 코드 로그인)
- 데이터베이스: Firestore
- 상태 관리: Zustand
- 비즈니스 로직: Manager 패턴 (API 호출 + 클라이언트 캐싱)
- 스타일링: Chakra UI + Emotion
- 애니메이션: Framer Motion
- 🔜 캐싱: Redis (예정)
시작하기
필수 요구사항
- Node.js 18.17 이상
- npm, yarn, pnpm, 또는 bun
설치
npm install
개발 서버 실행
npm run dev
개발 서버는 http://localhost:3001에서 실행됩니다.
참고: 이 프로젝트는 React Compiler를 사용하므로 webpack 모드로 실행됩니다 (Turbopack 아님).
기타 명령어
npm run build # 프로덕션 빌드
npm start # 프로덕션 서버 실행 (포트 3001)
npm run lint # ESLint 실행
프로젝트 구조
src/
├── app/ # Next.js App Router
│ ├── layout.tsx # 루트 레이아웃
│ ├── page.tsx # 랜딩 페이지
│ ├── home/ # 유저 대시보드
│ ├── write/ # 글쓰기 페이지
│ └── team/ # 팀 관련 페이지 (목록, 생성, 상세, 관리)
├── components/
│ ├── auth/ # 인증 (로그인, 회원가입, 학생 로그인)
│ ├── writing/ # 글쓰기 에디터, 주제 선택
│ ├── navigation/ # 네비게이션 바
│ └── ui/ # Chakra UI 기본 컴포넌트
├── managers/ # ✅ 비즈니스 로직 (API + 캐싱)
│ ├── ManagerBase.ts # 공통 기능 (authenticatedFetch, caching)
│ ├── TeamManager.ts # 팀 관련 API 호출
│ ├── WritingManager.ts # 글쓰기 API 호출
│ └── TopicManager.ts # 주제 API 호출
├── types/ # TypeScript 타입
│ ├── team.ts, writing.ts, topic.ts
│ └── api/ # API Request/Response 타입
├── config/ # Firebase 설정
├── services/ # Firebase Auth, Firestore
├── store/ # Zustand (인증 상태)
└── theme/ # Chakra UI 커스텀 테마
주요 기능
✅ 인증 시스템
정식 계정 (학부모/고학년):
- 이메일/비밀번호 로그인 및 회원가입
- Google OAuth 소셜 로그인
- 비밀번호 강도 체크 + HIBP API 유출 확인
학생 로그인 (초등 저학년):
- 팀 코드 기반 로그인 ("춤추는 파란 사자")
- Anonymous Auth 사용
- 이름만 입력 또는 이름 + PIN
- 정식 계정 연결 가능 (선택적)
✅ 팀 관리 시스템
- 팀 생성 (누구나 가능)
- 한글 팀 코드 자동 생성 (10만 가지 조합)
- 보안 모드 3종: simple, normal, open
- 팀원 관리 (이름 수정, 강퇴)
- 멤버 페이지 (팀 정보 및 멤버 목록)
✅ 글쓰기 기능
- Tiptap 순수 텍스트 에디터 (포맷팅 없음)
- 주제 선택 (자유/그룹/개인)
- 개인 주제 생성 (템플릿 지원)
- 실시간 글자수/단어수 카운터
- LocalStorage 자동 저장
✅ 댓글 및 피드백 시스템
- 계층형 댓글 구조 (대댓글 지원)
- 칭찬 및 격려 중심의 반응(Reaction) 시스템
- 작성자, 글 주인, 선생님(팀 소유자) 권한 관리
- 실시간 업데이트 및 알림
✅ UI/UX
- 다크 모드 지원
- 반응형 디자인 (모바일 우선)
- Chakra UI v3 커스텀 테마
- Framer Motion 애니메이션
- 초등학생 친화적 디자인
✅ 아키텍처
- Manager 패턴 (비즈니스 로직 레이어)
- API 추상화 (HTTP 호출)
- 클라이언트 사이드 캐싱 (TTL 기반)
- 타입 안전성 (완전한 타입 정의)
- 35개 API 엔드포인트 준비 완료
개발자 가이드
상세한 개발 가이드는 다음 문서를 참고하세요:
핵심 가이드
- CLAUDE.md - Claude Code 개발 가이드
- API_SPEC.md - API 명세서 (RESTful 원칙, Firebase Admin SDK)
- STYLE_GUIDE.md - 스타일 가이드 (Color, Icon 규칙)
- I18N_GUIDE.md - 다국어 지원 가이드 (필수)
- DEVELOPMENT_GUIDE.md - 구현 세부사항
프로젝트 문서
- PROJECT_STRUCTURE.md - 프로젝트 구조
- TECH_STACK.md - 기술 스택
- DATA_MODELS.md - 데이터 모델
- SECURITY.md - 보안 정책
- ROADMAP.md - 개발 로드맵
- CHANGELOG.md - 변경 내역
Manager 패턴 사용
❌ 서비스 직접 사용 금지:
// Bad - deprecated
import { getTeamsByOwner } from "@/services/teamService";
✅ Manager 사용:
// Good
import { teamManager, studentManager } from "@/managers";
// 팀 목록 (1분 캐싱)
const teams = await teamManager.getMyTeams();
// 학생 목록 (30초 캐싱)
const students = await studentManager.getStudentsByTeam(teamId);
// 팀 생성 (캐시 자동 무효화)
const teamId = await teamManager.createTeam({
name: "우리반",
code: "춤추는파란사자",
securityMode: "simple",
requirePin: false,
allowAnonymousJoin: true
});
보호된 페이지 만들기
"use client";
import { useAuthStore } from "@/store/authStore";
import { useRouter } from "next/navigation";
import { useEffect } from "react";
export default function ProtectedPage() {
const { isAuthenticated, isLoading } = useAuthStore();
const router = useRouter();
useEffect(() => {
if (!isLoading && !isAuthenticated) {
router.push("/");
}
}, [isAuthenticated, isLoading, router]);
if (isLoading) return <div>로딩 중...</div>;
if (!isAuthenticated) return null;
return <div>보호된 콘텐츠</div>;
}
환경 변수
Firebase 설정을 위해 .env.local 파일을 생성하세요:
# Firebase 설정 (필수)
NEXT_PUBLIC_FIREBASE_API_KEY=your_api_key
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=your_auth_domain
NEXT_PUBLIC_FIREBASE_PROJECT_ID=your_project_id
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=your_storage_bucket
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=your_sender_id
NEXT_PUBLIC_FIREBASE_APP_ID=your_app_id
# 사이트 URL (프로덕션)
NEXT_PUBLIC_URL=https://raonnuri.life
# API Base URL (선택적, 기본값: /api)
NEXT_PUBLIC_API_URL=/api
라이선스
MIT License
기여하기
기여는 언제나 환영합니다! Pull Request를 보내주세요.
라온누리 - 즐거운 글쓰기, 밝은 배움터
Description
Languages
Markdown
100%