React๋ก ์ฌ๊ณ ํ๊ธฐ
React๋ฅผ ์ฌ์ฉํ๋ฉด ์ฐ๋ฆฌ๊ฐ ๊ณ ๋ คํ๊ณ ์๋ ๋์์ธ์ด๋ ๋ง๋ค ์ฑ์ ๋ํ ์๊ฐ์ ๋ฐ๊ฟ ์ ์์ต๋๋ค. React๋ก ์ฌ์ฉ์ ์ธํฐํ์ด์ค๋ฅผ ๋น๋ํ ๋, ๋จผ์ ์ด๋ฅผ ์ปดํฌ๋ํธ๋ผ๋ ์กฐ๊ฐ์ผ๋ก ๋๋๋๋ค. ๊ทธ๋ฆฌ๊ณ ๊ฐ ์ปดํฌ๋ํธ์ ๋ค์ํ ์๊ฐ์ ์ํ๋ค์ ์ ์ํฉ๋๋ค. ๋ง์ง๋ง์ผ๋ก ์ปดํฌ๋ํธ๋ค์ ์ฐ๊ฒฐํ์ฌ ๋ฐ์ดํฐ๊ฐ ๊ทธ ์ฌ์ด๋ฅผ ํ๋ฌ๊ฐ๊ฒ ํฉ๋๋ค. ์ด ์์ต์์์๋ React๋ก ๊ฒ์ํ ์ ์๋ ์ํ ํ ์ด๋ธ์ ๋ง๋๋ ๊ณผ์ ์ ์ฒด๊ณ์ ์ผ๋ก ์๋ดํด ๋๋ฆฌ๊ฒ ์ต๋๋ค.
๋ชจ์ ์์๊ณผ ํจ๊ป ์์ํ๊ธฐ
์ด๋ฏธ JSON API์ ๋์์ด๋๋ก๋ถํฐ ์ ๊ณต๋ฐ์ ๋ชจ์ ์์์ด ์๋ค๊ณ ์๊ฐํด ๋ด ์๋ค. JSON API๋ ์๋์ ๊ฐ์ ํํ์ ๋ฐ์ดํฐ๋ฅผ ๋ฐํํฉ๋๋ค.
[
{ category: "Fruits", price: "$1", stocked: true, name: "Apple" },
{ category: "Fruits", price: "$1", stocked: true, name: "Dragonfruit" },
{ category: "Fruits", price: "$2", stocked: false, name: "Passionfruit" },
{ category: "Vegetables", price: "$2", stocked: true, name: "Spinach" },
{ category: "Vegetables", price: "$4", stocked: false, name: "Pumpkin" },
{ category: "Vegetables", price: "$1", stocked: true, name: "Peas" }
]
๋ชจ์ ์์์ ๋ค์๊ณผ ๊ฐ์ด ์๊ฒผ์ต๋๋ค.

React๋ก UI๋ฅผ ๊ตฌํํ๊ธฐ ์ํด์ ์ผ๋ฐ์ ์ผ๋ก ๋ค์ฏ ๊ฐ์ง ๋จ๊ณ๋ฅผ ๊ฑฐ์นฉ๋๋ค.
Step 1: UI๋ฅผ ์ปดํฌ๋ํธ ๊ณ์ธต์ผ๋ก ์ชผ๊ฐ๊ธฐ
๋จผ์ ๋ชจ์ ์์์ ์๋ ๋ชจ๋ ์ปดํฌ๋ํธ์ ํ์ ์ปดํฌ๋ํธ ์ฃผ๋ณ์ ๋ฐ์ค๋ฅผ ๊ทธ๋ฆฌ๊ณ ๊ทธ๋ค์๊ฒ ์ด๋ฆ์ ๋ถ์ด๋ฉด์ ์์ํด ๋ณด์ธ์. ๋์์ด๋์ ํจ๊ป ์ผํ๋ค๋ฉด ๊ทธ๋ค์ด ์ด๋ฏธ ๋์์ธ ํด์ ํตํ์ฌ ์ด ์ปดํฌ๋ํธ๋ค์ ์ด๋ฆ์ ์ ํด๋์์ ์๋ ์์ต๋๋ค. ํ๋ฒ ์ฌ์ญค๋ณด์ธ์!
์ด๋ค ๋ฐฐ๊ฒฝ์ ๊ฐ์ง๊ณ ์๋์ ๋ฐ๋ผ, ๋์์ธ์ ์ปดํฌ๋ํธ๋ก ๋๋๋ ๋ฐฉ๋ฒ์ ๋ํ ๊ด์ ์ด ๋ฌ๋ผ์ง ์ ์์ต๋๋ค.
- Programmingโ์๋ก์ด ํจ์๋ ๊ฐ์ฒด๋ฅผ ๋ง๋๋ ๋ฐฉ์์ผ๋ก ํด๋ด ์๋ค. ์ด ์ค ๋จ์ผ ์ฑ ์ ์์น์ ๋ฐ์ํ๊ณ ์ ํ๋ค๋ฉด ์ปดํฌ๋ํธ๋ ์ด์์ ์ผ๋ก๋ ํ ๋ฒ์ ํ ๊ฐ์ง ์ผ๋ง ํด์ผ ํฉ๋๋ค. ๋ง์ฝ ์ปดํฌ๋ํธ๊ฐ ์ ์ ์ปค์ง๋ค๋ฉด ์์ ํ์ ์ปดํฌ๋ํธ๋ก ์ชผ๊ฐ์ ธ์ผ ํ๊ฒ ์ฃ .
- CSSโํด๋์ค ์ ํ์๋ฅผ ๋ฌด์์ผ๋ก ๋ง๋ค์ง ์๊ฐํด ๋ด ์๋ค. (์ค์ ์ปดํฌ๋ํธ๋ค์ ์ฝ๊ฐ ๋ ์ธ๋ถ๋์ด ์์ต๋๋ค.)
- Designโ๋์์ธ ๊ณ์ธต์ ์ด๋ค ์์ผ๋ก ๊ตฌ์ฑํ ์ง ์๊ฐํด ๋ด ์๋ค.
JSON์ด ์ ๊ตฌ์กฐํ ๋์ด์๋ค๋ฉด, ์ข ์ข ์ด๊ฒ์ด UI์ ์ปดํฌ๋ํธ ๊ตฌ์กฐ๊ฐ ์์ฐ์ค๋ฝ๊ฒ ๋ฐ์ดํฐ ๋ชจ๋ธ์ ๋์๋๋ค๋ ๊ฒ์ ๋ฐ๊ฒฌํ ์ ์์ต๋๋ค. ์ด๋ UI์ ๋ฐ์ดํฐ ๋ชจ๋ธ์ ๋ณดํต ๊ฐ์ ์ ๋ณด ์ํคํ ์ฒ, ์ฆ ๊ฐ์ ๊ตฌ์กฐ๋ฅผ ๊ฐ์ง๊ธฐ ๋๋ฌธ์ ๋๋ค. UI๋ฅผ ์ปดํฌ๋ํธ๋ก ๋ถ๋ฆฌํ๊ณ , ๊ฐ ์ปดํฌ๋ํธ๊ฐ ๋ฐ์ดํฐ ๋ชจ๋ธ์ ๋งค์นญ๋ ์ ์๋๋ก ํ์ธ์.
์ฌ๊ธฐ ๋ค์ฏ ๊ฐ์ ์ปดํฌ๋ํธ๊ฐ ์์ต๋๋ค.

FilterableProductTable
(ํ์): ์์ ์ ์ฒด๋ฅผ ํฌ๊ดํฉ๋๋ค.SearchBar
(ํ๋์): ์ฌ์ฉ์์ ์ ๋ ฅ์ ๋ฐ์ต๋๋ค.ProductTable
(๋ผ๋ฒค๋์): ๋ฐ์ดํฐ ๋ฆฌ์คํธ๋ฅผ ๋ณด์ฌ์ฃผ๊ณ , ์ฌ์ฉ์์ ์ ๋ ฅ์ ๊ธฐ๋ฐ์ผ๋ก ํํฐ๋งํฉ๋๋ค.ProductCategoryRow
(์ด๋ก์): ๊ฐ ์นดํ ๊ณ ๋ฆฌ์ ํค๋๋ฅผ ๋ณด์ฌ์ค๋๋ค.ProductRow
(๋ ธ๋์): ๊ฐ๊ฐ์ ์ ํ์ ํด๋นํ๋ ํ์ ๋ณด์ฌ์ค๋๋ค.
ProductTable
์ ๋ณด๋ฉด โNameโ๊ณผ โPriceโ ๋ ์ด๋ธ์ ํฌํจํ ํ
์ด๋ธ ํค๋ ๊ธฐ๋ฅ๋ง์ ๊ฐ์ง ์ปดํฌ๋ํธ๋ ์์ต๋๋ค. ๋
๋ฆฝ๋ ์ปดํฌ๋ํธ๋ฅผ ๋ฐ๋ก ์์ฑํ ์ง ์์ฑํ์ง ์์์ง๋ ๋น์ ์ ์ ํ์
๋๋ค. ์ด ์์์์๋ ProductTable
์ ์์ ๋จ์ํ ํค๋๋ค์ด ProductTable
์ ์ผ๋ถ์ด๊ธฐ ๋๋ฌธ์ ์ ๋ ์ด๋ธ๋ค์ ์ปดํฌ๋ํธ๋ก ๋ง๋ค์ง ์๊ณ ๊ทธ๋ฅ ๋จ๊ฒจ๋์์ต๋๋ค. ๊ทธ๋ฌ๋ ์ด ํค๋๊ฐ ๋ณต์กํด์ง๋ฉด (์ฆ ์ ๋ ฌ์ ์ํ ๊ธฐ๋ฅ์ ์ถ๊ฐํ๋ ๋ฑ) ProductTableHeader
์ปดํฌ๋ํธ๋ฅผ ๋ง๋๋ ๊ฒ์ด ๋ ํฉ๋ฆฌ์ ์ผ ๊ฒ์
๋๋ค.
์ด์ ๋ชจ์ ์์ ๋ด์ ์ปดํฌ๋ํธ๋ค์ ํ์ธํ์ผ๋, ์ด๋ค์ ๊ณ์ธต ๊ตฌ์กฐ๋ก ์ ๋ฆฌํด ๋ด ์๋ค. ๋ชจ์ ์์์์ ํ ์ปดํฌ๋ํธ ๋ด์ ์๋ ๋ค๋ฅธ ์ปดํฌ๋ํธ๋ ๊ณ์ธต ๊ตฌ์กฐ์์ ์์์ผ๋ก ํํ๋ฉ๋๋ค.
FilterableProductTable
SearchBar
ProductTable
ProductCategoryRow
ProductRow
Step 2: React๋ก ์ ์ ์ธ ๋ฒ์ ๊ตฌํํ๊ธฐ
์ด์ ์ปดํฌ๋ํธ ๊ณ์ธต๊ตฌ์กฐ๋ฅผ ๋ง๋ค์์ผ๋, ์ฑ์ ์ค์ ๋ก ๊ตฌํํด ๋ณผ ์๊ฐ์ ๋๋ค. ๊ฐ์ฅ ์ฌ์ด ์ ๊ทผ ๋ฐฉ๋ฒ์ ์ํธ์์ฉ ๊ธฐ๋ฅ์ ์์ง ์ถ๊ฐํ์ง ์๊ณ ๋ฐ์ดํฐ ๋ชจ๋ธ๋ก๋ถํฐ UI๋ฅผ ๋ ๋๋งํ๋ ๋ฒ์ ์ ๋ง๋๋ ๊ฒ์ ๋๋ค. ๋์ฒด๋ก ๋จผ์ ์ ์ ์ธ ๋ฒ์ ์ ๋ง๋ค๊ณ ์ํธ์์ฉ ๊ธฐ๋ฅ์ ์ถ๊ฐํ๋ ๊ฒ ๋ ์ฝ์ต๋๋ค. ์ ์ ๋ฒ์ ์ ๋ง๋๋ ๊ฒ์ ๋ง์ ํ์ดํ์ด ํ์ํ์ง๋ง, ์๊ฐํ ๊ฒ์ ์ ์ผ๋ฉฐ, ๋ฐ๋๋ก ์ํธ์์ฉ ๊ธฐ๋ฅ์ ์ถ๊ฐํ๋ ๊ฒ์ ๋ง์ ์๊ฐ์ด ํ์ํ์ง๋ง, ํ์ดํ์ ๊ทธ๋ฆฌ ๋ง์ด ํ์ํ์ง ์์ต๋๋ค.
๋ฐ์ดํฐ ๋ชจ๋ธ์ ๋ ๋๋งํ๋ ์ฑ์ ์ ์ ์ธ ๋ฒ์ ์ ๋ง๋ค๊ธฐ ์ํด ๋ค๋ฅธ ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฌ์ฉํ๊ณ Props๋ฅผ ์ด์ฉํ์ฌ ๋ฐ์ดํฐ๋ฅผ ๋๊ฒจ์ฃผ๋ ์ปดํฌ๋ํธ๋ฅผ ๊ตฌํํ ์ ์์ต๋๋ค. Props๋ ๋ถ๋ชจ๊ฐ ์์์๊ฒ ๋ฐ์ดํฐ๋ฅผ ๋๊ฒจ์ค ๋ ์ฌ์ฉํ ์ ์๋ ๋ฐฉ๋ฒ์ ๋๋ค. (ํน์ State ๊ฐ๋ ์ ์ต์ํ๋ค๊ณ ํด๋ ์ ์ ์ธ ๋ฒ์ ์ ๋ง๋๋ ๋ฐ๋ State๋ฅผ ์ฐ์ง ๋ง์ธ์! State๋ ์ค์ง ์ํธ์์ฉ์ ์ํด, ์ฆ ์๊ฐ์ด ์ง๋จ์ ๋ฐ๋ผ ๋ฐ์ดํฐ๊ฐ ๋ฐ๋๋ ๊ฒ์ ์ฌ์ฉํฉ๋๋ค. ์ฐ๋ฆฌ๋ ์ฑ์ ์ ์ ๋ฒ์ ์ ๋ง๋ค๊ณ ์๊ธฐ ๋๋ฌธ์ ์ง๊ธ์ ํ์ํ์ง ์์ต๋๋ค.)
์ฑ์ ๋ง๋ค ๋ ๊ณ์ธต ๊ตฌ์กฐ์ ๋ฐ๋ผ ์์ธต๋ถ์ ์๋ ์ปดํฌ๋ํธ (์ฆ, FilterableProductTable
๋ถํฐ ์์ํ๋ ๊ฒ)๋ถํฐ ํํฅ์Top-Down์ผ๋ก ๋ง๋ค๊ฑฐ๋ ํน์ ํ์ธต๋ถ์ ์๋ ์ปดํฌ๋ํธ (ProductRow
)๋ถํฐ ์ํฅ์Bottom-Up์ผ๋ก ๋ง๋ค ์ ์์ต๋๋ค. ๊ฐ๋จํ ์์์์๋ ๋ณดํต ํํฅ์์ผ๋ก ๋ง๋๋ ๊ฒ ์ฝ์ง๋ง, ํ๋ก์ ํธ๊ฐ ์ปค์ง๋ฉด ์ํฅ์์ผ๋ก ๋ง๋ค๊ณ ํ
์คํธ๋ฅผ ์์ฑํ๋ฉด์ ๊ฐ๋ฐํ๋ ๊ฒ์ด ๋ ์ฝ์ต๋๋ค.
function ProductCategoryRow({ category }) { return ( <tr> <th colSpan="2"> {category} </th> </tr> ); } function ProductRow({ product }) { const name = product.stocked ? product.name : <span style={{ color: 'red' }}> {product.name} </span>; return ( <tr> <td>{name}</td> <td>{product.price}</td> </tr> ); } function ProductTable({ products }) { const rows = []; let lastCategory = null; products.forEach((product) => { if (product.category !== lastCategory) { rows.push( <ProductCategoryRow category={product.category} key={product.category} /> ); } rows.push( <ProductRow product={product} key={product.name} /> ); lastCategory = product.category; }); return ( <table> <thead> <tr> <th>Name</th> <th>Price</th> </tr> </thead> <tbody>{rows}</tbody> </table> ); } function SearchBar() { return ( <form> <input type="text" placeholder="Search..." /> <label> <input type="checkbox" /> {' '} Only show products in stock </label> </form> ); } function FilterableProductTable({ products }) { return ( <div> <SearchBar /> <ProductTable products={products} /> </div> ); } const PRODUCTS = [ {category: "Fruits", price: "$1", stocked: true, name: "Apple"}, {category: "Fruits", price: "$1", stocked: true, name: "Dragonfruit"}, {category: "Fruits", price: "$2", stocked: false, name: "Passionfruit"}, {category: "Vegetables", price: "$2", stocked: true, name: "Spinach"}, {category: "Vegetables", price: "$4", stocked: false, name: "Pumpkin"}, {category: "Vegetables", price: "$1", stocked: true, name: "Peas"} ]; export default function App() { return <FilterableProductTable products={PRODUCTS} />; }
(์ ์ฝ๋๊ฐ ์ด๋ ต๊ฒ ๋๊ปด์ง๋ค๋ฉด, ๋น ๋ฅด๊ฒ ์์ํ๊ธฐ๋ฅผ ๋จผ์ ์ฐธ๊ณ ํ์ธ์!)
์ด ๋จ๊ณ๊ฐ ๋๋๋ฉด ๋ฐ์ดํฐ ๋ ๋๋ง์ ์ํด ๋ง๋ค์ด์ง ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ์ปดํฌ๋ํธ๋ค์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๊ฐ์ง๊ฒ ๋ฉ๋๋ค. ํ์ฌ๋ ์ฑ์ ์ ์ ๋ฒ์ ์ด๊ธฐ ๋๋ฌธ์ ์ปดํฌ๋ํธ๋ ๋จ์ํ JSX๋ง ๋ฆฌํดํฉ๋๋ค. ๊ณ์ธต๊ตฌ์กฐ์ ์ต์๋จ ์ปดํฌ๋ํธ FilterableProductTable
์ Prop์ผ๋ก ๋ฐ์ดํฐ ๋ชจ๋ธ์ ๋ฐ์ต๋๋ค. ์ด๋ ๋ฐ์ดํฐ๊ฐ ์ต์๋จ ์ปดํฌ๋ํธ๋ถํฐ ํธ๋ฆฌ์ ๋งจ ์๋๊น์ง ํ๋ฌ๊ฐ๊ธฐ ๋๋ฌธ์ ๋จ๋ฐฉํฅ ๋ฐ์ดํฐ ํ๋ฆ์ด๋ผ๊ณ ๋ถ๋ฆ
๋๋ค.
Step 3: ์ต์ํ์ ๋ฐ์ดํฐ๋ง ์ด์ฉํด์ ์๋ฒฝํ๊ฒ UI State ํํํ๊ธฐ
UI๋ฅผ ์ํธ์์ฉInteractiveํ๊ฒ ๋ง๋ค๋ ค๋ฉด ์ฌ์ฉ์๊ฐ ๊ธฐ๋ฐ ๋ฐ์ดํฐ ๋ชจ๋ธ์ ๋ณ๊ฒฝํ ์ ์๊ฒ ํด์ผ ํฉ๋๋ค. React๋ State๋ฅผ ํตํด ๊ธฐ๋ฐ ๋ฐ์ดํฐ ๋ชจ๋ธ์ ๋ณ๊ฒฝํ ์ ์๊ฒ ํฉ๋๋ค.
State๋ ์ฑ์ด ๊ธฐ์ตํด์ผ ํ๋, ๋ณ๊ฒฝํ ์ ์๋ ๋ฐ์ดํฐ์ ์ต์ ์งํฉ์ด๋ผ๊ณ ์๊ฐํ์ธ์. State๋ฅผ ๊ตฌ์กฐํํ๋ ๋ฐ ๊ฐ์ฅ ์ค์ํ ์์น์ ์ค๋ณต ๋ฐฐ์ ์์นDonโt Repeat Yourself์ ๋๋ค. ์ ํ๋ฆฌ์ผ์ด์ ์ด ํ์๋ก ํ๋ ๊ฐ์ฅ ์ต์ํ์ State๋ฅผ ํ์ ํ๊ณ ๋๋จธ์ง ๋ชจ๋ ๊ฒ๋ค์ ํ์์ ๋ฐ๋ผ ์ค์๊ฐ์ผ๋ก ๊ณ์ฐํ์ธ์. ์๋ฅผ ๋ค์ด, ์ผํ ๋ฆฌ์คํธ๋ฅผ ๋ง๋ ๋ค๊ณ ํ๋ฉด ๋น์ ์ ๋ฐฐ์ด์ ์ํ ์์ดํ ๋ค์ ๋ฃ์ ๊ฒ๋๋ค. UI์ ์ํ ์์ดํ ์ ๊ฐ์๋ฅผ ๋ ธ์ถํ๊ณ ์ถ๋ค๊ณ ํ๋ฉด ์ํ ์์ดํ ๊ฐ์๋ฅผ ๋ฐ๋ก State ๊ฐ์ผ๋ก ๊ฐ์ง๋ ๊ฒ ์๋๋ผ ๋จ์ํ๊ฒ ๋ฐฐ์ด์ ๊ธธ์ด๋ง ์ฐ๋ฉด ๋ฉ๋๋ค.
์์ ์ ํ๋ฆฌ์ผ์ด์ ๋ด ๋ฐ์ดํฐ๋ค์ ์๊ฐํด ๋ด ์๋ค. ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ค์๊ณผ ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ง๊ณ ์์ต๋๋ค.
- ์ ํ์ ์๋ณธ ๋ชฉ๋ก
- ์ฌ์ฉ์๊ฐ ์ ๋ ฅํ ๊ฒ์์ด
- ์ฒดํฌ๋ฐ์ค์ ๊ฐ
- ํํฐ๋ง๋ ์ ํ ๋ชฉ๋ก
์ด ์ค ์ด๋ค ๊ฒ State๊ฐ ๋์ด์ผ ํ ๊น์? ์๋์ ์ธ ๊ฐ์ง ์ง๋ฌธ์ ํตํด ๊ฒฐ์ ํ ์ ์์ต๋๋ค.
- ์๊ฐ์ด ์ง๋๋ ๋ณํ์ง ์๋์? ๊ทธ๋ฌ๋ฉด ํ์คํ State๊ฐ ์๋๋๋ค.
- ๋ถ๋ชจ๋ก๋ถํฐ Props๋ฅผ ํตํด ์ ๋ฌ๋ฉ๋๊น? ๊ทธ๋ฌ๋ฉด ํ์คํ State๊ฐ ์๋๋๋ค.
- ์ปดํฌ๋ํธ ์์ ๋ค๋ฅธ State๋ Props๋ฅผ ๊ฐ์ง๊ณ ๊ณ์ฐ ๊ฐ๋ฅํ๊ฐ์? ๊ทธ๋ ๋ค๋ฉด ์ ๋๋ก State๊ฐ ์๋๋๋ค!
๊ทธ ์ธ ๋จ๋ ๊ฑด ์๋ง State์ผ ๊ฒ๋๋ค.
์ ๋ฐ์ดํฐ๋ค์ ๋ค์ ํ๋ฒ ์์๋๋ก ์ดํด๋ด ์๋ค.
- ์ ํ์ ์๋ณธ ๋ชฉ๋ก์ Props๋ก ์ ๋ฌ๋์๊ธฐ ๋๋ฌธ์ State๊ฐ ์๋๋๋ค.
- ์ฌ์ฉ์๊ฐ ์ ๋ ฅํ ๊ฒ์์ด๋ ์๊ฐ์ ๋ฐ๋ผ ๋ณํ๊ณ , ๋ค๋ฅธ ์์๋ก๋ถํฐ ๊ณ์ฐํ ์ ์๊ธฐ ๋๋ฌธ์ State๋ก ๋ณผ ์ ์์ต๋๋ค.
- ์ฒดํฌ๋ฐ์ค์ ๊ฐ์ ์๊ฐ์ ๋ฐ๋ผ ๋ฐ๋๊ณ ๋ค๋ฅธ ์์๋ก๋ถํฐ ๊ณ์ฐํ ์ ์๊ธฐ ๋๋ฌธ์ State๋ก ๋ณผ ์ ์์ต๋๋ค.
- ํํฐ๋ง๋ ์ ํ ๋ชฉ๋ก์ ์๋ณธ ์ ํ ๋ชฉ๋ก์ ๋ฐ์์ ๊ฒ์์ด์ ์ฒดํฌ๋ฐ์ค์ ๊ฐ์ ๋ฐ๋ผ ๊ณ์ฐํ ์ ์์ผ๋ฏ๋ก, ์ด๋ State๊ฐ ์๋๋๋ค.
๋ฐ๋ผ์, ๊ฒ์์ด์ ์ฒดํฌ๋ฐ์ค์ ๊ฐ๋ง์ด State์ ๋๋ค! ์ํ์ จ์ต๋๋ค!
์์ธํ ์ดํด๋ณด๊ธฐ
React๋ Props์ State๋ผ๋ ๋ ๊ฐ์ ๋ฐ์ดํฐ โ๋ชจ๋ธโ์ด ์กด์ฌํฉ๋๋ค. ๋์ ์ฑ๊ฒฉ์ ๋งค์ฐ ๋ค๋ฆ ๋๋ค.
- Props๋ ํจ์๋ฅผ ํตํด ์ ๋ฌ๋๋ ์ธ์ ๊ฐ์ ์ฑ๊ฒฉ์ ๊ฐ์ง๋๋ค. Props๋ ๋ถ๋ชจ ์ปดํฌ๋ํธ๋ก๋ถํฐ ์์ ์ปดํฌ๋ํธ๋ก ๋ฐ์ดํฐ๋ฅผ ๋๊ฒจ์ ์ธ๊ด์ ์ปค์คํฐ๋ง์ด์งํ๊ฒ ํด์ค๋๋ค. ์๋ฅผ ๋ค์ด,
Form
์color
๋ผ๋ Prop์Button
์ผ๋ก ๋ณด๋ด์Button
์ ๋ด๊ฐ ์ํ๋ ํํ๋ก ์ปค์คํฐ๋ง์ด์งํ ์ ์์ต๋๋ค. - State๋ ์ปดํฌ๋ํธ์ ๋ฉ๋ชจ๋ฆฌ ๊ฐ์ ์ฑ๊ฒฉ์ ๊ฐ์ง๋๋ค. State๋ ์ปดํฌ๋ํธ๊ฐ ๋ช๋ช ์ ๋ณด๋ฅผ ๊ณ์ ๋ฐ๋ผ๊ฐ ์ ์๊ฒ ํด์ฃผ๊ณ ๋ณํํ๋ฉด์ ์ํธ์์ฉInteraction์ ๋ง๋ค์ด ๋
๋๋ค. ์๋ฅผ ๋ค์ด,
Button
์isHovered
๋ผ๋ State๋ฅผ ๋ฐ๋ผ๊ฐ ๊ฒ์ ๋๋ค.
Props์ State๋ ๋ค๋ฅด์ง๋ง, ํจ๊ป ๋์ํฉ๋๋ค. State๋ ๋ณดํต ๋ถ๋ชจ ์ปดํฌ๋ํธ์ ์ ์ฅํฉ๋๋ค. (๊ทธ๋์ ๋ถ๋ชจ ์ปดํฌ๋ํธ๋ ๊ทธ State๋ฅผ ๋ณ๊ฒฝํ ์ ์์ต๋๋ค.) ๊ทธ๋ฆฌ๊ณ ๋ถ๋ชจ ์ปดํฌ๋ํธ๋ State๋ฅผ ์์ ์ปดํฌ๋ํธ์ Props๋ก์ ์ ๋ฌํฉ๋๋ค. ์ฒ์ ๋ดค์ ๋ ๋์ ์ฐจ์ด๋ฅผ ์ ์๊ธฐ ์ด๋ ค์๋ ๊ด์ฐฎ์ต๋๋ค. ์ฝ๊ฐ ์ฐ์ต์ด ํ์ํ ๊ฑฐ์์!
Step 4: State๊ฐ ์ด๋์ ์์ด์ผ ํ ์ง ์ ํ๊ธฐ
์ด์ ์ฑ์์ ํ์ํ ์ต์ํ์ State๋ฅผ ๊ฒฐ์ ํ์ต๋๋ค. ๋ค์์ผ๋ก๋ ์ด๋ค ์ปดํฌ๋ํธ๊ฐ ์ด State๋ฅผ ์์ ํ๊ณ , ๋ณ๊ฒฝํ ์ฑ ์์ ์ง๊ฒ ํ ์ง ์ ํด์ผ ํฉ๋๋ค. React๋ ํญ์ ์ปดํฌ๋ํธ ๊ณ์ธต๊ตฌ์กฐ๋ฅผ ๋ฐ๋ผ ๋ถ๋ชจ์์ ์์์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ๋ ๋จ๋ฐฉํฅ ๋ฐ์ดํฐ ํ๋ฆ์ ์ฌ์ฉํ๋ ๊ฒ์ ๊ธฐ์ตํ์ธ์! ์ฑ์ ๊ตฌํํ๋ฉด์ ์ด๋ค ์ปดํฌ๋ํธ๊ฐ State๋ฅผ ๊ฐ์ ธ์ผ ํ๋ ์ง ๋ช ํํ์ง ์์ ์ ์์ต๋๋ค. ์ด ๊ฐ๋ ์ด ์ฒ์์ด๋ผ๋ฉด ๋ ์ด๋ ค์ธ ์ ์์ต๋๋ค. ๊ทธ๋ฌ๋ ์๋์ ๊ณผ์ ์ ๋ฐ๋ผ๊ฐ๋ฉด ํด๊ฒฐํ ์ ์์ต๋๋ค.
์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฐ State์ ๋ํด์,
- ํด๋น State๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋ ๋๋งํ๋ ๋ชจ๋ ์ปดํฌ๋ํธ๋ฅผ ์ฐพ์ผ์ธ์.
- ๊ทธ๋ค์ ๊ฐ์ฅ ๊ฐ๊น์ด ๊ณตํต๋๋ ๋ถ๋ชจ ์ปดํฌ๋ํธ๋ฅผ ์ฐพ์ผ์ธ์. ๊ณ์ธต์์ ๋ชจ๋๋ฅผ ํฌ๊ดํ๋ ์์ ์ปดํฌ๋ํธ์ ๋๋ค.
- State๊ฐ ์ด๋์ ์์นํด์ผ ํ๋์ง ๊ฒฐ์ ํฉ๋๋ค.
- ๋๊ฐ, ๊ณตํต ๋ถ๋ชจ์ State๋ฅผ ๊ทธ๋ฅ ๋๋ฉด ๋ฉ๋๋ค.
- ํน์, ๊ณตํต ๋ถ๋ชจ ์์ ์ปดํฌ๋ํธ์ ๋ฌ๋ ๋ฉ๋๋ค.
- State๋ฅผ ์์ ํ ์ ์ ํ ์ปดํฌ๋ํธ๋ฅผ ์ฐพ์ง ๋ชปํ๋ค๋ฉด, State๋ฅผ ์์ ํ๋ ์ปดํฌ๋ํธ๋ฅผ ํ๋ ๋ง๋ค์ด์ ์์ ๊ณ์ธต์ ์ถ๊ฐํ์ธ์.
์ด์ ๋จ๊ณ์์, ์ด ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ ๊ฐ์ง State์ธ ์ฌ์ฉ์์ ๊ฒ์์ด ์ ๋ ฅ๊ณผ ์ฒดํฌ ๋ฐ์ค์ ๊ฐ์ ๋ฐ๊ฒฌํ์์ต๋๋ค. ์ด ์์์์ ๊ทธ๋ค์ ํญ์ ํจ๊ป ๋ํ๋๊ธฐ ๋๋ฌธ์ ๊ฐ์ ์์น์ ๋๋ ๊ฒ์ด ํฉ๋ฆฌ์ ์ ๋๋ค.
์ด์ ์ด ์ ๋ต์ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ ์ฉํด ๋ด ์๋ค.
- State๋ฅผ ์ฐ๋ ์ปดํฌ๋ํธ๋ฅผ ์ฐพ์๋ด
์๋ค:
ProductTable
์ State์ ๊ธฐ๋ฐํ ์ํ ๋ฆฌ์คํธ๋ฅผ ํํฐ๋งํด์ผ ํฉ๋๋ค. (๊ฒ์์ด์ ์ฒดํฌ ๋ฐ์ค์ ๊ฐ)SearchBar
๋ State๋ฅผ ํ์ํด ์ฃผ์ด์ผ ํฉ๋๋ค. (๊ฒ์์ด์ ์ฒดํฌ ๋ฐ์ค์ ๊ฐ)
- ๊ณตํต ๋ถ๋ชจ๋ฅผ ์ฐพ์๋ด
์๋ค: ๋ ๋ชจ๋๊ฐ ๊ณต์ ํ๋ ์ฒซ ๋ฒ์งธ ๋ถ๋ชจ๋
FilterableProductTable
์ ๋๋ค. - ์ด๋์ State๊ฐ ์กด์ฌํด์ผ ํ ์ง ์ ํด๋ด
์๋ค: ์ฐ๋ฆฌ๋
FilterableProductTable
์ ๊ฒ์์ด์ ์ฒดํฌ ๋ฐ์ค ๊ฐ์ State๋ก ๋ ๊ฒ๋๋ค.
์ด์ State ๊ฐ์ FilterableProductTable
์์ ์์ต๋๋ค.
useState()
Hook์ ์ด์ฉํด์ State๋ฅผ ์ปดํฌ๋ํธ์ ์ถ๊ฐํ์ธ์. Hook์ React ๊ธฐ๋ฅ์ โ์ฐ๊ฒฐํ ์Hook Intoโ ์๊ฒ ํด์ฃผ๋ ํน๋ณํ ํจ์์
๋๋ค. FilterableProductTable
์ ์๋จ์ ๋ ๊ฐ์ State ๋ณ์๋ฅผ ์ถ๊ฐํด์ ์ด๊น๊ฐ์ ๋ช
ํํ๊ฒ ๋ณด์ฌ์ฃผ์ธ์.
function FilterableProductTable({ products }) {
const [filterText, setFilterText] = useState('');
const [inStockOnly, setInStockOnly] = useState(false);
๋ค์์ผ๋ก, filterText
์ inStockOnly
๋ฅผ ProductTable
์ SearchBar
์๊ฒ Props๋ก ์ ๋ฌํ์ธ์.
<div>
<SearchBar
filterText={filterText}
inStockOnly={inStockOnly} />
<ProductTable
products={products}
filterText={filterText}
inStockOnly={inStockOnly} />
</div>
์ด์ ์ ํ๋ฆฌ์ผ์ด์
์ด ์ด๋ป๊ฒ ๋์ํ๋์ง ์ ์ ์์ต๋๋ค. filterText
์ ์ด๊น๊ฐ์ useState('')
์์ useState('fruit')
๋ก ์์ ํด ๋ณด์ธ์. ๊ฒ์์ฐฝ๊ณผ ๋ฐ์ดํฐ ํ
์ด๋ธ์ด ๋ชจ๋ ์
๋ฐ์ดํธ๋จ์ ํ์ธํ ์ ์์ต๋๋ค.
import { useState } from 'react'; function FilterableProductTable({ products }) { const [filterText, setFilterText] = useState(''); const [inStockOnly, setInStockOnly] = useState(false); return ( <div> <SearchBar filterText={filterText} inStockOnly={inStockOnly} /> <ProductTable products={products} filterText={filterText} inStockOnly={inStockOnly} /> </div> ); } function ProductCategoryRow({ category }) { return ( <tr> <th colSpan="2"> {category} </th> </tr> ); } function ProductRow({ product }) { const name = product.stocked ? product.name : <span style={{ color: 'red' }}> {product.name} </span>; return ( <tr> <td>{name}</td> <td>{product.price}</td> </tr> ); } function ProductTable({ products, filterText, inStockOnly }) { const rows = []; let lastCategory = null; products.forEach((product) => { if ( product.name.toLowerCase().indexOf( filterText.toLowerCase() ) === -1 ) { return; } if (inStockOnly && !product.stocked) { return; } if (product.category !== lastCategory) { rows.push( <ProductCategoryRow category={product.category} key={product.category} /> ); } rows.push( <ProductRow product={product} key={product.name} /> ); lastCategory = product.category; }); return ( <table> <thead> <tr> <th>Name</th> <th>Price</th> </tr> </thead> <tbody>{rows}</tbody> </table> ); } function SearchBar({ filterText, inStockOnly }) { return ( <form> <input type="text" value={filterText} placeholder="Search..."/> <label> <input type="checkbox" checked={inStockOnly} /> {' '} Only show products in stock </label> </form> ); } const PRODUCTS = [ {category: "Fruits", price: "$1", stocked: true, name: "Apple"}, {category: "Fruits", price: "$1", stocked: true, name: "Dragonfruit"}, {category: "Fruits", price: "$2", stocked: false, name: "Passionfruit"}, {category: "Vegetables", price: "$2", stocked: true, name: "Spinach"}, {category: "Vegetables", price: "$4", stocked: false, name: "Pumpkin"}, {category: "Vegetables", price: "$1", stocked: true, name: "Peas"} ]; export default function App() { return <FilterableProductTable products={PRODUCTS} />; }
์์ง ํผ์ ์์ ํ๋ ์์ ์ด ์๋ํ์ง ์์์ ์ ์ํ์ธ์. ์ ์๋๋ฐ์ค์์ ์ฝ์ ์๋ฌ๊ฐ ๋ฐ์ํ๋ฉฐ, ๊ทธ ์ด์ ๋ฅผ ์ค๋ช ํ๊ฒ ์ต๋๋ค.
์์ ์๋ ์๋๋ฐ์ค๋ฅผ ๋ณด๋ฉด, ProductTable
๊ณผ SearchBar
๊ฐ filterText
์ inStockOnly
Props๋ฅผ ํTable, ์
๋ ฅ์ฐฝInput, ์ฒดํฌ ๋ฐ์ค๋ฅผ ๋ ๋๋งํ๊ธฐ ์ํด์ ์ฝ๊ณ ์์ต๋๋ค. ์๋ฅผ ๋ค๋ฉด, SearchBar
์
๋ ฅ์ฐฝ์ value
๋ฅผ ์๋์ ๊ฐ์ด ์ฑ์ฐ๊ณ ์์ต๋๋ค.
function SearchBar({ filterText, inStockOnly }) {
return (
<form>
<input
type="text"
value={filterText}
placeholder="Search..."/>
๊ทธ๋ฌ๋ ์์ง ์ฌ์ฉ์์ ํค๋ณด๋ ์ ๋ ฅ๊ณผ ๊ฐ์ ํ๋์ ๋ฐ์ํ๋ ์ฝ๋๋ ์์ฑํ์ง ์์์ต๋๋ค. ์ด ๊ณผ์ ์ ๋ง์ง๋ง ๋จ๊ณ์์ ์งํํ ์์ ์ ๋๋ค.
Step 5: ์ญ ๋ฐ์ดํฐ ํ๋ฆ ์ถ๊ฐํ๊ธฐ
์ง๊ธ๊น์ง ์ฐ๋ฆฌ๋ ๊ณ์ธต ๊ตฌ์กฐ ์๋๋ก ํ๋ฅด๋ Props์ State์ ํจ์๋ก์จ ์ฑ์ ๋ง๋ค์์ต๋๋ค. ์ด์ ์ฌ์ฉ์ ์
๋ ฅ์ ๋ฐ๋ผ State๋ฅผ ๋ณ๊ฒฝํ๋ ค๋ฉด ๋ฐ๋ ๋ฐฉํฅ์ ๋ฐ์ดํฐ ํ๋ฆ์ ๋ง๋ค์ด์ผ ํฉ๋๋ค. ์ด๋ฅผ ์ํด์๋ ๊ณ์ธต ๊ตฌ์กฐ์ ํ๋จ์ ์๋ ์ปดํฌ๋ํธ์์ FilterableProductTable
์ State๋ฅผ ์
๋ฐ์ดํธํ ์ ์์ด์ผ ํฉ๋๋ค.
React๋ ๋ฐ์ดํฐ ํ๋ฆ์ ๋ช ์์ ์ผ๋ก ๋ณด์ด๊ฒ ๋ง๋ค์ด ์ค๋๋ค. ๊ทธ๋ฌ๋ ์ด๋ ์ ํต์ ์ธ ์๋ฐฉํฅ ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ๋ณด๋ค ์กฐ๊ธ ๋ ๋ง์ ํ์ดํ์ด ํ์ํฉ๋๋ค.
4๋จ๊ณ์ ์์์์ ์ฒดํฌํ๊ฑฐ๋ ํค๋ณด๋๋ฅผ ํ์ดํํ ๊ฒฝ์ฐ UI์ ๋ณํ๊ฐ ์๊ณ ์
๋ ฅ์ ๋ฌด์ํ๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค. ์ด๊ฑด ์๋์ ์ผ๋ก <input value={filterText} />
๋ก ์ฝ๋๋ฅผ ์ฐ๋ฉด์ value
๋ผ๋ Prop์ด ํญ์FilterableProductTable
์ filterText
๋ผ๋ State๋ฅผ ํตํด์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ๋๋ก ์ ํ๊ธฐ ๋๋ฌธ์
๋๋ค. filterText
๋ผ๋ State๊ฐ ๋ณ๊ฒฝ๋๋ ๊ฒ ์๋๊ธฐ ๋๋ฌธ์ input์ value
๋ ๋ณํ์ง ์๊ณ ํ๋ฉด๋ ๋ฐ๋๋ ๊ฒ ์์ต๋๋ค.
์ฐ๋ฆฌ๋ ์ฌ์ฉ์๊ฐ input์ ๋ณ๊ฒฝํ ๋๋ง๋ค ์ฌ์ฉ์์ ์
๋ ฅ์ ๋ฐ์ํ ์ ์๋๋ก State๋ฅผ ์
๋ฐ์ดํธํ๊ธฐ๋ฅผ ์ํฉ๋๋ค. State๋ FilterableProductTable
์ด ๊ฐ์ง๊ณ ์๊ณ State ๋ณ๊ฒฝ์ ์ํด์๋ setFilterText
์ setInStockOnly
๋ฅผ ํธ์ถ์ ํ๋ฉด ๋ฉ๋๋ค. SearchBar
๊ฐ FilterableProductTable
์ State๋ฅผ ์
๋ฐ์ดํธํ ์ ์๋๋ก ํ๋ ค๋ฉด, ์ด ํจ์๋ค์ SearchBar
๋ก ์ ๋ฌํด์ผ ํฉ๋๋ค.
function FilterableProductTable({ products }) {
const [filterText, setFilterText] = useState('');
const [inStockOnly, setInStockOnly] = useState(false);
return (
<div>
<SearchBar
filterText={filterText}
inStockOnly={inStockOnly}
onFilterTextChange={setFilterText}
onInStockOnlyChange={setInStockOnly} />
SearchBar
์์ onChange
์ด๋ฒคํธ ํธ๋ค๋ฌ๋ฅผ ์ถ๊ฐํ์ฌ ๋ถ๋ชจ State๋ฅผ ๋ณ๊ฒฝํ ์ ์๋๋ก ๊ตฌํํ ์ ์์ต๋๋ค.
function SearchBar({
filterText,
inStockOnly,
onFilterTextChange,
onInStockOnlyChange
}) {
return (
<form>
<input
type="text"
value={filterText}
placeholder="Search..."
onChange={(e) => onFilterTextChange(e.target.value)}
/>
<label>
<input
type="checkbox"
checked={inStockOnly}
onChange={(e) => onInStockOnlyChange(e.target.checked)}
์ด์ ์ ํ๋ฆฌ์ผ์ด์ ์ด ์์ ํ ๋์ํฉ๋๋ค!
import { useState } from 'react'; function FilterableProductTable({ products }) { const [filterText, setFilterText] = useState(''); const [inStockOnly, setInStockOnly] = useState(false); return ( <div> <SearchBar filterText={filterText} inStockOnly={inStockOnly} onFilterTextChange={setFilterText} onInStockOnlyChange={setInStockOnly} /> <ProductTable products={products} filterText={filterText} inStockOnly={inStockOnly} /> </div> ); } function ProductCategoryRow({ category }) { return ( <tr> <th colSpan="2"> {category} </th> </tr> ); } function ProductRow({ product }) { const name = product.stocked ? product.name : <span style={{ color: 'red' }}> {product.name} </span>; return ( <tr> <td>{name}</td> <td>{product.price}</td> </tr> ); } function ProductTable({ products, filterText, inStockOnly }) { const rows = []; let lastCategory = null; products.forEach((product) => { if ( product.name.toLowerCase().indexOf( filterText.toLowerCase() ) === -1 ) { return; } if (inStockOnly && !product.stocked) { return; } if (product.category !== lastCategory) { rows.push( <ProductCategoryRow category={product.category} key={product.category} /> ); } rows.push( <ProductRow product={product} key={product.name} /> ); lastCategory = product.category; }); return ( <table> <thead> <tr> <th>Name</th> <th>Price</th> </tr> </thead> <tbody>{rows}</tbody> </table> ); } function SearchBar({ filterText, inStockOnly, onFilterTextChange, onInStockOnlyChange }) { return ( <form> <input type="text" value={filterText} placeholder="Search..." onChange={(e) => onFilterTextChange(e.target.value)} /> <label> <input type="checkbox" checked={inStockOnly} onChange={(e) => onInStockOnlyChange(e.target.checked)} /> {' '} Only show products in stock </label> </form> ); } const PRODUCTS = [ {category: "Fruits", price: "$1", stocked: true, name: "Apple"}, {category: "Fruits", price: "$1", stocked: true, name: "Dragonfruit"}, {category: "Fruits", price: "$2", stocked: false, name: "Passionfruit"}, {category: "Vegetables", price: "$2", stocked: true, name: "Spinach"}, {category: "Vegetables", price: "$4", stocked: false, name: "Pumpkin"}, {category: "Vegetables", price: "$1", stocked: true, name: "Peas"} ]; export default function App() { return <FilterableProductTable products={PRODUCTS} />; }
์ํธ์์ฉ ์ถ๊ฐํ๊ธฐ ์น์ ์์ State๋ฅผ ๋ณ๊ฒฝํ๊ณ ์ด๋ฒคํธ๋ฅผ ๋ค๋ฃจ๋ ๊ฒ์ ๋ํด ๋ ๊น์ด์๊ฒ ๋ฐฐ์ธ ์ ์์ต๋๋ค.
๋ ๋์๊ฐ๊ธฐ
์ง๊ธ๊น์ง๋ React๋ฅผ ์ด์ฉํด์ ์ปดํฌ๋ํธ์ ์ฑ์ ๋ง๋ค๋ ค๊ณ ํ ๋ ์ด๋ป๊ฒ ์ฌ๊ณ ํ ์ง์ ๋ํด ๊ฐ๋จํ ์๊ฐํ์ต๋๋ค. ๋น์ฅ React๋ก ํ๋ก์ ํธ๋ฅผ ์์ํด๋ ์ข๊ณ ๋ค์ ๋จ๊ณ๋ก ๋์ด๊ฐ์ ์ด ์์ต์๋ฅผ ์ด์ฉํด์ ์ข ๋ ์ฌํ ํ์ตํด๋ ์ข์ต๋๋ค.