React 정리

React로 설문조사 페이지 만들기 (1)

알럽유 2024. 8. 30. 17:48
728x90
반응형

안녕하세요 우선 환경 설정부터 따라해 보겠습니다.

우선 폴더를 하나 만들어 주고

 

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에 저장된 값을 화면에 출력합니다.