React 배열 다루기 (기초)

ReactuseStatesetStatearraymaptsxTypescriptonChange
avatar
2025.04.10
·
4 min read

1. 가상의 데이터 타입 정의

data.d.ts

interface Survey {
  ASK_SQ: number;
  ASK_CN: string;
  ANSWER_LIST: {
    ANSWER_SQ: number;
    ANSWER_CN: string;
  }[];
  ANSWER: null | string | number;
};

2. 가상의 데이터 정의

data.ts

export const defaultList: Survey[] = [
  {
    ASK_SQ: 1,
    ASK_CN: "질문1",
    ANSWER: null,
    ANSWER_LIST: [
      { ANSWER_SQ: 1, ANSWER_CN: "보기 항목1" },
      { ANSWER_SQ: 2, ANSWER_CN: "보기 항목2" },
      { ANSWER_SQ: 3, ANSWER_CN: "보기 항목3" },
      { ANSWER_SQ: 4, ANSWER_CN: "보기 항목4" },
      { ANSWER_SQ: 5, ANSWER_CN: "보기 항목5" },
    ],
  },
  {
    ASK_SQ: 2,
    ASK_CN: "질문2",
    ANSWER: null,
    ANSWER_LIST: [
      { ANSWER_SQ: 6, ANSWER_CN: "보기 항목1" },
      { ANSWER_SQ: 7, ANSWER_CN: "보기 항목2" },
      { ANSWER_SQ: 8, ANSWER_CN: "보기 항목3" },
      { ANSWER_SQ: 9, ANSWER_CN: "보기 항목4" },
      { ANSWER_SQ: 10, ANSWER_CN: "보기 항목5" },
    ],
  },
  {
    ASK_SQ: 3,
    ASK_CN: "질문3",
    ANSWER: null,
    ANSWER_LIST: [
      { ANSWER_SQ: 11, ANSWER_CN: "보기 항목1" },
      { ANSWER_SQ: 12, ANSWER_CN: "보기 항목2" },
      { ANSWER_SQ: 13, ANSWER_CN: "보기 항목3" },
      { ANSWER_SQ: 14, ANSWER_CN: "보기 항목4" },
      { ANSWER_SQ: 15, ANSWER_CN: "보기 항목5" },
    ],
  },
  {
    ASK_SQ: 4,
    ASK_CN: "질문4",
    ANSWER: null,
    ANSWER_LIST: [
      { ANSWER_SQ: 16, ANSWER_CN: "보기 항목1" },
      { ANSWER_SQ: 17, ANSWER_CN: "보기 항목2" },
      { ANSWER_SQ: 18, ANSWER_CN: "보기 항목3" },
      { ANSWER_SQ: 19, ANSWER_CN: "보기 항목4" },
      { ANSWER_SQ: 20, ANSWER_CN: "보기 항목5" },
    ],
  },
  {
    ASK_SQ: 5,
    ASK_CN: "질문5",
    ANSWER: null,
    ANSWER_LIST: [],
  },
];

3. 화면 출력

App.tsx

import { FormEvent, useCallback, useEffect, useState } from "react";
import { Survey, defaultList } from "./data";

export default function App() {
  const [list, setList] = useState<Survey[]>([]);

  // 데이터 조회
  const getList = async () => {
    setList(defaultList);
  };

  // 데이터 변경
  const changeList = (ASK_SQ: number, ANSWER: string | number) => {
    // 일반.. (솔팅할 필드가 없어도 됨)
    setList((prev) => {
      const result = prev?.map((item) => {
        if (item?.ASK_SQ !== ASK_SQ) return item;
        return { ...item, ANSWER };
      });
      return result;
    });

    // 솔팅할 필드가 있을 때..
    // setList((prev) => {
    //   let copy = [...prev];
    //   let find = copy?.find((x) => x?.ASK_SQ === ASK_SQ) as Survey;
    //   let other = copy?.filter((x) => x?.ASK_SQ !== ASK_SQ);
    //   find = { ...find, ANSWER };
    //   let result = [...other, find];
    //   result = result?.sort((a, b) => a?.ASK_SQ - b?.ASK_SQ);
    //   return result;
    // });
  };

  // 데이터 저장
  const onSubmit = (e: FormEvent<HTMLFormElement>) => {
    e?.preventDefault();

    const result = list?.map((x) => ({ ASK: x?.ASK_SQ, ANSWER: x?.ANSWER }));

    const askTxt = `설문 내용을 저장하시겠습니까?
      
${result?.map((x) => `질문 ${x?.ASK} - 답변 ${x?.ANSWER}`)?.join("\n")}`;
    let ask = window?.confirm(askTxt);
    if (!ask) return;

    alert("전송이 완료되었습니다.");
    console.log(result);
    getList();
  };

  // 타입 (객관식/주관식)
  const getType = useCallback((ask: Survey) => {
    return (ask?.ANSWER_LIST?.length ? "객" : "주") + "관식";
  }, []);

  // 초기 실행
  useEffect(() => {
    getList();
  }, []);

  return (
    <form onSubmit={onSubmit}>
      <h1>설문지</h1>

      <ul>
        {list?.map((ask) => (
          <li key={ask?.ASK_SQ}>
            <h3>
              {ask?.ASK_CN} ({getType(ask)})
            </h3>
            <ol>
              {!ask?.ANSWER_LIST?.length ? (
                <input
                  required
                  type="text"
                  value={ask?.ANSWER ?? ""}
                  onChange={(e) => {
                    changeList(ask?.ASK_SQ, e?.target?.value);
                  }}
                />
              ) : (
                ask?.ANSWER_LIST?.map((answer) => (
                  <li key={answer?.ANSWER_SQ}>
                    <input
                      required
                      type="radio"
                      name={"ASK_" + ask?.ASK_SQ}
                      id={"ANSWER_" + answer?.ANSWER_SQ}
                      checked={ask?.ANSWER === answer?.ANSWER_SQ}
                      onChange={() => {
                        changeList(ask?.ASK_SQ, answer?.ANSWER_SQ);
                      }}
                    />
                    <label htmlFor={"ANSWER_" + answer?.ANSWER_SQ}>
                      {answer?.ANSWER_CN}
                    </label>
                  </li>
                ))
              )}
            </ol>
          </li>
        ))}
      </ul>

      <button>저장</button>
    </form>
  );
}

4. 결과 확인

4958

저장 버튼 클릭 시 화면







- 컬렉션 아티클