소개
학생들과 교육용 콘텐츠 만들기 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 스튜디오로 만들어야 하는데 아직 한 번 밖에 사용해보지 않아서 앞으로 이것저것 만들어봐야지 싶습니다.