HR / index.html
lss9566's picture
Upload 6 files
8c44c8d verified
<!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;
}
// 해설 표시 단계로 전환하며 점수 반영(최초 1회)
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>