문제와 원인
문항 조회 응답에는 새 문항과 빈 기록 배열 placeholder가 함께 들어왔다. 기존 병합은 [] !== undefined를 만족한 attempts·examSessions·evaluations를 그대로 채택해 브라우저의 풀이·시험·평가 상태를 지웠다.
수정 전 코드
refreshData
좌우로 이동해 전체 내용을 확인하세요. 키보드: ←·→, Home·End
(previous) => ({
...previous,
...payload,
questions: mergeById(previous.questions, payload.questions ?? []),
theories: mergeById(previous.theories, payload.theories ?? []),
attempts: payload.attempts !== undefined
? payload.attempts : previous.attempts,
examSessions: payload.examSessions !== undefined
? payload.examSessions : previous.examSessions,
theoryProgress: payload.theoryProgress !== undefined
? payload.theoryProgress : previous.theoryProgress,
evaluations: payload.evaluations !== undefined
? payload.evaluations : previous.evaluations,
overview: payload.overview ?? previous.overview,
practiceMeta: payload.practiceMeta ?? previous.practiceMeta,
theoryNavigation: payload.theoryNavigation ?? previous.theoryNavigation,
})
수정 후 코드
refreshData
응답의 scope를 병합 함수에 넘겨 해당 조회가 갱신하는 필드만 반영한다. practice·questions는 문항만 병합하고 기존 기록을 유지한다. mock의 시험 목록과 records의 풀이·평가 목록처럼 실제 갱신 대상인 배열은 빈 응답도 반영한다.
좌우로 이동해 전체 내용을 확인하세요. 키보드: ←·→, Home·End
const payload = await fetchStudyData(scope, params);
setData(previous => mergeStudyResponse(previous, payload, scope));
mergeStudyResponse
좌우로 이동해 전체 내용을 확인하세요. 키보드: ←·→, Home·End
export function mergeStudyResponse(
previous: StudyData,
payload: StudyData,
scope: StudyScope,
): StudyData {
const common = {
...previous,
settings: payload.settings ?? previous.settings,
site: payload.site ?? previous.site,
adminAccess: payload.adminAccess ?? previous.adminAccess,
};
const questions = () => mergeById(previous.questions, payload.questions ?? []);
switch (scope) {
case "shell": return common;
case "overview":
case "bootstrap":
return { ...common, overview: payload.overview ?? previous.overview };
case "theory":
case "theories":
return {
...common,
theories: mergeById(previous.theories, payload.theories ?? []),
theoryNavigation: payload.theoryNavigation ?? previous.theoryNavigation,
};
case "practice-meta":
return { ...common, practiceMeta: payload.practiceMeta ?? previous.practiceMeta };
case "practice":
case "questions":
return { ...common, questions: questions() };
case "records":
return {
...common,
questions: questions(),
attempts: payload.attempts,
examSessions: mergeById(previous.examSessions, payload.examSessions),
evaluations: payload.evaluations,
theoryProgress: payload.theoryProgress,
recordStats: payload.recordStats,
recordsSummary: payload.recordsSummary,
recordsPagination: payload.recordsPagination,
};
case "mock":
return {
...common, questions: questions(), examSessions: payload.examSessions,
mockPagination: payload.mockPagination,
};
case "mock-session":
return {
...common, questions: questions(),
examSessions: mergeById(previous.examSessions, payload.examSessions),
};
}
}
검증 결과
시험·풀이·평가가 각각 1개, 기존 문항이 1개인 이전 상태에 새 문항 1개와 빈 기록 배열을 가진 동일한 questions 응답을 적용했다.
좌우로 이동해 전체 내용을 확인하세요. 키보드: ←·→, Home·End
| 비교 항목 | 기대 결과 | 수정 전 | 수정 후 |
|---|---|---|---|
examSessions 보존 | 1개 | 0개 | 1개 |
attempts 보존 | 1개 | 0개 | 1개 |
evaluations 보존 | 1개 | 0개 | 1개 |
| 기존 문항·새 문항 병합 | 2개 | 2개 | 2개 |
같은 이전 상태와 questions 응답을 로컬 Node의 원본 전후 병합 함수에 다시 1회씩 전달했다. 응시·풀이·평가 배열은 각각 0개에서 1개 보존으로 바뀌었고 문항은 두 구현 모두 2개로 병합됐다.