12 KiB
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 연동
- 스피드 퀴즈 모드
문제 생성 규칙
문제 형태
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): stringkind === 'variable':6x,-6xkind === '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.tsxapp/(game)/variable-parentheses/VariableParenthesesClient.tsx
4-D. 첫 화면 수정
완료 기준: /에서 미지수 괄호 풀기가 활성 링크로 표시된다
app/page.tsx수정
4-E. 선택지형 풀이 화면 추가
완료 기준: /variable-parentheses/play?count=5에서 문제를 풀 수 있다
app/(game)/variable-parentheses/play/page.tsxapp/(game)/variable-parentheses/play/VariablePlayClient.tsxcomponents/game/VariableChoiceAnswerForm.tsx
4-F. 피드백 및 결과 화면 추가
완료 기준: 선택 결과에 따라 피드백과 최종 결과가 표시된다
components/game/VariableFeedbackPanel.tsxcomponents/game/VariableScoreSummary.tsx
4-G. 회귀 확인
완료 기준: 숫자 괄호와 미지수 괄호 흐름이 모두 동작한다
/→숫자 괄호 풀기→ 준비 화면 →/play?count=5→ 풀이 → 결과/→미지수 괄호 풀기→ 준비 화면 →/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통과