시험 엔진
Source: Dev.to
Source:
Chapter 5: The Exam Engine
이 장은 전체 LMS 시스템에서 가장 핵심적인 코어입니다. 이제 Academic Suite는 정적 데이터를 관리하는 수준을 넘어 시간, 상태, 사용자 행동을 실시간으로 처리하기 시작합니다.
시험 엔진은 다음을 보장하는 역할을 합니다:
- 지정된 시간에만 시험에 접근 가능하도록 함
- 모든 학생에게 공정한 시간 할당을 제공함
- 새로 고침, 재연결, 네트워크 장애가 발생해도 시험 상태가 일관되게 유지됨
이 부분의 작은 오류도 채점 불공정이나 시험 시스템 유출을 초래할 수 있습니다.
5.1 Design Philosophy: Quiz vs ExamBatch
온라인 시험 시스템에서 흔히 저지르는 실수는 문제 내용과 시험 일정을 하나의 엔터티에 섞어버리는 것입니다. Academic Suite는 두 개를 명확히 구분합니다:
Quiz
문제와 답변 옵션의 모음입니다. 정적이며 재사용이 가능합니다.
ExamBatch
특정 상황(클래스, 시간, 토큰)에서 시험을 진행하기 위한 표현입니다. 동적입니다.
Benefits of This Separation
이 구분을 통해 하나의 퀴즈를 데이터 중복 없이 여러 번 사용할 수 있습니다:
- 퀴즈 “Basic Math” → 클래스 A 배치 (월요일 08:00)
- 동일 퀴즈 → 클래스 B 배치 (화요일 10:00)
이 접근 방식은 시스템을:
- 더 유연하게
- 확장하기 쉽게
- 일정 변경에 대해 더 안전하게
만듭니다.
5.2 Exam State Machine
시험은 시간 기반 프로세스이므로 상태가 자동으로 변해야 합니다:
scheduledactivefinished
배치 상태를 주기적으로 업데이트하기 위해 cron job을 사용하는 대신, Academic Suite는 Lazy State Update 방식을 사용합니다.
Lazy State Update
시험 상태는 요청이 있을 때마다 현재 서버 시간을 기준으로 평가됩니다. 구현은 handlers/batch.go에 있습니다:
func GetBatches(c *fiber.Ctx) {
now := time.Now()
if now.After(batch.StartTime) && now.Before(batch.EndTime) {
batch.Status = "active"
} else if now.After(batch.EndTime) {
batch.Status = "finished"
}
}
Benefits of This Approach
- 백그라운드 워커가 필요 없음
- 상태가 동기화되지 않을 위험이 없음
- 로직이 단순하고 테스트하기 쉬움
시험 상태는 언제나 실제 서버 시간과 일치합니다.
5.3 Secure Timer: Server‑Side Time Authority
온라인 시험 시스템에서 치명적인 실수는 클라이언트 측 시간에 의존하는 것입니다. 학생 브라우저나 운영체제의 시계는 조작될 수 있기 때문에, JavaScript 기반 타이머를 진실된 시간 소스로 사용할 수 없습니다.
Solution: Server‑Side Time Authority
Academic Suite는 시간에 대한 유일한 진실된 출처를 서버로 지정합니다. 프론트엔드는 주기적으로 다음 엔드포인트를 호출합니다:
GET /api/attempts/:id/time
백엔드는 다음을 기준으로 남은 시간을 계산합니다:
- 시험 시작 시간
- 총 일시정지 시간
- 배치 기간
elapsed := time.Now().Sub(attempt.StartedAt)
effectiveElapsed := elapsed - attempt.TotalPausedTime
remaining := batch.Duration - effectiveElapsed
remaining <= 0 인 경우, 백엔드는 즉시:
- 새로운 답변을 거부하고
- 시도를 자동으로 종료합니다
이 방식으로 클라이언트 측 시간 조작은 무의미해집니다.
5.4 Exam Pause & Resume Features
현실 세계에서는 정전, 네트워크 장애, 비상 상황 등 외부 요인으로 시험이 중단될 수 있습니다. Academic Suite는 Freeze / Resume Batch 기능을 제공합니다.
Pause Mechanism
배치가 일시정지될 때, 백엔드는 PausedAt을 기록합니다. 일시정지 상태가 유지되는 동안 학생의 시간은 감소하지 않습니다.
Resume Mechanism
배치가 재개되면 PausedAt과 현재 시간 사이의 차이를 계산하여 TotalPausedTime에 추가합니다. 이를 통해 다음을 보장합니다:
- 모든 학생이 공정한 시간을 받음
- 시스템 통제 밖의 상황으로 인해 시도가 불이익을 받지 않음
5.5 Exam Token
추가적인 보안 레이어로, 각 ExamBatch는 시험 토큰으로 보호될 수 있습니다. 학생이 로그인하고 시험 시간이… (이하 내용은 다음 파트에 이어집니다)
e가 활성화된 경우, 유효한 토큰을 입력하지 않으면 시도를 시작할 수 없습니다.
시험 토큰의 장점
- 학생들이 일찍 입장하는 것을 방지합니다
- 무단 접근에 의해 시험이 열리는 위험을 줄입니다
- 시험 감독자에게 완전한 제어 권한을 부여합니다
이 토큰은 선택 사항이지만 공식 시험에서는 강력히 권장됩니다.
Source: …
장 요약
이 장에서는 Academic Suite의 핵심인 시험 엔진을 구축했습니다. 다룬 내용은 다음과 같습니다:
- 시험 콘텐츠와 컨텍스트의 분리
- 지연 업데이트를 활용한 시간 기반 상태 머신
- 보안 서버‑사이드 타이머
- 일시 정지 및 재개 메커니즘
- 시험 토큰을 통한 추가 보안
하지만 시험 시스템은 데이터 무결성과 참가자 행동을 보호하지 않으면 완전한 보안을 제공하지 못합니다. 6장에서는 재접속, 다중 탭, 기타 부정 행위 시도 등을 포함한 데이터 무결성 및 부정 방지 전략을 논의할 예정입니다.