- Published on
๐ ์ปฌ๋ฌ๋ฆฌ์คํธ ํ๋ก์ ํธ #9 api ์์ฒญ ํ์ธ
- ๊ธ์ด์ด
๐ ๋ชฉ์ฐจ
๐๐ป
- TestForm์์ article์ form์ผ๋ก ๊ฐ์ธ๊ธฐ
- Pallete.tsx์์ button ํ์ button์ผ๋ก ์ค์
- input์ ์๋ฒ์์ ์ ํด์ค name, type ์ค์
- data ์ ์ฒ๋ฆฌ ํ๋ก์ธ์ฑ
- type assumption - global.d.ts
- api.ts๋ฅผ ๋ง๋ค์ด postํจ์๋ฅผ ์บก์ํํ๊ธฐ
form
์ผ๋ก ๊ฐ์ธ๊ธฐ
1. TestForm์์ article์ TestForm์์ ์
๋ ฅํ๋ ๊ฐ๋ค์ ํ๋ฒ์ ๋ณด๋ด์ฃผ๊ธฐ ์ํด form
์ผ๋ก TestForm์์ ์์๋ค์ ๊ฐ์ธ์ค๋๋ค.
<form
onSubmit={(event) => {
event.preventDefault();
const rawData = new FormData(event.target as HTMLFormElement);
const colors = rawData.get("colors");
if (colors !== null) {
// ์ ์ฒ๋ฆฌ, preprocess
const data = [
{
jubogang: {
ju: rawData.get("ju"),
bo: rawData.get("bo"),
gang: rawData.get("gang"),
},
box: JSON.parse(colors as string),
answer: rawData.get("answer"),
},
];
saveColors(data as MyColors[]);
}
}}
>
2. Pallete์์ button์ ๋๋ ์๋ ๋ฐ๋ก ์ ์ถ๋์ง ์๋๋ก type์ button์ผ๋ก ๋ง๋ค์ด ์ค๋๋ค.
;<Tabs.List aria-label="select pallete type">
{/* ํ๋ ํธ์ ํ์
๋ง๋ค Tabs.Trigger๋ฅผ ๋ง๋ค์ด์ค๋ค*/}
{palleteTypes.map((value) => (
<Tabs.Trigger key={value} value={value} className="bg-yellow-200" type="button">
{value}
</Tabs.Trigger>
))}
</Tabs.List>
{
/* ["base", "deep"]*/
}
{
/* ํ๋ ํธ์ ํ์
๋ง๋ค Tabs.Content๋ฅผ ๋ง๋ค์ด์ค๋ค*/
}
{
palleteTypes.map((value) => (
<Tabs.Content key={value} value={value}>
<div id="pallete" className="flex flex-row flex-wrap">
{/* 4๋ฒ - pallete๋ ๊ฐ์ด ์ฌ๋ฌ ๊ฐ์ด๋ฏ๋ก map์ ์ฌ์ฉํ๋ค. */}
{allPallete[value].map(({ hex, colorName }, index) => (
<Tooltip.Root>
<Tooltip.Trigger asChild>
<button
type="button"
key={index}
onClick={() => addSelected(hex)}
className="roundButton"
style={{ backgroundColor: hex }}
>
{colorName}
</button>
</Tooltip.Trigger>
<Tooltip.Content className="tooltip-content">
{hex}
<Tooltip.Arrow className="tooltip-arrow" width="20" height="10" />
</Tooltip.Content>
</Tooltip.Root>
))}
</div>
</Tabs.Content>
))
}
3. ์๋ฒ api ์คํค๋ง์์ ์ ํด์ค๋๋ก TestForm์์ input์๋ type,name์ textarea์๋ name์ ๋ฃ์ด์ค๋๋ค.
<p id="allColor">์ฃผ๋ณด๊ฐ ๊ธฐ์
</p>
<label className="color-label" htmlFor="main-color">
์ฃผ์กฐ์
</label>
<input
id="color-selection"
placeholder="์ฃผ์กฐ์:"
value={mainColor}
// change event์ target์ธ textarea์ ์๋ก ๋ณ๊ฒฝ๋ ๊ฐ์
// react์ mainColor ์ํ์ ์ธํ
// ๊ฐ์ ๋๊ธฐํํด์ฃผ๋ ๊ฑฐ์์.
// https://beta.reactjs.org/learn/reacting-to-input-with-state
onChange={(event) => setMainColor(event.target.value)}
/>
<label className="color-label" htmlFor="sub-color">
๋ณด์กฐ์
</label>
<input
id="color-selection"
placeholder="๋ณด์กฐ์:"
value={subColor}
onChange={(event) => setSubColor(event.target.value)}
/>
<label className="color-label" htmlFor="point-color">
๊ฐ์กฐ์
</label>
<input
id="color-selection"
placeholder="๊ฐ์กฐ์:"
value={pointColor}
onChange={(event) => setPointColor(event.target.value)}
/>
<label className="color-label" htmlFor="explanation" id="explanation">
๋ฐฐ์ ์ค๋ช
</label>
<textarea
id="explanation"
placeholder={"์ปจ์
:\n์ฃผ์กฐ์:\n๋ณด์กฐ์:\n๊ฐ์กฐ์:\n๋ฐฐ์ ๊ธฐ๋ฒ:"}
value={explanation}
onChange={(event) => setExplanation(event.target.value)}
/>
<p id="allColor">์ฃผ๋ณด๊ฐ ๊ธฐ์
</p>
<label className="color-label" htmlFor="main-color">
์ฃผ์กฐ์
</label>
<input
id="color-selection"
type="text"
name="ju"
placeholder="์ฃผ์กฐ์:"
value={mainColor}
onChange={(event) => setMainColor(event.target.value)}
/>
<label className="color-label" htmlFor="sub-color">
๋ณด์กฐ์
</label>
<input
id="color-selection"
type="text"
name="bo"
placeholder="๋ณด์กฐ์:"
value={subColor}
onChange={(event) => setSubColor(event.target.value)}
/>
<label className="color-label" htmlFor="point-color">
๊ฐ์กฐ์
</label>
<input
id="color-selection"
type="text"
name="gang"
placeholder="๊ฐ์กฐ์:"
value={pointColor}
onChange={(event) => setPointColor(event.target.value)}
/>
<label className="color-label" htmlFor="explanation" id="explanation">
๋ฐฐ์ ์ค๋ช
</label>
<textarea
id="explanation"
name="answer"
placeholder={"์ปจ์
:\n์ฃผ์กฐ์:\n๋ณด์กฐ์:\n๊ฐ์กฐ์:\n๋ฐฐ์ ๊ธฐ๋ฒ:"}
value={explanation}
onChange={(event) => setExplanation(event.target.value)}
/>
4. data ์ ์ฒ๋ฆฌ ํ๋ก์ธ์ฑ
FormData๋ฅผ rawData๋ก ๊ฐ์ ธ์์
๋ฐฑ์๋ ์๋ฒ์ ๊ฐ์ ธ๊ฐ ํฌ๋งท์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ์ ์ฒ๋ฆฌ ํ๋ก์ธ์ฑํด์ data์ ๋ฃ์ด์ค๋๋ค.
const rawData = new FormData(event.target as HTMLFormElement)
const colors = rawData.get('colors')
if (colors !== null) {
// ์ ์ฒ๋ฆฌ, preprocess
const data = [
{
jubogang: {
ju: rawData.get('ju'),
bo: rawData.get('bo'),
gang: rawData.get('gang'),
},
box: JSON.parse(colors as string),
answer: rawData.get('answer'),
},
]
saveColors(data as MyColors[])
}
5. type assumption - global.d.ts
form์ ๊ฐ์ ธ๊ฐ ๋ฐ์ดํฐ type๋ค์ ์ฌ๋ฌ ํ์ผ์ ์ฐ์ด๋ฏ๋ก
global.d.ts
ํ์ผ์ type์ ์ค์ ํด์ค๋๋ค.
type MyColors = {
jubogang: {
ju: string
bo: string
gang: string
}
box: Array<{ color: string; width: number }>
answer: string
}
6. api.ts๋ฅผ ๋ง๋ค์ด postํจ์๋ฅผ ์บก์ํํ๊ธฐ
์๋ฒ์ ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ด์ฃผ๊ธฐ์ํด api.ts๋ฅผ ๋ง๋ญ๋๋ค.
์๋ฒ์์ ๋ฐ์ ์ฃผ์๋ฅผ Host๋ก ์ค์ ํฉ๋๋ค.
const HOST = 'http://localhost:8000'
๋ฐ์ดํฐ๋ฅผ ๋ณด๋ด๋ ํจ์ saveColors๋ฅผ ๋ง๋ค์ด export
ํด์ค๋๋ค.
export function saveColors(data: MyColors[]) {
return post('/colors', data)
}
์ฌ๊ธฐ์ ํฌํจ๋๋ postํจ์๋ ๋ค๋ฅธ ํจ์์๋ ์ฐ์ผ ์ ์์ผ๋ฏ๋ก ๋ฐ๋ก ๋ผ์ด ์ถ์ํ, ์บก์ํํด์ค๋๋ค.
function post<T>(path: string, data: T) {
return fetch(HOST + path, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
})
}
์ด๋ ๊ฒ ํจ์๋ฅผ ์บก์ํํด์ค ๊ฒฝ์ฐ
- ์ฐ๊ธฐ ์ฝ๋ค
- ๋์ค์ ๊ต์ฒดํ๊ธฐ ์ฝ๋ค
- ์ค์ํ๊ธฐ ์ด๋ ต๋ค ๋ ์ฅ์ ์ด ์์ต๋๋ค.