분류 Reactjs

React를 사용하여 퀴즈 앱을 빌드 하는 방법 – 팁 및 시작 코드 포함

컨텐츠 정보

  • 조회 6 (작성일 )

본문

이 초급 React 튜토리얼에서는 퀴즈 앱을 만들 것입니다. 

복잡한 상태 객체, 다양한 상태 후크를 처리하는 방법, 상태에 따라 렌더링 하는 방법에 대해 알아 보겠습니다.


https://www.freecodecamp.org/news/how-to-build-a-quiz-app-using-react/


project.gif 


직접 시도 


먼저 직접 해보고 싶다면 다음과 같은 시나리오가 있습니다 (아래에서 시작 코드를 가져올 수도 있음).


  • 사용자가 버튼을 클릭하면 다음 질문이 표시되어야 합니다.
  • 사용자가 질문을 맞히면 점수가 올라갑니다.
  • 사용자가 퀴즈를 마치면 총 점수가 표시되어야 합니다.

비디오 연습 


스타터 코드 

여기 GitHub에서 확인하세요.


Let's go! 


시작 코드를 열고 App.js로 이동하면 질문이라는 배열로 저장된 질문/답변 목록을 제공했음을 알 수 있습니다. 이것은 우리의 퀴즈입니다.


첫 번째 목표는 배열에서 질문 데이터를 가져 와서 화면에 표시하는 것입니다.


하드 코딩 된 텍스트를 제거하고 작업을 진행하기 위해 지금은 첫 번째 질문의 데이터를 가져 오겠습니다. 나중에 질문 전환에 대해 걱정할 것입니다.


JSX에서 하드 코딩 된 질문 텍스트를 제거하고 {questions[0]}을 입력하여 질문 배열의 첫 번째 항목 (또는 질문)을 가져옵니다.


<div className='question-text'>{questions[0]}</div>

질문 및 답변 렌더링 


첫 번째 질문은 객체이므로 "점 표기법"을 사용하여 속성에 액세스 할 수 있습니다. 이제 {question[0].questionText}를 수행하여 이 개체의 질문 텍스트에 액세스합니다.


<div className='question-text'>{questions[0].questionText}</div>

앱을 저장하고 실행합니다. 텍스트가 어떻게 업데이트 되는지 확인하십시오. 질문 배열의 첫 번째 개체에서 첫 번째 질문 텍스트를 가져 오는 것임을 기억하십시오.


답변 옵션에 대해서도 유사한 접근 방식을 취하겠습니다. 하드 코딩 된 버튼을 제거하면 map 함수를 사용하여 주어진 질문에 대한 답변 옵션을 반복합니다.


map 함수가 배열을 반복하고 루프가 현재 있는 현재 항목을 변수 형태로 제공한다는 것을 기억하십시오.


"answer-section"div를 다음으로 바꿉니다.


<div className='answer-section'>
	{questions[0].answerOptions.map((answerOption, index) => (
		<button>{answerOption.answerText}</button>
	))}
</div>

앱을 저장하고 실행합니다. 네 개의 대답 버튼이 어떻게 나타나고 텍스트가 동적으로 렌더링 되는지 확인하십시오.


요약하자 :


  • 질문 배열에서 첫 번째 질문을 얻습니다. questions[0]
  • 첫 번째 질문은 answerOptions 배열을 포함하는 객체입니다. 점 표기법을 사용하여 이 배열을 얻을 수 있습니다. questions[0].answerOptions
  • answerOptions는 배열이기 때문에, questions[0].answerOptions.map에 매핑 할 수 있습니다.
  • map 함수 내에서 각 answerOption에 대한 버튼을 렌더링하고 텍스트를 표시합니다.

상태를 사용하여 질문 변경 


이제 JSX로 돌아 갑시다. questions[0]을 questions[1] 또는 questions[2]로 변경하면 UI가 어떻게 업데이트 되는지 확인하십시오. 

이는 인덱스에 따라 질문 배열의 다른 질문에서 데이터를 가져 오기 때문입니다. 


우리가 하고 싶은 것은 상태 객체를 사용하여 사용자가 현재 어떤 질문에 있는지 저장하고 답변 버튼을 클릭하면 이를 업데이트하는 것입니다. 마지막 예제의 코드를 실행하여 이를 확인할 수 있습니다.


계속해서 사용자가 있는 현재 질문 번호를 보유 할 상태 개체를 추가합니다. 이 값은 0으로 초기화되므로 퀴즈는 배열에서 첫 번째 질문을 가져옵니다.


const [currentQuestion, setCurrentQuestion] = useState(0);

이제 JSX에서 하드 코딩 된 '0'을 이 변수로 바꾸고 싶습니다. 먼저 질문 텍스트 :


<div className='question-text'>{questions[currentQuestion].questionText}</div>

또한 질문 섹션의 경우 :

<div className='answer-section'>
	{questions[currentQuestion].answerOptions.map((answerOption, index) => (
		<button>{answerOption.answerText}</button>
	))}
</div>

이제 currentQuestion을 0이 아닌 것으로 초기화하면 (예 : 1 또는 2) UI가 업데이트 되어 해당 특정 질문에 대한 질문과 답변을 표시합니다. 정말 멋진!


답변을 클릭 할 때 currentQuestion 값을 증가 시켜 다음 질문으로 이동하도록 코드를 추가해 보겠습니다.


handleAnswerButtonClick이라는 새 함수를 만듭니다. 이것은 사용자가 답변을 클릭 할 때 호출되는 것입니다.


현재 질문 값을 1 씩 증가 시키고 새 변수에 저장 한 다음이 새 변수를 상태로 설정합니다.


const handleAnswerButtonClick = (answerOption) => {
	const nextQuestion = currentQuestion + 1;
	setCurrentQuestion(nextQuestion);
};

다음과 같이 버튼에 onClick 이벤트를 추가합니다.


<button onClick={() => handleAnswerButtonClick()}>{answerOption.answerText}</button>

이것을 시도하면 끝까지 작동하는 것을 볼 수 있습니다.


error.webp 


그래서 무슨 일이 일어나고 있습니까? handleAnswerButtonClick 함수에서 숫자를 늘리고 상태로 설정합니다. 괜찮아.


그러나 질문 및 답변 옵션을 얻기 위해 이 번호를 사용하여 배열에 액세스한다는 점을 기억하십시오. 5에 도달하면 5 번째 요소가 없기 때문에 깨질 것입니다!


한도를 초과하지 않는지 확인하겠습니다. handleAnswerButtonClick 함수에서 다음 조건을 추가하겠습니다.


if (nextQuestion < questions.length) {
	setCurrentQuestion(nextQuestion);
} else {
	alert('you reached the end of the quiz');
}

이것은 기본적으로 다음 질문 수가 총 질문 수보다 적으면 상태를 다음 질문으로 업데이트합니다. 그렇지 않으면 퀴즈가 끝났으므로 지금은 알림을 표시하세요.


점수 화면 표시 


경고를 표시하는 대신 "점수"화면을 표시하려고 합니다.


JSX를 살펴보면 여기에 마크업을 넣었음을 알 수 있습니다. "false"를 논리로 대체하면 됩니다.


그럼 우리는 어떻게 해야 합니까? 글쎄, 이것은 상태로 두기에 완벽한 것입니다!


점수 화면을 표시할지 여부를 저장할 다른 상태 개체를 추가합니다.


const [showScore, setShowScore] = useState(false);

그리고 JSX에서 false를 showScore로 바꿉니다.


<div className='app'>{showScore ? <div className='score-section'>// ... score section markup</div> : <>// ... quiz question/answer markup</>}</div>

아무것도 변경되지 않지만 상태 값을 true로 변경하면 점수 div가 표시됩니다. 이는 모든 것이 삼항으로 래핑 되어 있기 때문입니다.


"showScore가 true이면 점수 섹션 마크 업을 렌더링하고 그렇지 않으면 퀴즈 질문 / 답변 마크 업을 렌더링 합니다." 



이제 사용자가 퀴즈의 끝에 도달했을 때 이 상태 변수를 업데이트하려고합니다. handleAnswerButtonClick 함수에서 이에 대한 논리를 이미 작성했습니다.


우리가 해야 할 일은 showScore 변수를 true로 업데이트하는 경고 로직을 교체하는 것입니다.


if (nextQuestion < questions.length) {
	setCurrentQuestion(nextQuestion);
} else {
	setShowScore(true);
}

퀴즈의 답을 클릭하면 끝까지 점수 섹션이 표시됩니다. 현재 표시된 텍스트와 점수는 하드 코딩 된 문자열이므로 동적으로 만들어야 합니다.


점수 저장 


다음 작업은 앱 어딘가에 점수를 저장하고 사용자가 올바른 옵션을 선택하면 이 값을 증가 시키는 것입니다.


이를 수행하는 논리적 위치는 "handleAnswerOptonClick"함수 내에 있습니다.


answerOptions를 반복 할 때 map 함수는 questionText를 포함하는 각각에 대한 객체와 해당 답변이 올바른지 여부를 나타내는 부울 값을 제공합니다. 이 부울은 점수를 높이는 데 사용할 것입니다.


버튼에서 다음과 같이 함수를 업데이트합니다.


onClick={()=> handleAnswerButtonClick(answerOption.isCorrect)

다음으로 이 매개 변수를 허용하도록 함수를 업데이트합니다.

const handleAnswerButtonClick = (isCorrect) => {
	//... other code
};

이제 함수에 논리를 추가 할 수 있습니다. 지금은 "isCorrect가 참이면 경고를 표시합니다"라고 말하고 싶습니다.

const handleAnswerButtonClick = (isCorrect) => {
	if (isCorrect) {
		alert(“the answer is correct!)
	}

	//...other code
};

이것은 if (isCorrect === true)와 동일하며 축약 형 버전입니다. 이제 이것을 시도하면 정답을 클릭하면 경고가 표시됩니다.


지금까지 요약하면 다음과 같습니다.


  • 버튼을 반복 할 때 해당 버튼의 isCorrect 부울 값을 handleAnswerButtonClick 함수에 전달합니다.
  • 함수에서 이 값이 참인지 확인하고 참이면 경고를 표시합니다.

다음으로 실제로 점수를 저장하려고 합니다. 우리가 어떻게 할까요? 상태 가치를 말했다면 맞습니다!


계속해서 "score"라는 상태 값을 추가합니다. 값을 변경하려면 함수 앞에 "set"을 붙여야 setScore가 됩니다. 0으로 초기화하십시오.


const [score, setScore] = useState(0);

다음으로 경고를 표시하는 대신 사용자가 정답을 얻었으면 점수를 1 씩 업데이트하려고합니다.


handleAnswerButtonClick 함수에서 경고를 제거하고 점수를 1 씩 증가 시킵니다.


const handleAnswerButtonClick = (isCorrect) => {
	if (answerOption.isCorrect) {
		setScore(score + 1);
	}

	//...other code
};

점수 표시 


점수를 표시하려면 렌더링 코드를 약간 변경하면 됩니다. JSX에서 score 섹션의 하드 코딩 된 문자열을 제거하고 다음 새 변수를 추가합니다.


<div className='score-section'>
	You scored {score} out of {questions.length}
</div>
<div className='score-section'>
	You scored {score} out of {questions.length}
</div>

이제 답변을 살펴보면 점수가 동적이며 마지막에 올바르게 표시됩니다!


퀴즈 앱을 마무리하기 전에 마지막으로 해야 할 일 : UI에 표시되는 현재 질문은 하드 코딩 되었으므로 항상 "1"입니다. 이것을 좀 더 역동적으로 바꿔야 합니다.


"question-count"를 다음으로 바꿉니다.

<div className='question-count'>
	<span>Question {currentQuestionIndex + 1}</span>/{questions.length}
</div>

컴퓨터가 1이 아닌 0부터 계산하기 시작하므로 +1이 필요합니다.