[React] 퀴즈 결과 페이지 구현을 위한 점수 계산하기

Minsu·2024년 3월 29일
0

개요


15개의 퀴즈를 모두 푼 후, 퀴즈 결과 페이지로 이동해 전체 퀴즈 중 맞힌 퀴즈의 개수와 해당 결과에 대해 100점 만점 기준으로 점수를 환산해 보여주는 기능에 대한 요구사항이 있었다.

퀴즈는 UI상 한 문제당 한 개의 페이지로 구성되어 있다.

1차 구현


퀴즈 버튼을 누르면, 정답 여부에 따라 세션스토리지 score 값을 새롭게 set되도록 구현했다.
정답이면 +1로 처리되고, 오답이면 현재 값이 유지된다.

const currentScore = Number(sessionStorage.getItem("score"));
    isCorrect
      ? sessionStorage.setItem("score", currentScore + 1)
      : sessionStorage.setItem("score", currentScore);

발생 이슈

기존에 해당 서비스 동작 환경이 웹 키오스크 환경으로 한정되어 있었기 때문에, 브라우저에서 뒤로가기 버튼을 눌러 이전 페이지로 이동 후 퀴즈를 다시 푸는 케이스와 경로를 조작하여 다른 페이지로 이동하는 케이스는 대응하지 않아도 된다고 판단했다. (키오스크 환경에서는 해당 동작들이 불가능하다) 때문에 위와 같이 간단히 개수를 더하는 방식을 채택했다.

그러나 해당 서비스를 기념관 홈페이지 등에서 링크를 통해 접근할 수 있도록 기획이 확장되었고, 때문에 아래 화면과 같이 맞힌 퀴즈 개수가 15개 이상이 되는 현상이 충분히 발생할 수 있게 되면서 해당 케이스 대응이 필요해졌다.

2차 구현


퀴즈 점수를 전역 상태로 관리하기 위해 Context API를 사용했다.
먼저 createContext()를 통해 QuizScoreContext 를 만들었다.

// src > context > QuizScoreContext.js
import { createContext } from "react";

export const QuizScoreContext = createContext({});

App.js 파일에서 QuizScoreContext 의 Provider로 Route들을 감싸고 useState로 생성한 quizScore와 setQuizScore를 value라는 props로 설정했다.

// App.js
function App() {
const [quizScore, setQuizScore] = useState({});

  return (
    <QuizScoreContext.Provider value={{ quizScore, setQuizScore }}>
      // ... Routes ...
    </QuizScoreContext.Provider>
  );
}

아래와 같이 퀴즈 페이지에서 답안 버튼을 누를 때마다 setQuizScore를 통해 quizScore의 값을 변경하고자 했다. 기존에 푼 퀴즈의 정답 여부(true/false)에 해당 퀴즈 정답 여부를 추가해 객체 형태로 저장한다.

// Quiz.jsx
const Quiz2 = () => {
  const { quizScore, setQuizScore } = useContext(QuizScoreContext);

  // ...

  if (value === quizItem.answer) {
    setQuizScore((prevScore) => {
      const newScore = { ...prevScore, [id]: true };
      sessionStorage.setItem('QuizList', JSON.stringify(newScore));
      return newScore;
    });
  } else {
    setQuizScore((prevScore) => {
      const newScore = { ...prevScore, [id]: false };
      sessionStorage.setItem('QuizList', JSON.stringify(newScore));
      return newScore;
    });

    // ...
  }
};

마지막 문제를 풀면 퀴즈 결과 페이지로 이동하고 QuizResult 컴포넌트가 실행되면서 아래와 같이 정답/오답 개수를 파악하고 해당 개수를 통해 백분율로 점수가 계산된다.

//QuizResult.jsx
const QuizResult = () => {
  const quizScore = JSON.parse(sessionStorage.getItem('QuizList'));
  const quizScoreValues = Object.values(quizScore);
  const correctQuizList = quizScoreValues.filter((object) => object === true);
  const correctNum = correctQuizList.length;
  const score = ((correctNum / 15) * 100).toFixed();

  return (
    <div>
      <div id='quiz_result_wrong_num'>
        <p>{15 - correctNum}</p>
      </div>
      <div id='quiz_result_correct_num'>
        <p>{correctNum ? correctNum : 0}</p>
      </div>
      <div className='quiz_result_score'>
        <p>{score}</p>
      </div>
    </div>
  );
};

결과적으로 퀴즈를 풀다가 다른 문제로 이동하여 문제를 다시 풀면 정답 개수가 중복으로 누적되는 형태가 아닌 해당 문제의 정답 여부가 수정되는 형태로 잘 변경되었다.
단, 기획 의도 대로 퀴즈 페이지를 벗어나 메인페이지로 돌아갈 경우엔 quizScore이 0으로 초기화되도록 했다.

발생 이슈

페이지 새로고침 시 컴포넌트가 리렌더링 되면서 quizScore 값이 초기화되는 문제를 발견했다.

새로고침 시 1번 퀴즈 화면으로 되돌아가면 딱히 큰 문제가 되지는 않을 수 있어도, 새로고침한 퀴즈 페이지에 머물기 때문에 해당 페이지부터 다시 쭉 퀴즈를 풀게 되면 이전 문제들에 대한 정답 여부는 반영되지 않은채 퀴즈 점수가 계산된다.

이슈 해결

setQuizScore가 실행될 때마다 세션스토리지에서 정답 여부를 겟하고 quizScore 상태를 헤당 데이터로 변경하는 작업을 추가했다.

useEffect(() => {
    const storedQuizScore = sessionStorage.getItem("QuizList");
    if (storedQuizScore) {
      setQuizScore(JSON.parse(storedQuizScore));
    }
  }, [setQuizScore]);

결과


키오스크 환경 외 웹/모바일 환경에서 퀴즈를 풀 때, 서비스 메인 페이지에서 ‘전시 연계 퀴즈’ 버튼을 누르기 이전까지는 화면을 옮겨다니며 퀴즈를 풀어도 점수가 중복으로 누적되지 않도록 잘 구현되었다.

그러나 글을 쓰면서 살펴보니 보다 최적화되도록 코드를 수정할 필요가 있겠다는 생각이 들었다. 추후 리팩토링하여 추가하도록 하겠다.

profile
지식의 해상도를 높여가는 개발자 (っ'-')🖥️

0개의 댓글

관련 채용 정보