문제와 원인
복수 선택 함수를 한 이벤트에서 ①·② 순서로 호출하면 두 호출 모두 같은 렌더의 selected=[]를 읽었다. [0]과 [1]이 차례로 상태 큐에 들어가 마지막 [1]만 남았다.
수정 전 코드
toggleAnswer
좌우로 이동해 전체 내용을 확인하세요. 키보드: ←·→, Home·End
const next = currentQuestion.kind === "single"
? [index]
: selected.includes(index)
? selected.filter(item => item !== index)
: [...selected, index];
setSelected(next);
좌우로 이동해 전체 내용을 확인하세요. 키보드: ←·→, Home·End
if (bookmarkPractice) {
setBookmarkAnswers((previous) => ({ ...previous, [String(currentQuestion.id)]: next }));
}
수정 후 코드
toggleAnswer
완성 배열 대신 updater 함수를 전달해 앞선 갱신 결과로 다음 선택을 계산한다. selected와 문항별 bookmarkAnswers에 같은 선택·해제 함수를 적용했다.
좌우로 이동해 전체 내용을 확인하세요. 키보드: ←·→, Home·End
function toggleAnswer(index: number) {
if (!currentQuestion || revealed || practiceGradingRef.current) return;
const toggle = (answers: number[]) =>
currentQuestion.kind === "single"
? [index]
: answers.includes(index)
? answers.filter(item => item !== index)
: [...answers, index];
setSelected(toggle);
if (bookmarkPractice) {
setBookmarkAnswers(previous => ({
...previous,
[String(currentQuestion.id)]:
toggle(previous[String(currentQuestion.id)] ?? []),
}));
}
}
첫 updater가 []를 [0]으로 바꾸면 다음 updater는 [0]에서 [0,1]을 만든다. 단일 선택은 마지막 답 하나로 교체하고, 복수 선택은 포함 여부에 따라 추가·해제한다.
검증 결과
같은 React 격리 화면에서 수정 전후 구현에 입력 시나리오 5개를 적용했다. 한 이벤트의 ①·② 연속 입력은 최종 반영이 1/2개·50%에서 2/2개·100%로 바뀌었다.
좌우로 이동해 전체 내용을 확인하세요. 키보드: ←·→, Home·End
| 입력 조건 | 기대 결과 | 수정 전 | 수정 후 |
|---|---|---|---|
| 별개의 클릭 ① → ② | ①·② | ①·② | ①·② |
| ① 더블클릭 | 선택 없음 | 선택 없음 | 선택 없음 |
| 별개의 클릭 ① → ② → ③ | ①·②·③ | ①·②·③ | ①·②·③ |
| 단일 선택에서 ① → ② | ② | ② | ② |
| 한 이벤트에서 ①·② 연속 호출 | ①·② | ② | ①·② |
문항 1에서 ①·② 선택 → 문항 2에서 ③ 선택 → 문항 1 복귀 시 두 구현 모두 ①·②를 복원했다. 채점 시작 시 포착한 답안도 [0,1]이었다.
같은 이벤트 안의 추가·해제 연산은 아래 입력으로 계산했다.
좌우로 이동해 전체 내용을 확인하세요. 키보드: ←·→, Home·End
| 초기 선택과 입력 | 수정 전 계산 | 수정 후 계산 |
|---|---|---|
[]에서 ① → ① | [0], 두 번째 해제 누락 | [], 추가 후 해제 |
[0]에서 ① 해제 → ② 선택 | [0,1], ① 재포함 | [1], 해제 결과 유지 |
동일 이벤트의 상태 큐
로컬 Node에서 원본 toggleAnswer의 상태 갱신을 호출 순서대로 적용했다. 초기 선택 배열은 []이며 같은 이벤트 안의 네 입력을 각 1회 실행했다. 인덱스 0·1·2는 보기 ①·②·③에 해당한다. 기대 배열과 같은 결과는 1/4개에서 4/4개로 바뀌었다.
좌우로 이동해 전체 내용을 확인하세요. 키보드: ←·→, Home·End
| 동일 이벤트의 입력 | 수정 전 배열 | 수정 후 배열 |
|---|---|---|
| 복수 선택 ① → ② | [1] | [0,1] |
| 복수 선택 ① → ① | [0] | [] |
| 복수 선택 ① → ② → ③ | [2] | [0,1,2] |
| 단일 선택 ① → ② | [1] | [1] |