오늘은 퀴즈 유형 중 CBT 유형을 만들어보겠습니다!

자바스크립트 퀴즈 이펙트(일곱 번째) 만들기

구조 짜기

<main id="main">
    <div class="quiz__wrap__cbt">
        <div class="cbt__header">
            <h2>2007년 1회 정보처리기능사 기출문제</h2>
        </div>
        <div class="cbt__conts">
            <div class="cbt__quiz">
                <div class="cbt">
                    <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>
        </div>
        <div class="cbt__aside">
            <div class="cbt__info">
                <div>
                    <div class="cbt__title">수험자 : <em>여다슬</em></div>
                    <div class="cbt__score">
                        <span>전체 문제수 : 60문항</span>
                        <span>남은 문제수 : <em>59</em>문항</span>
                    </div>
                </div>
            </div>
            <div class="cbt__omr">
                <div class="omr">
                    <strong>01</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>
        </div>
        <div>
            <div class="cbt__time">59분 10초</div>
            <div class="cbt__submit">제출하기</div>
        </div>
    </div>
</main>

quiz__wrap에 cbt 유형으로 만들어 아예 새롭게 구조를 짜보겠습니다.

quiz__wrap__cbt에 cbt__header, cbt__conts, cbt__aside로 크게 나눠주겠습니다. 

 

CSS는 영역에 맞춰 속성을 설정해줍니다. CSS는 길고 복잡하니 코드는 따로 첨부하지 않겠습니다!

스크립트가 중요하니까요 ㅎㅎ

 

스크립트_선택자 만들기

// 선택자
const cbtQuiz = document.querySelector(".cbt__quiz");
const cbtOmr = document.querySelector(".cbt__omr");
const cbtSubmit = document.querySelector(".cbt__submit");
const cbtScore = document.querySelector(".cbt__score em");

cbtQuiz, cbtOmr, cbtSubmit, cbtScore 이렇게 각각 선택자를 만들어줍니다.

 

스크립트_데이터 가져오기

let questionAll = [];       // 모든 퀴즈 정보 

// 데이터 가져오기
const dataQuestion = () => {
    fetch("json/gineungsaWD2023_01.json")
    .then(res => res.json())
    .then(items => {
        // console.log(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;
            }

            // 문제에 대한 이미지가 있으면 출력
            if(item.hasOwnProperty("question_img")){
                formattedQuestion.questionImg = item.question_img;
            }

            // 해설이 있으면 출력
            if(item.hasOwnProperty("desc")){
                formattedQuestion.desc = item.desc;
            }

            console.log(formattedQuestion);
            return formattedQuestion;
        });
        newQuestion();  // 문제 만들기
    })
    .catch((err) => console.log(err));

    const quizImg = document.querySelectorAll(".cbt__question__img");
        if(quizImg[number].innerText == "img/undefined.png"){
            quizImg[number].classList.add("hide");
        }
}

questionAll에 모든 문제의 데이터를 저장하겠습니다. 문제 정보를 json 파일에 저장시켜 불러오겠습니다.

 

fetch() 메소드는 json/gineungsaWD2023_01.json 파일을 가져옵니다. res.json() 메소드는 가져온 데이터를 json 형식으로 변환합니다. 

 

그리고 then이라는 메소드로 fetch가 완료된 후 가져온 데이터를 처리해줍니다.

 

items.map() 메소드는 items 배열의 각 요소를 변환하여 questionAll 배열에 저장합니다. 그리고 questionAll 배열에 저장하고, formattedQuestion 객체를 생성해줍니다! 그리고 이 객체에 각각 question과 number 프로퍼티를 추가합니다.

 

answerChoices 배열에 item.incorrect_answers 배열을 복사합니다

 

formattedQuestion.answer 프로퍼티에 random()메서드를 통해 랜덤한 숫자를 저장합니다. 이 숫자는 answerChoices 배열의 인덱스와 일치합니다.answerChoices 배열의 formattedQuestion.answer - 1 인덱스 위치에 item.correct_answer 값을 추가합니다.answerChoices 배열을 순회하면서 formattedQuestion["choice" + (index + 1)] 프로퍼티를 생성합니다. 이 프로퍼티는 각 보기의 값을 저장합니다.

 

스크립트_문제 만들기

// 문제 만들기
const newQuestion = () => {
    const exam = [];
    const omr = [];

    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"><img src="img/${question.questionImg}.jpg" alt=""></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="omr${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="omr${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="omr${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="omr${number}_3">
                <label for="omr${number}_4"><span class="label-inner">4</span></label>
            </div>
        `);
    });

    cbtQuiz.innerHTML = exam.join('');
    cbtOmr.innerHTML = omr.join('');

};

const로 exam과 omr 배열을 생성해줍니다.  문제가 여러 개이므로 forEach()메서드를 사용해주겠습니다!

 

push를 이용해 아까의 구조를 ``안에 넣어줍니다. 그리고 innerHTML을 이용해 출력해주겠습니다. 

그리고 각각 보기에 name, value, id를 각각 붙여줘야 문제마다 선택이 되겠죠?

 

스크립트_정답 확인하기

// 정답 확인하기
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();

answerQuiz 함수는 cbtSubmit 버튼을 클릭하면 실행됩니다. 이 함수는 문항 데이터 배열 questionAll을 반복하면서, 사용자가 선택한 답안이 맞는지 검증하고, 결과를 출력합니다.

 

  • cbtSelects 변수는 .cbt__selects 클래스를 가진 DOM 요소들을 모두 선택하여 배열로 저장합니다.
  • questionAll 배열을 반복하면서, 현재 문항 번호 number와 해당 문항 데이터 question을 가져옵니다.
  • cbtSelects 배열에서 현재 문항 번호 number에 해당하는 DOM 요소를 선택하여, 사용자가 선택한 답안(userAnswer)을 가져옵니다.
  • userAnswer 변수는 선택한 답안의 값을 저장하며, 없는 경우(undefined)는 undefined로 유지됩니다.
  • userAnswer를 분석하여 정답 여부를 판단하고, 결과에 따라 해당 문항의 색상을 변경합니다.
  • 만약 선택한 답안이 오답일 경우, 정답을 표시합니다.
  • 문항에 대한 설명이 없는 경우(undefined) 해당 요소의 클래스에 hide 클래스를 추가하고, 그렇지 않은 경우 hide 클래스를 제거합니다.

 

answerSelect 함수는 빈 함수로 정의되어 있으며, 사용자가 문항에서 답안을 선택할 때마다 호출됩니다.

 

dataQuestion 함수는 questionAll 배열에 문항 데이터를 추가하는 함수로, 이전에 정의된 newQuestion 함수를 호출합니다. dataQuestion 함수는 이 코드 조각에서는 보이지 않지만, 아마도 시험 문항 데이터를 로드하는 함수일 것으로 추정됩니다.

 

728x90
반응형
다쭐◠‿◠