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

12 KiB
Raw Permalink Blame History

04. 미지수 괄호 학습과 선택지형 풀이 화면

계획 검토

Codex가 작성한 초안은 방향이 맞다. 엔진 설계, store 분리, 화면 구조가 기존 숫자 괄호 흐름과 일관성 있게 정리되어 있다.

조정이 필요한 부분:

  • VariableUserAnswerselectedChoiceIds는 보기 id 기반 채점이므로 그대로 유지한다. 보기가 매번 섞이므로 계수 값 자체보다 id로 선택을 추적하는 것이 명확하다.
  • 사용자가 요청한 "보기 하나는 음수, 보기 하나는 양수" 방식이 Codex 초안과 동일하다. 보기는 정답 계수의 절대값을 기준으로 양수/음수 두 개만 표시한다.
  • 선택지 순서는 매번 셔플한다.
  • 둘째 항 선택 후 정답 확인 버튼으로 명시적으로 제출한다. 자동 제출은 사용자가 선택을 확인할 여지 없이 넘어가므로 사용하지 않는다.
  • 준비 화면은 NumberParenthesesClient와 구조가 동일하므로 처음에는 별도 파일로 구현한다. 중복이 불편해지면 그때 공통 컴포넌트로 분리한다.

목표

미지수가 포함된 괄호 풀기 문제를 선택지 방식으로 풀 수 있는 학습 흐름을 추가한다.

이번 단계의 최종 상태:

  • /에서 미지수 괄호 풀기가 활성 링크로 표시된다.
  • /variable-parentheses 준비 화면에서 문제 수와 괄호 종류를 선택한다.
  • /variable-parentheses/play?count=N 풀이 화면에서 첫째 항, 둘째 항 순서로 보기를 고르고 정답을 확인한다.
  • 기존 숫자 괄호 풀이 화면은 현재 동작을 유지한다.

이번 단계에서 할 것

  • 첫 화면의 미지수 괄호 풀기 disabled 해제 (/variable-parentheses 링크)
  • 미지수 괄호 학습 준비 화면 추가
  • 미지수 괄호 문제 생성 엔진 추가
  • 미지수 괄호 전용 Zustand store 추가
  • 미지수 괄호 전용 풀이 화면 추가 (선택지형)
  • 정답·오답 피드백 및 결과 화면 추가
  • 엔진 단위 테스트 추가

이번 단계에서 하지 않을 것

  • 분수 괄호, 실수 괄호 문제 생성
  • {}, [] 괄호 문제 생성
  • 여러 미지수(x, y, z) 지원
  • 기존 숫자 괄호 /play 화면 동작 변경
  • 사용자가 직접 미지수 답을 타이핑하는 입력 방식
  • DB, Redis, NextAuth 연동
  • 스피드 퀴즈 모드

문제 생성 규칙

문제 형태

multiplier(a ± b)

괄호 안 두 항 중 정확히 하나가 정수 × x 항이다.

예시:

-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 붙임)

보기 생성

정답 계수의 절대값을 기준으로 두 보기를 만든다.

정답: -6x  →  보기: -6x, 6x
정답: 12   →  보기: -12, 12
  • 보기 두 개는 항 종류(미지수 항 / 숫자 항)가 같다.
  • 보기 순서는 매번 셔플한다.
  • 보기 id는 안정적으로 생성한다: ${problem.key}:term-0:negative, ${problem.key}:term-0:positive

중복 방지 key

${multiplier}:${firstKind}:${firstCoeff}:${operator}:${secondKind}:${secondCoeff}

예: -2:variable:3:-:constant:6


타입 설계

신규 파일: lib/engine/variable-types.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와 동일한 구조로 분리한다.

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 유지
  • 안내 문구를 숫자 괄호, 미지수 괄호 두 개가 활성화된 상태로 수정

미지수 괄호 학습 준비 화면

신규 파일:

app/(game)/variable-parentheses/page.tsx
app/(game)/variable-parentheses/VariableParenthesesClient.tsx

화면 구성:

  • 문제 수: 5, 10 버튼 그룹 (기본값 10)
  • 괄호 종류: () selected, {} disabled, [] disabled
  • 연습 시작 버튼 → /variable-parentheses/play?count=${selectedProblemCount} 이동, 클릭 시 세션 초기화

미지수 괄호 풀이 화면

신규 파일:

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

풀이 흐름:

문제 표시 (KaTeX 카드)
  ↓
첫째 항 보기 2개 표시 → 선택
  ↓
둘째 항 보기 2개 표시 → 선택
  ↓
[정답 확인] 버튼 활성화 → 클릭
  ↓
피드백 표시 → [다음 문제] 또는 [결과 보기]
  ↓
최종 결과 화면

VariableChoiceAnswerForm props 초안:

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 표시 형식:

(-2) × 3x = -6x  ○
(-2) × (-6) = 12  ○

VariableScoreSummary 표시 형식:

-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 → 풀이 → 결과

생성 또는 수정 파일

신규 생성

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

수정

app/page.tsx

변경 없음

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 통과