React로 설문조사 페이지 만들기 (1)
안녕하세요 우선 환경 설정부터 따라해 보겠습니다.
우선 폴더를 하나 만들어 주고
vscode를 들어가서 폴더를 열어줍니다.
그리고 터미널을 열고 npm create vite@latest를 쳐줍니다.
이런식으로 하시면 됩니다.
그리고 App.css, index.css를 지워줍니다.
그다음 src폴더안에 pages폴더와 components 폴더를 만들어 줍니다.
components 폴더안에는 surveyData.jsx를
pages폴더안에는 ServeyPage.js를 만들어줍니다.
설문조사 구성은 이런식으로 하나의 질문에 번호의 답변이 있고 그 답변의 대한 질문이 또 있고
이런식의 경우의 수를 생각하면서 구성하면 됩니다
surveyData.jsx
// 첫 번째 질문: 음식 종류
export const foodCategories = {
1: ["일식"],
2: ["한식"],
3: ["중식"],
4: ["양식"],
};
// 두 번째 질문: 각 음식 종류에 따른 메뉴
export const menuOptions = {
1: { a: "스시", b: "돈카츠", c: "라면" },
2: { a: "김치찌개", b: "된장찌개", c: "김밥" },
3: { a: "짜장면", b: "짬뽕", c: "마라탕" },
4: { a: "파스타", b: "스테이크", c: "리조또" },
};
// 세 번째 질문: 각 메뉴에 따른 맛 선택
export const flavorChoices = {
"1-a": { i: "매운 맛", ii: "단맛", iii: "짠맛" },
"1-b": { i: "매운 맛", ii: "단맛", iii: "짠맛" },
"1-c": { i: "매운 맛", ii: "단맛", iii: "짠맛" },
"2-a": { i: "매운 맛", ii: "단맛", iii: "짠맛" },
"2-b": { i: "매운 맛", ii: "단맛", iii: "짠맛" },
"2-c": { i: "매운 맛", ii: "단맛", iii: "짠맛" },
"3-a": { i: "매운 맛", ii: "단맛", iii: "짠맛" },
"3-b": { i: "매운 맛", ii: "단맛", iii: "짠맛" },
"3-c": { i: "매운 맛", ii: "단맛", iii: "짠맛" },
"4-a": { i: "매운 맛", ii: "단맛", iii: "짠맛" },
"4-b": { i: "매운 맛", ii: "단맛", iii: "짠맛" },
"4-c": { i: "매운 맛", ii: "단맛", iii: "짠맛" },
};
// 결과: 선택된 맛에 따른 추천 메뉴
export const results = {
"1-a-i": "매운 맛의 스시",
"1-a-ii": "단 맛의 스시",
"1-a-iii": "짠 맛의 스시",
"1-b-i": "매운 맛의 돈까스",
"1-b-ii": "단 맛의 돈까스",
"1-b-iii": "짠 맛의 돈까스",
"1-c-i": "매운 맛의 라면",
"1-c-ii": "단 맛의 라면",
"1-c-iii": "짠 맛의 라면",
"2-a-i": "매운 맛의 김치찌개",
"2-a-ii": "단 맛의 김치찌개",
"2-a-iii": "짠 맛의 김치찌개",
"2-b-i": "매운 맛의 된장찌개",
"2-b-ii": "단 맛의 된장찌개",
"2-b-iii": "짠 맛의 된장찌개",
"2-c-i": "매운 맛의 김밥",
"2-c-ii": "단 맛의 김밥",
"2-c-iii": "짠 맛의 김밥",
"3-a-i": "매운 맛의 짜장면",
"3-a-ii": "단 맛의 짜장면",
"3-a-iii": "짠 맛의 짜장면",
"3-b-i": "매운 맛의 짬뽕",
"3-b-ii": "단 맛의 짬뽕",
"3-b-iii": "짠 맛의 짬뽕",
"3-c-i": "매운 맛의 마라탕",
"3-c-ii": "단 맛의 마라탕",
"3-c-iii": "짠 맛의 마라탕",
"4-a-i": "매운 맛의 파스타",
"4-a-ii": "단 맛의 파스타",
"4-a-iii": "짠 맛의 파스타",
"4-b-i": "매운 맛의 스테이크",
"4-b-ii": "단 맛의 스테이크",
"4-b-iii": "짠 맛의 스테이크",
"4-c-i": "매운 맛의 리조또",
"4-c-ii": "단 맛의 리조또",
"4-c-iii": "짠 맛의 리조또",
};
설문지에 해당하는 내용을 이런식으로 대충 넣어줍니다.
ServeyPage.jsx
import React, { useState } from "react";
import {
foodCategories, // 변경된 변수명
menuOptions, // 변경된 변수명
flavorChoices, // 변경된 변수명
results,
} from "../components/surveyData"; // 데이터 가져오기
const ServeyPage = () => {
// 상태를 useState로 관리
const [first, setFirst] = useState(""); // 첫 번째 질문 상태
const [second, setSecond] = useState(""); // 두 번째 질문 상태
const [third, setThird] = useState(""); // 세 번째 질문 상태
const [selectedResult, setSelectedResult] = useState(""); // 결과 상태
const handleFirstChoiceChange = (event) => {
const choice = event.target.value;
setFirst(choice);
resetSecondAndThird(); // 두 번째와 세 번째 선택을 초기화하는 함수 호출
};
const handleSecondChoiceChange = (event) => {
const choice = event.target.value;
setSecond(choice);
resetThird(); // 세 번째 선택을 초기화하는 함수 호출
};
const handleThirdChoiceChange = (event) => {
const choice = event.target.value;
setThird(choice);
updateResult(choice); // 결과를 업데이트하는 함수 호출
};
// 두 번째와 세 번째 선택을 초기화하는 함수
const resetSecondAndThird = () => {
setSecond("");
setThird("");
setSelectedResult("");
};
// 세 번째 선택을 초기화하는 함수
const resetThird = () => {
setThird("");
setSelectedResult("");
};
// 결과를 업데이트하는 함수
const updateResult = (detailChoice) => {
const resultKey = `${first}-${second}-${detailChoice}`;
const finalResult = results[resultKey];
setSelectedResult(
finalResult ? finalResult : "선택에 맞는 추천 메뉴가 없습니다."
);
};
return (
<div className="App">
<h1>설문조사</h1>
{/* 첫 번째 질문 */}
<form>
<div>
<label>먹고 싶은 음식은 무엇인가요?</label>
{Object.entries(foodCategories).map(([key, value]) => (
<div key={key}>
<input
type="radio"
id={`firstChoice${key}`}
name="firstChoice"
value={key}
onChange={handleFirstChoiceChange}
/>
<label htmlFor={`firstChoice${key}`}>{value[0]}</label>
</div>
))}
</div>
</form>
{/* 두 번째 질문 */}
{first && (
<div id="subQuestionContainer">
<h2>{foodCategories[first][0]} 중 어떤 것을 좋아하나요?</h2>
<div>
{Object.keys(menuOptions[first]).map((option) => (
<div key={option}>
<input
type="radio"
id={`secondChoice${option}`}
name="secondChoice"
value={option}
onChange={handleSecondChoiceChange}
/>
<label htmlFor={`secondChoice${option}`}>
{menuOptions[first][option]}
</label>
</div>
))}
</div>
</div>
)}
{/* 세 번째 질문 */}
{second && first && (
<div id="detailQuestionContainer">
<h3>어떤 맛을 선호하시나요?</h3>
{flavorChoices[`${first}-${second}`] && (
<div>
{Object.keys(flavorChoices[`${first}-${second}`]).map(
(flavor) => (
<div key={flavor}>
<input
type="radio"
id={`thirdChoice${flavor}`}
name="thirdChoice"
value={flavor}
onChange={handleThirdChoiceChange}
/>
<label htmlFor={`thirdChoice${flavor}`}>
{flavorChoices[`${first}-${second}`][flavor]}
</label>
</div>
)
)}
</div>
)}
</div>
)}
{/* 최종 결과 */}
{third && (
<div id="resultContainer">
<h1>추천 메뉴</h1>
<h2>{selectedResult || "선택에 맞는 추천 메뉴가 없습니다."}</h2>
</div>
)}
</div>
);
};
export default ServeyPage;
App.jsx
import React from "react";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import ServeyPage from "./pages/ServeyPage";
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<ServeyPage />} />{" "}
</Routes>
</BrowserRouter>
);
}
export default App;
코드 설명
import React, { useState } from "react";
import {
foodCategories, // 음식 종류 데이터
menuOptions, // 각 음식 종류에 따른 메뉴 데이터
flavorChoices, // 각 메뉴에 따른 맛 선택 데이터
results, // 최종 결과 데이터
} from "../components/surveyData"; // 데이터 가져오기
useState: React의 기본 훅으로 상태를 관리합니다.
foodCategories, menuOptions, flavorChoices, results: 각각 음식 종류, 메뉴 옵션, 맛 선택, 최종 결과 데이터를 정의한 외부 파일에서 가져옵니다.
설문조사 앱은 사용자의 선택에 따라 상태를 관리합니다. 각 질문의 답변은 상태(state)로 저장되고, 사용자가 선택을 변경할 때마다 상태를 업데이트하여 UI를 동적으로 변경합니다.
const [first, setFirst] = useState(""); // 첫 번째 질문 상태
const [second, setSecond] = useState(""); // 두 번째 질문 상태
const [third, setThird] = useState(""); // 세 번째 질문 상태
const [selectedResult, setSelectedResult] = useState(""); // 결과 상태
first: 첫 번째 질문(음식 종류)에 대한 선택을 저장합니다.
second: 두 번째 질문(메뉴)에 대한 선택을 저장합니다.
third: 세 번째 질문(맛 선택)에 대한 선택을 저장합니다.
selectedResult: 최종적으로 선택된 메뉴 추천 결과를 저장합니다.
사용자가 각 질문에 답변할 때마다 호출되는 함수들입니다. 각 함수는 사용자의 선택을 받아 상태를 업데이트하고, 다음 단계의 선택을 초기화하거나 결과를 계산합니다.
첫 번째 질문: 음식 종류 선택
const handleFirstChoiceChange = (event) => {
const choice = event.target.value;
setFirst(choice);
resetSecondAndThird(); // 두 번째와 세 번째 선택을 초기화하는 함수 호출
};
handleFirstChoiceChange: 사용자가 음식 종류를 선택하면 호출됩니다.
선택한 값을 first 상태로 저장하고, 이후의 선택을 초기화합니다.
두 번째 질문: 메뉴 선택
const handleFirstChoiceChange = (event) => {
const choice = event.target.value;
setFirst(choice);
resetSecondAndThird(); // 두 번째와 세 번째 선택을 초기화하는 함수 호출
};
handleSecondChoiceChange: 사용자가 메뉴를 선택하면 호출됩니다.
선택한 값을 second 상태로 저장하고, 이후의 선택을 초기화합니다.
세 번째 질문: 맛 선택
const handleThirdChoiceChange = (event) => {
const choice = event.target.value;
setThird(choice);
updateResult(choice); // 결과를 업데이트하는 함수 호출
};
handleThirdChoiceChange: 사용자가 맛을 선택하면 호출됩니다.
선택한 값을 third 상태로 저장하고, 최종 결과를 업데이트합니다.
각 선택이 변경될 때 이전 선택들을 초기화하거나, 결과를 업데이트하는 함수들입니다.
const resetSecondAndThird = () => {
setSecond("");
setThird("");
setSelectedResult("");
};
const resetThird = () => {
setThird("");
setSelectedResult("");
};
resetSecondAndThird: 두 번째와 세 번째 선택을 초기화합니다.
resetThird: 세 번째 선택만 초기화합니다.
const updateResult = (detailChoice) => {
const resultKey = `${first}-${second}-${detailChoice}`;
const finalResult = results[resultKey];
setSelectedResult(
finalResult ? finalResult : "선택에 맞는 추천 메뉴가 없습니다."
);
};
updateResult: 사용자가 세 번째 질문을 선택할 때 호출됩니다.
사용자가 선택한 세 가지 값을 조합하여 results에서 최종 결과를 찾아 selectedResult로 설정합니다.
이제, 사용자가 선택할 수 있는 설문조사 UI를 구성합니다. 각 질문은 사용자의 선택에 따라 동적으로 렌더링됩니다.
(1) 첫 번째 질문: 음식 종류 선택하기
<form>
<div>
<label>먹고 싶은 음식은 무엇인가요?</label>
{Object.entries(foodCategories).map(([key, value]) => (
<div key={key}>
<input
type="radio"
id={`firstChoice${key}`}
name="firstChoice"
value={key}
onChange={handleFirstChoiceChange}
/>
<label htmlFor={`firstChoice${key}`}>{value[0]}</label>
</div>
))}
</div>
</form>
(2) 두 번째 질문: 메뉴 선택하기
{first && (
<div id="subQuestionContainer">
<h2>{foodCategories[first][0]} 중 어떤 것을 좋아하나요?</h2>
<div>
{Object.keys(menuOptions[first]).map((option) => (
<div key={option}>
<input
type="radio"
id={`secondChoice${option}`}
name="secondChoice"
value={option}
onChange={handleSecondChoiceChange}
/>
<label htmlFor={`secondChoice${option}`}>
{menuOptions[first][option]}
</label>
</div>
))}
</div>
</div>
)}
first 상태가 설정되면 두 번째 질문을 렌더링합니다.
사용자가 선택할 때마다 handleSecondChoiceChange가 호출됩니다.
(3) 세 번째 질문: 맛 선택하기
{second && first && (
<div id="detailQuestionContainer">
<h3>어떤 맛을 선호하시나요?</h3>
{flavorChoices[`${first}-${second}`] && (
<div>
{Object.keys(flavorChoices[`${first}-${second}`]).map((flavor) => (
<div key={flavor}>
<input
type="radio"
id={`thirdChoice${flavor}`}
name="thirdChoice"
value={flavor}
onChange={handleThirdChoiceChange}
/>
<label htmlFor={`thirdChoice${flavor}`}>
{flavorChoices[`${first}-${second}`][flavor]}
</label>
</div>
))}
</div>
)}
</div>
)}
first와 second 상태가 설정되면 세 번째 질문을 렌더링합니다.
사용자가 선택할 때마다 handleThirdChoiceChange가 호출됩니다.
{third && (
<div id="resultContainer">
<h1>추천 메뉴</h1>
<h2>{selectedResult || "선택에 맞는 추천 메뉴가 없습니다."}</h2>
</div>
)}
third 상태가 설정되면 최종 결과를 보여줍니다.
selectedResult에 저장된 값을 화면에 출력합니다.