Files
math-quiz/AGENTS.md
T

10 KiB
Raw Blame History

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)/                   # 게임 화면 라우트 그룹
    number-parentheses/     # 숫자 괄호 준비 화면
    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/game/ 하위 공통 컴포넌트로 분리한다.
  • 수식 렌더링 전용 컴포넌트는 components/math/ 하위에 둔다.
  • 라우트별 조립 컴포넌트(PlayClient, NumberParenthesesClient 등)는 해당 app/ 라우트 폴더에 둘 수 있다.

접근성 / 키보드 조작

  • 문제 풀이 화면은 키보드만으로 진행할 수 있어야 한다.
  • 문제 풀이 화면은 모든 항을 하나의 공유 input으로 입력한다.
  • input 아래에는 각 항의 입력값 버튼을 버튼 그룹으로 표시한다. 현재 입력 중인 항은 aria-pressed로 표시하고, 비어 있는 항은 ?로 표시한다.
  • 첫째 항에서 Enter를 누르면 같은 input이 둘째 항 입력으로 전환된다.
  • 둘째 항에서 Enter를 누르면 제출 동작을 실행한다. 별도 제출 버튼이 없어도 form submit 흐름이 유지되어야 한다.
  • 터치 기반 입력 환경에서는 input focus 시 커스텀 숫자 키패드를 표시한다. 키패드는 숫자, +/-, ., , 버튼을 제공한다.
  • 마우스/트랙패드처럼 hover 가능한 정밀 포인터가 있는 환경은 물리 키보드 사용 환경으로 간주하고 커스텀 숫자 키패드를 숨긴다. 웹 표준 API로 물리 키보드 연결 여부 자체를 안정적으로 판별할 수 없으므로 해상도 기준으로 분기하지 않는다.
  • 입력 중에는 -, 1. 같은 임시 값을 허용할 수 있지만 제출 시에는 정상적인 양의 실수 또는 음의 실수 표현만 통과시킨다.
  • 문제 풀이 화면 최초 진입 시 모바일 키보드를 강제로 열지 않는다. 사용자가 input을 직접 focus하거나 피드백 후 다음 문제로 넘어갈 때 input에 focus를 둔다.
  • 터치 기반 입력 환경에서 input focus 시 span.katex 상단이 브라우저 화면 상단에 오도록 조정한다.
  • 정답·오답 피드백이 표시되면 다음 진행 버튼에 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를 문제 생성에 사용하지 않는다.
  • 미지수의 계수는 항상 정수이어야 한다.
  • 생성 파라미터: 숫자 자릿수, 괄호 깊이(() / (){} / (){}[]), 괄호 개수(13), 미지수(없음/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 기반 실시간 점수판
일반 풀이 시간 제한 없음, 틀린 유형 반복 출제

화면 흐름

학습 내용 선택 → 숫자 괄호 준비 → 문제 풀이 → 결과 및 기록
  • 시작 화면(/)은 학습 내용 선택 화면이다.
  • 현재 활성 학습 내용은 숫자 괄호 풀기이며 /number-parentheses로 이동한다.
  • 분수 괄호 풀기, 실수 괄호 풀기, 미지수 괄호 풀기는 준비 중 항목으로 disabled 상태로 표시한다.
  • 숫자 괄호 준비 화면(/number-parentheses)은 문제 수 [5] [10] 선택 버튼 그룹을 제공하고 기본값은 10이다.
  • 숫자 괄호 준비 화면은 괄호 종류 (), {}, [] 버튼 그룹을 제공한다. 현재는 ()만 선택 가능하고 {}, []는 disabled 상태로 표시한다.
  • 준비 화면의 연습 시작은 Zustand 세션을 초기화하고 /play?count=N으로 이동한다.
  • 문제 풀이 화면은 URL의 count 쿼리와 Zustand의 선택 문제 수를 동기화한다. 허용 문제 수는 5, 10이며 유효하지 않은 값은 10으로 처리한다.
  • 정답·오답 피드백은 계산식 = 내가 입력한 답 형식으로 표시한다. 정답은 , 오답은 붉은색 입력값과 ×, 굵은 정답값을 함께 보여준다.
  • 최종 결과 화면은 지금까지 푼 문제를 문제 = 내가 입력한 답 형식으로 나열하고, 피드백 화면과 같은 정답·오답 색상 및 기호 규칙을 사용한다.

CI/CD

  • .gitea/workflows/deploy.yml이 CI/CD 진입점이다. main 브랜치 push 시 자동으로 빌드·배포된다.
  • 패키지 설치는 반드시 pnpm을 사용한다. npm / yarn 명령을 사용하지 않는다.
  • 빌드 확인은 pnpm build로 실행한다. standalone 배포 산출물에 필요한 정적 파일 복사까지 postbuild 스크립트가 처리한다.
  • 배포 환경: Synology NAS + Docker Compose + Nginx Proxy Manager.

작업 시 체크리스트

새 기능 또는 수정 작업 전에 다음을 확인한다.

  • 클라이언트 컴포넌트에 'use client' 선언 여부
  • 수식이 KaTeX로 렌더링되는지 여부
  • API 입력값이 Zod로 검증되는지 여부
  • DB 접근이 Prisma 싱글턴을 통하는지 여부
  • 환경변수가 .env.local에만 존재하는지 여부 (.env.example에 키 이름 추가)
  • 문제 생성 로직이 lib/engine/ 내에서 알고리즘으로만 처리되는지 여부