Piece of Cake ๋ฒค๋ฉ๋จธ์ ์ ์ฌ์ฉ์๊ฐ ์์ง๊ธ์ ์ถฉ์ ํ๊ณ , ์ํ๋ ๋์ ํธ๋ฅผ ์ ํํด ์ฅ๋ฐ๊ตฌ๋์ ์ถ๊ฐํ ์ ์๋ ์ธํฐ๋ํฐ๋ธ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ๋๋ค.
๊ตฌํ ๊ธฐ๊ฐ : 2024.04.28 ~ 2024.04.30
๋ฐฐํฌ ์ฌ์ดํธ : ๋ฐฐํฌ URL ๋ฐ๋ก๊ฐ๊ธฐ
๐ฐ PIECE-OF-CAKE
โโ ๐ฆ public
โโ ๐ฆ src
โ โโ ๐ assets
โ โโ ๐ components
โ โ โโ ๐ Button
โ โ โโ ๐ Cake
โ โ โโ ๐ Cart
โ โ โโ ๐ Header
โ โ โโ ๐ Modal
โ โ โโ ๐ Section
โ โ โโ ๐ Slider
โ โ โโ ๐ User
โ โ โโ ๐ Wallet
โ โโ ๐ data
โ โโ ๐ hooks
โ โโ ๐ pages
โ โ โโ ๐ ErrorPage
โ โ โโ ๐ MainPage
โ โ โโ ๐ StartPage
โ โโ ๐ state
โ โ โโ ๐ atoms
โ โ โโ ๐ selectors
โ โโ ๐ styles
โ โโ ๐ types
| โโ ๐ App.tsx
| โโ ๐ index.tsx
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hook-form": "^7.51.4",
"react-router-dom": "^6.23.0",
"react-scripts": "5.0.1",
"recoil": "^0.7.7",
"styled-components": "^6.1.8",
"styled-reset": "^4.5.2",
"typescript": "^4.9.5",
"web-vitals": "^2.1.4"
์ด๊ธฐ ํ๋ฉด | ๋ฉ๋ด์ผ ๋ชจ๋ฌ |
---|---|
์์ง๊ธ ์ถฉ์ | ์ ๊ธ์ก ์ถฉ์ |
---|---|
์์ดํ ํ๋ | ์ฅ๋ฐ๊ตฌ๋ ์ด๊ธฐํ |
---|---|
๊ฑฐ์ค๋ฆ๋ ๋ฐํ | 404 ์๋ฌ |
---|---|
์ด๊ธ์ก์ ๊ณ์ฐํ ๋, calculateTotalPrice
ํจ์๋ฅผ ์ปดํฌ๋ํธ ๋ด์์ ์ง์ ํธ์ถํ๊ณ , useRecoilState
ํ
์ ํตํด ์ํ ์ ์ฅ์์ totalCartItemState
์ํ๋ฅผ ๊ฐ์ ธ์์ ์ฌ์ฉํ๊ณ ์๋ค.
ํ์ง๋ง ์ด๋ ์ปดํฌ๋ํธ ๋ด์ ๋ก์ง์ด ์์ฌ ๋ณต์ก์ฑ์ด ์ฆ๊ฐํ๊ณ , ์ํ ์ ๋ฐ์ดํธ์ ๊ฐ์ ๋ค๋ฅธ ๋ถ๋ถ์ ์ํฅ์ ๋ฏธ์น ์ ์๋ค!
// TotalCart.tsx
const TotalCart = () => {
const [cartItem, setCartItem] = useRecoilState(totalCartItemState);
const calculateTotalPrice = (): number => {
return cartItem.reduce(
(total: number, item: SelectedCakeItem) => total + item.cost * item.quantity,
0
);
};
}
...
return (
<>
<S.TotalPrice>์ด๊ธ์ก : {calculateTotalPrice().toLocaleString()}์</S.TotalPrice>
</>
)
}
๋ฐ๋ผ์ ์ด๊ธ์ก ๊ณ์ฐํ๋ ๋ถ๋ถ์ ์ ๋ ํฐ๋ฅผ ์ฌ์ฉํด ์ํ ๊ด๋ฆฌ ๋ก์ง์์ ๋ถ๋ฆฌ์์ผ ์ปดํฌ๋ํธ๋ UI์๋ง ์ง์คํ๊ณ , ์ ํ์๋ ๋ฐ์ดํฐ์ ๊ณ์ฐ๊ณผ ์ฒ๋ฆฌ์ ์ง์คํ ์ ์๋๋ก ํ๋ค.
// selector.ts
export const totalPriceSelector = selector<number>({
key: 'totalPriceSelector',
get: ({ get }) => {
const cartItems: SelectedCakeItem[] = get(totalCartItemState);
return cartItems.reduce(
(total: number, item: SelectedCakeItem): number => total + item.cost * item.quantity,
0
);
},
});
useRecoilValue
ํ
์ ํตํด ์
๋ ํฐ์์ ๋ง๋ totalPriceSelector
๋ฅผ ๊ฐ์ ธ์์ totalPrice
๋ณ์์ ์ ์ฅํด์ ์ฌ์ฉํ๋ฉด ์ฝ๋๊ฐ ๊ฐ๊ฒฐํด์ง๊ณ , ์ ์ง๋ณด์์ ์ฉ์ดํด์ง๋ค!
// TotalCart.tsx
const TotalCart = () => {
const [cartItem, setCartItem] = useRecoilState(totalCartItemState);
const totalPrice = useRecoilValue(totalPriceSelector);
...
return (
<>
<S.TotalPrice>์ด๊ธ์ก : {totalPrice.toLocaleString()}์</S.TotalPrice>
</>
)
}
์ฅ๋ฐ๊ตฌ๋ ์ด๊ธฐํ ๋ฒํผ์ ํด๋ฆญํ ๋, cartItemRefresh
ํจ์๋ฅผ ํตํด ์ฅ๋ฐ๊ตฌ๋์ ์๋ ์์ดํ
๋ค์ ์ด๊ธฐํ์ํค๋ฉฐ useRecoilState
ํ
์ ํตํด ์ํ ์ ์ฅ์์ totalCartItemState
์ํ๋ฅผ ๊ฐ์ ธ์์setCartItem
์ ์
๋ฐ์ดํธํ๊ณ ์๋ค.
์ด๊ธฐํ๊ฐ ๋๋ฉด์ ์ด๊ธ์ก์ด ๋ค์ ์์ก๊ณผ ํฉ์ฐ๋์ด ๊ณ์ฐ๋์ด์ผ ํ๋๋ฐ, ์ฅ๋ฐ๊ตฌ๋๋ง ์ด๊ธฐํ๊ฐ ์ด๋ฃจ์ด์ง๊ณ ์๋ค!
// TotalCart.tsx
const TotalCart = () => {
const [cartItem, setCartItem] = useRecoilState(totalCartItemState);
const cartItemRefresh = (): void => {
setCartItem([]);
};
...
return (
<>
<S.RefreshBtn onClick={cartItemRefresh} />
</>
)
}
๋ฐ๋ผ์ ์ฅ๋ฐ๊ตฌ๋๋ฅผ ์ด๊ธฐํํ ๋ค, ์ด๊ธ์ก๊ณผ ์์ง๊ธ์ ๋ํด์ฃผ๋ ๊ฐ์ ๊ณ์ฐํด์ผํ๊ธฐ ๋๋ฌธ์, ์
๋ ํฐ์์ ์ด๊ธ์ก + ์์ก์ ๊ณ์ฐํ๋ ์ ํ์ totalPriceAmountSelector
๋ฅผ ๋ง๋ค์๋ค.
// selector.ts
export const totalPriceAmountSelector = selector<number>({
key: 'totalPriceAmountSelector',
get: ({ get }) => {
const cartItems = get(totalPriceSelector);
const balance = get(balanceState);
return cartItems + balance;
},
});
๊ทธ๋ฆฌ๊ณ cartItemRefresh
ํจ์์์ useSetRecoilState
ํ
์ ํตํด ์ํ ์ ์ฅ์์์ ์์ก ๊ด๋ จ balanceState
์ํ๋ฅผ ๊ฐ์ ธ์จ๋ค.
๊ทธ ๋ค์, useRecoilValue
ํ
์ ํตํด totalPriceAmountSelector
์ ํ์๋ฅผ totalPriceBalance
๋ณ์์ ์ ์ฅํ๊ณ , setBalance
๋ฅผ ํตํด ํด๋น ๋ณ์๋ฅผ ์
๋ฐ์ดํธ ํ๋ฉด ๋๋ค!
์ด๋ ๊ฒ ํ๋ฉด ์ฅ๋ฐ๊ตฌ๋๋ฅผ ์ด๊ธฐํํ๋ ๋์์ ์์ก๋ ํจ๊ป ์ ๋ฐ์ดํธ๋๋ค! ๐
// TotalCart.tsx
const TotalCart = () => {
const [cartItem, setCartItem] = useRecoilState(totalCartItemState);
const totalPriceBalance = useRecoilValue(totalPriceAmountSelector);
const setBalance = useSetRecoilState(balanceState);
const cartItemRefresh = (): void => {
// 1. ์๋ก๊ณ ์นจ ๋ฒํผ์ ๋๋ฅด๋ฉด ์ฅ๋ฐ๊ตฌ๋ ์ด๊ธฐํ
// 2. ์์ก === ์ด๊ธ์ก + ์์ก
setCartItem([]);
setBalance(totalPriceBalance);
};
...
return (
<>
<S.RefreshBtn onClick={cartItemRefresh} />
</>
)
}
import * as S from './TotalCart.style';
import Wallet from '../Wallet/Wallet';
import ManualModal from '../Modal/ManualModal';
import useModal from '../../hooks/useModal';
import { SelectedCakeItem, totalCartItemState } from '../../state/atoms/atoms';
import { useRecoilState } from 'recoil';
const TotalCart = () => {
const { isModalOpen, openModal, closeModal } = useModal();
const [cartItem, setCartItem] = useRecoilState(totalCartItemState);
const calculateTotalPrice = (): number => {
return cartItem.reduce(
(total: number, item: SelectedCakeItem) => total + item.cost * item.quantity,
0
);
};
const cartItemRefresh = (): void => {
setCartItem([]);
};
return (
<>
<Wallet />
<S.Wrapper>
<S.H2>์ฅ๋ฐ๊ตฌ๋</S.H2>
<S.GetCartUl>
{cartItem.map((item: SelectedCakeItem) => (
<S.GetCartLi>
<S.GetCartImg src={item.img} alt={item.name} />
<S.GetCartName>{item.name}</S.GetCartName>
<S.GetCartStrong>{item.quantity}</S.GetCartStrong>
</S.GetCartLi>
))}
</S.GetCartUl>
<S.BottomBox>
<S.ButtonBox>
<S.ManualBtn onClick={openModal}>์ค๋ช
์</S.ManualBtn>
<S.RefreshBtn onClick={cartItemRefresh} />
</S.ButtonBox>
<S.TotalPrice>์ด๊ธ์ก : {calculateTotalPrice().toLocaleString()}์</S.TotalPrice>
</S.BottomBox>
</S.Wrapper>
{isModalOpen && <ManualModal onClose={closeModal} />}
</>
);
};
export default TotalCart;
// selector.ts
import { selector } from 'recoil';
import { balanceState, SelectedCakeItem, totalCartItemState } from '../atoms/atoms';
// ์ด๊ธ์ก ๊ณ์ฐ
export const totalPriceSelector = selector<number>({
key: 'totalPriceSelector',
get: ({ get }) => {
const cartItems: SelectedCakeItem[] = get(totalCartItemState);
return cartItems.reduce(
(total: number, item: SelectedCakeItem): number => total + item.cost * item.quantity,
0
);
},
});
// ์ด๊ธ์ก + ์์ก
export const totalPriceAmountSelector = selector<number>({
key: 'totalPriceAmountSelector',
get: ({ get }) => {
const cartItems = get(totalPriceSelector);
const balance = get(balanceState);
return cartItems + balance;
},
});
import * as S from './TotalCart.style';
import Wallet from '../Wallet/Wallet';
import ManualModal from '../Modal/ManualModal';
import useModal from '../../hooks/useModal';
import { balanceState, SelectedCakeItem, totalCartItemState } from '../../state/atoms/atoms';
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
import { totalPriceAmountSelector, totalPriceSelector } from '../../state/selectors/selector';
const TotalCart = () => {
const { isModalOpen, openModal, closeModal } = useModal();
const [cartItem, setCartItem] = useRecoilState(totalCartItemState);
const totalPrice = useRecoilValue(totalPriceSelector);
const totalPriceBalance = useRecoilValue(totalPriceAmountSelector);
const setBalance = useSetRecoilState(balanceState);
const cartItemRefresh = (): void => {
// 1. ์๋ก๊ณ ์นจ ๋ฒํผ์ ๋๋ฅด๋ฉด ์ฅ๋ฐ๊ตฌ๋ ์ด๊ธฐํ
// 2. ์์ก === ์ด๊ธ์ก + ์์ก
setCartItem([]);
setBalance(totalPriceBalance);
};
return (
<>
<Wallet />
<S.Wrapper>
<S.H2>์ฅ๋ฐ๊ตฌ๋</S.H2>
<S.GetCartUl>
{cartItem.map((item: SelectedCakeItem) => (
<S.GetCartLi>
<S.GetCartImg src={item.img} alt={item.name} />
<S.GetCartName>{item.name}</S.GetCartName>
<S.GetCartStrong>{item.quantity}</S.GetCartStrong>
</S.GetCartLi>
))}
</S.GetCartUl>
<S.BottomBox>
<S.ButtonBox>
<S.ManualBtn onClick={openModal}>์ค๋ช
์</S.ManualBtn>
<S.RefreshBtn onClick={cartItemRefresh} />
</S.ButtonBox>
<S.TotalPrice>์ด๊ธ์ก : {totalPrice.toLocaleString()}์</S.TotalPrice>
</S.BottomBox>
</S.Wrapper>
{isModalOpen && <ManualModal onClose={closeModal} />}
</>
);
};
export default TotalCart;