| <!doctype html>
|
| <html lang="ko">
|
| <head>
|
| <meta charset="utf-8"/>
|
| <meta name="viewport" content="width=device-width, initial-scale=1"/>
|
| <title>웹 퀴즈</title>
|
| <style>
|
| @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
|
|
|
| * {
|
| box-sizing: border-box;
|
| }
|
|
|
| body {
|
| font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
| line-height: 1.6;
|
| margin: 0;
|
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
| color: #1a202c;
|
| min-height: 100vh;
|
| padding: 20px 0;
|
| }
|
|
|
| .container {
|
| max-width: 800px;
|
| margin: 0 auto;
|
| padding: 0 20px;
|
| }
|
|
|
| .quiz-header {
|
| background: rgba(255, 255, 255, 0.95);
|
| backdrop-filter: blur(20px);
|
| border-radius: 20px;
|
| padding: 30px;
|
| margin-bottom: 25px;
|
| box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
|
| border: 1px solid rgba(255, 255, 255, 0.2);
|
| }
|
|
|
| .quiz-title {
|
| font-size: 28px;
|
| font-weight: 700;
|
| margin: 0 0 15px 0;
|
| background: linear-gradient(135deg, #667eea, #764ba2);
|
| -webkit-background-clip: text;
|
| -webkit-text-fill-color: transparent;
|
| background-clip: text;
|
| text-align: center;
|
| }
|
|
|
| .quiz-controls {
|
| display: flex;
|
| gap: 12px;
|
| justify-content: center;
|
| flex-wrap: wrap;
|
| }
|
|
|
| .btn {
|
| background: linear-gradient(135deg, #667eea, #764ba2);
|
| color: white;
|
| border: none;
|
| border-radius: 12px;
|
| padding: 12px 24px;
|
| cursor: pointer;
|
| font-weight: 500;
|
| font-size: 14px;
|
| transition: all 0.3s ease;
|
| box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
|
| }
|
|
|
| .btn:hover:not(:disabled) {
|
| transform: translateY(-2px);
|
| box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4);
|
| }
|
|
|
| .btn:disabled {
|
| opacity: 0.5;
|
| cursor: not-allowed;
|
| transform: none;
|
| box-shadow: 0 2px 8px rgba(102, 126, 234, 0.2);
|
| }
|
|
|
| .status-bar {
|
| background: rgba(255, 255, 255, 0.9);
|
| backdrop-filter: blur(10px);
|
| border-radius: 15px;
|
| padding: 15px 25px;
|
| margin-bottom: 20px;
|
| display: flex;
|
| justify-content: space-between;
|
| align-items: center;
|
| box-shadow: 0 8px 20px rgba(0, 0, 0, 0.08);
|
| border: 1px solid rgba(255, 255, 255, 0.3);
|
| }
|
|
|
| .progress-info {
|
| font-weight: 500;
|
| color: #4a5568;
|
| }
|
|
|
| .score-display {
|
| background: linear-gradient(135deg, #48bb78, #38a169);
|
| color: white;
|
| padding: 8px 16px;
|
| border-radius: 20px;
|
| font-weight: 600;
|
| font-size: 14px;
|
| box-shadow: 0 4px 12px rgba(72, 187, 120, 0.3);
|
| }
|
|
|
| .message-area {
|
| min-height: 24px;
|
| text-align: center;
|
| font-weight: 500;
|
| color: #e53e3e;
|
| margin-bottom: 15px;
|
| }
|
|
|
| .card {
|
| background: rgba(255, 255, 255, 0.95);
|
| backdrop-filter: blur(20px);
|
| border-radius: 20px;
|
| padding: 30px;
|
| margin: 20px 0;
|
| box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
|
| border: 1px solid rgba(255, 255, 255, 0.2);
|
| transition: all 0.3s ease;
|
| }
|
|
|
| .card:hover {
|
| transform: translateY(-2px);
|
| box-shadow: 0 25px 50px rgba(0, 0, 0, 0.15);
|
| }
|
|
|
| .question-text {
|
| font-size: 18px;
|
| font-weight: 500;
|
| line-height: 1.7;
|
| margin-bottom: 25px;
|
| color: #2d3748;
|
| }
|
|
|
| .choice-wrapper {
|
| display: flex;
|
| align-items: center;
|
| margin: 12px 0;
|
| }
|
|
|
| .choice {
|
| display: block;
|
| border: 2px solid #e2e8f0;
|
| border-radius: 12px;
|
| padding: 16px 20px;
|
| margin: 0;
|
| background: white;
|
| cursor: pointer;
|
| transition: all 0.3s ease;
|
| font-weight: 500;
|
| color: #4a5568;
|
| flex: 1;
|
| }
|
|
|
| .choice:hover {
|
| border-color: #667eea;
|
| background: #f7fafc;
|
| transform: translateX(4px);
|
| }
|
|
|
| .choice.selected {
|
| border-color: #667eea;
|
| background: linear-gradient(135deg, #ebf4ff, #e6fffa);
|
| color: #2b6cb0;
|
| box-shadow: 0 4px 12px rgba(102, 126, 234, 0.2);
|
| }
|
|
|
| .choice.correct {
|
| border-color: #48bb78;
|
| background: linear-gradient(135deg, #f0fff4, #c6f6d5);
|
| color: #22543d;
|
| box-shadow: 0 4px 12px rgba(72, 187, 120, 0.3);
|
| }
|
|
|
| .choice.wrong {
|
| border-color: #f56565;
|
| background: linear-gradient(135deg, #fff5f5, #fed7d7);
|
| color: #742a2a;
|
| box-shadow: 0 4px 12px rgba(245, 101, 101, 0.3);
|
| }
|
|
|
| .choice:disabled {
|
| cursor: not-allowed;
|
| }
|
|
|
| .checkbox-input {
|
| margin-right: 12px;
|
| width: 18px;
|
| height: 18px;
|
| accent-color: #667eea;
|
| }
|
|
|
| .result-card {
|
| background: rgba(255, 255, 255, 0.95);
|
| backdrop-filter: blur(20px);
|
| border-radius: 20px;
|
| padding: 30px;
|
| margin-top: 20px;
|
| box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
|
| border: 1px solid rgba(255, 255, 255, 0.2);
|
| }
|
|
|
| .result-status {
|
| font-size: 20px;
|
| font-weight: 700;
|
| margin-bottom: 15px;
|
| text-align: center;
|
| padding: 15px;
|
| border-radius: 12px;
|
| }
|
|
|
| .result-correct {
|
| background: linear-gradient(135deg, #c6f6d5, #9ae6b4);
|
| color: #22543d;
|
| }
|
|
|
| .result-wrong {
|
| background: linear-gradient(135deg, #fed7d7, #fbb6ce);
|
| color: #742a2a;
|
| }
|
|
|
| .answer-display {
|
| font-size: 16px;
|
| font-weight: 600;
|
| margin: 15px 0;
|
| padding: 15px;
|
| background: #f7fafc;
|
| border-radius: 10px;
|
| border-left: 4px solid #667eea;
|
| }
|
|
|
| .explanation {
|
| color: #718096;
|
| font-size: 14px;
|
| line-height: 1.6;
|
| margin-top: 15px;
|
| padding: 15px;
|
| background: #f8f9fa;
|
| border-radius: 10px;
|
| border-left: 4px solid #a0aec0;
|
| }
|
|
|
| .meta-info {
|
| color: #a0aec0;
|
| font-size: 12px;
|
| font-weight: 500;
|
| margin-top: 20px;
|
| text-align: center;
|
| text-transform: uppercase;
|
| letter-spacing: 0.5px;
|
| }
|
|
|
| .completion-card {
|
| text-align: center;
|
| background: linear-gradient(135deg, #667eea, #764ba2);
|
| color: white;
|
| border-radius: 20px;
|
| padding: 40px;
|
| box-shadow: 0 25px 50px rgba(102, 126, 234, 0.3);
|
| }
|
|
|
| .completion-title {
|
| font-size: 32px;
|
| font-weight: 700;
|
| margin-bottom: 15px;
|
| }
|
|
|
| .completion-score {
|
| font-size: 18px;
|
| opacity: 0.9;
|
| margin-bottom: 25px;
|
| }
|
|
|
| .restart-btn {
|
| background: rgba(255, 255, 255, 0.2);
|
| border: 2px solid rgba(255, 255, 255, 0.3);
|
| color: white;
|
| padding: 15px 30px;
|
| border-radius: 12px;
|
| font-weight: 600;
|
| cursor: pointer;
|
| transition: all 0.3s ease;
|
| }
|
|
|
| .restart-btn:hover {
|
| background: rgba(255, 255, 255, 0.3);
|
| transform: translateY(-2px);
|
| }
|
|
|
| @media (max-width: 768px) {
|
| .container {
|
| padding: 0 10px;
|
| }
|
|
|
| .quiz-header {
|
| padding: 20px 15px;
|
| margin-bottom: 15px;
|
| }
|
|
|
| .quiz-title {
|
| font-size: 22px;
|
| }
|
|
|
| .quiz-controls {
|
| gap: 8px;
|
| }
|
|
|
| .btn {
|
| padding: 10px 18px;
|
| font-size: 13px;
|
| }
|
|
|
| .status-bar {
|
| padding: 12px 20px;
|
| flex-direction: column;
|
| gap: 10px;
|
| text-align: center;
|
| }
|
|
|
| .card {
|
| padding: 20px 15px;
|
| margin: 15px 0;
|
| }
|
|
|
| .question-text {
|
| font-size: 16px;
|
| line-height: 1.6;
|
| }
|
|
|
| .choice-wrapper {
|
| margin: 10px 0;
|
| }
|
|
|
| .choice {
|
| padding: 14px 16px;
|
| font-size: 14px;
|
| line-height: 1.5;
|
| }
|
|
|
| .checkbox-input {
|
| margin-right: 10px;
|
| width: 16px;
|
| height: 16px;
|
| }
|
|
|
| .result-card {
|
| padding: 20px 15px;
|
| }
|
|
|
| .result-status {
|
| font-size: 18px;
|
| padding: 12px;
|
| }
|
|
|
| .answer-display {
|
| font-size: 15px;
|
| padding: 12px;
|
| }
|
|
|
| .explanation {
|
| font-size: 13px;
|
| padding: 12px;
|
| }
|
|
|
| .completion-card {
|
| padding: 30px 20px;
|
| }
|
|
|
| .completion-title {
|
| font-size: 26px;
|
| }
|
|
|
| .completion-score {
|
| font-size: 16px;
|
| }
|
| }
|
|
|
| @media (max-width: 480px) {
|
| body {
|
| padding: 10px 0;
|
| }
|
|
|
| .container {
|
| padding: 0 8px;
|
| }
|
|
|
| .quiz-header {
|
| padding: 15px 12px;
|
| }
|
|
|
| .quiz-title {
|
| font-size: 20px;
|
| }
|
|
|
| .btn {
|
| padding: 8px 14px;
|
| font-size: 12px;
|
| }
|
|
|
| .status-bar {
|
| padding: 10px 15px;
|
| }
|
|
|
| .card {
|
| padding: 15px 12px;
|
| }
|
|
|
| .question-text {
|
| font-size: 15px;
|
| }
|
|
|
| .choice {
|
| padding: 12px 14px;
|
| font-size: 13px;
|
| }
|
|
|
| .checkbox-input {
|
| width: 14px;
|
| height: 14px;
|
| }
|
|
|
| .completion-title {
|
| font-size: 24px;
|
| }
|
|
|
| .completion-score {
|
| font-size: 15px;
|
| }
|
| }
|
| </style>
|
| </head>
|
| <body>
|
| <div class="container">
|
| <div class="quiz-header">
|
| <h1 class="quiz-title">인적자원관리 퀴즈</h1>
|
| <div class="quiz-controls">
|
| <button id="prev" class="btn">← 이전</button>
|
| <button id="next" class="btn">다음 →</button>
|
| </div>
|
| </div>
|
|
|
| <div class="status-bar">
|
| <div id="status" class="progress-info">문항 1/16</div>
|
| <div class="score-display">점수: <span id="score-text">0</span></div>
|
| </div>
|
|
|
| <div id="message" class="message-area"></div>
|
| <div id="quiz"></div>
|
| </div>
|
| <script>
|
| let questions=[], idx=0, score=0, state=new Map(), finished=false;
|
| function getState(i){ if(!state.has(i)) state.set(i,{selected:null, correct:false, revealed:false, scored:false}); return state.get(i); }
|
| async function load(){
|
|
|
| console.log('load() 함수가 호출되었습니다');
|
| try{
|
| const res=await fetch('questions.json',{cache:'no-store'});
|
| if(!res.ok) throw new Error('HTTP '+res.status);
|
| questions=await res.json();
|
| }catch(e){
|
| console.error('questions.json 로드 실패:', e);
|
| const msg=document.getElementById('message');
|
| if(msg) msg.textContent='문항 로드 실패: '+ (e && e.message ? e.message : e);
|
| return;
|
| }
|
| idx=0; score=0; state.clear(); finished=false; render();
|
| }
|
|
|
|
|
| document.addEventListener('DOMContentLoaded', function() {
|
| console.log('DOM 로드 완료');
|
| if (typeof questions === 'undefined' || questions.length === 0) {
|
| console.log('questions가 정의되지 않았거나 비어있음, load() 호출');
|
| load();
|
| } else {
|
| console.log('questions 이미 정의됨, render() 호출');
|
| render();
|
| }
|
| });
|
| function normalize(s){return (s||'').toString().replace(/[\s ]+/g,'').toLowerCase();}
|
| function escapeHtml(s){ const div=document.createElement('div'); div.textContent = (s ?? ''); return div.innerHTML; }
|
| function render(){
|
| const quiz=document.getElementById('quiz'); quiz.innerHTML='';
|
| if(finished){
|
| const done=document.createElement('div');
|
| done.className='completion-card';
|
| done.innerHTML = `
|
| <div class="completion-title">퀴즈 완료! 🎉</div>
|
| <div class="completion-score">최종 점수: ${score}/${questions.length} (${Math.round(score/questions.length*100)}%)</div>
|
| <button class="restart-btn" onclick="restartQuiz()">다시 시작하기</button>
|
| `;
|
| quiz.appendChild(done);
|
|
|
| const nextBtn=document.getElementById('next');
|
| const prevBtn=document.getElementById('prev');
|
| document.getElementById('status').textContent=`완료`;
|
| document.getElementById('score-text').textContent=`${score}/${questions.length}`;
|
| prevBtn.disabled = true;
|
| nextBtn.textContent = '처음으로';
|
| return;
|
| }
|
| const q=questions[idx]; const st=getState(idx);
|
|
|
| const card=document.createElement('div'); card.className='card';
|
| const questionDiv=document.createElement('div');
|
| questionDiv.className='question-text';
|
| questionDiv.textContent=q.prompt;
|
| card.appendChild(questionDiv);
|
|
|
| if(q.choices && q.choices.length){
|
| const isMultiple = q.qtype === 'multiple';
|
|
|
| q.choices.forEach((c,i)=>{
|
| const wrapper=document.createElement('div');
|
| wrapper.className='choice-wrapper';
|
|
|
| if(isMultiple){
|
|
|
| const checkbox=document.createElement('input');
|
| checkbox.type='checkbox'; checkbox.id=`choice_${i}`; checkbox.value=c;
|
| checkbox.className='checkbox-input';
|
|
|
| const label=document.createElement('label');
|
| label.htmlFor=`choice_${i}`; label.className='choice';
|
| label.textContent=(i+1)+'. '+c;
|
|
|
| if(!st.revealed){
|
| checkbox.onchange=()=>selectMultiple(c, checkbox.checked);
|
| if(st.selected && st.selected.includes && st.selected.includes(c)) {
|
| checkbox.checked=true; label.classList.add('selected');
|
| }
|
| }else{
|
|
|
| const isCorrect = Array.isArray(q.answer) ? q.answer.includes(c) : normalize(c)===normalize(q.answer);
|
| if(isCorrect) label.classList.add('correct');
|
| if(st.selected && st.selected.includes && st.selected.includes(c) && !isCorrect) label.classList.add('wrong');
|
| checkbox.disabled=true;
|
| }
|
|
|
| wrapper.appendChild(checkbox); wrapper.appendChild(label);
|
| }else{
|
|
|
| const btn=document.createElement('button'); btn.className='choice'; btn.textContent=(i+1)+'. '+c;
|
| if(!st.revealed){
|
| btn.onclick=()=>select(c);
|
| if(st.selected===c) btn.classList.add('selected');
|
| }else{
|
|
|
| const isCorrect = Array.isArray(q.answer) ? q.answer.includes(c) : normalize(c)===normalize(q.answer);
|
| if(isCorrect) btn.classList.add('correct');
|
| if(st.selected===c && !isCorrect) btn.classList.add('wrong');
|
| btn.disabled=true;
|
| }
|
| wrapper.appendChild(btn);
|
| }
|
|
|
| card.appendChild(wrapper);
|
| });
|
| }else{
|
| const input=document.createElement('input');
|
| input.placeholder='정답 입력';
|
| input.style='width:100%;padding:15px;border:2px solid #e2e8f0;border-radius:12px;margin-top:15px;font-size:16px;';
|
| input.value = st.selected||'';
|
| const check=document.createElement('button');
|
| check.className='btn';
|
| check.style='margin-top:15px;width:100%;';
|
| check.textContent= st.revealed? '제출됨' : '제출';
|
| if(!st.revealed){
|
| check.onclick=()=>select(input.value.trim());
|
| }else{
|
| check.disabled=true;
|
| }
|
| card.appendChild(input); card.appendChild(check);
|
| }
|
|
|
|
|
| if(st.revealed){
|
| const resultCard=document.createElement('div');
|
| resultCard.className='result-card';
|
|
|
|
|
| const resultDiv = document.createElement('div');
|
| resultDiv.className = st.correct ? 'result-status result-correct' : 'result-status result-wrong';
|
| resultDiv.textContent = st.correct ? '정답입니다! ✅' : '틀렸습니다! ❌';
|
| resultCard.appendChild(resultDiv);
|
|
|
|
|
| let answerText = '';
|
| if(Array.isArray(q.answer)){
|
|
|
| const answerNumbers = [];
|
| q.answer.forEach(ans => {
|
| const index = q.choices.findIndex(choice => normalize(choice) === normalize(ans));
|
| if(index !== -1) answerNumbers.push(index + 1);
|
| });
|
| answerText = answerNumbers.join(', ');
|
| }else{
|
|
|
| const index = q.choices.findIndex(choice => normalize(choice) === normalize(q.answer));
|
| answerText = index !== -1 ? (index + 1).toString() : '1';
|
| }
|
|
|
| const answerDiv = document.createElement('div');
|
| answerDiv.className = 'answer-display';
|
| answerDiv.innerHTML = `<strong>정답</strong>: ${answerText}`;
|
| resultCard.appendChild(answerDiv);
|
|
|
|
|
| if(q.explanation && q.explanation.toString().trim()){
|
| const explanationDiv = document.createElement('div');
|
| explanationDiv.className = 'explanation';
|
| explanationDiv.innerHTML = `<strong>해설:</strong> ${escapeHtml(q.explanation.toString())}`;
|
| resultCard.appendChild(explanationDiv);
|
| }
|
|
|
| card.appendChild(resultCard);
|
| }
|
|
|
| const meta=document.createElement('div');
|
| meta.className='meta-info';
|
| meta.textContent='유형: '+q.qtype.toUpperCase();
|
| card.appendChild(meta);
|
|
|
| quiz.appendChild(card);
|
|
|
| const nextBtn=document.getElementById('next');
|
| const prevBtn=document.getElementById('prev');
|
| document.getElementById('status').textContent=`문항 ${idx+1}/${questions.length}`;
|
| document.getElementById('score-text').textContent=`${score}/${questions.length}`;
|
| prevBtn.disabled = idx===0;
|
| nextBtn.textContent = st.revealed ? (idx===questions.length-1 ? '완료' : '다음 →') : '다음 →';
|
| }
|
| function selectMultiple(val, checked){
|
| const q=questions[idx]; const st=getState(idx);
|
| if(st.revealed) return;
|
|
|
| if(!st.selected) st.selected = [];
|
| if(!Array.isArray(st.selected)) st.selected = [];
|
|
|
| if(checked){
|
| if(!st.selected.includes(val)) st.selected.push(val);
|
| }else{
|
| st.selected = st.selected.filter(item => item !== val);
|
| }
|
|
|
| document.getElementById('message').textContent='';
|
| render();
|
| }
|
| function select(val){
|
| const q=questions[idx]; const st=getState(idx);
|
| if(st.revealed) return;
|
| st.selected = val;
|
| st.correct = normalize(val)===normalize(q.answer);
|
| document.getElementById('message').textContent='';
|
| render();
|
| }
|
| function restartQuiz(){
|
| idx=0; score=0; state.clear(); finished=false;
|
| document.getElementById('message').textContent='';
|
| render();
|
| }
|
| document.getElementById('prev').onclick=()=>{ if(finished) return; if(idx>0){ idx--; render(); }};
|
| document.getElementById('next').onclick=()=>{
|
| if(finished){ restartQuiz(); return; }
|
| const st=getState(idx);
|
| const q=questions[idx];
|
| const msg=document.getElementById('message');
|
|
|
| if(!st.revealed){
|
| if(st.selected==null || (Array.isArray(st.selected) && st.selected.length===0)){
|
| msg.textContent='먼저 답을 선택하세요.'; return;
|
| }
|
|
|
|
|
| st.revealed=true;
|
|
|
|
|
| let isCorrect = false;
|
| if(q.qtype === 'multiple'){
|
|
|
| if(Array.isArray(q.answer) && Array.isArray(st.selected)){
|
| const sortedAnswer = [...q.answer].sort();
|
| const sortedSelected = [...st.selected].sort();
|
| isCorrect = sortedAnswer.length === sortedSelected.length &&
|
| sortedAnswer.every((ans, i) => normalize(ans) === normalize(sortedSelected[i]));
|
| }
|
| }else{
|
|
|
| isCorrect = Array.isArray(q.answer) ?
|
| q.answer.some(ans => normalize(st.selected) === normalize(ans)) :
|
| normalize(st.selected) === normalize(q.answer);
|
| }
|
|
|
| st.correct = isCorrect;
|
| if(st.correct && !st.scored){ score++; st.scored=true; }
|
| render();
|
| }else{
|
| if(idx<questions.length-1){ idx++; render(); }
|
| else { finished=true; render(); }
|
| }
|
| };
|
| load();
|
| </script>
|
| </body></html> |