기초부터 시작하는 코딩/Javascript

Javascript를 이용한 사이트 만들기! - 퀴즈 효과 사이트 만들기07

kebab00 2023. 4. 3. 22:45

728x90

- 오늘도 퀴즈에 효과를 주는 사이트를 만들어 보았습니다.

- 이번에 만든 퀴즈효과는 cbt 같은 느낌으로 만들었습니다.

- 일단 html부분을 먼저 보겠습니다.

<main id="main">
    <div class="quiz__wrap__cbt">
        <div class="cbt__header">
            <h2>2020년 1회 정보처리기능사 기출문제</h2>
        </div>
        <div class="ccc">
            <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/gineungsaWD2023_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="bbb">
                    <div class="cbt__time">59분 10초</div>
                    <div class="cbt__sumit">제출하기</div>
                </div>
                <div class="aaa">
                    <div class="cbt__info">
                        <div>
                            <div class="cbt__title">수험자 : <em></em></div>
                            <div class="cbt__score">
                                <!-- <span>전체 문제수 : 60문항</span> -->
                                <!-- <span>남은 문제수 : <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>
        </div>
    </div>
</main>

- 주석처리가 되어 있는 부분은 반복되는 부분들로 스크립트부분에서 반복문을 통해서 문제의 갯수만큼 반복시켜 줄 부분 이거나 계속 바뀌는 데이터로 스크립트를 통해 처리할 부분 입니다.

- 크게 특별한 부분은 없습니다 스크립트를 보시죠

<script>
    const cbtQuiz = document.querySelector(".cbt__quiz");
    const cbtOmr = document.querySelector(".cbt__omr");
    const cbtSumit = document.querySelector(".cbt__sumit");
    const cbtScore = document.querySelector(".cbt__score");

    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 = [];
        const score = [];
        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"="select1"><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"="select2"><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"="select3"><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"="select4"><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>
            `)
        });
        score.push(`

            <span>전체 문제 수 : <em>${questionAll.length}</em>문항</span>
            <span>남은 문제 수 : <em>${questionAll.length}</em>문항</span>
            `)
        cbtQuiz.innerHTML = exam.join(``);
        cbtOmr.innerHTML = omr.join(``);
        cbtScore.innerHTML = score.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){
                cbtSelects[number].parentElement.classList.add("good");
            } else {
                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 = () => {

    }
    cbtSumit.addEventListener("click",answerQuiz)
    dataQuestion();
</script>

- push로 html를 넣어주고 정답을 가렸다가 나왔다가 하는 부분은 자주했던 부분이니 json파일을 불러오는 부분을 보겠습니다.

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

- dataQuestion라는 함수를 하나 만들어 줍니다.

- 그 후 fetch("json/gisa2020_01.json")를 통해서 gisa2020_01.json 파일을 불러와주고

- .then(res => res.json())라는 문법을 사용해 줍니다. 잘은 모르지만 사용해 주면 gisa2020_01.json안에 있는 데이터를 가져와 사용할 수 있습니다.

- 그리고 questionAll 이라는 빈 배열를 저장한 변수에 items.map((item, index) => const formattedQuestion = { question: item.question, number: index + 1 } 를 사용해서 문제와 번호를 배열로 저장해줍니다.

- answerChoices라는 변수에 일단 오답 3개를 넣은 후 0~3의 랜덤 값을 만들어서 랜덤값과 splice를 통해 문제가 선택지가 섞이도록 해주었습니다(약간의 오류가 있습니다만...)

- 그리고 다음은 위에서 만든 보기도 추가해 주고(정답1,오답3)

- 값이 있기도 하고 없기도 한 부분들은 if문과 hasOwnProperty를 이용하여 값이 있을 때 formattedQuestion라는 변수 안에 각각의 이름으로 저장을 해주었습니다. 없다면 저장이 되지 않아 불러오게 된다면 undefined가 나옵니다.

- hasOwnProperty()는 객체가 특정 프로퍼티를 가지고 있는지를 불린 값으로 반환하는 메서드 입니다.

- 이렇게 json 형식의 파일을 내가 원하는 데이터만 잘 가지고 와서 원하는 형식으로 만들어 주는 것이 이번 효과의 가장 중요한 부분입니다.

- 끝!