React로 중학생 퀴즈 앱 만들기: 게임화, 접근성, 적응형 질문

발행: (2026년 6월 6일 PM 06:00 GMT+9)
10 분 소요
원문: Dev.to

출처: Dev.to

내 조카는 6월에 브레베(Brevet) 시험을 본다 — 프랑스의 전국 중학교 시험이다. 그는 종이 플래시카드로 복습하지만 이틀 만에 모든 것을 잊어버리고, 수학 노트를 다시 읽는 것보다 유튜브 쇼츠를 보는 것을 더 좋아한다. 전형적인 상황이다. 나는 그에게 동기부여가 되는 무언가를 만들고 싶었다 — 또 다른 복습 PDF가 아니라, 숙제보다 게임에 더 가깝게 느껴지는 무언가. 결과물은 인터랙티브 퀴즈 플랫폼이다. 수학, 물리‑화학, 생물학을 6학년부터 9학년까지 다룬다.

프론트엔드는 React, 백엔드는 전혀 없으며(localStorage 사용), 코드보다 게이미피케이션과 접근성에 훨씬 더 많은 작업을 투자했다.


기존 퀴즈 도구들의 문제점

온라인에는 수십 개의 브레베 복습 도구가 있다. 하지만 대부분 같은 결함을 가지고 있다: 매번 동일한 질문, 진행 상황 시각화 부재, 그리고 탭을 닫고 싶게 만드는 UX. 14세 청소년이 정부 양식 같은 페이지에 머무를 리는 없다. 그들은 즉각적인 피드백, 보상, 그리고 진전감을 필요로 한다.

또 다른 문제는 대부분의 퀴즈가 모든 질문을 동등하게 취급한다는 점이다. 브레베에서 계수가 3인 삼각법 문제는 기본 산수 문제와 같은 무게를 가진다. 학생이 퀴즈에서 15/20점을 받아도, 가장 높은 가중치를 가진 영역에서 틈이 집중돼 있다면 실제 시험에서는 크게 떨어질 수 있다.


30개의 무작위 질문, 가중 점수

각 퀴즈는 도메인당 2~4개의 질문을 ~50개의 질문 은행에서 골라 총 약 30개의 질문을 만든다. 학생은 같은 순서를 두 번 이상 보지 않는다.

점수는 두 축으로 계산된다: 모든 질문이 동일한 무게를 갖는 원점수와, 실제 브레베 계수를 반영한 20점 만점의 가중 점수. 방정식과 선형 함수는 기본 산수보다 4배 더 높은 가중치를 가진다. 이는 학생들이 자신의 수행을 인식하는 방식을 완전히 바꾼다 — 정신 계산은 잘하지만 삼각법에 약한 학생은 두 점수 사이의 차이에서 바로 그 격차를 확인할 수 있다.

// Domain coefficients (reflect actual Brevet weight)
const DOMAIN_WEIGHTS = {
    arithmetic:    2,
    powers:        2,
    identities:    3,
    equations:     4,
    functions:    4,
    pythagoras:    3,
    thales:        3,
    trigonometry:  3,
    geometry3d:    2,
    statistics:    2
};

// Weighted score = sum(correct × coeff) / sum(coeff) × 20
function computeWeightedScore(results) {
    let totalWeight = 0, earnedWeight = 0;
    for (const [domain, answers] of Object.entries(results)) {
        const w = DOMAIN_WEIGHTS[domain];
        totalWeight += answers.length * w;
        earnedWeight += answers.filter(a => a.correct).length * w;
    }
    return (earnedWeight / totalWeight) * 20;
}

전체 화면 모드 진입

전체 화면 모드 종료


스킬 레이더와 진행 곡선

퀴즈 종료 후 보고서는 내가 가장 많은 시간을 투자한 부분이다. 단순히 “14/20점 받았다”는 충분하지 않다 — 학생은 어디가 강점이고 어디를 개선해야 하는지를 이해해야 한다.

스킬 레이더(동적으로 생성된 SVG)는 현재 퀴즈 프로필을 이전 시도들의 평균 프로필과 겹쳐 보여준다. 한눈에 삼각법이 개선되고 있는지, 여전히 약점인지 확인할 수 있다. 각 도메인마다 상세 카드가 있다: 정답률, 계수, 그리고 힌트와 함께 특정 실수를 복습할 수 있는 토글이 있다.

SVG 진행 곡선은 최근 시도들의 가중 점수를 플롯한다. 간단하지만, 상승하는 곡선을 보는 것은 청소년의 동기부여에 큰 심리적 효과를 준다. 점수 표보다 훨씬 효과적이다.

// SVG radar generation — one polygon per data series
function drawRadar(ctx, domains, current, average) {
    const cx = 150, cy = 150, r = 120;
    const n = domains.length;
    const angleStep = (2 * Math.PI) / n;

    function toPoints(scores) {
        return scores.map((s, i) => {
            const angle = angleStep * i - Math.PI / 2;
            const ratio = s / 100;
            return `${cx + r * ratio * Math.cos(angle)},${cy + r * ratio * Math.sin(angle)}`;
        }).join(' ');
    }

    return `
        
            
            
            ${domains.map((d, i) => {
                const angle = angleStep * i - Math.PI / 2;
                return `${d}`;
            }).join('')}
        `;
}

전체 화면 모드 진입

전체 화면 모드 종료


적응형 질문: 약점 공략

이 메커니즘이 기본 무작위 퀴즈와 차별화되는 핵심이다. 틀린 답변은 모두 약점 추적기(localStorage)에 기록된다. 다음 퀴즈에서는 자주 놓친 질문이 1~3배 높은 확률로 선택된다.

실제로, 학생이 탈레스 정리를 지속적으로 놓친다면 다음 번에 그 질문이 더 많이 등장한다 — 퀴즈가 반복적으로 느껴질 정도는 아니지만, 뇌가 방법을 고정시키기에 충분히 많이 보이게 된다. 이는 복잡한 Anki 스타일 알고리즘 없이도 구현된 간소화된 간격 반복이다.

// Weighted selection: frequently missed questions come back more
function selectQuestions(pool, wrongTracker, count) {
    const weighted = pool.map(q => ({
        ...q,
        weight: 1 + (wrongTracker[q.id] || 0)  // base 1, +1 per past error
    }));

    const selected = [];
    while (selected.length  0) {
        const totalWeight = weighted.reduce((sum, q) => sum + q.weight, 0);
        let random = Math.random() * totalWeight;
        const idx = weighted.findIndex(q => (random -= q.weight) <= 0);
        selected.push(weighted.splice(idx, 1)[0]);
    }
    return selected;
}

전체 화면 모드 진입

전체 화면 모드 종료


게이미피케이션: 배지, 연속 기록 및 격려

배지 시스템은 결과뿐 아니라 행동을 보상한다. 첫 번째 퀴즈를 완료하면 배지가 풀린다. 연속 5일째면 또 다른 배지가 해제된다. 세 과목을 모두 시도하면 배지, 6학년부터 9학년까지 전 학년을 커버하면 배지, 두 시도 사이 점수가 3점 이상 상승하면 배지가 주어진다.

일일 연속 기록은 달력 날짜를 기준으로 계산한다 — 하루에 10번 퀴즈를 할 필요는 없으며, 한 번만 하면 연속 기록이 유지된다. 목표는 시험 전날 마라톤식 공부가 아니라 규칙적인 습관을 만드는 것이다. 9학년에게는 시험일까지 남은 일수를 표시해 긴박감을 주되 스트레스를 유발하지 않는다.

퀴즈 중간에 격려 메시지가 팝업된다 (“절반 왔어요, 계속해요!”) 그리고 배지를 획득하면 축하용 콘페티 애니메이션이 재생된다. 사소한 디테일이지만, 바로 이런 것이 청소년이 다음 날에도 돌아오게 만든다.


접근성: 난독증 모드와 키보드 네비게이션

토글을 켜면 OpenDyslexic 폰트가 적용되고, 줄 간격이 늘어나며 문자 간격이 조정된다. 이것은 장난이 아니다 — 학생의 약 5~10%가 난독증을 겪으며, 일반

0 조회
Back to Blog

관련 글

더 보기 »

모바일 한여름 열풍

!Cover image for Mobile Midsommer Madnesshttps://media2.dev.to/dynamic/image/width=1000,height=420,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploa...