문제 풀이 사이트를 cbt 형식으로 만들어 보았습니다.
제출하기 버튼을 클릭하면 채점하고 문제를 풀기 시작하면 시간이 카운팅 되는 기능은 다음시간에 넣을 예정입니다.
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>퀴즈 이펙트07</title>
<link rel="stylesheet" href="css/quiz.css">
<link rel="stylesheet" href="css/reset.css">
<link href="https://unpkg.com/pattern.css" rel="stylesheet">
<!-- 파비콘 -->
<link rel="shortcut icon" type="image/x-icon" href="img/favicon.png"/>
<link rel="apple-touch-icon" sizes="114x114" href="img/favicon.png"/>
<link rel="apple-touch-icon" href="img/favicon.png"/>
</head>
<body>
<header id="header">
<h1><a href="../javascript14.html">Quiz</a> <em>객관식 CBT 유형</em></h1>
<ul>
<li><a href="quizEffect01.html">1</a></li>
<li><a href="quizEffect02.html">2</a></li>
<li><a href="quizEffect03.html">3</a></li>
<li><a href="quizEffect04.html">4</a></li>
<li><a href="quizEffect05.html">5</a></li>
<li><a href="quizEffect05-1.html">5-1</a></li>
<li><a href="quizEffect06.html">6</a></li>
<li class="active"><a href="quizEffect07.html">7</a></li>
<li><a href="quizEffect07-1.html">7-1</a></li>
</ul>
</header>
<!-- //header -->
<main id="main">
<div class="quiz__wrap__cbt">
<div class="cbt__header">
<h2>2015년 4월 4일 웹디자인 기능사 기출문제</h2>
</div>
<div class="cbt__conts">
<div class="cbt__quiz">
<div class="cbt good">
<div class="cbt__question"><span>1. </span>객체지향 프로그램에서 데이터를 추상화하는 단위는 ?</div>
<div class="cbt__question__img"><img src="img/gineungsaJC2023_01_01.jpg" alt="기능사"></div>
<div class="cbt__selects">
<input type="radio" id="select1">
<label for="select1"><span>클래스</span></label>
<input type="radio" id="select2">
<label for="select2"><span>메서드</span></label>
<input type="radio" id="select3">
<label for="select3"><span>상속</span></label>
<input type="radio" id="select4">
<label for="select4"><span>메시지</span></label>
</div>
<div class="cbt__desc">객체지향 언어는 ___입니다.</div>
<div class="cbt__keyword">객체지향언어</div>
</div>
<div class="cbt bad">
<div class="cbt__question"><span>2. </span>다음 빈칸을 채우시오.</div>
<div class="cbt__question__desc">객체지향 언어는 ____ 입니다.객체지향 언어는 ____ 입니다.객체지향 언어는 ____ 입니다.객체지향 언어는 ____ 입니다.객체지향 언어는 ____ 입니다.</div>
<div class="cbt__selects">
<input type="radio" id="select1">
<label for="select1"><span>클래스</span></label>
<input type="radio" id="select2">
<label for="select2"><span>메서드</span></label>
<input type="radio" id="select3">
<label for="select3"><span>상속</span></label>
<input type="radio" id="select4">
<label for="select4"><span>메시지</span></label>
</div>
<div class="cbt__desc">객체지향 언어는 ___입니다.</div>
<div class="cbt__keyword">객체지향언어</div>
</div>
</div>
</div>
<!-- cbt__conts -->
<div class="cbt__aside">
<div class="cbt__info">
<div class="cbt__title">수험자 : <em>장진용</em></div>
<!-- <div class="cbt__score">
<span>전체 문항 : <em>60</em> 문항</span>
<span>남은 문항 : <em>59</em> 문항</span>
</div> -->
</div>
<!-- cbt__info -->
<div class="cbt__omr">
<!-- <div class="omr">
<strong>1</strong>
<input type="radio" id="omr0_1">
<label for="omr0_1">
<span class="label-inner">1</span>
</label>
<input type="radio" id="omr0_2">
<label for="omr0_2">
<span class="label-inner">2</span>
</label>
<input type="radio" id="omr0_3">
<label for="omr0_3">
<span class="label-inner">3</span>
</label>
<input type="radio" id="omr0_4">
<label for="omr0_4">
<span class="label-inner">4</span>
</label>
</div> -->
</div>
<!-- cbt__omr -->
</div>
<!-- cbt__aside -->
<div>
<div class="cbt__time">59분 10초</div>
<div class="cbt__submit">제출하기</div>
</div>
</div>
<!-- quiz__wrap__cbt -->
</main>
</body>
</html>
문제를 푸는 부분과 omr 부분을 나누어 줍니다.
문제의 구조를 문제, 문제 번호, 문제 이미지, 객관식 보기를 각각 태그를 사용해서 구성을 해줍니다.
문제를 풀고 제출하는 영역과 시간이 카운팅 되는 영역을 만들어 줍니다.
css부분에는 각각의 알맞는 속성을 넣어 줍니다.
<script>
// let questionAll = []; //모든 퀴즈 정보
// const dataQuestion = () => {
// fetch("json/EngineerJC2023_01.json") // 파일 부르기
// .then(res => res.json()) // 형식설정
// .then(items => { // 제이슨 파일을 사용하기 편하게 변수에 지정(이름을 만들어 줘야 해서 아이템스라고 만듬)
// // console.log(item);
// questionAll = items.map((item, index) => { //안에 아이템이 여러개가 있으니 foreach 대신 map을 사용합니다.
// const formattedQuestion = { //객체로 만듬.
// question: item.question, //퀘스쳔이라는 키 값을 만들었음.
// num: index + 1, //숫자 넣기
// }
// console.log(formattedQuestion);
// const answerChoices = [...item.incorrect_answers]; //(오답) answerChoices에는 틀린 답을 불러오게 변수선언함. 그리고 안에 있는 요소를 각 한개씩 따로 가져올 것이기 때문에 item.하고 불러올 값을 씀.
// formattedQuestion.answer = Math.floor(Math.random() * answerChoices.length) + 1 //(정답) 정답 랜덤으로 불러오기
// answerChoices.splice(formattedQuestion.answer -1, 0, item.incorrect_answers); //정답, 오답 합치기 (위의 두개)
// answerChoices.forEach((choice, index) => {
// formattedQuestion["choice" + (index + 1)] = choice;
// });
// });
// });
// }
// dataQuestion();
const cbtQuiz = document.querySelector(".cbt__quiz");
const cbtOmr = document.querySelector(".cbt__omr");
const cbtSubmit = document.querySelector(".cbt__submit");
const cbtInfo = document.querySelector(".cbt__info");
let questionAll = []; // 모든 퀴즈 정보
//데이터 가져오기
const dataQuestion = () => {
fetch("json/gisa2020_01.json")
.then(res => res.json())
.then(items => {
questionAll = items.map((item, index) => {
const formattedQuestion ={
question: item.question,
number: index + 1,
}
const answerChoices = [...item.incorrect_answers]; //오답 불러오기
formattedQuestion.answer = Math.floor(Math.random() * answerChoices.length) + 1; //정답을 랜덤으로 불러오기
answerChoices.splice(formattedQuestion.answer - 1, 0, item.correct_answer); //정답을 랜덤으로 추가
//보기를추가
answerChoices.forEach((choice, index) => {
formattedQuestion["choice" + (index+1)] = choice;
});
//문제에 대한 해설이 있으면 출력
if(item.hasOwnProperty("question_desc")){
formattedQuestion.questionDesc = item.question_desc;
} else {
}
//문제에 대한 이미지가 있으면 출력
if(item.hasOwnProperty("question_img")){
formattedQuestion.questionImg = item.question_img;
}
//해설이 있으면 출력
if(item.hasOwnProperty("desc")){
formattedQuestion.desc = item.desc;
}
return formattedQuestion;
});
newQuestion();
})
.catch((err) => console.log(err));
}
//문제 만들기
const newQuestion = () => {
const exam =[];
const omr =[];
const total =[];
questionAll.forEach((question, number) => {
exam.push(`
<div class="cbt">
<div class="cbt__question"><span>${question.number}</span>. ${question.question}</div>
<div class="cbt__question__img"></div>
<div class="cbt__selects">
<input type="radio" id="select${number}_1" name="select${number}" value="${number+1}_1" onclick="answerSelect(this)">
<label for="select${number}_1"><span>${question.choice1}</span></label>
<input type="radio" id="select${number}_2" name="select${number}" value="${number+1}_2" onclick="answerSelect(this)">
<label for="select${number}_2"><span>${question.choice2}</span></label>
<input type="radio" id="select${number}_3" name="select${number}" value="${number+1}_3" onclick="answerSelect(this)">
<label for="select${number}_3"><span>${question.choice3}</span></label>
<input type="radio" id="select${number}_4" name="select${number}" value="${number+1}_4" onclick="answerSelect(this)">
<label for="select${number}_4"><span>${question.choice4}</span></label>
</div>
<div class="cbt__desc hide">${question.desc}</div>
</div>
`);
omr.push(`
<div class="omr">
<strong>${question.number}</strong>
<input type="radio" name="omr${number}" id="omr${number}_1" value="${number}_0">
<label for="omr${number}_1">
<span class="label-inner">1</span>
</label>
<input type="radio" name="omr${number}" id="omr${number}_2" value="${number}_1">
<label for="omr${number}_2">
<span class="label-inner">2</span>
</label>
<input type="radio" name="omr${number}" id="omr${number}_3" value="${number}_2">
<label for="omr${number}_3">
<span class="label-inner">3</span>
</label>
<input type="radio" name="omr${number}" id="omr${number}_4" value="${number}_3">
<label for="omr${number}_4">
<span class="label-inner">4</span>
</label>
</div>
`);
});
total.push(`
<div class="cbt__title">수험자 : <em>장진용</em></div>
<div class="cbt__score">
<span>전체 문항 : <em>${questionAll.length}</em> 문항</span>
</div>
`)
cbtQuiz.innerHTML=exam.join('');
cbtOmr.innerHTML=omr.join('');
cbtInfo.innerHTML=total.join('');
}
// 정답 확인
const answerQuiz = () => {
const cbtSelects = document.querySelectorAll(".cbt__selects");
questionAll.forEach((question, number) => {
const quizSelectsWrap = cbtSelects[number];
const userSelector = `input[name=select${number}]:checked`;
const userAnswer = (quizSelectsWrap.querySelector(userSelector) || {}).value;
const numberAnswer = userAnswer ? userAnswer.slice(-1) : undefined;
if(numberAnswer == question.answer){
console.log("정답")
cbtSelects[number].parentElement.classList.add("good")
} else {
console.log("오답")
cbtSelects[number].parentElement.classList.add("bad")
//오답일 경우 정답 표시
const label = cbtSelects[number].querySelectorAll("label")
label[question.answer-1].classList.add("correct");
}
const quizDesc = document.querySelectorAll(".cbt__desc");
if(quizDesc[number].innerText == "undefined"){
quizDesc[number].classList.add("hide")
} else {
quizDesc[number].classList.remove("hide")
}
});
}
const answerSelect = () => {
}
cbtSubmit.addEventListener("click", answerQuiz);
dataQuestion();
</script>
fetch() 함수를 사용하여 JSON 형식의 데이터 파일을 가져오고, 이를 기반으로 퀴즈 문제를 생성합니다. 가져온 데이터 파일은 "json/gisa2020_01.json"의 경로에 위치하며, 이 파일에는 여러 개의 퀴즈 문제 정보가 포함되어 있습니다. fetch() 함수로 데이터 파일을 가져온 후, then() 메서드를 사용하여 데이터를 처리합니다. 이 때, map() 메서드를 사용하여 모든 퀴즈 정보를 순회하며, 각 퀴즈 문제에 대한 정보를 객체 형태로 생성합니다.
각 퀴즈 문제 객체는 "formattedQuestion" 변수에 할당되며, 다음 정보를 포함합니다.
question: 문제 내용
number: 문제 번호
answer: 정답 번호
choice1~4: 보기 내용
questionDesc: 문제에 대한 해설 (있을 경우)
questionImg: 문제 이미지 (있을 경우)
desc: 문제에 대한 추가 설명 (있을 경우)
answer 프로퍼티에 랜덤한 정수 값을 할당합니다. 이 값은 answerChoices 배열에서 정답의 위치를 나타냅니다.
answerChoices 배열에서 formattedQuestion.answer의 위치에 correct_answer를 추가합니다. 이것은 랜덤하게 선택된 보기들 중에서 정답을 추가하는 것입니다.
forEach 메서드를 사용하여 answerChoices 배열의 각 보기에 대해 formattedQuestion 객체에 choice1, choice2, choice3, choice4 프로퍼티를 추가합니다.
item 객체에 question_desc 프로퍼티가 있으면, formattedQuestion 객체에 questionDesc 프로퍼티를 추가합니다.
item 객체에 question_img 프로퍼티가 있으면, formattedQuestion 객체에 questionImg 프로퍼티를 추가합니다.
item 객체에 desc 프로퍼티가 있으면, formattedQuestion 객체에 desc 프로퍼티를 추가합니다.
formattedQuestion 객체를 questionAll 배열에 추가합니다.
마지막으로 newQuestion 함수를 호출합니다. 이어서 작성된 코드에서 정의되어 있으며, 새로운 퀴즈를 화면에 출력합니다.
// 문제 만들기 부분
먼저, fetch 함수를 이용하여 json 파일에서 문제 정보를 가져온 후, 해당 문제에 대한 정보를 변수 formattedQuestion에 저장합니다. 그리고 오답 정보를 불러와 formattedQuestion 객체에 랜덤한 위치에 정답 정보를 추가합니다.
이후, forEach 함수를 이용하여 모든 문제에 대해 화면에 출력할 문제와 보기를 exam, omr 변수에 각각 저장합니다. 그리고 마지막으로 cbtInfo 변수에는 시험자 이름과 전체 문항 수를 출력하는 코드를 작성합니다.
마지막으로, innerHTML 속성을 이용하여 exam, omr, cbtInfo 변수에 저장된 내용을 HTML 문서에 출력합니다.
// 정답 확인 부분
answerQuiz() 함수: 이 함수는 시험지를 제출할 때 호출됩니다. 이 함수는 선택한 답안을 평가하고, 정답 및 오답에 따라 시험지를 채색하고, 오답인 경우 올바른 답안을 표시합니다.
answerSelect() 함수: 이 함수는 사용자가 선택한 각 답변에 대한 이벤트를 처리합니다.
cbtSubmit 버튼: 사용자가 시험지를 제출하기 위해 클릭하는 버튼입니다.
questionAll 변수: 이 변수는 배열로, 시험지의 모든 문제가 포함됩니다.
cbtSelects 변수: 이 변수는 문제의 선택지를 포함하는 DOM 요소를 참조하는 NodeList입니다.
answerQuiz() 함수에서는 questionAll 배열의 모든 문제를 반복하며, 사용자가 선택한 답변을 찾습니다. 그런 다음, 사용자가 선택한 답변이 올바른지 여부를 확인하고, 맞으면 good 클래스를 추가하고, 틀리면 bad 클래스를 추가합니다. 틀린 경우, 올바른 답변을 표시하기 위해 해당 라벨에 correct 클래스를 추가합니다. 마지막으로, 설명이 제공된 문제의 경우 숨김 클래스를 추가하거나 제거하여 설명을 보이거나 숨깁니다.
마지막으로 cbtSubmit 버튼을 클릭하면 answerQuiz() 함수가 호출되고 시험 문제가 채점됩니다. dataQuestion() 함수는 페이지가 로드될 때 시험 문제를 생성하는 역할을 합니다.