이벤트 위임 패턴으로 최적화한 자바스크립트 OX 퀴즈 게임

웹 개발 공부를 시작하면서 가장 먼저 마주하는 난관 중 하나가 “내가 배운 문법을 어떻게 실제 기능으로 구현할 것인가?”입니다. 변수, 함수, 조건문은 익혔지만 이를 조합해 하나의 ‘서비스’를 만드는 경험은 또 다른 영역이죠.

오늘 함께 만들어볼 ‘자바스크립트 OX 퀴즈‘는 자바스크립트의 핵심 개념들을 골고루 실습해볼 수 있는 아주 좋은 프로젝트입니다. 화면에 문제를 띄우는 DOM 조작부터, 정답 여부를 판별하는 조건문, 그리고 사용자의 긴장감을 유발하는 타이머(Timer API) 기능까지 한 번에 학습할 수 있습니다.

특히 이번 프로젝트에서는 단순히 정답만 맞히는 것이 아니라, 5초라는 제한 시간을 두어 시각적으로 시간이 줄어드는 ‘타이머 바’ 애니메이션까지 추가하여 구현해 보겠습니다.

이벤트 처리 방식은 [자바스크립트 사다리 타기]의 클릭 로직과 비교해 보면 이해가 빠릅니다.


자바스크립트 OX 퀴즈 만들기 메인 화면

1. 설계

코드를 작성하기 전, 우리가 만들 프로그램의 로직을 머릿속으로 그려보는 과정이 필요합니다.

  • 데이터 관리: 퀴즈 문제와 정답 정보를 담고 있는 객체 배열이 필요합니다.
  • 화면 구성: 퀴즈가 진행되는 화면과 결과가 출력되는 화면을 각각 설계하고 상황에 따라 전환해줍니다.
  • 시간 제한: 문제마다 일정 시간을 부여하고, 시간이 흐를수록 줄어드는 게이지를 시각적으로 보여줍니다.
  • 점수 계산: 사용자가 클릭한 답과 실제 정답을 비교해 점수를 누적합니다.

2. 자바스크립트 OX 퀴즈 HTML & CSS

전체적인 외형은 심플하고 깔끔하게 구성하였습니다.
웹 서비스에서 사용자 경험(UX)은 매우 중요합니다.
모바일에서도 편하게 퀴즈를 풀 수 있도록 반응형 레이아웃을 구성했습니다.
CSS 변수(:root)를 사용하여 주요 색상을 관리하도록 하였습니다.

<div class="quiz-container">
    <div class="timer-wrapper">
        <div id="timer-bar"></div>
    </div>

    <div id="quiz-screen">
        <div class="quiz-header">
            <h2 id="question-number">Question 1</h2>
        </div>
        <div id="question-text">준비중...</div>
        <div class="options">
            <button class="btn btn-o" onclick="handleAnswer(true)">O</button>
            <button class="btn btn-x" onclick="handleAnswer(false)">X</button>
        </div>
    </div>

    <div id="result-screen" class="hidden">
        <div class="result-icon">🏆</div>
        <h3>퀴즈 종료!</h3>
        <p id="score-text"></p>
        <button class="btn btn-retry" onclick="resetQuiz()">다시 시작하기</button>
    </div>
</div>

3. 자바스크립트 OX 퀴즈 코드 분석

이제 가장 중요한 자바스크립트 로직을 살펴보겠습니다. 이 코드의 핵심은 “시간을 어떻게 정밀하게 계산하는가”“화면을 어떻게 갱신하는가”에 있습니다.

① 정밀한 타이머 구현 (Date.now() 활용)

일반적으로 setInterval을 사용해 시간을 깎아내려가지만, 자바스크립트의 이벤트 루프 특성상 미세한 오차가 발생할 수 있습니다. 그래서 이번 코드에서는 Date.now()를 활용해 시작 시점과 현재 시점의 차이(elapsedTime)를 계산했습니다. 이렇게 하면 브라우저의 성능 상황과 관계없이 항상 정확하게 5초 뒤에 타이머가 종료됩니다.

const elapsedTime = Date.now() - startTime;
const remaining = Math.max(0, 100 - (elapsedTime / TIME_LIMIT) * 100);
  • 대부분 타이머를 만들 때 단순히 setInterval만 사용하여 시간을 1초씩 깎는 방식을 선택하곤 합니다. 하지만 자바스크립트의 엔진은 싱글 스레드로 동작하기 때문에, 다른 복잡한 연산이 실행 중일 경우 setInterval의 호출이 지연될 수 있습니다. 이번 프로젝트에서 제가 Date.now()를 활용해 실제 경과 시간을 계산한 이유가 바로 여기에 있습니다. 시작 시간과 현재 시간을 비교하는 방식은 브라우저에 부하가 걸리더라도 정확한 ‘절대 시간’을 반영하므로, 사용자에게 훨씬 더 정밀하고 끊김 없는 타이머 경험을 제공할 수 있습니다.

② 사용자 피드백: 시각적 긴박감 조성

시간이 70% 이상 남았을 때는 안정감을 주는 오렌지색을 유지하다가, 30% 미만으로 떨어지는 순간 강렬한 빨간색으로 변하게 설정했습니다. 이러한 시각적 피드백은 텍스트로 ‘시간이 얼마 남지 않았습니다’라고 알려주는 것보다 훨씬 강력하게 사용자의 긴장감을 유발하고 게임에 몰입하게 만듭니다.

③ 결과 화면 전환 (Hidden 클래스)

워드프레스나 최신 웹 프레임워크에서 자주 쓰이는 방식인 ‘클래스 제어’를 통해 화면을 전환합니다. hidden이라는 클래스를 추가하거나 제거함으로써 HTML 구조를 바꾸지 않고도 매끄럽게 결과 화면으로 이동할 수 있습니다.

④ 퀴즈 데이터 구조

문제 데이터는 객체 배열로 관리합니다. 문제와 정답의 의미가 분리되어 가독성이 좋고, 추후 문제를 서버나 JSON 파일에서 불러오기 쉽습니다.

const quizData = [
 { question: "자바스크립트는 브라우저에서만 동작한다.", answer: false },
 { question: "JSON.stringify는 객체를 문자열로 변환한다.", answer: true }
];

4. 확장 아이디어

  • 문제 랜덤 섞기: 퀴즈 데이터의 순서를 섞어주는 sort() 로직을 추가하면 사용자가 게임을 재시작할 때마다 새로운 느낌을 줄 수 있습니다.
  • 난이도 조절: 문제 번호가 올라갈수록 TIME_LIMIT 값을 줄여서 난이도를 높이는 코드를 구현해볼 수도 있습니다.
  • 로컬 스토리지 활용: 사용자의 최고 점수를 브라우저에 저장해두고, 다음 방문 때 보여준다면 훨씬 ‘프로그램’다운 면모를 갖추게 됩니다.
  • 서버 연동: 지금은 문제 데이터를 코드 내부에 하드코딩하여 추가해주었지만, 서버에서 관리하면 더욱 완벽한 퀴즈 페이지를 만들 수 있습니다.

5. 자바스크립트 OX 퀴즈 실행 영상


오늘 만든 OX 퀴즈는 자바스크립트에서 중요한 요소를 배우기에 좋은 프로젝트 중 하나입니다. 배열과 객체, 이벤트 처리, 시간 제어, UI 등 코드를 한 줄씩 타이핑해보면서, 각 함수가 어떤 역할을 하는지 고민하다보면 금방 익숙해 질 수 있을 것입니다.

문제마다 이벤트 리스너를 일일이 달지 않고, 부모 요소에서 한 번에 관리하는 ‘이벤트 위임’ 방식을 적용해 성능을 높였습니다. 코드가 훨씬 간결해지는 짜릿함을 느꼈던 프로젝트입니다.

혹시 코드를 실행하는 중에 오류가 발생하거나, 추가하고 싶은 기능이 있다면 댓글로 남겨주세요.

댓글 남기기