diff --git a/frontend/src/routes/study/topics/[id]/questions/new/+page.svelte b/frontend/src/routes/study/topics/[id]/questions/new/+page.svelte index dec4c7b..7e7769e 100644 --- a/frontend/src/routes/study/topics/[id]/questions/new/+page.svelte +++ b/frontend/src/routes/study/topics/[id]/questions/new/+page.svelte @@ -48,7 +48,11 @@ let f_exam_round = $state(''); let f_exam_round_mode = $state('select'); // 'select' | 'new' let f_exam_round_new = $state(''); - let f_qnum = $state(1); // 문항 번호 + let f_qnum = $state(1); // 문항 번호 (표시용 — 저장 시 서버 max+1 우선) + // PR-7: 사용자가 input 박스를 직접 편집했는지 추적. true 면 저장 시 명시 전송, + // false 면 null 보내고 서버가 같은 회차 max+1 자동 채움. 회차 변경/저장 후/onMount + // 에서 false 로 reset. + let f_qnum_user_edited = $state(false); // 본문 자동 reset 안 되는 메타 (편의성 — 비워둠이 default) let explanation = $state(''); @@ -146,6 +150,8 @@ f_qnum = found.next_question_number; } } + // 진입 시점은 사용자가 input 박스를 만진 적 없음 — false 로 reset. + f_qnum_user_edited = false; // $effect 의 lastExamRound 를 현재 값으로 sync — 첫 실행이 또 reset 하지 않도록. lastExamRound = f_exam_round; }); @@ -176,6 +182,7 @@ } else if (!found) { f_qnum = 1; } + f_qnum_user_edited = false; lastExamRound = f_exam_round; refreshCompleteFlag(); } @@ -200,11 +207,20 @@ addToast('error', '회차명을 입력하세요'); return; } - f_exam_round = f_exam_round_new.trim(); + const newRound = f_exam_round_new.trim(); + f_exam_round = newRound; f_exam_round_new = ''; f_exam_round_mode = 'select'; - f_qnum = 1; - lastExamRound = f_exam_round; + // PR-7 fix: 사용자가 "새 회차" 모드에 이미 존재하는 회차명을 입력했으면 + // next_question_number 로 시작 (1번부터 다시 시작 X). dropdown 선택과 동일 동작. + const found = examRounds.find((r) => r.exam_round === newRound); + if (found && found.next_question_number) { + f_qnum = found.next_question_number; + } else { + f_qnum = 1; + } + f_qnum_user_edited = false; + lastExamRound = newRound; refreshCompleteFlag(); } @@ -234,6 +250,9 @@ persist(); try { const subj = effectiveSubject(); + // PR-7 fix: 클라이언트 카운터를 신뢰하지 않고 서버가 항상 max+1 결정. + // 사용자가 직접 input 박스를 다른 값으로 수정한 경우만 명시 전송. + const userEditedQnum = f_qnum_user_edited && f_exam_round; const body = { question_text: q_text.trim(), choice_1: c1.trim(), @@ -245,18 +264,25 @@ scope: f_scope || null, exam_name: f_exam_name || null, exam_round: f_exam_round || null, - exam_question_number: f_exam_round ? Number(f_qnum) : null, + // 사용자가 직접 수정 안 했으면 null → 서버가 같은 회차 max+1 자동 채움 + exam_question_number: userEditedQnum ? Number(f_qnum) : null, explanation: explanation || null, source_note: autoSourceNote() || null, }; - await api(`/study-topics/${topicId}/questions`, { + const saved = await api(`/study-topics/${topicId}/questions`, { method: 'POST', body: JSON.stringify(body), }); - addToast('success', `문제 저장됨${f_exam_round ? ` (${f_exam_round} ${f_qnum}번)` : ''}`); + // 응답의 실제 저장값으로 표시 동기화 (서버가 결정한 qnum) + const actualQnum = saved?.exam_question_number ?? null; + if (actualQnum) f_qnum = actualQnum; + addToast('success', `문제 저장됨${f_exam_round && actualQnum ? ` (${f_exam_round} ${actualQnum}번)` : ''}`); if (continueAfter) { clearForCont(); - f_qnum = Number(f_qnum) + 1; + // 다음 표시값 = 방금 저장된 qnum + 1. 사용자가 다시 수정하기 전까지는 + // 자동(서버 max+1) 모드 유지. + if (actualQnum) f_qnum = actualQnum + 1; + f_qnum_user_edited = false; // 회차 진행률 갱신 (도달 체크) await refreshExamRounds(); persist(); @@ -406,6 +432,7 @@ {#if currentProgress() && currentProgress().size}