Files
math-quiz/docs/vibe/04-subject-variable.md

414 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 04. 미지수 괄호 학습과 선택지형 풀이 화면
## 계획 검토
Codex가 작성한 초안은 방향이 맞다. 엔진 설계, store 분리, 화면 구조가 기존 숫자 괄호 흐름과 일관성 있게 정리되어 있다.
조정이 필요한 부분:
- `VariableUserAnswer``selectedChoiceIds`는 보기 id 기반 채점이므로 그대로 유지한다. 보기가 매번 섞이므로 계수 값 자체보다 id로 선택을 추적하는 것이 명확하다.
- 사용자가 요청한 "보기 하나는 음수, 보기 하나는 양수" 방식이 Codex 초안과 동일하다. 보기는 정답 계수의 절대값을 기준으로 양수/음수 두 개만 표시한다.
- 선택지 순서는 매번 셔플한다.
- 둘째 항 선택 후 `정답 확인` 버튼으로 명시적으로 제출한다. 자동 제출은 사용자가 선택을 확인할 여지 없이 넘어가므로 사용하지 않는다.
- 준비 화면은 `NumberParenthesesClient`와 구조가 동일하므로 처음에는 별도 파일로 구현한다. 중복이 불편해지면 그때 공통 컴포넌트로 분리한다.
---
## 목표
미지수가 포함된 괄호 풀기 문제를 선택지 방식으로 풀 수 있는 학습 흐름을 추가한다.
이번 단계의 최종 상태:
- `/`에서 `미지수 괄호 풀기`가 활성 링크로 표시된다.
- `/variable-parentheses` 준비 화면에서 문제 수와 괄호 종류를 선택한다.
- `/variable-parentheses/play?count=N` 풀이 화면에서 첫째 항, 둘째 항 순서로 보기를 고르고 정답을 확인한다.
- 기존 숫자 괄호 풀이 화면은 현재 동작을 유지한다.
---
## 이번 단계에서 할 것
- 첫 화면의 `미지수 괄호 풀기` disabled 해제 (`/variable-parentheses` 링크)
- 미지수 괄호 학습 준비 화면 추가
- 미지수 괄호 문제 생성 엔진 추가
- 미지수 괄호 전용 Zustand store 추가
- 미지수 괄호 전용 풀이 화면 추가 (선택지형)
- 정답·오답 피드백 및 결과 화면 추가
- 엔진 단위 테스트 추가
## 이번 단계에서 하지 않을 것
- 분수 괄호, 실수 괄호 문제 생성
- `{}`, `[]` 괄호 문제 생성
- 여러 미지수(`x, y, z`) 지원
- 기존 숫자 괄호 `/play` 화면 동작 변경
- 사용자가 직접 미지수 답을 타이핑하는 입력 방식
- DB, Redis, NextAuth 연동
- 스피드 퀴즈 모드
---
## 문제 생성 규칙
### 문제 형태
```text
multiplier(a ± b)
```
괄호 안 두 항 중 정확히 하나가 `정수 × x` 항이다.
예시:
```text
-2(3x - 6) → -6x + 12
-5(4 + 7x) → -20 - 35x
-3(8x + 2) → -24x - 6
```
### 생성 조건
| 항목 | 범위 |
|---|---|
| `multiplier` | `-9`~`-1` 정수 |
| 숫자 항 계수 | `1`~`9` 정수 |
| 미지수 항 계수 | `1`~`9` 정수 |
| 미지수 | `x` 고정 |
| 연산자 | `+` 또는 `-` |
| 미지수 항 위치 | 첫째 항 또는 둘째 항 (한 문제에 하나만) |
두 항 계수가 같아도 허용한다 (숫자 괄호와 달리 항의 종류가 다르므로 혼동이 없다).
### 정답 계산
- 첫째 항 정답: `multiplier × firstTerm.coefficient` (미지수 항이면 `x` 붙임)
- 둘째 항 정답: `multiplier × (operator === '-' ? -secondTerm.coefficient : secondTerm.coefficient)` (미지수 항이면 `x` 붙임)
### 보기 생성
정답 계수의 절대값을 기준으로 두 보기를 만든다.
```text
정답: -6x → 보기: -6x, 6x
정답: 12 → 보기: -12, 12
```
- 보기 두 개는 항 종류(미지수 항 / 숫자 항)가 같다.
- 보기 순서는 매번 셔플한다.
- 보기 `id`는 안정적으로 생성한다: `${problem.key}:term-0:negative`, `${problem.key}:term-0:positive`
### 중복 방지 key
```text
${multiplier}:${firstKind}:${firstCoeff}:${operator}:${secondKind}:${secondCoeff}
```
예: `-2:variable:3:-:constant:6`
---
## 타입 설계
신규 파일: `lib/engine/variable-types.ts`
```ts
export type VariableTermKind = 'constant' | 'variable'
export interface VariableParenthesisTerm {
kind: VariableTermKind
coefficient: number
}
export interface DistributedVariableTerm {
kind: VariableTermKind
coefficient: number
latex: string
}
export interface VariableTermChoice {
id: string
coefficient: number
kind: VariableTermKind
latex: string
}
export interface VariableParenthesisProblem {
id: string
key: string
multiplier: number
operator: '+' | '-'
firstTerm: VariableParenthesisTerm
secondTerm: VariableParenthesisTerm
correctTerms: [DistributedVariableTerm, DistributedVariableTerm]
choices: [VariableTermChoice[], VariableTermChoice[]]
latexBefore: string
latexAfter: string
}
export interface VariableUserAnswer {
selectedChoiceIds: [string, string]
}
export interface VariableGradeResult {
correct: boolean
firstCorrect: boolean
secondCorrect: boolean
userChoiceIds: [string, string]
correctTerms: [DistributedVariableTerm, DistributedVariableTerm]
}
```
---
## 엔진 설계
신규 파일: `lib/engine/variable-parenthesis.ts`
구현 함수:
- `generateVariableParenthesisSession(count?: number): VariableParenthesisProblem[]`
- `gradeVariableParenthesisAnswer(problem, answer): VariableGradeResult`
내부 헬퍼:
- `formatVariableTermLatex(kind, coefficient): string`
- `kind === 'variable'`: `6x`, `-6x`
- `kind === 'constant'`: `6`, `-6`
- `makeChoices(termIndex, key, distributedTerm): VariableTermChoice[]`
- 양수/음수 두 보기를 만들고 셔플
- `createAllVariableSeeds()`: 전체 가능 시드 목록 (최초 1회 생성)
- `shuffle()`: 기존 숫자 괄호 엔진과 동일한 방식
세션 생성 방식은 기존 숫자 괄호 엔진과 동일하게 전체 시드 셔플 후 슬라이스로 중복 방지한다.
---
## 상태 관리 설계
신규 파일: `store/variableGameStore.ts`
기존 `gameStore.ts`와 동일한 구조로 분리한다.
```ts
interface VariableGameState {
session: VariableGameSession | null
selectedProblemCount: ProblemCount
setProblemCount: (count: ProblemCount) => void
initSession: (count?: ProblemCount) => void
submitAnswer: (answer: VariableUserAnswer) => void
nextProblem: () => void
resetSession: () => void
}
```
주의:
- selector 안에서 새 객체·배열을 반환하지 않는다.
- 현재 선택 중인 보기 상태(`selectedChoiceIds`, `activeTermIndex`)는 `VariablePlayClient` 로컬 state로 관리한다.
---
## 화면 설계
### 첫 화면 (`app/page.tsx`)
- `미지수 괄호 풀기` disabled 해제, `href="/variable-parentheses"` 추가
- 분수 괄호, 실수 괄호는 계속 disabled 유지
- 안내 문구를 `숫자 괄호`, `미지수 괄호` 두 개가 활성화된 상태로 수정
### 미지수 괄호 학습 준비 화면
신규 파일:
```text
app/(game)/variable-parentheses/page.tsx
app/(game)/variable-parentheses/VariableParenthesesClient.tsx
```
화면 구성:
- 문제 수: `5`, `10` 버튼 그룹 (기본값 `10`)
- 괄호 종류: `()` selected, `{}` disabled, `[]` disabled
- `연습 시작` 버튼 → `/variable-parentheses/play?count=${selectedProblemCount}` 이동, 클릭 시 세션 초기화
### 미지수 괄호 풀이 화면
신규 파일:
```text
app/(game)/variable-parentheses/play/page.tsx
app/(game)/variable-parentheses/play/VariablePlayClient.tsx
components/game/VariableChoiceAnswerForm.tsx
components/game/VariableFeedbackPanel.tsx
components/game/VariableScoreSummary.tsx
```
풀이 흐름:
```text
문제 표시 (KaTeX 카드)
첫째 항 보기 2개 표시 → 선택
둘째 항 보기 2개 표시 → 선택
[정답 확인] 버튼 활성화 → 클릭
피드백 표시 → [다음 문제] 또는 [결과 보기]
최종 결과 화면
```
`VariableChoiceAnswerForm` props 초안:
```ts
interface VariableChoiceAnswerFormProps {
choices: [VariableTermChoice[], VariableTermChoice[]]
selectedChoiceIds: [string | null, string | null]
activeTermIndex: 0 | 1
onSelectChoice: (termIndex: 0 | 1, choiceId: string) => void
onSubmit: () => void
}
```
접근성:
- 첫째 항 선택 후 둘째 항 첫 번째 보기 버튼에 focus 이동
- 두 항 모두 선택되면 `정답 확인` 버튼 활성화
- 피드백 표시 시 `다음 문제` 버튼에 focus 이동
- 결과 화면 표시 시 `다시 시작` 버튼에 focus 이동
`VariableFeedbackPanel` 표시 형식:
```text
(-2) × 3x = -6x ○
(-2) × (-6) = 12 ○
```
`VariableScoreSummary` 표시 형식:
```text
-2(3x - 6) = -6x + 12 ○
```
기존 `AnswerResultLine` 컴포넌트를 재사용한다. 항 표현이 복잡해지는 경우 `userAnswerContent` / `correctAnswerContent` prop으로 커스텀 렌더링한다.
---
## 작업 순서
### 4-A. 미지수 괄호 엔진 추가
완료 기준: `pnpm test` 통과
- `lib/engine/variable-types.ts` 타입 정의
- `lib/engine/variable-parenthesis.ts` 엔진 구현
- `lib/engine/variable-parenthesis.test.ts` 단위 테스트
테스트 항목:
- 괄호 안 두 항 중 정확히 하나만 미지수 항이다
- 계수는 모두 정수다
- 정답 분배 계산이 맞다
- 둘째 항이 `-` 뒤에 있을 때 부호가 올바르게 뒤집힌다
- 각 항의 보기는 정답 절대값 기준 양수/음수 두 개다
- choice id 기준으로 채점이 올바르다
- 세션 문제 key가 중복되지 않는다
- 기존 숫자 괄호 엔진 테스트가 회귀 없이 통과한다
### 4-B. 미지수 괄호 store 추가
완료 기준: 세션 생성 및 진행 가능
- `store/variableGameStore.ts`
### 4-C. 준비 화면 추가
완료 기준: `/variable-parentheses` 접속 시 준비 화면이 표시된다
- `app/(game)/variable-parentheses/page.tsx`
- `app/(game)/variable-parentheses/VariableParenthesesClient.tsx`
### 4-D. 첫 화면 수정
완료 기준: `/`에서 `미지수 괄호 풀기`가 활성 링크로 표시된다
- `app/page.tsx` 수정
### 4-E. 선택지형 풀이 화면 추가
완료 기준: `/variable-parentheses/play?count=5`에서 문제를 풀 수 있다
- `app/(game)/variable-parentheses/play/page.tsx`
- `app/(game)/variable-parentheses/play/VariablePlayClient.tsx`
- `components/game/VariableChoiceAnswerForm.tsx`
### 4-F. 피드백 및 결과 화면 추가
완료 기준: 선택 결과에 따라 피드백과 최종 결과가 표시된다
- `components/game/VariableFeedbackPanel.tsx`
- `components/game/VariableScoreSummary.tsx`
### 4-G. 회귀 확인
완료 기준: 숫자 괄호와 미지수 괄호 흐름이 모두 동작한다
1. `/``숫자 괄호 풀기` → 준비 화면 → `/play?count=5` → 풀이 → 결과
2. `/``미지수 괄호 풀기` → 준비 화면 → `/variable-parentheses/play?count=5` → 풀이 → 결과
---
## 생성 또는 수정 파일
### 신규 생성
```text
lib/engine/variable-types.ts
lib/engine/variable-parenthesis.ts
lib/engine/variable-parenthesis.test.ts
store/variableGameStore.ts
app/(game)/variable-parentheses/page.tsx
app/(game)/variable-parentheses/VariableParenthesesClient.tsx
app/(game)/variable-parentheses/play/page.tsx
app/(game)/variable-parentheses/play/VariablePlayClient.tsx
components/game/VariableChoiceAnswerForm.tsx
components/game/VariableFeedbackPanel.tsx
components/game/VariableScoreSummary.tsx
```
### 수정
```text
app/page.tsx
```
### 변경 없음
```text
app/(game)/play/page.tsx
app/(game)/play/PlayClient.tsx
store/gameStore.ts
lib/engine/parenthesis-sign.ts
lib/engine/parenthesis-sign.test.ts
```
---
## 완료 기준
- `/`에서 `미지수 괄호 풀기`가 활성화되어 있다
- `/variable-parentheses` 준비 화면에서 문제 수 `5`, `10`을 선택할 수 있다
- `()`가 selected 상태이고 `{}`, `[]`는 disabled 상태다
- `연습 시작`으로 `/variable-parentheses/play?count=N`에 진입한다
- 미지수 괄호 문제는 괄호 안 두 항 중 하나만 `정수x` 항을 포함한다
- 풀이 화면에서 첫째 항, 둘째 항을 순서대로 보기에서 선택할 수 있다
- 각 항의 보기는 정답 절대값 기준 음수/양수 두 개다
- 두 항 선택 후 `정답 확인` 버튼으로 제출할 수 있다
- 정답·오답 피드백이 기존 색상과 기호 규칙(`○`, `×`)으로 표시된다
- 최종 결과 화면이 표시된다
- 기존 숫자 괄호 풀이 화면은 현재 동작을 유지한다
- `pnpm test` 통과
- `pnpm build` 통과