예제/퀴즈 이펙트

퀴즈 이펙트_여러문제를 풀 수 있도록 만들어 봅시다!(JSON)

아라라_ 2023. 4. 3. 23:48

“ 지연되는 프로젝트에 인력을 더 투입하면 오히려 더 늦어진다. ”

Frederick Philips Brooks
Mythical Man-Month 저자
728x90

HTML

 <main id="main">
        <div class="quiz__wrap__cbt">
            <div class="cbt__header">
                <h2>2020년 1회 정보처리기능사 기출문제</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>
            </div>
            <div class="cbt__aside">
                <div class="cbt__info">
                    <div>
                        <div class="cbt__tittle">수험자 : <em>노지영</em></div>
                        <div class="cbt__score">
                            <span class="quiz__sum">전체 문제수 : <em></em> 문항</span>
                            <span class="quiz__remain">남은 문제수 : <em></em>문항</span>
                        </div>
                    </div>
                </div>             
                <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>
            </div>
            <div class="time_submit">
                <div class="cbt__submit">제출하기</div>
                <div class="cbt__time">59분 10초</div>
            </div>
        </div>
    </main>
    <!-- // main -->

    

    <!-- <footer id="footer">
        <a href="mailto:jo0132@naver.com">jo0132@naver.com</a>
    </footer> -->
    <!-- // footer -->

<div> 태그를 사용하여, 퀴즈의 틀을 만드는데 사용됩니다. 여기서 퀴즈의 제목, 문제, 보기, OMR 카드 등이 들어갑니다.
<input> 태그를 사용하여, 객관식 문제의 보기를 만듭니다. type="radio" 속성을 사용하여, 라디오 버튼 형태로 만듭니다. 객관식 문제에서는 보기 중 하나만 선택할 수 있기 때문에, 라디오 버튼이 사용됩니다.
그리고 마지막으로 제출하기 버튼과 시간을 나타내는 부분이 들어갑니다.

 

SCRIPT

<script>
    const cbt = document.querySelectorAll(".cbt");
    const cbtQuiz = document.querySelector(".cbt__quiz");
    const cbtOmr = document.querySelector(".cbt__omr");
    const cbtSubmit = document.querySelector(".cbt__submit");

    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;
                }

                //문제에 대한 이미지가 있으면 출력
                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 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"></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>
            `)

        });

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

        //남은 문제
        const quizSum = document.querySelector(".cbt__score .quiz__sum em")

        quizSum.innerHTML= exam.length;

    }

    // const quizRemain = document.querySelector(".cbt__score .quiz__remain em")
    // quizRemain.innerHTML= exam.length- ;


    //정답 확인
    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 label = cbtselects[number].querySelectorAll("label");
                // label[question.answer-1].classList.add("correct");
            }
            const quizDesc = document.querySelectorAll(".cbt__desc");

            if(quizDesc[number].innerHTML == "undefined"){
                quizDesc[number].classList.add("hide")
            }else{
                quizDesc[number].classList.remove("hide")
            }

        });
    }

    const answerSelect = () => {

    }


    cbtSubmit.addEventListener("click", answerQuiz);
    dataQuestion();

</script>

우선const 를 사용하여 변수 cbt, cbtQuiz, cbtOmr, cbtSubmit를 선언합니다. 이 변수들은 이후 DOM 요소를 참조하는 데 사용됩니다.
let 키워드를 사용하여 배열 questionAll을 선언합니다. 이 배열은 모든 퀴즈 정보를 저장하는 데 사용됩니다.
fetch() 메서드를 사용하여 JSON 파일에서 데이터를 가져옵니다. 이 데이터에는 각각의 퀴즈의 정보가 포함되어 있습니다.
map() 메서드를 사용하여 questionAll 배열을 만듭니다. 이 배열은 각 퀴즈의 질문, 보기, 정답, 해설 등의 정보를 담습니다.
newQuestion() 함수를 호출하여 문제를 만듭니다. 이 함수는 questionAll 배열을 사용하여 HTML 코드를 동적으로 생성합니다.
answerSelect() 함수가 라디오 버튼 클릭 이벤트에 할당되어 있습니다. 이 함수는 라디오 버튼의 값이 변경될 때마다 실행되며, 선택한 정답을 저장합니다.
HTML 코드의 일부가 동적으로 생성됩니다. 이 코드는 cbtQuiz 요소와 cbtOmr 요소에 삽입됩니다.
코드에서 사용된 fetch() 메서드는 비동기적으로 데이터를 가져오기 때문에 프로미스 객체를 반환합니다. 이 프로미스 객체는 then() 메서드를 사용하여 데이터를 처리합니다. then() 메서드는 콜백 함수를 인자로 받아 데이터를 처리합니다. catch() 메서드는 데이터를 가져오는 중에 에러가 발생할 경우 실행됩니다.

dataQuestion() 함수는 exam 배열에서 문제들을 가져와서 HTML 요소를 생성하고, 이를 문서 객체 모델(DOM)에 추가합니다. 각 문제는 제목, 선택지, 정답, 설명 등의 정보를 가지고 있습니다.
answerQuiz() 함수는 사용자가 선택한 답안과 실제 정답을 비교하여 결과를 출력하고, 문제별로 정답 여부에 따라 해당 HTML 요소에 CSS 클래스를 추가합니다. 또한 문제 설명이 있는 경우, 이를 숨기거나 표시합니다.
answerSelect() 함수는 사용자가 답안을 선택할 때마다 실행되어 선택한 답안을 기록합니다.
마지막으로 cbtSubmit 버튼에 이벤트 리스너를 등록하여 사용자가 제출 버튼을 누를 때 answerQuiz() 함수가 실행되도록 합니다.