180 lines
7.3 KiB
Markdown
180 lines
7.3 KiB
Markdown
# AGENTS.md
|
||
|
||
AI 에이전트(Claude Code 등)가 이 프로젝트에서 작업할 때 따라야 할 규칙과 맥락을 정의합니다.
|
||
|
||
---
|
||
|
||
## 프로젝트 개요
|
||
|
||
중학교 2학년 대상 **수학 실수 유형 반복 연습** 교육용 게임 서비스.
|
||
문제 생성은 AI 없이 자체 알고리즘 엔진으로 처리한다.
|
||
|
||
---
|
||
|
||
## 기술 스택 요약
|
||
|
||
| 레이어 | 기술 |
|
||
|---|---|
|
||
| 프레임워크 | Next.js 14 (App Router) + TypeScript 5 |
|
||
| 스타일 | Tailwind CSS 3 |
|
||
| 상태 관리 | Zustand |
|
||
| 수식 렌더링 | KaTeX |
|
||
| 애니메이션 | Framer Motion |
|
||
| 인증 | NextAuth.js 4 |
|
||
| ORM | Prisma (MariaDB 11) |
|
||
| 캐시 | Redis 7 |
|
||
| 유효성 검증 | Zod |
|
||
| 패키지 매니저 | pnpm |
|
||
| 테스트 | Vitest (단위) / Playwright (E2E) |
|
||
|
||
---
|
||
|
||
## 디렉토리 구조 및 파일 위치 규칙
|
||
|
||
```
|
||
app/ # Next.js App Router 루트
|
||
(game)/ # 게임 화면 라우트 그룹
|
||
select-type/ # 문제 유형 선택
|
||
select-mode/ # 게임 모드 선택
|
||
play/ # 문제 풀이
|
||
api/
|
||
auth/ # NextAuth.js 핸들러
|
||
problem/ # 문제 생성 API
|
||
record/ # 풀이 기록 저장 API
|
||
layout.tsx
|
||
page.tsx # 시작 화면
|
||
components/
|
||
math/ # KaTeX 수식 컴포넌트
|
||
game/ # 게임 UI 컴포넌트
|
||
lib/
|
||
engine/ # 문제 생성 알고리즘 엔진
|
||
prisma.ts # Prisma Client 싱글턴
|
||
redis.ts # Redis Client 싱글턴
|
||
store/
|
||
gameStore.ts # Zustand 전역 게임 상태
|
||
types/
|
||
*.d.ts # 타입 선언이 없는 서드파티 모듈 보강
|
||
prisma/
|
||
schema.prisma # MariaDB 모델 정의
|
||
.gitea/workflows/deploy.yml # CI/CD 워크플로우
|
||
```
|
||
|
||
---
|
||
|
||
## 코드 작성 규칙
|
||
|
||
### Next.js / React
|
||
|
||
- 페이지 컴포넌트는 반드시 `app/` 디렉토리에 위치한다.
|
||
- 클라이언트 컴포넌트에는 `'use client'` 지시어를 파일 최상단에 명시한다.
|
||
- 서버 컴포넌트가 기본값이므로, 클라이언트 기능(훅, 이벤트)이 필요할 때만 `'use client'`를 붙인다.
|
||
|
||
### 수식 렌더링
|
||
|
||
- 모든 수학 수식은 KaTeX로 렌더링한다. 일반 텍스트나 Unicode 수식 문자를 직접 사용하지 않는다.
|
||
- KaTeX 컴포넌트는 `components/math/` 하위에 위치한다.
|
||
- 현재 수식 컴포넌트는 `katex.renderToString()`을 직접 사용한다. 별도 래퍼 라이브러리를 추가하기 전에 Next.js App Router 클라이언트 번들에서 런타임 문제가 없는지 확인한다.
|
||
|
||
### 상태 관리
|
||
|
||
- 전역 게임 상태(점수, 진행도, 선택한 모드·유형)는 `store/gameStore.ts`의 Zustand store로 관리한다.
|
||
- 컴포넌트 내부의 로컬 상태는 `useState`를 사용한다.
|
||
- Zustand selector 안에서 매번 새 객체·배열을 반환하는 함수(`score()` 등)를 호출하지 않는다. 필요한 파생값은 구독한 state를 기준으로 컴포넌트 내부에서 계산하거나 안정적인 selector로 분리한다.
|
||
- store에는 세션 원본 상태와 상태 변경 action을 우선 두고, 렌더링 전용 파생값은 컴포넌트 또는 별도 순수 유틸에서 계산한다.
|
||
|
||
### 컴포넌트 배치
|
||
|
||
- 게임 화면에서 재사용되는 입력, 피드백, 점수 요약, 진행 단계 UI는 `components/game/` 하위에 둔다.
|
||
- 수식 렌더링 전용 컴포넌트는 `components/math/` 하위에 둔다.
|
||
- 라우트별 조립 컴포넌트(`PlayClient` 등)는 해당 `app/` 라우트 폴더에 둘 수 있다.
|
||
|
||
### 접근성 / 키보드 조작
|
||
|
||
- 문제 풀이 화면은 키보드만으로 진행할 수 있어야 한다.
|
||
- 새 문제가 표시되면 첫째 항 입력 칸에 focus를 둔다.
|
||
- 첫째 항에서 Enter를 누르면 둘째 항 입력 칸으로 focus를 이동한다.
|
||
- 둘째 항에서 Enter를 누르면 제출 동작을 실행한다.
|
||
- 정답·오답 피드백이 표시되면 다음 진행 버튼에 focus를 둔다.
|
||
- 최종 결과 화면이 표시되면 다시 시작 버튼에 focus를 둔다.
|
||
- 아이콘이나 기호만으로 상태를 표시할 때는 `aria-label`, `sr-only` 텍스트 등으로 의미를 함께 제공한다.
|
||
|
||
### API / 백엔드
|
||
|
||
- API는 `app/api/` 하위의 Route Handlers로만 구현한다. 별도의 백엔드 서버를 추가하지 않는다.
|
||
- 모든 API 요청 입력값은 Zod 스키마로 검증한다.
|
||
- DB 접근은 `lib/prisma.ts`의 Prisma Client 싱글턴을 통해서만 한다.
|
||
- Redis 접근은 `lib/redis.ts`의 Redis Client 싱글턴을 통해서만 한다.
|
||
|
||
### 문제 생성 엔진
|
||
|
||
- 문제 생성 로직은 `lib/engine/` 하위에 위치한다.
|
||
- 문제는 AI 호출 없이 알고리즘으로 생성한다. 외부 AI API를 문제 생성에 사용하지 않는다.
|
||
- 미지수의 계수는 항상 정수이어야 한다.
|
||
- 생성 파라미터: 숫자 자릿수, 괄호 깊이(`()` / `(){}` / `(){}[]`), 괄호 개수(1–3), 미지수(없음/x/x,y/x,y,z).
|
||
- 문제 세션에서 중복을 방지할 때는 랜덤 id가 아니라 수학적 문제 조건을 나타내는 안정적인 key를 기준으로 한다.
|
||
|
||
---
|
||
|
||
## 환경변수
|
||
|
||
새로운 환경변수를 추가할 때는 `.env.example`에 키 이름만 추가하고 값은 비워둔다.
|
||
`.env.local`은 절대 Git에 커밋하지 않는다.
|
||
|
||
```
|
||
DATABASE_URL=mysql://user:pass@mariadb:3306/mathgame
|
||
REDIS_URL=redis://redis:6379
|
||
NEXTAUTH_SECRET=your-secret
|
||
```
|
||
|
||
---
|
||
|
||
## 보안 규칙
|
||
|
||
- `.env.local`을 Git에 커밋하거나 코드에 하드코딩하지 않는다.
|
||
- MariaDB, Redis 포트는 컨테이너 내부 통신 전용이다. 외부 노출 설정을 추가하지 않는다.
|
||
- SQL 쿼리는 반드시 Prisma를 통해 실행한다. Raw query 사용 시 파라미터 바인딩을 반드시 사용한다.
|
||
- 사용자 입력은 API 경계에서 Zod로 검증한다. 클라이언트 측 검증만으로 보안을 의존하지 않는다.
|
||
|
||
---
|
||
|
||
## 게임 도메인 컨텍스트
|
||
|
||
### 문제 유형
|
||
|
||
1. **괄호 음수 분배**: 괄호 앞 음수 곱셈 시 괄호 내 부호 변경 (예: `-2 × (3 - 6)`)
|
||
2. **이항 부호 변경**: 등식의 항을 이항할 때 부호 반전 (예: `3x - 5y + 8 = -2x + 2y - 2`)
|
||
|
||
### 게임 모드
|
||
|
||
| 모드 | 설명 |
|
||
|---|---|
|
||
| 스피드 퀴즈 | 제한 시간 내 최대 문제 풀기, Redis Sorted Set 기반 실시간 점수판 |
|
||
| 일반 풀이 | 시간 제한 없음, 틀린 유형 반복 출제 |
|
||
|
||
### 화면 흐름
|
||
|
||
```
|
||
시작 화면 → 문제 유형 선택 → 게임 모드 선택 → 문제 풀이 → 결과 및 기록
|
||
```
|
||
|
||
---
|
||
|
||
## CI/CD
|
||
|
||
- `.gitea/workflows/deploy.yml`이 CI/CD 진입점이다. main 브랜치 push 시 자동으로 빌드·배포된다.
|
||
- 패키지 설치는 반드시 `pnpm`을 사용한다. `npm` / `yarn` 명령을 사용하지 않는다.
|
||
- 배포 환경: Synology NAS + Docker Compose + Nginx Proxy Manager.
|
||
|
||
---
|
||
|
||
## 작업 시 체크리스트
|
||
|
||
새 기능 또는 수정 작업 전에 다음을 확인한다.
|
||
|
||
- [ ] 클라이언트 컴포넌트에 `'use client'` 선언 여부
|
||
- [ ] 수식이 KaTeX로 렌더링되는지 여부
|
||
- [ ] API 입력값이 Zod로 검증되는지 여부
|
||
- [ ] DB 접근이 Prisma 싱글턴을 통하는지 여부
|
||
- [ ] 환경변수가 `.env.local`에만 존재하는지 여부 (`.env.example`에 키 이름 추가)
|
||
- [ ] 문제 생성 로직이 `lib/engine/` 내에서 알고리즘으로만 처리되는지 여부
|