틀린 문제를 그냥 넘기면 안 된다.

오답 노트에 모아서 나중에 다시 보자.


오답 데이터 구조

const wrongAnswer = {
  id: Date.now(),
  num1: 7,
  num2: 8,
  op: '+',
  userAnswer: 14,      // 내가 쓴 답
  correctAnswer: 15,   // 정답
  reason: '오답',       // 또는 '시간초과'
  difficulty: 'easy',
  date: '2024-12-20T10:30:00',
  reviewed: false      // 복습 여부
};

오답 추가

틀렸거나 시간 초과일 때:

const addWrongAnswer = (num1, num2, op, userAns, correctAns, reason) => {
  const newEntry = {
    id: Date.now(),
    num1, num2, op,
    userAnswer: userAns,
    correctAnswer: correctAns,
    reason,
    difficulty,
    date: new Date().toISOString(),
    reviewed: false
  };
  
  // 최대 100개까지만 저장
  const updated = [newEntry, ...wrongAnswers].slice(0, 100);
  saveWrongAnswers(updated);
};

오답 호출

// 오답일 때
if (!isCorrect) {
  addWrongAnswer(
    problem.num1, problem.num2, problem.op, 
    parseInt(userAnswer), problem.answer, 
    '오답'
  );
}

// 시간 초과일 때
const handleTimeout = () => {
  addWrongAnswer(
    problem.num1, problem.num2, problem.op, 
    null, problem.answer, 
    '시간초과'
  );
};

오답 노트 화면

{screen === 'wrong' && (
  <div className="bg-white/90 backdrop-blur rounded-2xl shadow-xl p-4 space-y-4">
    <div className="text-center">
      <div className="text-4xl mb-1">📝</div>
      <h2 className="text-2xl text-rose-600">오답 노트</h2>
      <p className="text-sm text-gray-500">
        {wrongAnswers.length} 
        ({wrongAnswers.filter(w => !w.reviewed).length} 미복습)
      </p>
    </div>
    
    {wrongAnswers.length === 0 ? (
      <div className="text-center py-10 text-gray-500">
        틀린 문제가 없어요!<br/>대단해요! 🌟
      </div>
    ) : (
      <div className="space-y-2 max-h-80 overflow-y-auto">
        {wrongAnswers.map((item) => (
          <WrongAnswerCard key={item.id} item={item} />
        ))}
      </div>
    )}
  </div>
)}

오답 카드

<div className={`p-3 rounded-xl border-2 ${
  item.reviewed 
    ? 'bg-gray-50 border-gray-200' 
    : 'bg-rose-50 border-rose-200'
}`}>
  <div className="flex items-start justify-between gap-2">
    <div className="flex-1">
      {/* 세로 계산식 */}
      <div className="bg-white rounded-lg p-2 inline-block font-mono text-right text-lg">
        <div>{String(item.num1).padStart(4, '\u00A0')}</div>
        <div>
          <span className="text-orange-500">{item.op}</span>
          {String(item.num2).padStart(3, '\u00A0')}
        </div>
        <div className="border-t-2 border-gray-300 pt-1">
          <span className="text-red-500 line-through mr-2">
            {item.userAnswer !== null ? item.userAnswer : '?'}
          </span>
          <span className="text-emerald-600 font-bold">
            {item.correctAnswer}
          </span>
        </div>
      </div>
      <div className="text-xs text-gray-400 mt-1">
        {item.reason} · {DIFFICULTY_SETTINGS[item.difficulty]?.label}
        {item.reviewed && ' · ✓ 복습완료'}
      </div>
    </div>
    
    {/* 버튼들 */}
    <div className="flex flex-col gap-1">
      {!item.reviewed && (
        <button onClick={() => markAsReviewed(item.id)} 
          className="px-2 py-1 bg-emerald-100 text-emerald-600 text-xs rounded-lg">
           복습
        </button>
      )}
      <button onClick={() => removeFromWrongAnswers(item.id)} 
        className="px-2 py-1 bg-gray-100 text-gray-500 text-xs rounded-lg">
        삭제
      </button>
    </div>
  </div>
</div>

틀린 답은 취소선, 정답은 초록색으로.


복습 표시

const markAsReviewed = (id) => {
  const updated = wrongAnswers.map(w => 
    w.id === id ? { ...w, reviewed: true } : w
  );
  saveWrongAnswers(updated);
};

미복습 알림

홈 화면에서 뱃지로 표시:

<button onClick={() => setScreen('wrong')} className="... relative">
  📝 오답 노트
  {wrongAnswers.filter(w => !w.reviewed).length > 0 && (
    <span className="absolute -top-2 -right-2 bg-red-500 text-white text-xs w-6 h-6 rounded-full flex items-center justify-center">
      {wrongAnswers.filter(w => !w.reviewed).length}
    </span>
  )}
</button>

“복습 안 한 게 3개 있어!” 느낌.


다음 글에서 리더보드.

#10 - 리더보드