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. 결과 확인

저장 버튼 클릭 시 화면