중, 고등학교 학생들과 함께 진행할 OX 스피드 퀴즈 만들기

소개

학생들과 교육용 콘텐츠 만들기 2주까지는 아이디어가 있었는데 아이디어를 더 만들어야 해서 clude에게 아이디어를 좀 내 달라고 했습니다.

진행 방법

어떤 도구를 사용했고, 어떻게 활용하셨나요?

claude에게 부탁해 보았습니다.

Tip: 사용한 프롬프트 전문을 꼭 포함하고, 내용을 짧게 소개해 주세요.

한국어로 된 문자 메시지의 스크린샷

Tip: 활용 이미지나 캡처 화면을 꼭 남겨주세요.

한국어가 포함된 웹사이트 스크린샷

Tip: 코드 전문은 코드블록에 감싸서 작성해주세요. ( / 을 눌러 '코드 블록'을 선택)


<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>OX 스피드 퀴즈</title>
<link href="https://fonts.googleapis.com/css2?family=Jua&family=Nanum+Gothic:wght@400;700;800&display=swap" rel="stylesheet">
<style>
  :root {
    --bg: #fdf6ec;
    --surface: #ffffff;
    --o-color: #ff6b35;
    --x-color: #3b82f6;
    --correct: #22c55e;
    --wrong: #ef4444;
    --text: #1a1a2e;
    --text-dim: #6b7280;
    --shadow: rgba(0,0,0,0.1);
    --card-shadow: 0 8px 32px rgba(0,0,0,0.12);
    --tab-active: #1a1a2e;
  }

  * { box-sizing: border-box; margin: 0; padding: 0; }

  body {
    background: var(--bg);
    font-family: 'Nanum Gothic', sans-serif;
    color: var(--text);
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 0 0 60px;
    overflow-x: hidden;
  }

  /* Top bar */
  .topbar {
    width: 100%;
    background: var(--text);
    padding: 18px 24px 14px;
    display: flex;
    align-items: center;
    justify-content: center;
    position: relative;
  }

  .topbar h1 {
    font-family: 'Jua', sans-serif;
    font-size: 22px;
    color: #fff;
    letter-spacing: 0.04em;
  }

  .topbar .subtitle {
    position: absolute;
    right: 20px;
    font-size: 11px;
    color: rgba(255,255,255,0.45);
    letter-spacing: 0.06em;
  }

  /* Subject tabs */
  .tabs {
    display: flex;
    gap: 0;
    background: #fff;
    width: 100%;
    overflow-x: auto;
    border-bottom: 2px solid #e5e7eb;
    scrollbar-width: none;
  }
  .tabs::-webkit-scrollbar { display: none; }

  .tab {
    padding: 12px 20px;
    font-family: 'Jua', sans-serif;
    font-size: 14px;
    color: var(--text-dim);
    cursor: pointer;
    border-bottom: 3px solid transparent;
    white-space: nowrap;
    transition: all 0.2s;
    background: none;
    border-top: none;
    border-left: none;
    border-right: none;
    margin-bottom: -2px;
  }
  .tab:hover { color: var(--text); }
  .tab.active {
    color: var(--text);
    border-bottom-color: var(--text);
  }

  /* Main container */
  .main {
    width: 100%;
    max-width: 680px;
    padding: 28px 20px 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 20px;
  }

  /* Score & combo row */
  .hud {
    display: flex;
    justify-content: space-between;
    align-items: center;
    width: 100%;
  }

  .hud-item {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
  }

  .hud-label {
    font-size: 10px;
    color: var(--text-dim);
    letter-spacing: 0.08em;
    text-transform: uppercase;
  }

  .hud-value {
    font-family: 'Jua', sans-serif;
    font-size: 26px;
    color: var(--text);
    line-height: 1;
  }

  .hud-value.combo {
    color: var(--o-color);
    transition: transform 0.15s;
  }

  .hud-value.combo.bump {
    animation: comboBump 0.25s ease;
  }

  @keyframes comboBump {
    0%   { transform: scale(1); }
    50%  { transform: scale(1.4); }
    100% { transform: scale(1); }
  }

  /* Timer bar */
  .timer-wrap {
    width: 100%;
    height: 10px;
    background: #e5e7eb;
    border-radius: 99px;
    overflow: hidden;
  }

  .timer-bar {
    height: 100%;
    border-radius: 99px;
    background: linear-gradient(90deg, var(--correct), #86efac);
    transition: width 0.1s linear, background 0.3s;
  }

  .timer-bar.danger {
    background: linear-gradient(90deg, var(--wrong), #fca5a5);
    animation: timerPulse 0.4s ease infinite alternate;
  }

  @keyframes timerPulse {
    from { opacity: 1; }
    to   { opacity: 0.6; }
  }

  /* Question card */
  .question-card {
    width: 100%;
    background: var(--surface);
    border-radius: 20px;
    box-shadow: var(--card-shadow);
    padding: 36px 32px;
    text-align: center;
    position: relative;
    min-height: 160px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 12px;
    transition: transform 0.2s;
  }

  .q-number {
    font-size: 11px;
    color: var(--text-dim);
    letter-spacing: 0.1em;
    text-transform: uppercase;
  }

  .q-text {
    font-size: clamp(16px, 4vw, 20px);
    font-weight: 800;
    line-height: 1.5;
    color: var(--text);
  }

  /* card feedback overlay */
  .question-card.correct-flash {
    animation: correctFlash 0.4s ease;
  }
  .question-card.wrong-flash {
    animation: wrongFlash 0.4s ease;
  }

  @keyframes correctFlash {
    0%   { background: #fff; }
    30%  { background: #dcfce7; transform: scale(1.02); }
    100% { background: #fff; transform: scale(1); }
  }
  @keyframes wrongFlash {
    0%   { background: #fff; }
    30%  { background: #fee2e2; transform: scale(0.98); }
    100% { background: #fff; transform: scale(1); }
  }

  /* OX Buttons */
  .ox-row {
    display: flex;
    gap: 20px;
    width: 100%;
    justify-content: center;
  }

  .ox-btn {
    flex: 1;
    max-width: 200px;
    height: 80px;
    border-radius: 18px;
    border: 3px solid transparent;
    font-family: 'Jua', sans-serif;
    font-size: 42px;
    cursor: pointer;
    transition: transform 0.15s, box-shadow 0.15s;
    display: flex;
    align-items: center;
    justify-content: center;
    position: relative;
    overflow: hidden;
  }

  .ox-btn::after {
    content: '';
    position: absolute;
    inset: 0;
    background: rgba(255,255,255,0.15);
    opacity: 0;
    transition: opacity 0.15s;
  }

  .ox-btn:hover::after { opacity: 1; }

  .ox-btn:hover {
    transform: translateY(-4px);
    box-shadow: 0 12px 28px rgba(0,0,0,0.18);
  }

  .ox-btn:active { transform: translateY(0) scale(0.96); }

  .btn-o {
    background: linear-gradient(135deg, #ff6b35, #ff9f1c);
    color: white;
    box-shadow: 0 6px 20px rgba(255,107,53,0.35);
  }

  .btn-x {
    background: linear-gradient(135deg, #3b82f6, #6366f1);
    color: white;
    box-shadow: 0 6px 20px rgba(59,130,246,0.35);
  }

  .ox-btn:disabled {
    opacity: 0.5;
    pointer-events: none;
  }

  /* Explanation popup */
  .explanation {
    width: 100%;
    background: var(--surface);
    border-radius: 16px;
    padding: 20px 24px;
    box-shadow: var(--card-shadow);
    display: none;
    flex-direction: column;
    gap: 8px;
    animation: slideUp 0.3s cubic-bezier(0.34,1.56,0.64,1);
  }

  .explanation.show { display: flex; }

  @keyframes slideUp {
    from { opacity: 0; transform: translateY(16px); }
    to   { opacity: 1; transform: translateY(0); }
  }

  .exp-badge {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-family: 'Jua', sans-serif;
    font-size: 14px;
    padding: 4px 12px;
    border-radius: 99px;
    width: fit-content;
  }

  .exp-badge.correct { background: #dcfce7; color: #16a34a; }
  .exp-badge.wrong   { background: #fee2e2; color: #dc2626; }

  .exp-text {
    font-size: 14px;
    line-height: 1.7;
    color: var(--text);
  }

  .next-btn {
    margin-top: 4px;
    padding: 10px 28px;
    background: var(--text);
    color: #fff;
    border: none;
    border-radius: 99px;
    font-family: 'Jua', sans-serif;
    font-size: 15px;
    cursor: pointer;
    align-self: flex-end;
    transition: transform 0.15s, box-shadow 0.15s;
    box-shadow: 0 4px 12px rgba(0,0,0,0.2);
  }
  .next-btn:hover {
    transform: translateY(-2px);
    box-shadow: 0 8px 20px rgba(0,0,0,0.25);
  }

  /* Progress dots */
  .progress-dots {
    display: flex;
    gap: 6px;
    flex-wrap: wrap;
    justify-content: center;
  }

  .dot {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: #e5e7eb;
    transition: background 0.3s;
  }
  .dot.done-correct { background: var(--correct); }
  .dot.done-wrong   { background: var(--wrong); }
  .dot.current      { background: var(--text); transform: scale(1.3); }

  /* Result screen */
  .result-screen {
    display: none;
    flex-direction: column;
    align-items: center;
    gap: 20px;
    width: 100%;
    padding-top: 10px;
  }

  .result-screen.show { display: flex; }

  .result-emoji {
    font-size: 72px;
    animation: popIn 0.5s cubic-bezier(0.34,1.56,0.64,1);
  }

  @keyframes popIn {
    from { transform: scale(0); opacity: 0; }
    to   { transform: scale(1); opacity: 1; }
  }

  .result-title {
    font-family: 'Jua', sans-serif;
    font-size: 32px;
    color: var(--text);
  }

  .result-score {
    font-family: 'Jua', sans-serif;
    font-size: 64px;
    color: var(--o-color);
    line-height: 1;
  }

  .result-detail {
    font-size: 15px;
    color: var(--text-dim);
    text-align: center;
    line-height: 1.8;
  }

  .best-badge {
    background: linear-gradient(135deg, #fbbf24, #f59e0b);
    color: white;
    padding: 6px 20px;
    border-radius: 99px;
    font-family: 'Jua', sans-serif;
    font-size: 14px;
    display: none;
    animation: popIn 0.4s ease 0.3s both;
  }
  .best-badge.show { display: block; }

  .restart-btn {
    padding: 14px 48px;
    background: linear-gradient(135deg, var(--o-color), #f59e0b);
    color: white;
    border: none;
    border-radius: 99px;
    font-family: 'Jua', sans-serif;
    font-size: 18px;
    cursor: pointer;
    box-shadow: 0 6px 20px rgba(255,107,53,0.4);
    transition: transform 0.15s, box-shadow 0.15s;
  }
  .restart-btn:hover {
    transform: translateY(-3px);
    box-shadow: 0 12px 28px rgba(255,107,53,0.5);
  }

  /* Timeout overlay */
  .timeout-msg {
    position: absolute;
    inset: 0;
    background: rgba(239,68,68,0.92);
    border-radius: 20px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-family: 'Jua', sans-serif;
    font-size: 32px;
    color: white;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.2s;
  }
  .timeout-msg.show { opacity: 1; }

  /* Start screen */
  .start-screen {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 16px;
    padding-top: 20px;
    width: 100%;
  }

  .start-desc {
    text-align: center;
    font-size: 15px;
    line-height: 1.8;
    color: var(--text-dim);
    max-width: 400px;
  }

  .start-btn {
    padding: 16px 56px;
    background: var(--text);
    color: #fff;
    border: none;
    border-radius: 99px;
    font-family: 'Jua', sans-serif;
    font-size: 20px;
    cursor: pointer;
    box-shadow: 0 6px 20px rgba(0,0,0,0.2);
    transition: transform 0.15s, box-shadow 0.15s;
    margin-top: 8px;
  }
  .start-btn:hover {
    transform: translateY(-3px);
    box-shadow: 0 12px 28px rgba(0,0,0,0.3);
  }

  .rules {
    background: white;
    border-radius: 16px;
    padding: 20px 24px;
    display: flex;
    flex-direction: column;
    gap: 10px;
    width: 100%;
    box-shadow: var(--card-shadow);
  }
  .rules li {
    font-size: 14px;
    color: var(--text);
    list-style: none;
    display: flex;
    align-items: flex-start;
    gap: 10px;
    line-height: 1.6;
  }
  .rules li span { font-size: 18px; flex-shrink: 0; }

  @media (max-width: 480px) {
    .question-card { padding: 24px 20px; }
    .ox-btn { height: 68px; font-size: 36px; }
  }
</style>
</head>
<body>

<div class="topbar">
  <h1>⚡ OX 스피드 퀴즈</h1>
  <span class="subtitle">EDU GAME</span>
</div>

<div class="tabs" id="tabs"></div>

<div class="main" id="main">
  <!-- Start screen -->
  <div class="start-screen" id="startScreen">
    <p class="start-desc">과목을 선택하고 10초 안에 O 또는 X를 선택하세요.<br>빠르게, 정확하게!</p>
    <ul class="rules">
      <li><span>⏱️</span> 문제당 <b>10초</b> 제한 — 시간 초과 시 오답 처리</li>
      <li><span>🔥</span> 연속 정답 <b>콤보</b>로 보너스 점수 획득</li>
      <li><span>💡</span> 정답 후 <b>한 줄 해설</b>로 개념 확인</li>
      <li><span>🏆</span> 최고 점수는 자동 저장됩니다</li>
    </ul>
    <button class="start-btn" id="startBtn">시작하기 🚀</button>
  </div>

  <!-- Game screen -->
  <div id="gameScreen" style="display:none; flex-direction:column; align-items:center; gap:20px; width:100%;">
    <div class="hud">
      <div class="hud-item">
        <span class="hud-label">점수</span>
        <span class="hud-value" id="scoreDisplay">0</span>
      </div>
      <div class="hud-item">
        <span class="hud-label">콤보</span>
        <span class="hud-value combo" id="comboDisplay">x0</span>
      </div>
      <div class="hud-item">
        <span class="hud-label">최고</span>
        <span class="hud-value" id="bestDisplay">0</span>
      </div>
    </div>

    <div class="timer-wrap">
      <div class="timer-bar" id="timerBar"></div>
    </div>

    <div class="question-card" id="questionCard">
      <span class="timeout-msg" id="timeoutMsg">⏰ 시간 초과!</span>
      <span class="q-number" id="qNumber">Q 1 / 10</span>
      <p class="q-text" id="qText"></p>
    </div>

    <div class="ox-row">
      <button class="ox-btn btn-o" id="btnO" onclick="answer(true)"></button>
      <button class="ox-btn btn-x" id="btnX" onclick="answer(false)"></button>
    </div>

    <div class="explanation" id="explanation">
      <span class="exp-badge" id="expBadge"></span>
      <p class="exp-text" id="expText"></p>
      <button class="next-btn" id="nextBtn" onclick="nextQuestion()">다음 문제 →</button>
    </div>

    <div class="progress-dots" id="progressDots"></div>
  </div>

  <!-- Result screen -->
  <div class="result-screen" id="resultScreen">
    <span class="result-emoji" id="resultEmoji">🎉</span>
    <p class="result-title">퀴즈 완료!</p>
    <span class="result-score" id="resultScore">0</span>
    <p class="result-detail" id="resultDetail"></p>
    <div class="best-badge" id="bestBadge">🏆 최고 점수 갱신!</div>
    <button class="restart-btn" onclick="restart()">다시 도전 🔄</button>
  </div>
</div>

<script>
const SUBJECTS = {
  '🧪 소프트웨어 테스트': [
    { q: '블랙박스 테스트는 코드 내부 구조를 보지 않는 테스트이다.', a: true,  exp: '맞습니다! 블랙박스 테스트는 입력과 출력만 확인하며 내부 구현은 신경 쓰지 않습니다.' },
    { q: '결함(Bug)은 개발자가 의도적으로 만든 기능이다.', a: false, exp: '아닙니다. 결함은 프로그램이 의도하지 않게 잘못 동작하는 것입니다.' },
    { q: '회귀 테스트는 코드 수정 후 기존 기능이 여전히 잘 동작하는지 확인한다.', a: true,  exp: '정확합니다! 회귀 테스트는 변경 사항이 기존 기능에 영향을 주지 않는지 재점검합니다.' },
    { q: '테스트 케이스(TC)에는 예상 결과가 포함되지 않는다.', a: false, exp: '틀렸습니다. 테스트 케이스는 입력값과 예상 결과를 함께 포함해야 합니다.' },
    { q: '커버리지는 전체 코드 중 테스트가 된 비율을 나타낸다.', a: true,  exp: '맞습니다! 커버리지가 높을수록 더 많은 부분이 테스트되었다는 의미입니다.' },
    { q: '화이트박스 테스트는 코드의 내부 구조를 기반으로 테스트한다.', a: true,  exp: '정확합니다. 화이트박스 테스트는 소스코드의 흐름과 구조를 직접 확인합니다.' },
    { q: '정적 분석은 프로그램을 실행하면서 오류를 찾는 기법이다.', a: false, exp: '아닙니다. 정적 분석은 코드를 실행하지 않고 검사하는 기법입니다.' },
    { q: '프로시저는 테스트를 수행하는 순서와 절차를 의미한다.', a: true,  exp: '맞습니다! 테스트 프로시저는 테스트 케이스를 실행하는 단계별 순서입니다.' },
    { q: '테스트는 소프트웨어에 버그가 없음을 증명할 수 있다.', a: false, exp: '틀렸습니다. 테스트는 버그를 발견할 수는 있지만, 버그가 없음을 완전히 증명하지는 못합니다.' },
    { q: '블랙박스 테스트와 화이트박스 테스트는 함께 사용할 수 있다.', a: true,  exp: '맞습니다! 두 기법은 서로 보완적으로 함께 사용할 때 효과적입니다.' },
  ],
  '💻 프로그래밍 기초': [
    { q: '변수는 데이터를 저장하는 공간이다.', a: true,  exp: '맞습니다! 변수는 프로그램에서 값을 저장하고 참조하기 위한 이름 붙은 메모리 공간입니다.' },
    { q: '함수는 한 번만 사용할 수 있다.', a: false, exp: '아닙니다. 함수는 여러 번 호출해서 재사용할 수 있는 코드 블록입니다.' },
    { q: '반복문은 조건이 참인 동안 코드를 반복 실행한다.', a: true,  exp: '정확합니다! for, while 등의 반복문은 조건에 따라 코드를 여러 번 실행합니다.' },
    { q: '조건문은 항상 else 절이 있어야 한다.', a: false, exp: '틀렸습니다. if 조건문은 else 없이도 사용할 수 있습니다.' },
    { q: '배열은 여러 개의 값을 하나의 변수에 저장할 수 있다.', a: true,  exp: '맞습니다! 배열을 사용하면 같은 타입의 여러 값을 순서대로 저장할 수 있습니다.' },
    { q: '컴파일러는 소스코드를 실행 중에 한 줄씩 번역한다.', a: false, exp: '아닙니다. 그것은 인터프리터의 역할입니다. 컴파일러는 전체 코드를 한 번에 기계어로 번역합니다.' },
    { q: '알고리즘은 문제를 해결하기 위한 단계별 절차이다.', a: true,  exp: '맞습니다! 알고리즘은 명확하고 유한한 단계로 문제를 해결하는 방법입니다.' },
    { q: '주석은 프로그램 실행에 영향을 준다.', a: false, exp: '틀렸습니다. 주석은 개발자가 코드를 설명하기 위한 메모이며 실행 시 무시됩니다.' },
    { q: '객체지향 프로그래밍에서 클래스는 객체의 설계도이다.', a: true,  exp: '맞습니다! 클래스는 객체를 만들기 위한 템플릿(설계도) 역할을 합니다.' },
    { q: '버그(Bug)는 프로그래밍에서 의도된 기능이다.', a: false, exp: '아닙니다. 버그는 소프트웨어의 오류나 결함으로, 의도하지 않은 동작을 유발합니다.' },
  ],
  '🌍 영어 상식': [
    { q: '"Vocabulary"는 어휘, 단어의 뜻을 가진 영단어이다.', a: true,  exp: '맞습니다! Vocabulary는 언어의 어휘 목록 또는 단어 실력을 뜻합니다.' },
    { q: '"Noun"은 동사를 의미하는 문법 용어이다.', a: false, exp: '아닙니다. Noun은 명사입니다. 동사는 "Verb"입니다.' },
    { q: '"Synonym"은 같은 의미를 가진 단어를 뜻한다.', a: true,  exp: '정확합니다! Synonym은 유의어로, 같은 뜻을 가진 다른 단어를 의미합니다.' },
    { q: 'Alphabet은 총 26개의 글자로 이루어져 있다.', a: true,  exp: '맞습니다! 영어 알파벳은 A부터 Z까지 26개의 문자로 구성됩니다.' },
    { q: '"Antonym"은 비슷한 의미의 단어를 뜻한다.', a: false, exp: '틀렸습니다. Antonym은 반의어입니다. 비슷한 의미는 Synonym입니다.' },
    { q: '"Grammar"는 언어의 규칙과 구조를 연구하는 학문이다.', a: true,  exp: '맞습니다! Grammar(문법)는 언어를 올바르게 사용하기 위한 규칙 체계입니다.' },
    { q: '영어에서 "A, E, I, O, U"는 자음(Consonant)이다.', a: false, exp: '아닙니다. A, E, I, O, U는 모음(Vowel)입니다. 나머지 21개가 자음입니다.' },
    { q: '"Pronunciation"은 발음을 의미하는 단어이다.', a: true,  exp: '맞습니다! Pronunciation은 단어나 언어를 소리 내는 방식, 즉 발음을 뜻합니다.' },
    { q: 'Paragraph는 한 문장으로만 이루어져야 한다.', a: false, exp: '틀렸습니다. Paragraph(단락)는 하나의 주제를 다루는 여러 문장의 묶음입니다.' },
    { q: '"Fluency"는 언어를 자연스럽고 유창하게 구사하는 능력이다.', a: true,  exp: '맞습니다! Fluency는 언어를 막힘 없이 자연스럽게 사용하는 능력을 말합니다.' },
  ],
};

let currentSubject = Object.keys(SUBJECTS)[0];
let questions = [];
let qIndex = 0;
let score = 0;
let combo = 0;
let bestScores = {};
let timerInterval = null;
let timeLeft = 10;
let answered = false;
let dotResults = [];

const TIMER_MAX = 10;

function initTabs() {
  const tabsEl = document.getElementById('tabs');
  tabsEl.innerHTML = '';
  Object.keys(SUBJECTS).forEach((name, i) => {
    const btn = document.createElement('button');
    btn.className = 'tab' + (i === 0 ? ' active' : '');
    btn.textContent = name;
    btn.onclick = () => {
      currentSubject = name;
      document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
      btn.classList.add('active');
      showStart();
    };
    tabsEl.appendChild(btn);
  });
}

function showStart() {
  document.getElementById('startScreen').style.display = 'flex';
  document.getElementById('gameScreen').style.display = 'none';
  document.getElementById('resultScreen').classList.remove('show');
  document.getElementById('bestDisplay').textContent = bestScores[currentSubject] || 0;
}

function restart() {
  document.getElementById('resultScreen').classList.remove('show');
  showStart();
}

function startGame() {
  questions = [...SUBJECTS[currentSubject]].sort(() => Math.random() - 0.5).slice(0, 10);
  qIndex = 0; score = 0; combo = 0; dotResults = [];
  document.getElementById('scoreDisplay').textContent = 0;
  document.getElementById('comboDisplay').textContent = 'x0';
  document.getElementById('bestDisplay').textContent = bestScores[currentSubject] || 0;
  document.getElementById('startScreen').style.display = 'none';
  document.getElementById('gameScreen').style.display = 'flex';
  document.getElementById('resultScreen').classList.remove('show');

  buildDots();
  loadQuestion();
}

function buildDots() {
  const el = document.getElementById('progressDots');
  el.innerHTML = '';
  questions.forEach((_, i) => {
    const d = document.createElement('div');
    d.className = 'dot' + (i === 0 ? ' current' : '');
    d.id = 'dot' + i;
    el.appendChild(d);
  });
}

function loadQuestion() {
  answered = false;
  const q = questions[qIndex];
  document.getElementById('qNumber').textContent = `Q ${qIndex + 1} / ${questions.length}`;
  document.getElementById('qText').textContent = q.q;
  document.getElementById('explanation').classList.remove('show');
  document.getElementById('timeoutMsg').classList.remove('show');
  document.getElementById('btnO').disabled = false;
  document.getElementById('btnX').disabled = false;
  document.getElementById('questionCard').className = 'question-card';

  // Update dots
  if (qIndex > 0) {
    const prev = document.getElementById('dot' + (qIndex - 1));
    if (prev) {
      prev.classList.remove('current');
      prev.classList.add(dotResults[qIndex - 1] ? 'done-correct' : 'done-wrong');
    }
  }
  const cur = document.getElementById('dot' + qIndex);
  if (cur) cur.classList.add('current');

  startTimer();
}

function startTimer() {
  clearInterval(timerInterval);
  timeLeft = TIMER_MAX;
  updateTimerBar();
  timerInterval = setInterval(() => {
    timeLeft -= 0.1;
    updateTimerBar();
    if (timeLeft <= 0) {
      clearInterval(timerInterval);
      onTimeout();
    }
  }, 100);
}

function updateTimerBar() {
  const pct = Math.max(0, (timeLeft / TIMER_MAX) * 100);
  const bar = document.getElementById('timerBar');
  bar.style.width = pct + '%';
  bar.className = 'timer-bar' + (timeLeft < 3 ? ' danger' : '');
}

function onTimeout() {
  if (answered) return;
  answered = true;
  document.getElementById('btnO').disabled = true;
  document.getElementById('btnX').disabled = true;
  document.getElementById('timeoutMsg').classList.add('show');
  combo = 0;
  document.getElementById('comboDisplay').textContent = 'x0';
  dotResults.push(false);

  const expEl = document.getElementById('explanation');
  const badge = document.getElementById('expBadge');
  const expText = document.getElementById('expText');
  badge.className = 'exp-badge wrong';
  badge.textContent = '⏰ 시간 초과';
  expText.textContent = questions[qIndex].exp;
  setTimeout(() => {
    document.getElementById('timeoutMsg').classList.remove('show');
    expEl.classList.add('show');
  }, 600);
}

function answer(isO) {
  if (answered) return;
  clearInterval(timerInterval);
  answered = true;

  const q = questions[qIndex];
  const correct = (isO === q.a);

  document.getElementById('btnO').disabled = true;
  document.getElementById('btnX').disabled = true;

  const card = document.getElementById('questionCard');
  const expEl = document.getElementById('explanation');
  const badge = document.getElementById('expBadge');
  const expText = document.getElementById('expText');

  if (correct) {
    combo++;
    const bonus = combo >= 3 ? combo * 15 : combo >= 2 ? 120 : 100;
    score += bonus;
    card.className = 'question-card correct-flash';
    badge.className = 'exp-badge correct';
    badge.textContent = combo >= 2 ? `✅ 정답! 🔥 ${combo}콤보 (+${bonus}점)` : `✅ 정답! (+${bonus}점)`;
    const comboEl = document.getElementById('comboDisplay');
    comboEl.textContent = 'x' + combo;
    comboEl.classList.remove('bump');
    void comboEl.offsetWidth;
    comboEl.classList.add('bump');
  } else {
    combo = 0;
    card.className = 'question-card wrong-flash';
    badge.className = 'exp-badge wrong';
    badge.textContent = '❌ 오답';
    document.getElementById('comboDisplay').textContent = 'x0';
  }

  document.getElementById('scoreDisplay').textContent = score;
  expText.textContent = q.exp;
  expEl.classList.add('show');
  dotResults.push(correct);
}

function nextQuestion() {
  qIndex++;
  if (qIndex >= questions.length) {
    showResult();
  } else {
    loadQuestion();
  }
}

function showResult() {
  // finalize last dot
  const last = document.getElementById('dot' + (qIndex - 1));
  if (last) {
    last.classList.remove('current');
    last.classList.add(dotResults[qIndex - 1] ? 'done-correct' : 'done-wrong');
  }

  document.getElementById('gameScreen').style.display = 'none';
  const rs = document.getElementById('resultScreen');
  rs.classList.add('show');

  const correct = dotResults.filter(Boolean).length;
  const pct = Math.round((correct / questions.length) * 100);
  const emoji = pct >= 80 ? '🏆' : pct >= 60 ? '😊' : pct >= 40 ? '🤔' : '😅';
  const msg = pct >= 80 ? '완벽한 실력이에요!' : pct >= 60 ? '잘 하셨어요!' : pct >= 40 ? '조금 더 연습해봐요!' : '다시 도전해보세요!';

  document.getElementById('resultEmoji').textContent = emoji;
  document.getElementById('resultScore').textContent = score;
  document.getElementById('resultDetail').innerHTML =
    `${questions.length}문제 중 <b>${correct}문제</b> 정답 (${pct}%)<br>${msg}`;

  const prev = bestScores[currentSubject] || 0;
  if (score > prev) {
    bestScores[currentSubject] = score;
    document.getElementById('bestBadge').classList.add('show');
  } else {
    document.getElementById('bestBadge').classList.remove('show');
  }
}

document.getElementById('startBtn').onclick = startGame;

initTabs();
showStart();
</script>
</body>
</html>

결과와 배운 점

4개를 만들어야 하는데 3개까지 완성이 되었습니다.

중.고등학교 학생들에게 4주간 교육용 앱을 개발하는 수업을 진행해야 하는데 아이디어를 클로드가 내 주었습니다.

구글 AI 스튜디오로 만들어야 하는데 아직 한 번 밖에 사용해보지 않아서 앞으로 이것저것 만들어봐야지 싶습니다.

밀어주고 끌어주는

온·오프라인 AI 스터디

AI로 어디까지 할 수 있는지
직접 확인하실 분만 신청하세요.