퀴즈 관리 & 문제 은행

발행: (2025년 12월 30일 오후 07:06 GMT+9)
6 min read
원문: Dev.to

Source: Dev.to

Data Model

  • One Quiz는 여러 Questions를 가집니다
  • One Question는 여러 Options를 가집니다
  • One Option은 정답으로 표시될 수 있습니다

이러한 중첩 관계는 초기 데이터 생성 시에는 단순해 보이지만, 편집 단계에 들어가면 훨씬 복잡해집니다.

복잡한 데이터 편집

일반적인 편집 워크플로우는 다음과 같습니다:

  1. 교사가 퀴즈 편집기를 엽니다.
  2. 퀴즈 제목을 변경합니다.
  3. 질문 #2를 삭제합니다.
  4. 질문 #5의 텍스트를 편집합니다.
  5. 마지막에 새 질문을 추가합니다.

모든 변경 사항은 Save 버튼 하나로 백엔드에 전송됩니다. 백엔드는 이러한 수정 사항을 어떻게 적용할지 결정해야 합니다.

접근 방식

접근 방식장점단점
Partial Update (Diffing)변경된 부분만 업데이트합니다.복잡하고 유지 보수가 어렵으며 중첩 관계에서 버그가 발생하기 쉽습니다.
Full Replacement구현이 간단합니다.데이터 참조가 손실될 위험이 있습니다.

수정된 전체 교체 (Academic Suite)

Academic Suite는 단순성, 데이터 일관성 및 안전성을 균형 있게 유지하기 위해 데이터베이스 트랜잭션으로 감싼 수정된 전체 교체 방식을 사용합니다. 전체 업데이트 과정은 원자적으로 수행되며, 어느 단계에서든 실패하면 모든 변경 사항이 롤백됩니다.

UpdateQuiz 구현 (handlers/quiz.go)

// Go (gorm)
err := database.DB.Transaction(func(tx *gorm.DB) error {
    // 1. Update main quiz data (title, duration, etc.)
    if err := tx.Save(&quiz).Error; err != nil {
        return err
    }

    // 2. Delete all old questions
    if err := tx.Delete(&models.Question{}, "quiz_id = ?", id).Error; err != nil {
        return err
    }

    // 3. Re‑insert questions from request
    for _, q := range req.Questions {
        q.QuizID = id
        if err := tx.Create(&q).Error; err != nil {
            return err
        }
    }

    return nil
})

결과

  • 질문 ID가 매 편집마다 변경됩니다.
  • answers 또는 attempts 테이블이 question_id를 참조하고 있다면, 학생 성적 데이터가 손상될 수 있습니다.

운영 시 안전 장치

  • Active 상태이거나 Attempts가 있는 퀴즈는 잠금(동결)되어야 합니다.
  • 또는 질문에 대해 soft delete 또는 versioning을 사용하여 시험 기록을 보존할 수 있습니다.

Excel 질문 가져오기

웹 양식으로 수십 개의 질문을 입력하는 것은 비효율적이므로 Academic Suite는 Excel (.xlsx) 파일에서 가져오는 기능을 제공합니다.

  • 사용 라이브러리: github.com/xuri/excelize/v2

데이터 검증 문제점

Excel 파일은 자유 형식입니다:

  • 열이 비어 있을 수 있습니다.
  • 서식이 일관되지 않을 수 있습니다.
  • 정답이 잘못된 위치에 있을 수 있습니다.

백엔드는 각 행을 개별적으로 검증합니다:

// Go
for i, row := range rows {
    if len(row) < 7 {
        errors = append(errors, fmt.Sprintf(
            "Row %d: Incomplete columns",
            i+1,
        ))
        continue
    }
    // Further parsing and validation...
}

장점

  • 유효한 행은 그대로 저장됩니다.
  • 문제 있는 행은 사용자에게 구체적으로 보고되어, 데이터 일관성을 해치지 않으면서 경험을 향상시킵니다.

효율적인 데이터 검색

퀴즈와 그에 포함된 모든 질문 및 답변 옵션을 표시하기 위해 Academic Suite는 GORM의 eager loading을 사용하여 N+1 쿼리 문제를 방지합니다.

// Go (gorm) – eager loading
database.DB.
    Preload("Questions").
    Preload("Questions.Options").
    First(&quiz, "id = ?", id)

eager loading을 사용하면 질문 수가 많은 퀴즈에서도 성능이 안정적으로 유지됩니다.

요약

이 장에서는 안정적이고 확장 가능한 Question BankQuiz Management 시스템의 기반을 구축했습니다. 주요 내용:

  • 중첩 업데이트의 과제.
  • 데이터 일관성을 유지하기 위한 트랜잭션 전략.
  • 진행 중인 퀴즈를 편집할 때의 위험 및 완화 기법.
  • 행 수준 검증이 포함된 사용자 친화적인 Excel 가져오기.
  • 최적 성능을 위한 즉시 로딩(eager loading) 기법.

다음: Chapter 5에서는 Exam Engine을 다루며, 타이머 설정, 시험 상태, 정밀 채점 메커니즘을 살펴볼 예정입니다.

Back to Blog

관련 글

더 보기 »

Spring Data JPA 관계

소개 새해 복 많이 받으세요! 풀스택 여정의 지난 10일 동안, 입사 직후 프로젝트를 진행해 왔습니다. 처음에는 Re...

🚀 GraphQL APIs 설명 (실제 Node.js 예제와 함께)

REST는 어디에나 존재하지만, GraphQL은 현대 API가 구축되는 방식을 바꾸고 있습니다. 이 게시물에서는 다음을 배울 수 있습니다: - GraphQL이 실제로 무엇인지 - 전문 용어 없이 어떻게 작동하는지 - A rea...