
๐ useCallback์ ํจ์๋ฅผ ๋ฉ๋ชจ์ด์ ์ด์ ํ์ฌ, ๊ฐ์ด ๋ณ๊ฒฝ๋์ง ์๋ ํ ์ด์ ์ ์์ฑ๋ ํจ์๋ฅผ ์ฌ์ฉ. inputํ๊ทธ์์ ์ด๋ฒคํธ๋ฅผ ์ฌ์ฉํ๊ฒ์ ํ ๊ธ์์ฉ ์์ฑํ ๋๋ง๋ค handleChange๋ฉ์๋๊ฐ ํธ์ถ๋์ด ์ํ๋ฅผ ์ ๋ฐ์ดํธํ๋๋ฐ, usecallback์ ์ด์ฉํ๋ฉด handleChange๊ฐ ๊ฐ์ ์ฐธ์กฐ๋ฅผ ๊ฐ์ง๋ ๋ฉ๋ชจ์ด์ ์ด์ ์ญํ ์ ํ๋ค.
function TodoAddComponent() {
const handleChange = useCallback((e:ChangeEvent<HTMLInputElement>) => {
// @ts-ignore
todo[e.target.name] = e.target.value
setTodo({...todo})
},[])
return (
<input
type="text"
name="title"
className="border border-gray-300 rounded-lg p-3 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition duration-300"
placeholder="Enter Title"
value={todo.title}
onChange={e => handleChange(e)}
/>
)
}
import {ChangeEvent, useRef, useState} from "react";
import {postAdd} from "../../api/productAPI.ts";
const initialState = {
pname: '',
pdesc: '',
price: '',
}
function ProductAddPage() {
const [product, setProduct] = useState({...initialState}) // ์ด๊ธฐ๊ฐ์ด ์์ผ๋ฉด ์๋ฌ๊ฐ ๋ฐ์ํ ์์์ผ๋ฏ๋ก ๋ณต์ฌ๋ณธ ๋ฃ๊ธฐ
const filesRef = useRef<HTMLInputElement>(null) // id,name์ ์ฌ์ฉํ๋ ๋์ ์
const handleChange = (e:ChangeEvent<HTMLInputElement>) => {
product[e.target.name] = e.target.value
}
const handleClick = () => {
const files = filesRef.current.files // ๋ฑ๋กํ ํ์ผ์ ๋ํ ์ ๋ณด
console.log(filesRef)
const formData = new FormData() // ํ์ผ ์
๋ก๋์ ํ
์คํธ ๋ฐ์ดํฐ๋ฅผ ๋์์ ์ฒ๋ฆฌํ๊ธฐ์ํด ์ผ๋ฐ์ ์ธ json๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
formData.append("pname", product.pname)
formData.append("pdesc", product.pdesc)
formData.append("price", product.price)
for(let i=0; i<files.length; i++) {
formData.append("files", files[i])
}
postAdd(formData).then(data =>{
console.log(data)
filesRef.current.value = null
})
}
return (
<div>
<div>Product Add Page</div>
<div>
<input type='text' name='pname' placeholder='Product Name' onChange={(e) => handleChange(e)}/>
<input type='text' name='pdesc' placeholder='Product Desc' onChange={(e) => handleChange(e)}/>
<input type='number' name='price' placeholder='1000' onChange={(e) => handleChange(e)}/>
<input type='file' ref={filesRef} name='files' multiple={true}/>
<button onClick={() => handleClick()}>ADD</button>
</div>
</div>
);
}
export default ProductAddPage;
๐ ๊ธฐ๋ณธ์ ์ผ๋ก input ํ๊ทธ๋ฅผ ์ด์ฉํ addํ์ด์ง์์๋ useState๋ฅผ ์ด์ฉํ ์ํ๊ฐ์ฒด์ handleChange ๋ฉ์๋๋ฅผ ์ด์ฉํ์ฌ product[e.target.name] = e.target.value ์ด๊ฒ์ฒ๋ผ ๋ฃ์ด์ api์๋ฒ์ prodcut๊ฐ์ฒด๋ฅผ ์ ๋ฌํ๋ ๋ฐฉ์์ ์ฌ์ฉํ์ง๋ง, ์ฒจ๋ถํ์ผ์ด ๋ค์ด๊ฐ๋ ๊ฒฝ์ฐ๋ ๋ค๋ฅด๋ค
๐ ์ฐ์ useRef๋ฅผ ์ฌ์ฉํ๊ฒ ๋๋ฉด filesRef.current.files ํด๋น ์์ฑ์ ์ด์ฉํ์ฌ ๋ฑ๋กํ ํ์ผ์ ๋ํ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ฌ ์ ์๋ค. ์ด ์ ๋ณด๋ inputํ๊ทธ๋ด์ file type์ ref={filesRef}๋ฅผ ์ด์ฉํ์ฌ inputํ๊ทธ์ file์์ฑ์ ๋ปํ๋ค. ๋ช๊ฐ์ ํ์ผ์ ์ฒจ๋ถํ๋์ง์ ๊ฐ์ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ฌ์๋ ์๋ค.
๐ ์ดํ์๋ formData ๊ฐ์ฒด๋ฅผ ์์ฑํด์ผํ๋๋ฐ, ์ด๋ ํ์ผ์ ๋ก๋์ ํ ์คํธ ๋ฐ์ดํฐ๋ฅผ ๋์์ ์ฒ๋ฆฌํ๋ ค๋ฉด ์ผ๋ฐ์ ์ธ json ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ ์ ์๊ธฐ ๋๋ฌธ์ ํ ์คํธ ๋ฐ์ดํฐ๋ formData๊ฐ์ฒด์ append๋ก ๊ฐ๊ฐ ๋ถํ์ฃผ๊ณ file๊ฐ์ ๊ฒฝ์ฐ์๋ filesRef์ file๊ฐ์๋ฅผ ์ด์ฉํ์ฌ ๋ช๊ฐ์ธ์ง ํ์ ํ์ฌ file์ appendํด์ค๋ค.
๐ ๋ง์ง๋ง์ผ๋ก api์๋ฒ ํธ์ถํ์ฌ post๋ฅผ ํด์ค๋ค์ filesRef.current.value = null ์ด ์ฝ๋๋ ์ด์ ์ ์ ๋ ฅํ file์ ์ง์์ ๋ค์ post์์ ํธํ๋๋ก ๋น์์ฃผ๋ ์ญํ ์ ํ๋ค.
import axios from "axios";
const host:string = 'http://localhost:8089/api/products'
const header = { //post๋ฐฉ์์์๋ ์ฌ์ฉํ์ง ์์๋ ๋์ง๋ง, put์์๋ ์ฌ์ฉํด์ผํ๋ค.
headers: {
'Content-Type': 'multipart/form-data', // ํ์ผ ์ ์ก ํ์ ์ง์
}
}
export const postAdd = async (formData: FormData): Promise<number> => {
const res = await axios.post(`${host}/`, formData)
return Number(res.data.result) // ๋ฑ๋ก์์ result์ ์๋ฒ๊ฐ์ด ๋์ค๋ฏ๋ก data.result์ด๊ณ ํ์
์ number
}
๐ API ๋ํ ๋งค๊ฐ๋ณ์์ ํ์ผ์ด ํฌํจ๋์ด์์ผ๋ฏ๋ก ํ์ ์ formData๋ก ์ค์ ํ๋ค.