javascript

사이트제작 : CBT유형 제작

IC 2023. 4. 3.

CBT,OMR형식의 퀴즈 사이트를 제작해보았습니다.

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 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>
                <div class="cbt__info">
                    <div>
                        <div class="cbt__title">수험자 : <em></em></div>
                        <div class="cbt__score">
                            <span>전체 문항 : <em class="total__count"></em> 문항</span>
                            <span>남은 문항 : <em class="last__count"></em> 문항</span>
                        </div>
                    </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>

이 코드는 HTML 코드로 이루어진 문제 출제 페이지의 일부분입니다.

페이지에는 2020년 1회 정보처리기능사 시험의 기출문제가 포함되어 있습니다.

페이지는 메인 요소 안에 위치하며, 퀴즈 래퍼(cbt__wrap__quiz) 요소사이드 바(cbt__aside) 요소를 포함합니다.

퀴즈 래퍼(cbt__wrap__quiz) 요소에는 두 개의 문제가 있습니다.

각 문제는 cbt 클래스를 가진 요소로 나타내며, 문제, 이미지, 선택 항목, 문제 설명 및 키워드로 구성됩니다.

사이드 바(cbt__aside) 요소에는 수험자 이름, 전체 문항 수, 남은 문항 수, OMR 요소, 시간 및 제출 버튼이 있습니다.

JavaScript

<script>
    const cbtQuiz = document.querySelector(".cbt__quiz");
    const cbtOmr = document.querySelector(".cbt__omr");
    const cbtSubmit = document.querySelector(".cbt__submit");
    const totalCount = document.querySelector(".total__count");
    const lastCount = document.querySelector(".last__count");

        let questionAll = []; //questionAll이라는 배열을 생성합니다.
        //데이터 가져오기
        const dataQuestion = () => {
            fetch("json/gineungsaWD2005_02.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;
                    }
                    //전체 문항 불러오기
                    totalCount.innerText=items.length;

                    //남은 문항 불러오기
                    lastCount.innerText=items.length;

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

        //문제 만들기
        const updateQuiz = (index) => {
            let questionnumTag = `
                ${cbtQuiz.length}문제중에 ${cbtQuiz.length - index}문제 남았습니다 
            `;
        }
        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 answerQuiz = () => {
            const cbtSelects = document.querySelectorAll(".cbt__selects");
            let remainingCount = 0;

            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("corrent");
                }

                const quizDesc = document.querySelectorAll(".cbt__desc");

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

        const answerSelect = () => {
            const answeredCount = document.querySelectorAll('input[type="radio"]:checked').length;
            const remainingCount = questionAll.length - answeredCount;
            lastCount.innerText = remainingCount;
            return remainingCount;
        };

        

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

    </script>

이 코드는 JSON 파일에서 데이터를 사용하여 퀴즈를 만드는 JavaScript 코드입니다.

const cbtQuizconst cbtOmrconst cbtSubmitconst totalCountconst lastCount 등의 변수들은 HTML 요소를 참조합니다.

dataQuestion함수는 fetch 메소드를 사용하여 JSON 파일에서 데이터를 가져옵니다.

가져온 데이터는 map 메소드를 사용하여 형식이 맞는 새로운 배열로 변환됩니다. 이 때 각 문제의 보기 순서를 무작위로 섞어주는 작업도 수행됩니다.

newQuestion 함수는 questionAll 배열에 저장된 문제를 HTML 요소로 만들어 출력합니다.

answerQuiz함수는 사용자가 선택한 답안과 정답을 비교하여 맞은 개수와 틀린 개수를 계산합니다.

나머지 함수들은 각각 문제 번호를 출력하거나, 사용자의 선택에 따라 다른 작업을 수행하는 등의 역할을 합니다.

 

댓글