시험 합격 판정에 클라이언트 자기점수가 합산되는 문제

이 글의 목차 5개 항목

문제와 원인

SQLP 시험은 서술형 자기점수를 총점과 passed에 합산한다. 객관식 50점을 유지한 채 각 서술형의 자기점수를 0점에서 100점으로 바꾸면 총점은 50점에서 80점, passed는 false에서 true로 바뀐다. 서버는 클라이언트의 자기평가 점수를 합격 판정에 그대로 사용했다.

서버에 저장된 snapshot이 없어도 제출 답안과 snapshot 문자열이 같으면 snapshot을 받아들이고, 같은 요청의 0–100점 점수를 저장한다. 이 수용 조건은 과거·현재 SQLP 경로에 모두 남아 있다.

현재 코드

SQLP 상태 정리·제출 계산

const snapshot = storedSnapshot
  || (submittedSnapshot && submittedAnswer.trim() === submittedSnapshot ? submittedSnapshot : "");
if (snapshot) {
  descriptiveSnapshots[id] = snapshot;
  descriptiveAnswers[id] = snapshot;
  const score = Number(scoreSource[id]);
  if (Number.isFinite(score) && score >= 0 && score <= 100) {
    descriptiveScores[id] = Math.round(score);
  }
}

현재 계산부는 provider: "self"로 저장한 자기점수를 문항 배점으로 환산해 합격 판정에 사용한다.

const converted = Math.min(
  config.descriptivePoint,
  selfScore * (config.descriptivePoint / 100),
);
descriptiveScore += converted;
const unroundedTotal = Math.min(config.totalPoints, objectiveScore + descriptiveScore);
const totalScore = rounded(unroundedTotal, decimals);
passed: (selectedExam === "IPEP" ? unroundedTotal + 1e-9 : totalScore)
  >= config.passingScore && !failedMinimum,

기존 서버 채점 예시 · IPEP

IPEP 실기 분기는 답안 텍스트를 받아 gradePracticalAnswer()의 서버 채점 결과로 점수를 계산한다. SQLP는 위 자기점수 합산 경로를 사용한다.

if (session.examType === "IPEP") {
  const value = descriptiveSource[id];
  // 답안 타입과 길이 검증
  const answer = value ?? "";
  if (answer) descriptiveAnswers[id] = answer;
  continue;
}
const grade = await gradePracticalAnswer(rawQuestion, answer);
// grade가 없으면 제출을 보류
const earned = grade.score / 100 * config.descriptivePoint;
descriptiveScore += earned;
descriptiveScores[String(id)] = grade.score;

진단 결과

과거·현재 원본 study API·저장소를 Node.js·SQLite의 로컬 DB와 같은 현재 스키마에서 실행했다. SQLP 72문항 중 객관식 70문항은 과락 없이 50점을 유지하고 서술형 2개에는 합성 답안과 0점·100점 자기점수를 넣어 조건별·버전별 1회 제출했다.

실행 코드각 서술형 자기점수객관식서술형 환산총점passed
과거 원본0점50점0점50점false
과거 원본100점50점30점80점true
현재 원본0점50점0점50점false
현재 원본100점50점30점80점true

서술형 환산은 문항당 15점 × 2문항이다. 네 제출 모두 HTTP 200, 과락 false, 저장된 평가의 provider는 self였다.

로컬 Swagger UI의 1280×720 CSS px·DPR 2 화면에서는 72문항 전체를 합성 자료로 구성해 현재 API에 자기점수 0점·100점을 각 1회 제출했다. 객관식 정답 수는 7/10·14/20·29/40으로 50점을 고정하고, exam-start가 생성한 session ID·revision과 같은 서술형 답안·snapshot을 사용했다.

X-Local-* 점수·합격 헤더는 API가 저장한 시험 결과의 DB 조회값이다.

자기점수 0점: 총점 50점·passed false원본 보기 (새 창)

자기점수 0점: 총점 50점·passed false

100%원본 보기 (새 창)

확대한 이미지는 상하좌우로 이동할 수 있습니다. 키보드는 이미지 영역에 초점을 두고 방향키를 사용하세요. Esc로 닫습니다.

각 서술형 자기점수HTTP객관식 / 서술형 환산총점passed / provider
0점20050점 / 0점50점false / self
100점20050점 / 30점80점true / self
자기점수 100점: 총점 80점·passed true원본 보기 (새 창)

자기점수 100점: 총점 80점·passed true

100%원본 보기 (새 창)

확대한 이미지는 상하좌우로 이동할 수 있습니다. 키보드는 이미지 영역에 초점을 두고 방향키를 사용하세요. Esc로 닫습니다.