Upload 6 files
Browse files- README.md +70 -0
- app.py +99 -0
- index.html +712 -0
- questions.json +198 -0
- requirements.txt +2 -0
- simple_quiz.html +337 -0
README.md
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: 인적자원관리 퀴즈
|
| 3 |
+
emoji: 📚
|
| 4 |
+
colorFrom: blue
|
| 5 |
+
colorTo: purple
|
| 6 |
+
sdk: gradio
|
| 7 |
+
sdk_version: "4.0.0"
|
| 8 |
+
app_file: app.py
|
| 9 |
+
pinned: false
|
| 10 |
+
---
|
| 11 |
+
|
| 12 |
+
# 📚 인적자원관리 퀴즈
|
| 13 |
+
|
| 14 |
+
스마트폰에서도 편리하게 학습할 수 있는 인적자원관리 웹 퀴즈입니다.
|
| 15 |
+
|
| 16 |
+
## ✨ 주요 기능
|
| 17 |
+
|
| 18 |
+
- **📱 모바일 최적화**: 스마트폰에서도 완벽한 사용자 경험
|
| 19 |
+
- **🎯 다중 정답 지원**: 체크박스를 통한 다중 선택 문제
|
| 20 |
+
- **🎨 현대적 디자인**: 글래스모피즘과 그라데이션을 활용한 세련된 UI
|
| 21 |
+
- **📊 실시간 점수**: 진행률과 점수를 실시간으로 확인
|
| 22 |
+
- **💡 상세한 해설**: 정답과 함께 제공되는 학습 해설
|
| 23 |
+
|
| 24 |
+
## 🎮 사용 방법
|
| 25 |
+
|
| 26 |
+
1. **문제 풀이**: 선택지를 클릭하여 답을 선택
|
| 27 |
+
2. **다중 정답**: 체크박스가 있는 문제는 여러 개 선택 가능
|
| 28 |
+
3. **해설 확인**: "다음" 버튼을 눌러 정답과 해설 확인
|
| 29 |
+
4. **진행**: 이전/다음 버튼으로 문항 간 이동
|
| 30 |
+
5. **완료**: 모든 문항 완료 후 최종 점수 확인
|
| 31 |
+
|
| 32 |
+
## 📋 문제 구성
|
| 33 |
+
|
| 34 |
+
총 **16개 문항**의 인적자원관리 관련 문제로 구성되어 있습니다:
|
| 35 |
+
|
| 36 |
+
- 전략적 인적자원관리
|
| 37 |
+
- 직무분석 및 직무설계
|
| 38 |
+
- 인력계획 및 채용
|
| 39 |
+
- 교육훈련 및 경력개발
|
| 40 |
+
- 인사평가 및 면접
|
| 41 |
+
- 기타 인적자원관리 이론
|
| 42 |
+
|
| 43 |
+
## 🛠️ 기술 스택
|
| 44 |
+
|
| 45 |
+
- **Frontend**: HTML5, CSS3, JavaScript (Vanilla)
|
| 46 |
+
- **Backend**: Python, Gradio
|
| 47 |
+
- **Deployment**: Hugging Face Spaces
|
| 48 |
+
- **Design**: 글래스모피즘, 반응형 웹 디자인
|
| 49 |
+
|
| 50 |
+
## 📱 모바일 지원
|
| 51 |
+
|
| 52 |
+
이 퀴즈는 다양한 디바이스에서 최적화되어 작동합니다:
|
| 53 |
+
|
| 54 |
+
- 📱 스마트폰 (iOS, Android)
|
| 55 |
+
- 💻 태블릿
|
| 56 |
+
- 🖥️ 데스크톱
|
| 57 |
+
|
| 58 |
+
## 🎯 학습 목표
|
| 59 |
+
|
| 60 |
+
- 인적자원관리의 핵심 개념 이해
|
| 61 |
+
- 실무 적용 가능한 지식 습득
|
| 62 |
+
- 체계적인 학습을 통한 전문성 향상
|
| 63 |
+
|
| 64 |
+
## 📞 문의사항
|
| 65 |
+
|
| 66 |
+
문제나 개선사항이 있으시면 언제든지 연락주세요!
|
| 67 |
+
|
| 68 |
+
---
|
| 69 |
+
|
| 70 |
+
**💡 팁**: 틀린 문제는 해설을 꼼꼼히 읽어보세요. 반복 학습을 통해 더 나은 결과를 얻을 수 있습니다!
|
app.py
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import gradio as gr
|
| 2 |
+
import json
|
| 3 |
+
from pathlib import Path
|
| 4 |
+
import re
|
| 5 |
+
|
| 6 |
+
# 현재 디렉토리 설정
|
| 7 |
+
current_dir = Path(__file__).parent
|
| 8 |
+
|
| 9 |
+
def create_quiz_app():
|
| 10 |
+
"""Gradio 앱 생성"""
|
| 11 |
+
|
| 12 |
+
# simple_quiz.html 파일 읽기 (테스트용 전체 문서)
|
| 13 |
+
html_file = current_dir / "simple_quiz.html"
|
| 14 |
+
if html_file.exists():
|
| 15 |
+
with open(html_file, 'r', encoding='utf-8') as f:
|
| 16 |
+
full_html = f.read()
|
| 17 |
+
else:
|
| 18 |
+
full_html = """
|
| 19 |
+
<!DOCTYPE html>
|
| 20 |
+
<html lang=\"ko\">
|
| 21 |
+
<head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><title>퀴즈</title></head>
|
| 22 |
+
<body><h1>퀴즈 파일을 찾을 수 없습니다.</h1></body>
|
| 23 |
+
</html>
|
| 24 |
+
"""
|
| 25 |
+
|
| 26 |
+
# questions.json 파일 읽기 (webquiz/ 하위 우선, 없으면 루트 fallback)
|
| 27 |
+
questions_file = current_dir / "webquiz" / "questions.json"
|
| 28 |
+
if not questions_file.exists():
|
| 29 |
+
fallback = current_dir / "questions.json"
|
| 30 |
+
if fallback.exists():
|
| 31 |
+
questions_file = fallback
|
| 32 |
+
|
| 33 |
+
if questions_file.exists():
|
| 34 |
+
with open(questions_file, 'r', encoding='utf-8') as f:
|
| 35 |
+
questions_data = json.load(f)
|
| 36 |
+
# JavaScript에서 questions 데이터를 직접 사용할 수 있도록 수정
|
| 37 |
+
questions_js = json.dumps(questions_data, ensure_ascii=False)
|
| 38 |
+
|
| 39 |
+
# 기존 문서 내에 샘플 데이터가 있다면 교체, 없으면 주입
|
| 40 |
+
if 'const questions = [' in full_html:
|
| 41 |
+
full_html = full_html.replace(
|
| 42 |
+
'const questions = [',
|
| 43 |
+
f'const questions = {questions_js}; const _old_questions = ['
|
| 44 |
+
)
|
| 45 |
+
else:
|
| 46 |
+
# <body> 바로 뒤에 questions 변수를 주입
|
| 47 |
+
full_html = re.sub(
|
| 48 |
+
r"<body[^>]*>",
|
| 49 |
+
lambda m: f"{m.group(0)}\n<script>const questions = {questions_js};</script>",
|
| 50 |
+
full_html,
|
| 51 |
+
flags=re.IGNORECASE
|
| 52 |
+
)
|
| 53 |
+
|
| 54 |
+
# srcdoc에 안전하게 삽입하기 위해 작은따옴표를 HTML 엔티티로 치환
|
| 55 |
+
srcdoc_html = full_html.replace("'", "'")
|
| 56 |
+
|
| 57 |
+
# iframe(srcdoc)로 전체 문서를 로드하여 스크립트가 확실히 실행되도록 함
|
| 58 |
+
iframe_html = f"""
|
| 59 |
+
<div style=\"width:100%; height:100vh;\">
|
| 60 |
+
<iframe id=\"quiz_iframe\" style=\"width:100%; height:100%; border:none;\" srcdoc='{srcdoc_html}'></iframe>
|
| 61 |
+
</div>
|
| 62 |
+
"""
|
| 63 |
+
|
| 64 |
+
with gr.Blocks(
|
| 65 |
+
title="인적자원관리 퀴즈",
|
| 66 |
+
theme=gr.themes.Soft(),
|
| 67 |
+
css="""
|
| 68 |
+
.gradio-container {
|
| 69 |
+
max-width: none !important;
|
| 70 |
+
padding: 0 !important;
|
| 71 |
+
}
|
| 72 |
+
.main {
|
| 73 |
+
padding: 0 !important;
|
| 74 |
+
}
|
| 75 |
+
.block {
|
| 76 |
+
border: none !important;
|
| 77 |
+
box-shadow: none !important;
|
| 78 |
+
margin: 0 !important;
|
| 79 |
+
}
|
| 80 |
+
"""
|
| 81 |
+
) as app:
|
| 82 |
+
|
| 83 |
+
# 퀴즈 인터페이스를 HTML로 직접 렌더링 (iframe 사용)
|
| 84 |
+
gr.HTML(
|
| 85 |
+
value=iframe_html,
|
| 86 |
+
elem_id="quiz_interface"
|
| 87 |
+
)
|
| 88 |
+
|
| 89 |
+
return app
|
| 90 |
+
|
| 91 |
+
# Gradio 앱 생성 및 실행
|
| 92 |
+
if __name__ == "__main__":
|
| 93 |
+
app = create_quiz_app()
|
| 94 |
+
app.launch(
|
| 95 |
+
server_name="0.0.0.0",
|
| 96 |
+
server_port=7860,
|
| 97 |
+
share=False,
|
| 98 |
+
show_error=True
|
| 99 |
+
)
|
index.html
ADDED
|
@@ -0,0 +1,712 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!doctype html>
|
| 2 |
+
<html lang="ko">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="utf-8"/>
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
| 6 |
+
<title>웹 퀴즈</title>
|
| 7 |
+
<style>
|
| 8 |
+
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
|
| 9 |
+
|
| 10 |
+
* {
|
| 11 |
+
box-sizing: border-box;
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
body {
|
| 15 |
+
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
| 16 |
+
line-height: 1.6;
|
| 17 |
+
margin: 0;
|
| 18 |
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
| 19 |
+
color: #1a202c;
|
| 20 |
+
min-height: 100vh;
|
| 21 |
+
padding: 20px 0;
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
.container {
|
| 25 |
+
max-width: 800px;
|
| 26 |
+
margin: 0 auto;
|
| 27 |
+
padding: 0 20px;
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
.quiz-header {
|
| 31 |
+
background: rgba(255, 255, 255, 0.95);
|
| 32 |
+
backdrop-filter: blur(20px);
|
| 33 |
+
border-radius: 20px;
|
| 34 |
+
padding: 30px;
|
| 35 |
+
margin-bottom: 25px;
|
| 36 |
+
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
|
| 37 |
+
border: 1px solid rgba(255, 255, 255, 0.2);
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
.quiz-title {
|
| 41 |
+
font-size: 28px;
|
| 42 |
+
font-weight: 700;
|
| 43 |
+
margin: 0 0 15px 0;
|
| 44 |
+
background: linear-gradient(135deg, #667eea, #764ba2);
|
| 45 |
+
-webkit-background-clip: text;
|
| 46 |
+
-webkit-text-fill-color: transparent;
|
| 47 |
+
background-clip: text;
|
| 48 |
+
text-align: center;
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
.quiz-controls {
|
| 52 |
+
display: flex;
|
| 53 |
+
gap: 12px;
|
| 54 |
+
justify-content: center;
|
| 55 |
+
flex-wrap: wrap;
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
.btn {
|
| 59 |
+
background: linear-gradient(135deg, #667eea, #764ba2);
|
| 60 |
+
color: white;
|
| 61 |
+
border: none;
|
| 62 |
+
border-radius: 12px;
|
| 63 |
+
padding: 12px 24px;
|
| 64 |
+
cursor: pointer;
|
| 65 |
+
font-weight: 500;
|
| 66 |
+
font-size: 14px;
|
| 67 |
+
transition: all 0.3s ease;
|
| 68 |
+
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
.btn:hover:not(:disabled) {
|
| 72 |
+
transform: translateY(-2px);
|
| 73 |
+
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4);
|
| 74 |
+
}
|
| 75 |
+
|
| 76 |
+
.btn:disabled {
|
| 77 |
+
opacity: 0.5;
|
| 78 |
+
cursor: not-allowed;
|
| 79 |
+
transform: none;
|
| 80 |
+
box-shadow: 0 2px 8px rgba(102, 126, 234, 0.2);
|
| 81 |
+
}
|
| 82 |
+
|
| 83 |
+
.status-bar {
|
| 84 |
+
background: rgba(255, 255, 255, 0.9);
|
| 85 |
+
backdrop-filter: blur(10px);
|
| 86 |
+
border-radius: 15px;
|
| 87 |
+
padding: 15px 25px;
|
| 88 |
+
margin-bottom: 20px;
|
| 89 |
+
display: flex;
|
| 90 |
+
justify-content: space-between;
|
| 91 |
+
align-items: center;
|
| 92 |
+
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.08);
|
| 93 |
+
border: 1px solid rgba(255, 255, 255, 0.3);
|
| 94 |
+
}
|
| 95 |
+
|
| 96 |
+
.progress-info {
|
| 97 |
+
font-weight: 500;
|
| 98 |
+
color: #4a5568;
|
| 99 |
+
}
|
| 100 |
+
|
| 101 |
+
.score-display {
|
| 102 |
+
background: linear-gradient(135deg, #48bb78, #38a169);
|
| 103 |
+
color: white;
|
| 104 |
+
padding: 8px 16px;
|
| 105 |
+
border-radius: 20px;
|
| 106 |
+
font-weight: 600;
|
| 107 |
+
font-size: 14px;
|
| 108 |
+
box-shadow: 0 4px 12px rgba(72, 187, 120, 0.3);
|
| 109 |
+
}
|
| 110 |
+
|
| 111 |
+
.message-area {
|
| 112 |
+
min-height: 24px;
|
| 113 |
+
text-align: center;
|
| 114 |
+
font-weight: 500;
|
| 115 |
+
color: #e53e3e;
|
| 116 |
+
margin-bottom: 15px;
|
| 117 |
+
}
|
| 118 |
+
|
| 119 |
+
.card {
|
| 120 |
+
background: rgba(255, 255, 255, 0.95);
|
| 121 |
+
backdrop-filter: blur(20px);
|
| 122 |
+
border-radius: 20px;
|
| 123 |
+
padding: 30px;
|
| 124 |
+
margin: 20px 0;
|
| 125 |
+
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
|
| 126 |
+
border: 1px solid rgba(255, 255, 255, 0.2);
|
| 127 |
+
transition: all 0.3s ease;
|
| 128 |
+
}
|
| 129 |
+
|
| 130 |
+
.card:hover {
|
| 131 |
+
transform: translateY(-2px);
|
| 132 |
+
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.15);
|
| 133 |
+
}
|
| 134 |
+
|
| 135 |
+
.question-text {
|
| 136 |
+
font-size: 18px;
|
| 137 |
+
font-weight: 500;
|
| 138 |
+
line-height: 1.7;
|
| 139 |
+
margin-bottom: 25px;
|
| 140 |
+
color: #2d3748;
|
| 141 |
+
}
|
| 142 |
+
|
| 143 |
+
.choice-wrapper {
|
| 144 |
+
display: flex;
|
| 145 |
+
align-items: center;
|
| 146 |
+
margin: 12px 0;
|
| 147 |
+
}
|
| 148 |
+
|
| 149 |
+
.choice {
|
| 150 |
+
display: block;
|
| 151 |
+
border: 2px solid #e2e8f0;
|
| 152 |
+
border-radius: 12px;
|
| 153 |
+
padding: 16px 20px;
|
| 154 |
+
margin: 0;
|
| 155 |
+
background: white;
|
| 156 |
+
cursor: pointer;
|
| 157 |
+
transition: all 0.3s ease;
|
| 158 |
+
font-weight: 500;
|
| 159 |
+
color: #4a5568;
|
| 160 |
+
flex: 1;
|
| 161 |
+
}
|
| 162 |
+
|
| 163 |
+
.choice:hover {
|
| 164 |
+
border-color: #667eea;
|
| 165 |
+
background: #f7fafc;
|
| 166 |
+
transform: translateX(4px);
|
| 167 |
+
}
|
| 168 |
+
|
| 169 |
+
.choice.selected {
|
| 170 |
+
border-color: #667eea;
|
| 171 |
+
background: linear-gradient(135deg, #ebf4ff, #e6fffa);
|
| 172 |
+
color: #2b6cb0;
|
| 173 |
+
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.2);
|
| 174 |
+
}
|
| 175 |
+
|
| 176 |
+
.choice.correct {
|
| 177 |
+
border-color: #48bb78;
|
| 178 |
+
background: linear-gradient(135deg, #f0fff4, #c6f6d5);
|
| 179 |
+
color: #22543d;
|
| 180 |
+
box-shadow: 0 4px 12px rgba(72, 187, 120, 0.3);
|
| 181 |
+
}
|
| 182 |
+
|
| 183 |
+
.choice.wrong {
|
| 184 |
+
border-color: #f56565;
|
| 185 |
+
background: linear-gradient(135deg, #fff5f5, #fed7d7);
|
| 186 |
+
color: #742a2a;
|
| 187 |
+
box-shadow: 0 4px 12px rgba(245, 101, 101, 0.3);
|
| 188 |
+
}
|
| 189 |
+
|
| 190 |
+
.choice:disabled {
|
| 191 |
+
cursor: not-allowed;
|
| 192 |
+
}
|
| 193 |
+
|
| 194 |
+
.checkbox-input {
|
| 195 |
+
margin-right: 12px;
|
| 196 |
+
width: 18px;
|
| 197 |
+
height: 18px;
|
| 198 |
+
accent-color: #667eea;
|
| 199 |
+
}
|
| 200 |
+
|
| 201 |
+
.result-card {
|
| 202 |
+
background: rgba(255, 255, 255, 0.95);
|
| 203 |
+
backdrop-filter: blur(20px);
|
| 204 |
+
border-radius: 20px;
|
| 205 |
+
padding: 30px;
|
| 206 |
+
margin-top: 20px;
|
| 207 |
+
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
|
| 208 |
+
border: 1px solid rgba(255, 255, 255, 0.2);
|
| 209 |
+
}
|
| 210 |
+
|
| 211 |
+
.result-status {
|
| 212 |
+
font-size: 20px;
|
| 213 |
+
font-weight: 700;
|
| 214 |
+
margin-bottom: 15px;
|
| 215 |
+
text-align: center;
|
| 216 |
+
padding: 15px;
|
| 217 |
+
border-radius: 12px;
|
| 218 |
+
}
|
| 219 |
+
|
| 220 |
+
.result-correct {
|
| 221 |
+
background: linear-gradient(135deg, #c6f6d5, #9ae6b4);
|
| 222 |
+
color: #22543d;
|
| 223 |
+
}
|
| 224 |
+
|
| 225 |
+
.result-wrong {
|
| 226 |
+
background: linear-gradient(135deg, #fed7d7, #fbb6ce);
|
| 227 |
+
color: #742a2a;
|
| 228 |
+
}
|
| 229 |
+
|
| 230 |
+
.answer-display {
|
| 231 |
+
font-size: 16px;
|
| 232 |
+
font-weight: 600;
|
| 233 |
+
margin: 15px 0;
|
| 234 |
+
padding: 15px;
|
| 235 |
+
background: #f7fafc;
|
| 236 |
+
border-radius: 10px;
|
| 237 |
+
border-left: 4px solid #667eea;
|
| 238 |
+
}
|
| 239 |
+
|
| 240 |
+
.explanation {
|
| 241 |
+
color: #718096;
|
| 242 |
+
font-size: 14px;
|
| 243 |
+
line-height: 1.6;
|
| 244 |
+
margin-top: 15px;
|
| 245 |
+
padding: 15px;
|
| 246 |
+
background: #f8f9fa;
|
| 247 |
+
border-radius: 10px;
|
| 248 |
+
border-left: 4px solid #a0aec0;
|
| 249 |
+
}
|
| 250 |
+
|
| 251 |
+
.meta-info {
|
| 252 |
+
color: #a0aec0;
|
| 253 |
+
font-size: 12px;
|
| 254 |
+
font-weight: 500;
|
| 255 |
+
margin-top: 20px;
|
| 256 |
+
text-align: center;
|
| 257 |
+
text-transform: uppercase;
|
| 258 |
+
letter-spacing: 0.5px;
|
| 259 |
+
}
|
| 260 |
+
|
| 261 |
+
.completion-card {
|
| 262 |
+
text-align: center;
|
| 263 |
+
background: linear-gradient(135deg, #667eea, #764ba2);
|
| 264 |
+
color: white;
|
| 265 |
+
border-radius: 20px;
|
| 266 |
+
padding: 40px;
|
| 267 |
+
box-shadow: 0 25px 50px rgba(102, 126, 234, 0.3);
|
| 268 |
+
}
|
| 269 |
+
|
| 270 |
+
.completion-title {
|
| 271 |
+
font-size: 32px;
|
| 272 |
+
font-weight: 700;
|
| 273 |
+
margin-bottom: 15px;
|
| 274 |
+
}
|
| 275 |
+
|
| 276 |
+
.completion-score {
|
| 277 |
+
font-size: 18px;
|
| 278 |
+
opacity: 0.9;
|
| 279 |
+
margin-bottom: 25px;
|
| 280 |
+
}
|
| 281 |
+
|
| 282 |
+
.restart-btn {
|
| 283 |
+
background: rgba(255, 255, 255, 0.2);
|
| 284 |
+
border: 2px solid rgba(255, 255, 255, 0.3);
|
| 285 |
+
color: white;
|
| 286 |
+
padding: 15px 30px;
|
| 287 |
+
border-radius: 12px;
|
| 288 |
+
font-weight: 600;
|
| 289 |
+
cursor: pointer;
|
| 290 |
+
transition: all 0.3s ease;
|
| 291 |
+
}
|
| 292 |
+
|
| 293 |
+
.restart-btn:hover {
|
| 294 |
+
background: rgba(255, 255, 255, 0.3);
|
| 295 |
+
transform: translateY(-2px);
|
| 296 |
+
}
|
| 297 |
+
|
| 298 |
+
@media (max-width: 768px) {
|
| 299 |
+
.container {
|
| 300 |
+
padding: 0 10px;
|
| 301 |
+
}
|
| 302 |
+
|
| 303 |
+
.quiz-header {
|
| 304 |
+
padding: 20px 15px;
|
| 305 |
+
margin-bottom: 15px;
|
| 306 |
+
}
|
| 307 |
+
|
| 308 |
+
.quiz-title {
|
| 309 |
+
font-size: 22px;
|
| 310 |
+
}
|
| 311 |
+
|
| 312 |
+
.quiz-controls {
|
| 313 |
+
gap: 8px;
|
| 314 |
+
}
|
| 315 |
+
|
| 316 |
+
.btn {
|
| 317 |
+
padding: 10px 18px;
|
| 318 |
+
font-size: 13px;
|
| 319 |
+
}
|
| 320 |
+
|
| 321 |
+
.status-bar {
|
| 322 |
+
padding: 12px 20px;
|
| 323 |
+
flex-direction: column;
|
| 324 |
+
gap: 10px;
|
| 325 |
+
text-align: center;
|
| 326 |
+
}
|
| 327 |
+
|
| 328 |
+
.card {
|
| 329 |
+
padding: 20px 15px;
|
| 330 |
+
margin: 15px 0;
|
| 331 |
+
}
|
| 332 |
+
|
| 333 |
+
.question-text {
|
| 334 |
+
font-size: 16px;
|
| 335 |
+
line-height: 1.6;
|
| 336 |
+
}
|
| 337 |
+
|
| 338 |
+
.choice-wrapper {
|
| 339 |
+
margin: 10px 0;
|
| 340 |
+
}
|
| 341 |
+
|
| 342 |
+
.choice {
|
| 343 |
+
padding: 14px 16px;
|
| 344 |
+
font-size: 14px;
|
| 345 |
+
line-height: 1.5;
|
| 346 |
+
}
|
| 347 |
+
|
| 348 |
+
.checkbox-input {
|
| 349 |
+
margin-right: 10px;
|
| 350 |
+
width: 16px;
|
| 351 |
+
height: 16px;
|
| 352 |
+
}
|
| 353 |
+
|
| 354 |
+
.result-card {
|
| 355 |
+
padding: 20px 15px;
|
| 356 |
+
}
|
| 357 |
+
|
| 358 |
+
.result-status {
|
| 359 |
+
font-size: 18px;
|
| 360 |
+
padding: 12px;
|
| 361 |
+
}
|
| 362 |
+
|
| 363 |
+
.answer-display {
|
| 364 |
+
font-size: 15px;
|
| 365 |
+
padding: 12px;
|
| 366 |
+
}
|
| 367 |
+
|
| 368 |
+
.explanation {
|
| 369 |
+
font-size: 13px;
|
| 370 |
+
padding: 12px;
|
| 371 |
+
}
|
| 372 |
+
|
| 373 |
+
.completion-card {
|
| 374 |
+
padding: 30px 20px;
|
| 375 |
+
}
|
| 376 |
+
|
| 377 |
+
.completion-title {
|
| 378 |
+
font-size: 26px;
|
| 379 |
+
}
|
| 380 |
+
|
| 381 |
+
.completion-score {
|
| 382 |
+
font-size: 16px;
|
| 383 |
+
}
|
| 384 |
+
}
|
| 385 |
+
|
| 386 |
+
@media (max-width: 480px) {
|
| 387 |
+
body {
|
| 388 |
+
padding: 10px 0;
|
| 389 |
+
}
|
| 390 |
+
|
| 391 |
+
.container {
|
| 392 |
+
padding: 0 8px;
|
| 393 |
+
}
|
| 394 |
+
|
| 395 |
+
.quiz-header {
|
| 396 |
+
padding: 15px 12px;
|
| 397 |
+
}
|
| 398 |
+
|
| 399 |
+
.quiz-title {
|
| 400 |
+
font-size: 20px;
|
| 401 |
+
}
|
| 402 |
+
|
| 403 |
+
.btn {
|
| 404 |
+
padding: 8px 14px;
|
| 405 |
+
font-size: 12px;
|
| 406 |
+
}
|
| 407 |
+
|
| 408 |
+
.status-bar {
|
| 409 |
+
padding: 10px 15px;
|
| 410 |
+
}
|
| 411 |
+
|
| 412 |
+
.card {
|
| 413 |
+
padding: 15px 12px;
|
| 414 |
+
}
|
| 415 |
+
|
| 416 |
+
.question-text {
|
| 417 |
+
font-size: 15px;
|
| 418 |
+
}
|
| 419 |
+
|
| 420 |
+
.choice {
|
| 421 |
+
padding: 12px 14px;
|
| 422 |
+
font-size: 13px;
|
| 423 |
+
}
|
| 424 |
+
|
| 425 |
+
.checkbox-input {
|
| 426 |
+
width: 14px;
|
| 427 |
+
height: 14px;
|
| 428 |
+
}
|
| 429 |
+
|
| 430 |
+
.completion-title {
|
| 431 |
+
font-size: 24px;
|
| 432 |
+
}
|
| 433 |
+
|
| 434 |
+
.completion-score {
|
| 435 |
+
font-size: 15px;
|
| 436 |
+
}
|
| 437 |
+
}
|
| 438 |
+
</style>
|
| 439 |
+
</head>
|
| 440 |
+
<body>
|
| 441 |
+
<div class="container">
|
| 442 |
+
<div class="quiz-header">
|
| 443 |
+
<h1 class="quiz-title">인적자원관리 퀴즈</h1>
|
| 444 |
+
<div class="quiz-controls">
|
| 445 |
+
<button id="prev" class="btn">← 이전</button>
|
| 446 |
+
<button id="next" class="btn">다음 →</button>
|
| 447 |
+
</div>
|
| 448 |
+
</div>
|
| 449 |
+
|
| 450 |
+
<div class="status-bar">
|
| 451 |
+
<div id="status" class="progress-info">문항 1/16</div>
|
| 452 |
+
<div class="score-display">점수: <span id="score-text">0</span></div>
|
| 453 |
+
</div>
|
| 454 |
+
|
| 455 |
+
<div id="message" class="message-area"></div>
|
| 456 |
+
<div id="quiz"></div>
|
| 457 |
+
</div>
|
| 458 |
+
<script>
|
| 459 |
+
let questions=[], idx=0, score=0, state=new Map(), finished=false;
|
| 460 |
+
function getState(i){ if(!state.has(i)) state.set(i,{selected:null, correct:false, revealed:false, scored:false}); return state.get(i); }
|
| 461 |
+
async function load(){
|
| 462 |
+
// 허깅페이스 환경에서는 이 함수가 호출되지 않을 수 있음
|
| 463 |
+
console.log('load() 함수가 호출되었습니다');
|
| 464 |
+
try{
|
| 465 |
+
const res=await fetch('questions.json',{cache:'no-store'});
|
| 466 |
+
if(!res.ok) throw new Error('HTTP '+res.status);
|
| 467 |
+
questions=await res.json();
|
| 468 |
+
}catch(e){
|
| 469 |
+
console.error('questions.json 로드 실패:', e);
|
| 470 |
+
const msg=document.getElementById('message');
|
| 471 |
+
if(msg) msg.textContent='문항 로드 실패: '+ (e && e.message ? e.message : e);
|
| 472 |
+
return;
|
| 473 |
+
}
|
| 474 |
+
idx=0; score=0; state.clear(); finished=false; render();
|
| 475 |
+
}
|
| 476 |
+
|
| 477 |
+
// 페이지 로드 완료 시 자동 실행
|
| 478 |
+
document.addEventListener('DOMContentLoaded', function() {
|
| 479 |
+
console.log('DOM 로드 완료');
|
| 480 |
+
if (typeof questions === 'undefined' || questions.length === 0) {
|
| 481 |
+
console.log('questions가 정의되지 않았거나 비어있음, load() 호출');
|
| 482 |
+
load();
|
| 483 |
+
} else {
|
| 484 |
+
console.log('questions 이미 정의됨, render() 호출');
|
| 485 |
+
render();
|
| 486 |
+
}
|
| 487 |
+
});
|
| 488 |
+
function normalize(s){return (s||'').toString().replace(/[\s ]+/g,'').toLowerCase();}
|
| 489 |
+
function escapeHtml(s){ const div=document.createElement('div'); div.textContent = (s ?? ''); return div.innerHTML; }
|
| 490 |
+
function render(){
|
| 491 |
+
const quiz=document.getElementById('quiz'); quiz.innerHTML='';
|
| 492 |
+
if(finished){
|
| 493 |
+
const done=document.createElement('div');
|
| 494 |
+
done.className='completion-card';
|
| 495 |
+
done.innerHTML = `
|
| 496 |
+
<div class="completion-title">퀴즈 완료! 🎉</div>
|
| 497 |
+
<div class="completion-score">최종 점수: ${score}/${questions.length} (${Math.round(score/questions.length*100)}%)</div>
|
| 498 |
+
<button class="restart-btn" onclick="restartQuiz()">다시 시작하기</button>
|
| 499 |
+
`;
|
| 500 |
+
quiz.appendChild(done);
|
| 501 |
+
|
| 502 |
+
const nextBtn=document.getElementById('next');
|
| 503 |
+
const prevBtn=document.getElementById('prev');
|
| 504 |
+
document.getElementById('status').textContent=`완료`;
|
| 505 |
+
document.getElementById('score-text').textContent=`${score}/${questions.length}`;
|
| 506 |
+
prevBtn.disabled = true;
|
| 507 |
+
nextBtn.textContent = '처음으로';
|
| 508 |
+
return;
|
| 509 |
+
}
|
| 510 |
+
const q=questions[idx]; const st=getState(idx);
|
| 511 |
+
|
| 512 |
+
const card=document.createElement('div'); card.className='card';
|
| 513 |
+
const questionDiv=document.createElement('div');
|
| 514 |
+
questionDiv.className='question-text';
|
| 515 |
+
questionDiv.textContent=q.prompt;
|
| 516 |
+
card.appendChild(questionDiv);
|
| 517 |
+
|
| 518 |
+
if(q.choices && q.choices.length){
|
| 519 |
+
const isMultiple = q.qtype === 'multiple';
|
| 520 |
+
|
| 521 |
+
q.choices.forEach((c,i)=>{
|
| 522 |
+
const wrapper=document.createElement('div');
|
| 523 |
+
wrapper.className='choice-wrapper';
|
| 524 |
+
|
| 525 |
+
if(isMultiple){
|
| 526 |
+
// 체크박스 방식
|
| 527 |
+
const checkbox=document.createElement('input');
|
| 528 |
+
checkbox.type='checkbox'; checkbox.id=`choice_${i}`; checkbox.value=c;
|
| 529 |
+
checkbox.className='checkbox-input';
|
| 530 |
+
|
| 531 |
+
const label=document.createElement('label');
|
| 532 |
+
label.htmlFor=`choice_${i}`; label.className='choice';
|
| 533 |
+
label.textContent=(i+1)+'. '+c;
|
| 534 |
+
|
| 535 |
+
if(!st.revealed){
|
| 536 |
+
checkbox.onchange=()=>selectMultiple(c, checkbox.checked);
|
| 537 |
+
if(st.selected && st.selected.includes && st.selected.includes(c)) {
|
| 538 |
+
checkbox.checked=true; label.classList.add('selected');
|
| 539 |
+
}
|
| 540 |
+
}else{
|
| 541 |
+
// 해설 단계: 정답/오답 시각화
|
| 542 |
+
const isCorrect = Array.isArray(q.answer) ? q.answer.includes(c) : normalize(c)===normalize(q.answer);
|
| 543 |
+
if(isCorrect) label.classList.add('correct');
|
| 544 |
+
if(st.selected && st.selected.includes && st.selected.includes(c) && !isCorrect) label.classList.add('wrong');
|
| 545 |
+
checkbox.disabled=true;
|
| 546 |
+
}
|
| 547 |
+
|
| 548 |
+
wrapper.appendChild(checkbox); wrapper.appendChild(label);
|
| 549 |
+
}else{
|
| 550 |
+
// 기존 라디오 버튼 방식
|
| 551 |
+
const btn=document.createElement('button'); btn.className='choice'; btn.textContent=(i+1)+'. '+c;
|
| 552 |
+
if(!st.revealed){
|
| 553 |
+
btn.onclick=()=>select(c);
|
| 554 |
+
if(st.selected===c) btn.classList.add('selected');
|
| 555 |
+
}else{
|
| 556 |
+
// 해설 단계: 정답/오답 시각화
|
| 557 |
+
const isCorrect = Array.isArray(q.answer) ? q.answer.includes(c) : normalize(c)===normalize(q.answer);
|
| 558 |
+
if(isCorrect) btn.classList.add('correct');
|
| 559 |
+
if(st.selected===c && !isCorrect) btn.classList.add('wrong');
|
| 560 |
+
btn.disabled=true;
|
| 561 |
+
}
|
| 562 |
+
wrapper.appendChild(btn);
|
| 563 |
+
}
|
| 564 |
+
|
| 565 |
+
card.appendChild(wrapper);
|
| 566 |
+
});
|
| 567 |
+
}else{
|
| 568 |
+
const input=document.createElement('input');
|
| 569 |
+
input.placeholder='정답 입력';
|
| 570 |
+
input.style='width:100%;padding:15px;border:2px solid #e2e8f0;border-radius:12px;margin-top:15px;font-size:16px;';
|
| 571 |
+
input.value = st.selected||'';
|
| 572 |
+
const check=document.createElement('button');
|
| 573 |
+
check.className='btn';
|
| 574 |
+
check.style='margin-top:15px;width:100%;';
|
| 575 |
+
check.textContent= st.revealed? '제출됨' : '제출';
|
| 576 |
+
if(!st.revealed){
|
| 577 |
+
check.onclick=()=>select(input.value.trim());
|
| 578 |
+
}else{
|
| 579 |
+
check.disabled=true;
|
| 580 |
+
}
|
| 581 |
+
card.appendChild(input); card.appendChild(check);
|
| 582 |
+
}
|
| 583 |
+
|
| 584 |
+
// 해설 영역 (선택 후 다음을 누르면 표시)
|
| 585 |
+
if(st.revealed){
|
| 586 |
+
const resultCard=document.createElement('div');
|
| 587 |
+
resultCard.className='result-card';
|
| 588 |
+
|
| 589 |
+
// 정답/오답 표시
|
| 590 |
+
const resultDiv = document.createElement('div');
|
| 591 |
+
resultDiv.className = st.correct ? 'result-status result-correct' : 'result-status result-wrong';
|
| 592 |
+
resultDiv.textContent = st.correct ? '정답입니다! ✅' : '틀렸습니다! ❌';
|
| 593 |
+
resultCard.appendChild(resultDiv);
|
| 594 |
+
|
| 595 |
+
// 정답 번호 표시
|
| 596 |
+
let answerText = '';
|
| 597 |
+
if(Array.isArray(q.answer)){
|
| 598 |
+
// 다�� 정답: 번호로 표시
|
| 599 |
+
const answerNumbers = [];
|
| 600 |
+
q.answer.forEach(ans => {
|
| 601 |
+
const index = q.choices.findIndex(choice => normalize(choice) === normalize(ans));
|
| 602 |
+
if(index !== -1) answerNumbers.push(index + 1);
|
| 603 |
+
});
|
| 604 |
+
answerText = answerNumbers.join(', ');
|
| 605 |
+
}else{
|
| 606 |
+
// 단일 정답: 번호로 표시
|
| 607 |
+
const index = q.choices.findIndex(choice => normalize(choice) === normalize(q.answer));
|
| 608 |
+
answerText = index !== -1 ? (index + 1).toString() : '1';
|
| 609 |
+
}
|
| 610 |
+
|
| 611 |
+
const answerDiv = document.createElement('div');
|
| 612 |
+
answerDiv.className = 'answer-display';
|
| 613 |
+
answerDiv.innerHTML = `<strong>정답</strong>: ${answerText}`;
|
| 614 |
+
resultCard.appendChild(answerDiv);
|
| 615 |
+
|
| 616 |
+
// 해설 표시
|
| 617 |
+
if(q.explanation && q.explanation.toString().trim()){
|
| 618 |
+
const explanationDiv = document.createElement('div');
|
| 619 |
+
explanationDiv.className = 'explanation';
|
| 620 |
+
explanationDiv.innerHTML = `<strong>해설:</strong> ${escapeHtml(q.explanation.toString())}`;
|
| 621 |
+
resultCard.appendChild(explanationDiv);
|
| 622 |
+
}
|
| 623 |
+
|
| 624 |
+
card.appendChild(resultCard);
|
| 625 |
+
}
|
| 626 |
+
|
| 627 |
+
const meta=document.createElement('div');
|
| 628 |
+
meta.className='meta-info';
|
| 629 |
+
meta.textContent='유형: '+q.qtype.toUpperCase();
|
| 630 |
+
card.appendChild(meta);
|
| 631 |
+
|
| 632 |
+
quiz.appendChild(card);
|
| 633 |
+
|
| 634 |
+
const nextBtn=document.getElementById('next');
|
| 635 |
+
const prevBtn=document.getElementById('prev');
|
| 636 |
+
document.getElementById('status').textContent=`문항 ${idx+1}/${questions.length}`;
|
| 637 |
+
document.getElementById('score-text').textContent=`${score}/${questions.length}`;
|
| 638 |
+
prevBtn.disabled = idx===0;
|
| 639 |
+
nextBtn.textContent = st.revealed ? (idx===questions.length-1 ? '완료' : '다음 →') : '다음 →';
|
| 640 |
+
}
|
| 641 |
+
function selectMultiple(val, checked){
|
| 642 |
+
const q=questions[idx]; const st=getState(idx);
|
| 643 |
+
if(st.revealed) return; // 해설 단계에서는 선택 잠금
|
| 644 |
+
|
| 645 |
+
if(!st.selected) st.selected = [];
|
| 646 |
+
if(!Array.isArray(st.selected)) st.selected = [];
|
| 647 |
+
|
| 648 |
+
if(checked){
|
| 649 |
+
if(!st.selected.includes(val)) st.selected.push(val);
|
| 650 |
+
}else{
|
| 651 |
+
st.selected = st.selected.filter(item => item !== val);
|
| 652 |
+
}
|
| 653 |
+
|
| 654 |
+
document.getElementById('message').textContent='';
|
| 655 |
+
render(); // 시각적 반영
|
| 656 |
+
}
|
| 657 |
+
function select(val){
|
| 658 |
+
const q=questions[idx]; const st=getState(idx);
|
| 659 |
+
if(st.revealed) return; // 해설 단계에서는 선택 잠금
|
| 660 |
+
st.selected = val;
|
| 661 |
+
st.correct = normalize(val)===normalize(q.answer);
|
| 662 |
+
document.getElementById('message').textContent='';
|
| 663 |
+
render(); // 시각적 반영
|
| 664 |
+
}
|
| 665 |
+
function restartQuiz(){
|
| 666 |
+
idx=0; score=0; state.clear(); finished=false;
|
| 667 |
+
document.getElementById('message').textContent='';
|
| 668 |
+
render();
|
| 669 |
+
}
|
| 670 |
+
document.getElementById('prev').onclick=()=>{ if(finished) return; if(idx>0){ idx--; render(); }};
|
| 671 |
+
document.getElementById('next').onclick=()=>{
|
| 672 |
+
if(finished){ restartQuiz(); return; }
|
| 673 |
+
const st=getState(idx);
|
| 674 |
+
const q=questions[idx];
|
| 675 |
+
const msg=document.getElementById('message');
|
| 676 |
+
|
| 677 |
+
if(!st.revealed){
|
| 678 |
+
if(st.selected==null || (Array.isArray(st.selected) && st.selected.length===0)){
|
| 679 |
+
msg.textContent='먼저 답을 선택하세요.'; return;
|
| 680 |
+
}
|
| 681 |
+
|
| 682 |
+
// 해설 표시 단계로 전환하며 점수 반영(최초 1회)
|
| 683 |
+
st.revealed=true;
|
| 684 |
+
|
| 685 |
+
// 채점 로직
|
| 686 |
+
let isCorrect = false;
|
| 687 |
+
if(q.qtype === 'multiple'){
|
| 688 |
+
// 다중 정답: 선택한 답과 정답이 정확히 일치해야 함
|
| 689 |
+
if(Array.isArray(q.answer) && Array.isArray(st.selected)){
|
| 690 |
+
const sortedAnswer = [...q.answer].sort();
|
| 691 |
+
const sortedSelected = [...st.selected].sort();
|
| 692 |
+
isCorrect = sortedAnswer.length === sortedSelected.length &&
|
| 693 |
+
sortedAnswer.every((ans, i) => normalize(ans) === normalize(sortedSelected[i]));
|
| 694 |
+
}
|
| 695 |
+
}else{
|
| 696 |
+
// 단일 정답
|
| 697 |
+
isCorrect = Array.isArray(q.answer) ?
|
| 698 |
+
q.answer.some(ans => normalize(st.selected) === normalize(ans)) :
|
| 699 |
+
normalize(st.selected) === normalize(q.answer);
|
| 700 |
+
}
|
| 701 |
+
|
| 702 |
+
st.correct = isCorrect;
|
| 703 |
+
if(st.correct && !st.scored){ score++; st.scored=true; }
|
| 704 |
+
render();
|
| 705 |
+
}else{
|
| 706 |
+
if(idx<questions.length-1){ idx++; render(); }
|
| 707 |
+
else { finished=true; render(); }
|
| 708 |
+
}
|
| 709 |
+
};
|
| 710 |
+
load();
|
| 711 |
+
</script>
|
| 712 |
+
</body></html>
|
questions.json
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[
|
| 2 |
+
{
|
| 3 |
+
"qtype": "mcq",
|
| 4 |
+
"prompt": "조직이 더욱 커져 해외로 진출하거나 글로벌 지점을 늘리는 등 운영 범위가 확장될 때 나타납니다.해외 사업장의 법인장이나 임원들을 관리해야 할 필요성이 생기면서, 인사 담당 임원 (전무, 부사장, 상무 등)을 조직에 보직합니다. 이와 같은 인적자원관리의 유형을 무엇이라 하는가?",
|
| 5 |
+
"choices": [
|
| 6 |
+
"원시적인사관리(PPM)",
|
| 7 |
+
"인사관리(PM)",
|
| 8 |
+
"인적자원관리(HRM)",
|
| 9 |
+
"전략적인적자원관리(SHRM)"
|
| 10 |
+
],
|
| 11 |
+
"answer": "전략적인적자원관리(SHRM)"
|
| 12 |
+
},
|
| 13 |
+
{
|
| 14 |
+
"qtype": "mcq",
|
| 15 |
+
"prompt": "다음 중 직무분석에 활용에 대해 틀린 내용을 고르시오.",
|
| 16 |
+
"choices": [
|
| 17 |
+
"직무분석을 기초로 직무가치를 산정하고 보상체계에 반영할 수 있다.",
|
| 18 |
+
"직무 분석은 조직 내에 존재하는 각 직무의 구체적인 내용, 요건, 역할과 책임을 체계적으로 분석하고 정의하는 활동. 인사 관리의 기초.",
|
| 19 |
+
"직무 기술서는 직무 분석을 통해 도출된 결과물로, 특정 직무를 구성하는 일의 전체 내용(과업, 절차, 책임 등)을 기술한 문서. 채용, 평가, 교육 등의 기준으로 활용.",
|
| 20 |
+
"경력개발을 위한 직무수행 요구조건(지식, 기술 등) 정립 및 이동, 승진을 위한 필요한 자료 제공 및 교육훈련 정보 제공"
|
| 21 |
+
],
|
| 22 |
+
"answer": "직무분석을 기초로 직무가치를 산정하고 보상체계에 반영할 수 있다."
|
| 23 |
+
},
|
| 24 |
+
{
|
| 25 |
+
"qtype": "mcq",
|
| 26 |
+
"prompt": "다음 중 직무개념에 대하여 틀린 설명을 고르시오.",
|
| 27 |
+
"choices": [
|
| 28 |
+
"직위 (Position)는 한 개인이 수행하는 하나 혹은 그 이상의 의무로 구성 ,특정 개인이 수행하는 모든 과업의 집단",
|
| 29 |
+
"의무 및 책임(Duty & Responsibility)은 특정 개인이 수행하는 것으로 여러 가지 유사한 과업으로 이루어짐",
|
| 30 |
+
"요소 (要所/Element)는 업무의 가장 작은 단위이며 전화를 받는다던가, 기록을 하는 등의 작은 단위업무",
|
| 31 |
+
"과업(Task)은 업무량 분석의 단위가 된다."
|
| 32 |
+
],
|
| 33 |
+
"answer": "과업(Task)은 업무량 분석의 단위가 된다."
|
| 34 |
+
},
|
| 35 |
+
{
|
| 36 |
+
"qtype": "multiple",
|
| 37 |
+
"prompt": "직무분석 절차에 따른 설명이다. 틀린 설명을 고르시오.",
|
| 38 |
+
"choices": [
|
| 39 |
+
"직무정보수집방법에는 면접법, 관찰법, 설문지법(조사표법)이 있다.",
|
| 40 |
+
"TFT 등 일시적 활동은 기재하지 않음",
|
| 41 |
+
"작성원칙에 인원/시간 부족 등으로 수행하지 못하는 일이지만 수행이 필요한 일은 기록",
|
| 42 |
+
"직무조사표 작성시에 조직내 참여한 활동(예, TFT 활동)들을 모두 포함하도록 함.",
|
| 43 |
+
"직무담당기간이 짧은 직무담당자의 경우 전임자에게 작성하도록 함."
|
| 44 |
+
],
|
| 45 |
+
"answer": [
|
| 46 |
+
"직무조사표 작성시에 조직내 참여한 활동(예, TFT 활동)들을 모두 포함하도록 함.",
|
| 47 |
+
"직무담당기간이 짧은 직무담당자의 경우 전임자에게 작성하도록 함."
|
| 48 |
+
],
|
| 49 |
+
"explanation": "작성원칙 -원칙적으로 직무담당자가 작성(경험이 짧은 경우, 전임자나 차상위자 조언) -TFT 등 일시적 활동은 기재하지 않음 -인원/시간 부족 등으로 수행하지 못하는 일지만 수행이 필요한 일은 기록"
|
| 50 |
+
},
|
| 51 |
+
{
|
| 52 |
+
"qtype": "mcq",
|
| 53 |
+
"prompt": "인력계획의 의의와 중요성에 대한 설명 중 틀린 것을 고르시오.",
|
| 54 |
+
"choices": [
|
| 55 |
+
"기업의 전략적 목표를 달성하기 위하여 각 직무, 과업별로 적정자격을 보유한 인력을 규명하고,적정한 인력 수를 확보하여야 함.",
|
| 56 |
+
"인력계획은 현재 및 장래의 각 시점에서 기업이 필요로 하는 특성을 지닌 인원의 수를 예측하고, 사내, 사외에서 공급 가능한 인력을 고려하여 인력 의 수급을 조정하는 활동.",
|
| 57 |
+
"정원계획이란 현재의 시점에서 정태적인 인력계획을 말한다.",
|
| 58 |
+
"미래 적정한 기간동안(예, 5개년) 중장기 인력계획을 수립하기도 한다.",
|
| 59 |
+
"인력계획은 외부노동시장의 상황을 고려하여 현재 조직내부에서가용할 수 있은 인원의 수를 파악하는 것이다."
|
| 60 |
+
],
|
| 61 |
+
"answer": "인력계획은 외부노동시장의 상황을 고려하여 현재 조직내부에서가용할 수 있은 인원의 수를 파악하는 것이다."
|
| 62 |
+
},
|
| 63 |
+
{
|
| 64 |
+
"qtype": "mcq",
|
| 65 |
+
"prompt": "인력산정시 근로시간 기준에 대한 설명이다. 틀린 것을 고르시오.",
|
| 66 |
+
"choices": [
|
| 67 |
+
"ILO 여유율은 여유시간에 업무준비, 수리 등등 용변, 휴식 등도 포함.",
|
| 68 |
+
"생산직, 육체근로자 여유시간 많아야 한다.",
|
| 69 |
+
"팀별 적정인원 산정시 여유율을 고려해야 한다. ILO에서는 약 15%를 ���유율 기준으로 권고하고 있다.",
|
| 70 |
+
"남성 보다 여성의 여유시간 더 많아야 한다. 남성 9%, 여성 11%"
|
| 71 |
+
],
|
| 72 |
+
"answer": "팀별 적정인원 산정시 여유율을 고려해야 한다. ILO에서는 약 15%를 여유율 기준으로 권고하고 있다."
|
| 73 |
+
},
|
| 74 |
+
{
|
| 75 |
+
"qtype": "mcq",
|
| 76 |
+
"prompt": "다음 중 경력에 관한 내용으로 올바르지 않은 것을 고르시오.",
|
| 77 |
+
"choices": [
|
| 78 |
+
"한 개인이 입사로부터 퇴직에 이르기까지 경력경로를 개인과 조직이 함께 계획하고 실천하는 활동",
|
| 79 |
+
"경력개발이란 구성원 자신이 성공적인 조직생활을 하겠다는 의지와 경영자들이 인재를 육성하겠다는 의도를 포괄하는 개념",
|
| 80 |
+
"기업내 전 종업원, 특정 부류 직책에 있는 사원을 대상으로 개개인의 장기적인 직무수행 능력과 적성을 평가하고 개발하여 적성에 맞는 직무를 보임하는 계획적 활동",
|
| 81 |
+
"조기퇴직, 조기이탈을 막기 위한 방안 경력개발활동 등을 지원하기 위한 다양한 제도를 도입한다."
|
| 82 |
+
],
|
| 83 |
+
"answer": "조기퇴직, 조기이탈을 막기 위한 방안 경력개발활동 등을 지원하기 위한 다양한 제도를 도입한다."
|
| 84 |
+
},
|
| 85 |
+
{
|
| 86 |
+
"qtype": "mcq",
|
| 87 |
+
"prompt": "역량 면접(Competency-based Interview)이 미래의 성과를 예측하기 위해 주로 무엇을 하는지 틀린 설명을 고르시오",
|
| 88 |
+
"choices": [
|
| 89 |
+
"역량 면접이 미래의 성과를 예측하는 가장 중요한 근거는 과거의 행동이 미래의 행동을 예측한다는 기본 원칙에 있다.",
|
| 90 |
+
"면접에서는 지원자의 과거 행동을 파악한다. 이를 위하여 탐색질문(Probing Question)을 한다.",
|
| 91 |
+
"지원자가 경험한 모든 것이 미래에 반복된다는 것이 전제이다(행동의 전이가능성), 면접에서 이를 체크하는 것.",
|
| 92 |
+
"지원자의 지원에 생각이나 미래 상황에 대한 의견을 묻고 이를 중심으로 평가한다."
|
| 93 |
+
],
|
| 94 |
+
"answer": "지원자의 지원에 생각이나 미래 상황에 대한 의견을 묻고 이를 중심으로 평가한다."
|
| 95 |
+
},
|
| 96 |
+
{
|
| 97 |
+
"qtype": "multiple",
|
| 98 |
+
"prompt": "다양한 면접에 관한 내용 중 틀린 것을 고르시오.",
|
| 99 |
+
"choices": [
|
| 100 |
+
"1:1 면접 -면접관 1, 지원자 1 -시간과 비용 소요 -임원면접",
|
| 101 |
+
"Presentation 면접 -5~10분간 PT -면접관 3~4명 -사전에 문제와 30분 제공",
|
| 102 |
+
"Role Play -지원자와 면접관 -지원자 多 -금융권 활용(금융상품 설명)",
|
| 103 |
+
"Assessment Center -위 모든 면접을 종합 -가장 객관적 평가 -임원 승진 시 활용",
|
| 104 |
+
"역량면접은 경험이나 상황을 중심으로 1:1 심층면접으로 진행해야 한다.",
|
| 105 |
+
"패널면접은 피면접자 다수, 면접관 다수로 구성되는 면접으로 면접 시간을 절약할 수 있는 장점이 있다.",
|
| 106 |
+
"Panel 면접 -면접관 4, 지원자 1 -지원자 당황 -실무팀장 면접",
|
| 107 |
+
"Group Discussion -지원자 多 -찬반 토론 등 다양 -대기업 신입사원 전형"
|
| 108 |
+
],
|
| 109 |
+
"answer": [
|
| 110 |
+
"역량면접은 경험이나 상황을 중심으로 1:1 심층면접으로 진행해야 한다.",
|
| 111 |
+
"패널면접은 피면접자 다수, 면접관 다수로 구성되는 면접으로 면접 시간을 절약할 수 있는 장점이 있다."
|
| 112 |
+
]
|
| 113 |
+
},
|
| 114 |
+
{
|
| 115 |
+
"qtype": "mcq",
|
| 116 |
+
"prompt": "다음은 인사평가요소에 대한 설명이다. 바르지 않은 내용을 고르시오.",
|
| 117 |
+
"choices": [
|
| 118 |
+
"개인별 직무수행 결과와 관련된 업적평가, 성공적인 직무 수행방법과 관련된 역량평가라는 두 개의 축으로 설계.",
|
| 119 |
+
"업적평가(What)의 평가지표는 계량지표(정량지표)와 비계량 지표(정성지표)이다.",
|
| 120 |
+
"역량평가(How)의 평가지표는 역량 모델링을 통한 역량 및 행동지표 도출, 역량별 평가지표(Behavior Indicator) 활용",
|
| 121 |
+
"역량평가(How)의 평가방법은 BOS(Behavioral Observation Scales) 방식의 평정 척도법이다.",
|
| 122 |
+
"전통적 인사고과-평가요소는 업적, 역량이다"
|
| 123 |
+
],
|
| 124 |
+
"answer": "전통적 인사고과-평가요소는 업적, 역량이다"
|
| 125 |
+
},
|
| 126 |
+
{
|
| 127 |
+
"qtype": "mcq",
|
| 128 |
+
"prompt": "사내모집과 사외모집의 장점과 단점에 관한 내용이다. 틀린 것을 고르시오.",
|
| 129 |
+
"choices": [
|
| 130 |
+
"사내모집 - 성장기의 인력수요를 충족시킬 수 있다.",
|
| 131 |
+
"사내모집 - 능력이 충분히 검증된 사람을 확보할 수 있다.",
|
| 132 |
+
"사내모집 - 성장기에는 유자격자를 충분히 공급하지 못함",
|
| 133 |
+
"사외모집 - 새로운 아이디어와 견해가 유인됨."
|
| 134 |
+
],
|
| 135 |
+
"answer": "사내모집 - 성장기의 인력수요를 충족시킬 수 있다."
|
| 136 |
+
},
|
| 137 |
+
{
|
| 138 |
+
"qtype": "mcq",
|
| 139 |
+
"prompt": "다음 Calibration Meeting에 관한 설명 중 틀린 것을 고르시오.",
|
| 140 |
+
"choices": [
|
| 141 |
+
"캘리브레이션 미팅은 평가과정에서 평가의 공정성과 일관성을 확보하기 위해 매우 중요한 과정.",
|
| 142 |
+
"평가대상자의 상사와 다른 상사(또는 차 상위 상사)가 모여서 평가대상자의 평가결과를 조정하고 평가등급을 확인",
|
| 143 |
+
"Calibration Meeting은 상대평가에 따른 우수한 집단의 평가상의 불이익을 방지하기 위한 제도",
|
| 144 |
+
"주요 목적은 ‘평가 일관성 확보’, ‘편견의 최소화’, 그리고 ‘성과관리 강화’이다."
|
| 145 |
+
],
|
| 146 |
+
"answer": "Calibration Meeting은 상대평가에 따른 우수한 집단의 평가상의 불이익을 방지하기 위한 제도"
|
| 147 |
+
},
|
| 148 |
+
{
|
| 149 |
+
"qtype": "mcq",
|
| 150 |
+
"prompt": "버디의 선정요건 중 틀린 것을 고르시오.",
|
| 151 |
+
"choices": [
|
| 152 |
+
"신입 사원의 질문에 친절하고 꼼꼼하게 답변할 수 있는 사람",
|
| 153 |
+
"신입 사원의 업무와 관련된 경험 및 지식을 갖춘 사람",
|
| 154 |
+
"신입사원의 성공적 조직적응을 위하여 같은 경력목표를 가진 사람",
|
| 155 |
+
"신입 사원과 친밀한 관계를 형성할 수 있는 성격의 사람"
|
| 156 |
+
],
|
| 157 |
+
"answer": "신입사원의 성공적 조직적응을 위하여 같은 경력목표를 가진 사람"
|
| 158 |
+
},
|
| 159 |
+
{
|
| 160 |
+
"qtype": "mcq",
|
| 161 |
+
"prompt": "목표설정 원칙 중 틀린 내용을 고르시오.",
|
| 162 |
+
"choices": [
|
| 163 |
+
"Time-based (달성 시한) - Time-bound (목표를 달성하는 데 필요한 기간은?)",
|
| 164 |
+
"Action-Oriented (행동 지향) - Attainable (무엇을 어떻게 하면 목표를 달성할 수 있는지 알 수 있는가?)",
|
| 165 |
+
"Attainable: 달성 가능",
|
| 166 |
+
"Realistic (현실적) - Relevant (부서 또는 개인의 노력과 역량으로 달성할 수 있는가?)"
|
| 167 |
+
],
|
| 168 |
+
"answer": "Attainable: 달성 가능"
|
| 169 |
+
},
|
| 170 |
+
{
|
| 171 |
+
"qtype": "mcq",
|
| 172 |
+
"prompt": "다음은 교육의 종류에 대한 설명이다. 틀린 내용을 고르시오.",
|
| 173 |
+
"choices": [
|
| 174 |
+
"액션 러닝은 교육에서 배운 지식(Learning)이 실제 행동(Action)으로 이어지지 않는 문제를 해결하기 위한 교육 방식.",
|
| 175 |
+
"OJT – 직장 내에서 자기 직무를 수행하면서 받는 informal learning이다. 이는 현장실습이 어려운 사무직 직원의 능력개발에 유용.",
|
| 176 |
+
"토론은 교육대상자에게 주제를 주어 각자의 의견발표를 통해 스스로 문제를 해결하도록 하는 방법",
|
| 177 |
+
"강의는 여러 사람을 대상으로 강사가 일방적 정보와 지식을 전달하는 교육"
|
| 178 |
+
],
|
| 179 |
+
"answer": "OJT – 직장 내에서 자기 직무를 수행하면서 받는 informal learning이다. 이는 현장실습이 어려운 사무직 직원의 능력개발에 유용.",
|
| 180 |
+
"explanation": "OJT(현장훈련): 직무를 수행하면서 상사로부터 지도를 받는 비공식적 교육으로 직장내 교육이라 함 최근 ‘S-OJT’ 구체화되고, PBL 방식 기반으로 한 문제해결형 교육으로 추진되고 있음"
|
| 181 |
+
},
|
| 182 |
+
{
|
| 183 |
+
"qtype": "multiple",
|
| 184 |
+
"prompt": "샤인(Schein) 교수의 Career Anchor에 대한 설명으로 틀린 것을 모두 고르시오.",
|
| 185 |
+
"choices": [
|
| 186 |
+
"안정성 추구형 : 정해진 일정, 일을 추구",
|
| 187 |
+
"E. Schein 교수는 시대변화와 사회의 다원화 등을 고려하여 Career Anchor를 9가지로 다시 정의하였다.",
|
| 188 |
+
"도전 추구형: 도전, 문제해결, 장애극복 일",
|
| 189 |
+
"삶의 질 추구형: 경력목표 낮음, 개인의 삶 중시",
|
| 190 |
+
"전문가 추구형 – 도전, 문제해결 추구"
|
| 191 |
+
],
|
| 192 |
+
"answer": [
|
| 193 |
+
"E. Schein 교수는 시대변화와 사회의 다원화 등을 고려하여 Career Anchor를 9가지로 다시 정의하였다.",
|
| 194 |
+
"전문가 추구형 – 도전, 문제해결 추구"
|
| 195 |
+
],
|
| 196 |
+
"explanation": "개인이 경력을 선택할 때 포기하기 어려운 핵심적인 가치관, 동기, 역량의 조합. 샤인(Schein) 교수는 전문가, 리더십, 자율성, 안정성 등 8가지 유형을 제시했다."
|
| 197 |
+
}
|
| 198 |
+
]
|
requirements.txt
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
gradio>=4.0.0
|
| 2 |
+
pathlib
|
simple_quiz.html
ADDED
|
@@ -0,0 +1,337 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="ko">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>인적자원관리 퀴즈</title>
|
| 7 |
+
<style>
|
| 8 |
+
body {
|
| 9 |
+
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
| 10 |
+
line-height: 1.6;
|
| 11 |
+
margin: 0;
|
| 12 |
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
| 13 |
+
color: #1a202c;
|
| 14 |
+
min-height: 100vh;
|
| 15 |
+
padding: 20px;
|
| 16 |
+
}
|
| 17 |
+
.container {
|
| 18 |
+
max-width: 800px;
|
| 19 |
+
margin: 0 auto;
|
| 20 |
+
padding: 0 20px;
|
| 21 |
+
}
|
| 22 |
+
.quiz-header {
|
| 23 |
+
background: rgba(255, 255, 255, 0.95);
|
| 24 |
+
backdrop-filter: blur(20px);
|
| 25 |
+
border-radius: 20px;
|
| 26 |
+
padding: 30px;
|
| 27 |
+
margin-bottom: 25px;
|
| 28 |
+
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
|
| 29 |
+
border: 1px solid rgba(255, 255, 255, 0.2);
|
| 30 |
+
text-align: center;
|
| 31 |
+
}
|
| 32 |
+
.quiz-title {
|
| 33 |
+
font-size: 28px;
|
| 34 |
+
font-weight: 700;
|
| 35 |
+
margin: 0 0 15px 0;
|
| 36 |
+
background: linear-gradient(135deg, #667eea, #764ba2);
|
| 37 |
+
-webkit-background-clip: text;
|
| 38 |
+
-webkit-text-fill-color: transparent;
|
| 39 |
+
background-clip: text;
|
| 40 |
+
}
|
| 41 |
+
.card {
|
| 42 |
+
background: rgba(255, 255, 255, 0.95);
|
| 43 |
+
backdrop-filter: blur(20px);
|
| 44 |
+
border-radius: 20px;
|
| 45 |
+
padding: 30px;
|
| 46 |
+
margin: 20px 0;
|
| 47 |
+
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
|
| 48 |
+
border: 1px solid rgba(255, 255, 255, 0.2);
|
| 49 |
+
}
|
| 50 |
+
.question-text {
|
| 51 |
+
font-size: 18px;
|
| 52 |
+
font-weight: 500;
|
| 53 |
+
line-height: 1.7;
|
| 54 |
+
margin-bottom: 25px;
|
| 55 |
+
color: #2d3748;
|
| 56 |
+
}
|
| 57 |
+
.choice {
|
| 58 |
+
display: block;
|
| 59 |
+
border: 2px solid #e2e8f0;
|
| 60 |
+
border-radius: 12px;
|
| 61 |
+
padding: 16px 20px;
|
| 62 |
+
margin: 12px 0;
|
| 63 |
+
background: white;
|
| 64 |
+
cursor: pointer;
|
| 65 |
+
transition: all 0.3s ease;
|
| 66 |
+
font-weight: 500;
|
| 67 |
+
color: #4a5568;
|
| 68 |
+
width: 100%;
|
| 69 |
+
text-align: left;
|
| 70 |
+
}
|
| 71 |
+
.choice:hover {
|
| 72 |
+
border-color: #667eea;
|
| 73 |
+
background: #f7fafc;
|
| 74 |
+
transform: translateX(4px);
|
| 75 |
+
}
|
| 76 |
+
.choice.selected {
|
| 77 |
+
border-color: #667eea;
|
| 78 |
+
background: linear-gradient(135deg, #ebf4ff, #e6fffa);
|
| 79 |
+
color: #2b6cb0;
|
| 80 |
+
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.2);
|
| 81 |
+
}
|
| 82 |
+
.btn {
|
| 83 |
+
background: linear-gradient(135deg, #667eea, #764ba2);
|
| 84 |
+
color: white;
|
| 85 |
+
border: none;
|
| 86 |
+
border-radius: 12px;
|
| 87 |
+
padding: 12px 24px;
|
| 88 |
+
cursor: pointer;
|
| 89 |
+
font-weight: 500;
|
| 90 |
+
font-size: 14px;
|
| 91 |
+
transition: all 0.3s ease;
|
| 92 |
+
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
|
| 93 |
+
margin: 10px 5px;
|
| 94 |
+
}
|
| 95 |
+
.btn:hover:not(:disabled) {
|
| 96 |
+
transform: translateY(-2px);
|
| 97 |
+
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4);
|
| 98 |
+
}
|
| 99 |
+
.btn:disabled {
|
| 100 |
+
opacity: 0.5;
|
| 101 |
+
cursor: not-allowed;
|
| 102 |
+
}
|
| 103 |
+
.status-bar {
|
| 104 |
+
background: rgba(255, 255, 255, 0.9);
|
| 105 |
+
backdrop-filter: blur(10px);
|
| 106 |
+
border-radius: 15px;
|
| 107 |
+
padding: 15px 25px;
|
| 108 |
+
margin-bottom: 20px;
|
| 109 |
+
display: flex;
|
| 110 |
+
justify-content: space-between;
|
| 111 |
+
align-items: center;
|
| 112 |
+
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.08);
|
| 113 |
+
border: 1px solid rgba(255, 255, 255, 0.3);
|
| 114 |
+
}
|
| 115 |
+
.progress-info {
|
| 116 |
+
font-weight: 500;
|
| 117 |
+
color: #4a5568;
|
| 118 |
+
}
|
| 119 |
+
.score-display {
|
| 120 |
+
background: linear-gradient(135deg, #48bb78, #38a169);
|
| 121 |
+
color: white;
|
| 122 |
+
padding: 8px 16px;
|
| 123 |
+
border-radius: 20px;
|
| 124 |
+
font-weight: 600;
|
| 125 |
+
font-size: 14px;
|
| 126 |
+
box-shadow: 0 4px 12px rgba(72, 187, 120, 0.3);
|
| 127 |
+
}
|
| 128 |
+
</style>
|
| 129 |
+
</head>
|
| 130 |
+
<body>
|
| 131 |
+
<div class="container">
|
| 132 |
+
<div class="quiz-header">
|
| 133 |
+
<h1 class="quiz-title">인적자원관리 퀴즈</h1>
|
| 134 |
+
</div>
|
| 135 |
+
|
| 136 |
+
<div class="status-bar">
|
| 137 |
+
<div id="status" class="progress-info">문항 1/16</div>
|
| 138 |
+
<div class="score-display">점수: <span id="score-text">0/16</span></div>
|
| 139 |
+
</div>
|
| 140 |
+
|
| 141 |
+
<div id="quiz"></div>
|
| 142 |
+
|
| 143 |
+
<div style="text-align: center; margin-top: 20px;">
|
| 144 |
+
<button id="prev" class="btn">← 이전</button>
|
| 145 |
+
<button id="next" class="btn">다음 →</button>
|
| 146 |
+
</div>
|
| 147 |
+
</div>
|
| 148 |
+
|
| 149 |
+
<script>
|
| 150 |
+
// 문제 데이터
|
| 151 |
+
const questions = [
|
| 152 |
+
{
|
| 153 |
+
"qtype": "mcq",
|
| 154 |
+
"prompt": "조직이 더욱 커져 해외로 진출하거나 글로벌 지점을 늘리는 등 운영 범위가 확장될 때 나타납니다.해외 사업장의 법인장이나 임원들을 관리해야 할 필요성이 생기면서, 인사 담당 임원 (전무, 부사장, 상무 등)을 조직에 보직합니다. 이와 같은 인적자원관리의 유형을 무엇이라 하는가?",
|
| 155 |
+
"choices": [
|
| 156 |
+
"원시적인사관리(PPM)",
|
| 157 |
+
"인사관리(PM)",
|
| 158 |
+
"인적자원관리(HRM)",
|
| 159 |
+
"전략적인적자원관리(SHRM)"
|
| 160 |
+
],
|
| 161 |
+
"answer": "전략적인적자원관리(SHRM)"
|
| 162 |
+
},
|
| 163 |
+
{
|
| 164 |
+
"qtype": "mcq",
|
| 165 |
+
"prompt": "다음 중 직무분석에 활용에 대해 틀린 내용을 고르시오.",
|
| 166 |
+
"choices": [
|
| 167 |
+
"직무분석을 기초로 직무가치를 산정하고 보상체계에 반영할 수 있다.",
|
| 168 |
+
"직무 분석은 조직 내에 존재하는 각 직무의 구체적인 내용, 요건, 역할과 책임을 체계적으로 분석하고 정의하는 활동. 인사 관리의 기초.",
|
| 169 |
+
"직무 기술서는 직무 분석을 통해 도출된 결과물로, 특정 직무를 구성하는 일의 전체 내용(과업, 절차, 책임 등)을 기술한 문서. 채용, 평가, 교육 등의 기준으로 활용.",
|
| 170 |
+
"경력개발을 위한 직무수행 요구조건(지식, 기술 등) 정립 및 이동, 승진을 위한 필요한 자료 제공 및 교육훈련 정보 제공"
|
| 171 |
+
],
|
| 172 |
+
"answer": "직무분석을 기초로 직무가치를 산정하고 보상체계에 반영할 수 있다."
|
| 173 |
+
}
|
| 174 |
+
];
|
| 175 |
+
|
| 176 |
+
let idx = 0;
|
| 177 |
+
let score = 0;
|
| 178 |
+
let state = new Map();
|
| 179 |
+
let finished = false;
|
| 180 |
+
|
| 181 |
+
function getState(i) {
|
| 182 |
+
if (!state.has(i)) {
|
| 183 |
+
state.set(i, {selected: null, correct: false, revealed: false, scored: false});
|
| 184 |
+
}
|
| 185 |
+
return state.get(i);
|
| 186 |
+
}
|
| 187 |
+
|
| 188 |
+
function normalize(s) {
|
| 189 |
+
return (s || '').toString().replace(/[\s ]+/g, '').toLowerCase();
|
| 190 |
+
}
|
| 191 |
+
|
| 192 |
+
function select(val) {
|
| 193 |
+
const q = questions[idx];
|
| 194 |
+
const st = getState(idx);
|
| 195 |
+
if (st.revealed) return;
|
| 196 |
+
st.selected = val;
|
| 197 |
+
st.correct = normalize(val) === normalize(q.answer);
|
| 198 |
+
render();
|
| 199 |
+
}
|
| 200 |
+
|
| 201 |
+
function render() {
|
| 202 |
+
const quiz = document.getElementById('quiz');
|
| 203 |
+
quiz.innerHTML = '';
|
| 204 |
+
|
| 205 |
+
if (finished) {
|
| 206 |
+
const done = document.createElement('div');
|
| 207 |
+
done.className = 'card';
|
| 208 |
+
done.innerHTML = `
|
| 209 |
+
<div style="text-align: center;">
|
| 210 |
+
<h2>퀴즈 완료! 🎉</h2>
|
| 211 |
+
<p>최종 점수: ${score}/${questions.length} (${Math.round(score/questions.length*100)}%)</p>
|
| 212 |
+
<button class="btn" onclick="restart()">다시 시작하기</button>
|
| 213 |
+
</div>
|
| 214 |
+
`;
|
| 215 |
+
quiz.appendChild(done);
|
| 216 |
+
return;
|
| 217 |
+
}
|
| 218 |
+
|
| 219 |
+
const q = questions[idx];
|
| 220 |
+
const st = getState(idx);
|
| 221 |
+
|
| 222 |
+
const card = document.createElement('div');
|
| 223 |
+
card.className = 'card';
|
| 224 |
+
|
| 225 |
+
const questionDiv = document.createElement('div');
|
| 226 |
+
questionDiv.className = 'question-text';
|
| 227 |
+
questionDiv.textContent = q.prompt;
|
| 228 |
+
card.appendChild(questionDiv);
|
| 229 |
+
|
| 230 |
+
if (q.choices && q.choices.length) {
|
| 231 |
+
q.choices.forEach((c, i) => {
|
| 232 |
+
const btn = document.createElement('button');
|
| 233 |
+
btn.className = 'choice';
|
| 234 |
+
btn.textContent = (i + 1) + '. ' + c;
|
| 235 |
+
|
| 236 |
+
if (!st.revealed) {
|
| 237 |
+
btn.onclick = () => select(c);
|
| 238 |
+
if (st.selected === c) btn.classList.add('selected');
|
| 239 |
+
} else {
|
| 240 |
+
btn.disabled = true;
|
| 241 |
+
if (normalize(c) === normalize(q.answer)) {
|
| 242 |
+
btn.style.backgroundColor = '#c6f6d5';
|
| 243 |
+
btn.style.borderColor = '#48bb78';
|
| 244 |
+
}
|
| 245 |
+
if (st.selected === c && !st.correct) {
|
| 246 |
+
btn.style.backgroundColor = '#fed7d7';
|
| 247 |
+
btn.style.borderColor = '#f56565';
|
| 248 |
+
}
|
| 249 |
+
}
|
| 250 |
+
card.appendChild(btn);
|
| 251 |
+
});
|
| 252 |
+
}
|
| 253 |
+
|
| 254 |
+
if (st.revealed) {
|
| 255 |
+
const resultDiv = document.createElement('div');
|
| 256 |
+
resultDiv.style.cssText = 'margin-top: 20px; padding: 15px; border-radius: 10px; text-align: center; font-weight: 600;';
|
| 257 |
+
if (st.correct) {
|
| 258 |
+
resultDiv.textContent = '정답입니다! ✅';
|
| 259 |
+
resultDiv.style.backgroundColor = '#c6f6d5';
|
| 260 |
+
resultDiv.style.color = '#22543d';
|
| 261 |
+
} else {
|
| 262 |
+
resultDiv.textContent = '틀렸습니다! ❌';
|
| 263 |
+
resultDiv.style.backgroundColor = '#fed7d7';
|
| 264 |
+
resultDiv.style.color = '#742a2a';
|
| 265 |
+
}
|
| 266 |
+
card.appendChild(resultDiv);
|
| 267 |
+
|
| 268 |
+
const answerDiv = document.createElement('div');
|
| 269 |
+
answerDiv.style.cssText = 'margin-top: 10px; padding: 10px; background: #f7fafc; border-radius: 8px;';
|
| 270 |
+
const answerIndex = q.choices.findIndex(choice => normalize(choice) === normalize(q.answer));
|
| 271 |
+
answerDiv.innerHTML = `<strong>정답:</strong> ${answerIndex + 1}`;
|
| 272 |
+
card.appendChild(answerDiv);
|
| 273 |
+
}
|
| 274 |
+
|
| 275 |
+
quiz.appendChild(card);
|
| 276 |
+
|
| 277 |
+
// 상태 업데이트
|
| 278 |
+
document.getElementById('status').textContent = `문항 ${idx + 1}/${questions.length}`;
|
| 279 |
+
document.getElementById('score-text').textContent = `${score}/${questions.length}`;
|
| 280 |
+
|
| 281 |
+
const prevBtn = document.getElementById('prev');
|
| 282 |
+
const nextBtn = document.getElementById('next');
|
| 283 |
+
prevBtn.disabled = idx === 0;
|
| 284 |
+
nextBtn.textContent = st.revealed ? (idx === questions.length - 1 ? '완료' : '다음 →') : '다음 →';
|
| 285 |
+
}
|
| 286 |
+
|
| 287 |
+
function restart() {
|
| 288 |
+
idx = 0;
|
| 289 |
+
score = 0;
|
| 290 |
+
state.clear();
|
| 291 |
+
finished = false;
|
| 292 |
+
render();
|
| 293 |
+
}
|
| 294 |
+
|
| 295 |
+
// 이벤트 리스너
|
| 296 |
+
document.getElementById('prev').onclick = () => {
|
| 297 |
+
if (finished) return;
|
| 298 |
+
if (idx > 0) {
|
| 299 |
+
idx--;
|
| 300 |
+
render();
|
| 301 |
+
}
|
| 302 |
+
};
|
| 303 |
+
|
| 304 |
+
document.getElementById('next').onclick = () => {
|
| 305 |
+
if (finished) {
|
| 306 |
+
restart();
|
| 307 |
+
return;
|
| 308 |
+
}
|
| 309 |
+
|
| 310 |
+
const st = getState(idx);
|
| 311 |
+
if (!st.revealed) {
|
| 312 |
+
if (st.selected == null) {
|
| 313 |
+
alert('먼저 답을 선택하세요.');
|
| 314 |
+
return;
|
| 315 |
+
}
|
| 316 |
+
st.revealed = true;
|
| 317 |
+
if (st.correct && !st.scored) {
|
| 318 |
+
score++;
|
| 319 |
+
st.scored = true;
|
| 320 |
+
}
|
| 321 |
+
render();
|
| 322 |
+
} else {
|
| 323 |
+
if (idx < questions.length - 1) {
|
| 324 |
+
idx++;
|
| 325 |
+
render();
|
| 326 |
+
} else {
|
| 327 |
+
finished = true;
|
| 328 |
+
render();
|
| 329 |
+
}
|
| 330 |
+
}
|
| 331 |
+
};
|
| 332 |
+
|
| 333 |
+
// 초기 렌더링
|
| 334 |
+
render();
|
| 335 |
+
</script>
|
| 336 |
+
</body>
|
| 337 |
+
</html>
|