
๐ styleํ๊ทธ๋ฅผ ์ด์ฉํ์ฌ ๋ณ๋์ css๋ฅผ ์ฌ์ฉํ์ง ์๊ณ , javaํ๊ทธ ๋ด๋ถ์ css๋ฅผ ์ฌ์ฉํ ์ ์์.
ex)
<h1 className="h1 bg-red-400"> hello </h1>
๐ https://tailwindcss.com/docs/guides/vite
content: [
"./index.html",
"./src/**/*.{js,ts,jsx,tsx}",
],
@tailwind base;
@tailwind components;
@tailwind utilities;
import CartDiv from "./CartDiv.jsx";
import ProductsList from "./ProductsList.jsx";
import {useState} from "react";
function KioskMain() {
const products = [
{pid:1, pname:'M1', price:8000, kind:'N', img:'http://localhost:8081/food/M1.jpeg'},
{pid:2, pname:'M2', price:1000, kind:'C', img:'http://localhost:8081/food/M2.jpeg'},
{pid:3, pname:'M3', price:2000, kind:'N', img:'http://localhost:8081/food/M3.jpeg'},
{pid:4, pname:'M4', price:3000, kind:'C', img:'http://localhost:8081/food/M4.jpeg'},
{pid:5, pname:'M5', price:4000, kind:'N', img:'http://localhost:8081/food/M5.jpeg'},
]
// cartItems - ์นดํธ ๋ด๋ถ์ํ, setCartItems - ์นดํธ์ ๋ด๊ฒผ์๋ ๋ค์ ์ํ
const [cartItems, setCartItems] = useState([])
// Cart์ ๋ด๋ ๊ธฐ๋ฅ
const addToCart = (newProduct) => { // newProduct = ์๋ก ์ถ๊ฐ๋๋ ๋ฌผํ
const target = cartItems.find( item => { // item = [{product{pid,pname,price,kind}, qty:1 }]
const p = item.product
console.log("item.product : " + p)
const result = p.pid === newProduct.pid // ์ฅ๋ฐ๊ตฌ๋์ ์๋ product์ pid์ ์๋ก๋ค์ด์จ pid
console.log("์ฅ๋ฐ๊ตฌ๋์ ์๋์ง? " + result)
return result ? p : null
})
if(!target) {
setCartItems([...cartItems, {product: newProduct, qty: 1}]) // ๊ธฐ์กด์นดํธ๊ฐ์ ์๋ก๋ค์ด์จ ๋ฌผํ์ ์ถ๊ฐ, ์๋์ ์
return
}
target.qty += 1 // ์ฅ๋ฐ๊ตฌ๋์ ์๋ค๋ฉด, ์๋๋ง ๋ณ๊ฒฝ
setCartItems([...cartItems]) // ๊ฐ์ฒด ์๋ก์์ฑ
}
const changeCartItem = (pid, amount) => {
const target = cartItems.find( item => item.product.pid === parseInt(pid)) // item = [{product{pid,pname,price,kind}, qty:1 }]
if(!target) {
return
}
target.qty += amount
if(target.qty <= 0) {
const filtered = cartItems.filter(item => item.product.pid !== parseInt(pid))
// ๋ด๊ฐ ์ ํํ์ง ์์ pid๊ฐ๋ง ๋จ๊ธฐ๊ธฐ
setCartItems(filtered)
return
}
setCartItems([...cartItems])
}
return (
<div className="w-full flex">
<div className='border-2 w-2/3 bg-blue-400'>
<ProductsList products={products} addToCart={addToCart}></ProductsList>
</div>
<div className='border-2 w-1/3 bg-red-400'>
<CartDiv cartItems={cartItems} changeCartItem={changeCartItem} ></CartDiv>
</div>
</div>
);
}
export default KioskMain;
๐ KioskMain์์๋ products, cartItems, setCartItems, addToCart, ChangeCartItem์ ์ ์ํ์ฌ CartDiv, ProductsList ๊ฐ๊ฐ์ ์ปดํฌ๋ํธ์ ๋ถ์ฌํ๋ค.
๐ products๋ ๋ฌผํ๊ณผ ๊ฐ๊ฒฉ์ด ์ถ๋ ฅ๋์ด์ผํ๊ณ , addToCart๋ ๋ฌผํ์ด ๋ด๊ฒจ์ผํ๋ ๊ธฐ๋ฅ์ด๋ฏ๋ก ProductList์ ๋ถ์ฌํ๋ค.
๐ cartItems๋ ์นดํธ์ ๋ด๊ธด ์ํ๋ฅผ ๋ณด์ฌ์ฃผ๊ณ , ChangeCartItem์ ์ฅ๋ฐ๊ตฌ๋์์ ์๋์ ๋ณ๊ฒฝํด์ผํ๋ฏ๋ก CartDiv์ ๋ถ์ฌํ๋ค.
function ProductsList({products,addToCart}) {
const liList = products.map( p => { // ์ฌ๊ธฐ์ p๊ฐ์ product(๊ฐ ์ํ๋ค์ ๋ปํจ. 1๋ฒ์ํ , 2๋ฒ์ํ์ ์ํ)
const {pid, pname, price, kind, img} = p
return <li className="w-1/6 border-2 h-1/6 min-h-[10rem]" key={pid} onClick={() => addToCart(p)}>
<img src={img}/>
{pname} -- {price}
</li>
})
return (
<div>
<h1>Products List</h1>
<div className='w-full h-full'>
<ul className='flex gap-2 flex-auto p-6'>
{liList}
</ul>
</div>
</div>
);
}
export default ProductsList;
๐ Main์์ ๋ถ์ฌ๋ฐ์ products,addToCart ๋๊ฐ์ ๊ธฐ๋ฅ์ ๋ถ์ฌํ๊ณ , products์์ ๊ฐ๊ฐ์ ์ํ(m1,m2,m3...)์ด ์์ํ ๋ฐ, ๊ทธ ์ํ์ ์์ฑ๊ฐ์ ์ถ๋ ฅํ๊ธฐ ์ํจ.
๐ addToCart์์๋ ์ ํํ ์ํ์ ๊ตฌ์กฐ๋ถํดํ ๋น๋ ๊ฐ์ ๋ฃ๋๋ค.
function CartDiv({cartItems, changeCartItem}) {
const cartItemsTag = cartItems.map((item) => { // item = {product{pid,pname,price,kind}, qty}
const product = item.product;
const qty = item.qty;
return (
<li key={product.pid} className='m-2 p-2'>
{product.name}: {qty} : {product.price * qty}
<button className='bg-blue-400 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded'
onClick={() => changeCartItem(product.pid, 1)}>+
</button>
<button className='bg-red-400 hover:bg-red-700 text-white font-bold py-2 px-4 rounded'
onClick={() => changeCartItem(product.pid, -1)}>-
</button>
</li>
)
})
return (
<div>
<h1>Cart Div</h1>
<div className='w-full h-full'>
{cartItemsTag}
</div>
</div>
);
}
export default CartDiv;
๐ main์์ ๋ฐ์ cartItems๋ฅผ ํตํด ์นดํธ์ ๋ด๊ธด ๋ฌผํ๋ค์ ์ถ๋ ฅํ๋ค.
์ฌ๊ธฐ์ item๊ฐ์ item = {product{pid,pname,price,kind}, qty} ์์ ์๊ฐํ์.
๐ ๋ฒํผ์ ํด๋ฆญํ์๋, CartItems๋ด๋ถ์ product๋ด๋ถ pid์์ฑ๊ฐ๊ณผ, amount์ธ 1๊ฐ์ด ์ ๋ฌ๋์ด ์๋์ด ๋ณ๊ฒฝ๋๋ค.
export interface IProduct {
pid : number;
pname : string;
price : number;
img? : string;
kind? : string;
}
export interface ICartItem {
product : IProduct, // product๋ IProduct์ ์์ฑ๊ฐ๋ค์ ๊ฐ์ง๋ฏ๋ก ํด๋น ๋ฐ์ดํฐํ์
์ ๊ฐ๊ฒ๋๋ค.
qty : number
}
๐ typeScript๋ type ํจํค์ง๋ฅผ ๋ฐ๋ก ์์ฑํด์ ์์ฑ๊ฐ๊ณผ ๋ฐ์ดํฐํ์ ์ ๋ฏธ๋ฆฌ ๊ตฌ์กฐํ๋ค.
๐ ์ฌ๊ธฐ์ img? ์ ๊ฐ์ ?๋ ์์์๋์๊ณ ์์์๋์๋ค๋ ์๋ฏธ
import ProductList from "./ProductList.tsx";
import CartDiv from "./CartDiv.tsx";
import {ICartItem, IProduct} from "../../type/kiosk.ts";
import {useState} from "react";
function KioskMain() {
const products : IProduct = [
{pid:1, pname:'M1', price:3000, img:'http://localhost:8081/food/M1.jpeg'},
{pid:2, pname:'M2', price:4000, img:'http://localhost:8081/food/M2.jpeg'},
{pid:3, pname:'M3', price:5000, img:'http://localhost:8081/food/M3.jpeg'},
{pid:4, pname:'M4', price:6000, img:'http://localhost:8081/food/M4.jpeg'},
{pid:5, pname:'M5', price:7000, img:'http://localhost:8081/food/M5.jpeg'},
]
const [ cartItems, setCartItems ] = useState<ICartItem[]>([]) // cartItems๋ ICartItem[]์ ํ์
์ ๊ฐ์ง๋ค.
const addToCart = (newProduct: IProduct):void => { // ํน์ ํ ๊ฐ์ ๋ฐํํ๋๊ฒ์ด ์๋๋ฏ๋ก void
const target: ICartItem|undefined = cartItems.find( item => item.product.pid === newProduct.pid)
// find์ ๋ง์กฑํ๋ ๊ฐ์ด ์๋ค๋ฉด undefined๋ฅผ ๋ฐํ -> ์ ํํ ๋ฌผ๊ฑด์ด ์ฅ๋ฐ๊ตฌ๋์ ์๋ค๋ฉด
if(!target) {
setCartItems([...cartItems, {product:newProduct, qty:1}])
return;
}
else{
target.qty += 1
setCartItems([...cartItems])
}
}
const changeQty = (pid:number, qty:number):void => {
const target: ICartItem|undefined = cartItems.find( item => item.product.pid === pid)
if(!target) {
return;
} else {
target.qty += qty
setCartItems([...cartItems])
}
}
return (
<div className='w-full h-full p-3 flex'>
<div className='w-2/3 bg-blue-400 m-2 p-2'>
<h1 className='p-2 font-bold' >Product List</h1>
<ProductList products={products} addToCart={addToCart}></ProductList>
</div>
<div className='w-1/3 bg-red-400 m-2 p-2'>
<h1 className='p-2 font-bold'>Cart List</h1>
<CartDiv cartItems={cartItems} changeQty={changeQty}></CartDiv>
</div>
</div>
);
}
export default KioskMain;
๐ cartItems์ ๊ฐ์ ์นดํธ ๋ด๋ถ์ ์ํ ๋ฐ์ดํฐ๋ ICartItem[] ์ ๋ฐ์ดํฐํ์ ์ ๊ฐ์ง๋ค.
๐ addCart์์ ์๋กญ๊ฒ ๋ด๊ธฐ๋ product์ ํ์ ์ IProduct์ ํ์ ์ด๋ฏ๋ก ๋ฏธ๋ฆฌ ์ ์ธํ๊ณ , target์ด ์๋ค๋ฉด ICartItem์ ํ์ ์ ๊ฐ๊ฒ ๋๊ณ , ์๋ค๋ฉด undefined ํ์ ์ ๊ฐ๋๋ค.
import {IProduct} from "../../type/kiosk.ts";
import {ReactElement} from "react";
interface ProductListProps {
products : IProduct[];
addToCart : (newProduct: IProduct) => void;
}
function ProductList({products, addToCart} : ProductListProps): ReactElement {
console.log(products);
const productLI = products.map((product: IProduct) => {
const {pid, pname, price, img} = product;
return (
<li className='border-2 w-1/5' key={pid} onClick={() => addToCart(product)}>
{img && <img src={img} alt={pname}/>}
{pname} -- {price}
</li>
)
})
return (
<div>
<ul className='flex flex-wrap'>
{productLI}
</ul>
</div>
);
}
export default ProductList;
๐ js์ ๋ค๋ฅด๊ฒ ts์์๋ ๋ถ์ฌ๋ฐ์ ๊ฐ์ฒด ๋ฐ ๋ฉ์๋๋ direct๋ก ์ฌ์ฉํ์ง ์๊ณ , interface๋ก ์์์ ์ ์ํ๋ค์ ์๋์์ ์ฌ์ฉํ๊ฒ ๋๋ค. ์๋์์๋ ({products, addToCart} : ProductListProps) ์ด์ฒ๋ผ ๋ถ์ฌ๋ฐ์ ๋ด์ฉ๋ค์ ์์ฑํ๊ณ , ReactElement๋ React ์ปดํฌ๋ํธ๋ก์ ์ ํจํ React ์๋ฆฌ๋จผํธ๋ฅผ ๋ฐํํด์ผ ํ๋ค๋ ๊ฒ์ ์๋ฏธํ๋ค.
๐ ์๋์ฝ๋๋ img๊ฐ ์์๊ฒฝ์ฐ img๋ฅผ ์ถ๋ ฅํ๋ค๋ ์๋ฏธ
{img && <img src={img} alt={pname}/>}
import {ICartItem, IProduct} from "../../type/kiosk.ts";
import {ReactElement} from "react";
interface CartListProps{
cartItems : ICartItem[]
changeQty : (pid: number, qty: number) => void;
}
function CartDiv({cartItems, changeQty}: CartListProps ):ReactElement {
console.log(cartItems)
const listLI = cartItems.map((item: ICartItem) => {
const {product, qty} = item
return <li key={product.pid} className='flex flex-wrap border-2 gap-3'>
{product.img && <img className='w-1/6' src={product.img}/>}
{product.pname} -- {qty} -- {product.price * qty}
<button onClick={()=> changeQty(product.pid,1)}>+</button>
<button onClick={()=>changeQty(product.pid,-1)}>-</button>
</li>
})
return (
<div>
{listLI}
</div>
);
}
export default CartDiv;
๐ ๋ถ์ฌ๋ฐ์ ๊ฐ์ฒด์ ๋ฉ์๋๋ฅผ interface๋ก ์ ์ํ๊ณ , changeQty๋ ๋งค๊ฐ๋ณ์๊ฐ๊ณผ ,return๊ฐ์ธ void๋ฅผ ์์ฑํ๋ค.
interface CartListProps{
cartItems : ICartItem[]
changeQty : (pid: number, qty: number) => void;
}
๐ ์๋์ฝ๋์์๋ return๊ฐ์ ๋ณต์กํจ์ ์ค์ด๊ธฐ์ํด listLI๋ผ๋ ๋ณ์๊ฐ์ return๊ฐ์ ๋ณ๋๋ก ๋ง๋ค์ด ์ต์ข return๊ฐ์์๋ listLI๋ณ์๋ง ์ฌ์ฉํ๋๋ก ํ๋ค.
const listLI = cartItems.map((item: ICartItem) => {
const {product, qty} = item
return <li key={product.pid} className='flex flex-wrap border-2 gap-3'>
{product.img && <img className='w-1/6' src={product.img}/>}
{product.pname} -- {qty} -- {product.price * qty}
<button onClick={()=> changeQty(product.pid,1)}>+</button>
<button onClick={()=>changeQty(product.pid,-1)}>-</button>
</li>
})
ํ์ ์ ์์ ์ฑ : JS์ ๋ฌ๋ฆฌ TS๋ type์ปดํฌ๋ํธ๋ด๋ถ์ interface๊ฐ ์๊ธฐ ๋๋ฌธ์, ์ ํ๊ณผ ์ฅ๋ฐ๊ตฌ๋์ ๊ตฌ์กฐ๋ฅผ ๋ช ํํ๊ฒ ํ ์ ์๋ค. ํ์ฌ, ๋ฐ์ดํฐํ์ ์ ๊ตฌ์กฐ์ ๋ํ ์ค๋ฅ๊ฐ ์ค์ด๋ ๋ค.
addToCart์์์ ๋ฐ์ดํฐํ์ ๋ช ์, changeQty์์์ ๋งค๊ฐ๋ณ์ ๋ช ์ํ์ฌ ์ค์๊ฐ ์ค์ด๋ฌ
์ ๋์ธํ์ : const target: ICartItem|undefined ์ ๊ฐ์ ์ฝ๋๋ฅผ ์ฌ์ฉํ์ฌ ๋ณ์์ ์ฌ๋ฌํ์ ์ ๋ช ์๊ฐ๋ฅ