Notice
Recent Posts
Recent Comments
Link
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
Archives
Today
Total
관리 메뉴

Leeyanggoo

[JS] 퀴즈 이펙트 만들기 5!! 정보처리 기능사 문제집 만들기 본문

2023/JavaScript

[JS] 퀴즈 이펙트 만들기 5!! 정보처리 기능사 문제집 만들기

Leeyanggoo 2023. 3. 25. 12:48

 

 

문제 개수가 많을 때 어떻게 해야 할까?

 

이전 포스팅에서 객관식 문제를 만들기 위한 코드를 알아봤습니다.

4개의 보기가 있을 때 객체 속의 배열을 불러오기 위해 "querySelectAll()"과 for문을 사용했습니다.

그렇다면 마치 정보처리 기출 문제집처럼 여러 문제가 한 회에 있는 경우라면 어떻게 해야 할까요?

 

<main id="main">
    <div class="quiz__wrap">
        <div class="quiz">
            <div class="quiz__header">
                <h2 class="quiz__title"></h2>
            </div>
            <div class="quiz__mian">
                <div class="quiz__question">
                    <em></em>. <span></span>
                </div>
                <div class="quiz__view">
                    <div class="dog__wrap">
                        <div class="true">정답입니다!</div>
                        <div class="false">땡!</div>
                        <div class="card-container">
                            <div class="dog">
                                <div class="head">
                                    <div class="ears"></div>
                                    <div class="face"></div>
                                    <div class="eyes">
                                        <div class="teardrop"></div>
                                    </div>
                                    <div class="nose"></div>
                                    <div class="mouth">
                                        <div class="tongue"></div>
                                    </div>
                                    <div class="chin"></div>
                                </div>
                                <div class="body">
                                    <div class="tail"></div>
                                    <div class="legs"></div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
                <div class="quiz__answer">
                    <input class="input" type="text" placeholder="정답을 입력해주세요.">
                    <button class="confirm">정답 확인하기</button>
                    <div class="result"></div>
                </div>
                <div class="quiz__desc">설명</div>
            </div>
        </div>
        <div class="quiz">
            <div class="quiz__header">
                <h2 class="quiz__title"></h2>
            </div>
            <div class="quiz__mian">
                <div class="quiz__question">
                    <em></em>. <span></span>
                </div>
                <div class="quiz__view">
                    <div class="dog__wrap">
                        <div class="true">정답입니다!</div>
                        <div class="false">땡!</div>
                        <div class="card-container">
                            <div class="dog">
                                <div class="head">
                                    <div class="ears"></div>
                                    <div class="face"></div>
                                    <div class="eyes">
                                        <div class="teardrop"></div>
                                    </div>
                                    <div class="nose"></div>
                                    <div class="mouth">
                                        <div class="tongue"></div>
                                    </div>
                                    <div class="chin"></div>
                                </div>
                                <div class="body">
                                    <div class="tail"></div>
                                    <div class="legs"></div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
                <div class="quiz__answer">
                    <input class="input" type="text" placeholder="정답을 입력해주세요.">
                    <button class="confirm">정답 확인하기</button>
                    <div class="result"></div>
                </div>
                <div class="quiz__desc">설명</div>
            </div>
        </div>
        <div class="quiz">
            <div class="quiz__header">
                <h2 class="quiz__title"></h2>
            </div>
            <div class="quiz__mian">
                <div class="quiz__question">
                    <em></em>. <span></span>
                </div>
                <div class="quiz__view">
                    <div class="dog__wrap">
                        <div class="true">정답입니다!</div>
                        <div class="false">땡!</div>
                        <div class="card-container">
                            <div class="dog">
                                <div class="head">
                                    <div class="ears"></div>
                                    <div class="face"></div>
                                    <div class="eyes">
                                        <div class="teardrop"></div>
                                    </div>
                                    <div class="nose"></div>
                                    <div class="mouth">
                                        <div class="tongue"></div>
                                    </div>
                                    <div class="chin"></div>
                                </div>
                                <div class="body">
                                    <div class="tail"></div>
                                    <div class="legs"></div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
                <div class="quiz__answer">
                    <input class="input" type="text" placeholder="정답을 입력해주세요.">
                    <button class="confirm">정답 확인하기</button>
                    <div class="result"></div>
                </div>
            </div>
            <div class="quiz__desc">설명</div>
        </div>
    </div>
</main>

 

이전에 이펙트 세 번째에서 주관식이 세 문제가 있는 HTML 코드를 가져왔습니다.

문제 3개에 이렇게 많은 코드가 나열된다면, 문제가 60개 있으면  어떻게 될까요?

이 코드를 20배 써야 한다면 알아보기도 힘들고 적기도 힘들지 않을까요?

 

우리는 여러 보기가 있는 객관식 문제를 만들면서 객체 속에 배열의 형태로 저장을 하고 불러왔습니다.

그렇다면 문제를 담는 HTML 코드도 그렇게 객체 속에 넣어서 문제를 대입한다면 좀 더 코드를 효율적으로 쓸 수 있지 않겠습니까!

 

<main id="main">
    <div class="quiz__wrap">
    </div>
</main>

<script>
    // 문제 정보
    const quizInfo = [
        {
            infoType: "정보처리기능사",
            infoTime: "2006년 1회",
            infoNumber: "20060101",
            infoQuestion: "프로그램들이 기억장치 내의 임의의 장소에 적재될 수 있도록 조정하는 작업을 재배치(Relocation)라 하는데 이 기능을 수행하는 재배치 로더(Loader)의 역할이 아닌 것은?",
            infoChoice: {
                1 : "기억 장소 할당",                    
                2 : "목적 프로그램의 기호적 호출 연결",
                3 : "원시 프로그램을 읽어서 명령어를 해석",
                4 : "기계어 명령들을 기억장치에 적재"
            },
            infoAnswer: "3",
            infoDesc: "원시 프로그램을 읽어서 명령어를 해석하는것은 인터프리터나 컴파일러가 하는 일 입니다.."
        },
        {
            infoType: "정보처리기능사",
            infoTime: "2006년 1회",
            infoNumber: "20060102",
            infoQuestion: "기억장치에 액세스(Access)할 필요 없이 스택(Stack)을 이용하여 연산을 행하는 명령어 형식은?",
            infoChoice: {
                1 : "0-주소 명령어",                    
                2 : "1-주소 명령어",
                3 : "2-주소 명령어",
                4 : "3-주소 명령어"
            },
            infoAnswer: "1",
            infoDesc: "스택은 0 주소입니다."
        },
        //... 60문제의 정보 담았음.
    ] 

    //선택자
    const quizWrap = document.querySelector(".quiz__wrap");
    let quizScore = 0;

 

먼저 과감하게 "quiz__wrap"에 담겨있던 문제의 HTML을 잘라내고 "quiz__wrap"을 선택자로 변수 "quizWrap"에 할당했습니다.

그리고 quizInfo에 우리가 출제할 문제들을 모두 담았습니다. 문제 정보는 너무 코드가 길어서 생략했습니다!

quizScore는 이후에 사용자가 문제를 얼마나 맞췄는지 저장하기 위해 선언했습니다.

 

push()와 join()으로 배열에 문제를 입력하자!

 

    //문제 출력
    const updateQuiz = () => {
        const exam = [];

        quizInfo.forEach((question, number)=>{
            exam.push(`
            <div class="quiz">
                <div class="quiz__header">
                    <h2 class="quiz__title">${question.infoType} ${question.infoTime}</h2>
                </div>
                <div class="quiz__mian">
                    <div class="quiz__question"><em>${number+1}</em>. ${question.infoQuestion}</div>
                    <div class="quiz__view">
                        <div class="dog__wrap">
                            <div class="true">정답입니다!</div>
                            <div class="false">땡!</div>
                            <div class="card-container">
                                <div class="dog">
                                    <div class="head">
                                        <div class="ears"></div>
                                        <div class="face"></div>
                                        <div class="eyes">
                                            <div class="teardrop"></div>
                                        </div>
                                        <div class="nose"></div>
                                        <div class="mouth">
                                            <div class="tongue"></div>
                                        </div>
                                        <div class="chin"></div>
                                    </div>
                                    <div class="body">
                                        <div class="tail"></div>
                                        <div class="legs"></div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                    <div class="quiz__choice">
                        <label for="choice1${number}">
                            <input type="radio" id="choice1${number}" name="choice${number}" value="1">
                            <span>${question.infoChoice[1]}</span>
                        </label>
                        <label for="choice2${number}">
                            <input type="radio" id="choice2${number}" name="choice${number}" value="2">
                            <span>${question.infoChoice[2]}</span>
                        </label>
                        <label for="choice3${number}">
                            <input type="radio" id="choice3${number}" name="choice${number}" value="3">
                            <span>${question.infoChoice[3]}</span>
                        </label>
                        <label for="choice4${number}">
                            <input type="radio" id="choice4${number}" name="choice${number}" value="4">
                            <span>${question.infoChoice[4]}</span>
                        </label>
                    </div>
                    <div class="quiz__desc">정답은 <em>${question.infoAnswer}</em>번 입니다.<br>${question.infoDesc}</div>
                </div>
            </div>`);
        });

        exam.push(`
            <div class="quiz__info">?</div>
            <div class="quiz__check">정답 확인</div>
        `);

        quizWrap.innerHTML = exam.join('');

        //설명 숨기기
        document.querySelectorAll(".quiz__desc").forEach(e=>e.style.display="none");

    }

    updateQuiz();

 

함수 updateQuiz()를 만들고 그 안에 "const exam = []"으로 변수 exam에 배열 선언을 했습니다.

퀴즈 정보를 넣어야 하기 때문에 quizInfo에 forEach() 메서드로 각각 요소(question)와 순서(문제 번호가 되는 number)를 가져오기로 했습니다.

그리고 exam.push()를 이용해 exam 배열 안에 문제 정보를 넣을 겁니다!

push() 메서드는 배열 끝에 요소를 추가하고, 배열의 새로운 길이값(배열 인덱스)을 반환합니다.

따라서 우리는 일일이 문제의 코드를 적지 않고도 quiz__wrap에 문제 코드를 넣을 수 있게 됩니다.

 

또한 들어간 "quiz" class에 quizInfo의 요소인 qeustion의 여러 문제 정보를 문제 코드에 넣음으로써 더욱 효율적으로 코드 관리를 할 수 있습니다.

각각에 쓰인 리터럴 "${}"의 내용은 모두 quizInfo의 정보들입니다.

 

이제 배열 안에 문제 정보를 모두 넣었으니 HTML의 "quiz__wrap"에 코드를 추가해야 합니다.

우리는 quizwrap.innerHTML = exam.join('')을 이용해서 HTML 코드를 문자열로 추가하고 있습니다.

join()배열 요소를 문자열로 결합하고, 문자열로 반환하는 메서드입니다.

따라서 우리가 입력한 문제 정보가 모두 quiz__wrap에 들어가게 됩니다.

 

Math.ceil() 메서드로 점수까지 표현하자!

 

    //정답 확인
    const answerQuiz = () => {
        const quizChoices = document.querySelectorAll(".quiz__choice");

        //사용자가 체크한 정답 == 문제 정답
        quizInfo.forEach((question, number)=>{
            const userSelector = `input[name=choice${number}]:checked`;
            const quizSelectorWrap = quizChoices[number];
            const userAnswer = (quizSelectorWrap.querySelector(userSelector)||{}).value;
            const dogWrap = quizWrap.querySelectorAll(".dog__wrap");       //갱얼쥐

            if(userAnswer == question.infoAnswer){
                dogWrap[number].classList.add("like");
                quizScore++;
            } else {
                dogWrap[number].classList.add("dislike");
            }
        });
        //설명 보이기
        document.querySelectorAll(".quiz__desc").forEach(e=>e.style.display="block");

        //점수 보이기
        document.querySelector(".quiz__info").innerHTML = Math.ceil((quizScore / quizInfo.length) * 100) + "점"
    }

    //정답 클릭
    document.querySelector(".quiz__check").addEventListener("click", answerQuiz);

 

이제 문제를 모두 입력했으니 웹 페이지에 출력할 일만 남았습니다!

문제 출력은 이전의 방식과 동일하지만 이번엔 사용자가 문제를 풀면 점수까지 표현하는 기능을 추가했습니다!

스크립트 선택자 부분에 "let quizScore = 0"을 선언했던 거 기억하시나요?

변수 "quizScore"는 if문에서 사용자가 정답을 맞춘 경우(userAnswer == question.infoAnswer) quizSore++;로 인해 1씩 증가하고 있습니다.

우리는 document.querySelector(".quiz__info").innerHTML = Math.ceil((quizScore / quizInfo.length) * 100) + "점"의 코드를 작성했는데요.

이 코드는 "quiz__info"에 계산한 점수를 "innerHTML"로 입력하겠다는 의미입니다.

Math.ceil() 메서드는 매개변수로 전달된 숫자를 올림한 값을 반환합니다.

즉, 소수점 이하를 모두 올림하여 정수로 반환하는 메서드입니다.

(quizScore / quizInfo.length) * 100)로 문제 개수 대비 사용자가 맞춘 문제의 개수를 나누고 100을 곱해서 평균 점수를 구하고 출력하는 것입니다.

 

이제 사용자는 문제를 모두 풀고 우측 하단의 "정답 확인"을 누르면 점수도 알 수 있고, 해답도 볼 수 있겠죠? ㅎㅎ


😮‍💨 이전 포스팅 보러 가기

😮‍💨 이번 예제 보러 가기

😮‍💨 더 다양한 퀴즈 이펙트 보러 가기

😮‍💨 codepen design by David López