일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 어윈 사용법
- 오라클
- 예외미루기
- 컬렉션 타입
- 메소드오버로딩
- GRANT VIEW
- 자바
- exception
- cursor문
- 제네릭
- 집합_SET
- 인터페이스
- EnhancedFor
- 컬렉션프레임워크
- 환경설정
- 참조형변수
- 정수형타입
- NestedFor
- 생성자오버로드
- 대덕인재개발원
- 한국건설관리시스템
- abstract
- 객체 비교
- 다형성
- 자동차수리시스템
- oracle
- 사용자예외클래스생성
- 추상메서드
- Java
- 예외처리
- Today
- Total
거니의 velog
(54) 리액트 쿼리와 리코일 6 본문
6. 장바구니 처리
* 장바구니 처리는 서버와 연동해야 하는 부분은 리액트 쿼리를 이용해서 처리하고, 장바구니의 아이템에 대한 상태 처리는 리코일을 이용한다.
import { atom } from "recoil";
export const cartState = atom({
key: "cartState",
default: [],
});
* cartState는 카트에 담긴 장바구니 아이템의 배열을 선언한다.
(1) 리코일의 Selector
* 리코일의 Atom이 데이터 자체를 의미한다면, Selector는 데이터를 이용해서 처리할 수 있는 기능을 의미한다. 예를 들어 장바구니의 경우 해당 상품의 가격과 수량을 이용해서 전체 장바구니의 총액을 구하는 기능을 사용할 수 있다.
* 리코일의 Selector는 데이터를 가공해서 원하는 기능을 제공하기 때문에 getter처럼 사용되지만, Atom 으로 관리되는 데이터를 변경하는 setter의 기능도 같이 사용할 수 있다.
import { atom, selector } from "recoil";
export const cartState = atom({
key: "cartState",
default: [],
});
export const cartTotalState = selector({
key: "cartTotalState",
get: ({ get }) => {
const arr = get(cartState);
const initialValue = 0;
const total = arr.reduce(
(total, current) => total + current.price * current.qty,
initialValue
);
return total;
},
});
(2) 장바구니 데이터 보관
* 대부분의 작업은 useCustomCart를 통해서 처리된다. useMutation을 통해서 서버에 장바구니 아이템을 조정하고 useQuery를 이용해서 리액트 내에 데이터를 보관하게 만든다. 다른 컴포넌트들이 장바구니 데이터를 공유해서 사용할 수도 있기 때문에 이 부분은 리코일로 만든 cartState를 이용한다.
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { getCartItems, postChangeCart } from "../api/cartApi";
import { useRecoilState } from "recoil";
import { cartState } from "../atoms/cartState";
import { useEffect } from "react";
const useCustomCart = () => {
const [cartItems, setCartItems] = useRecoilState(cartState);
const queryClient = useQueryClient();
const changeMutation = useMutation((param) => postChangeCart(param), {
onSuccess: (result) => {
setCartItems(result);
},
});
const query = useQuery(["cart"], getCartItems, { staleTime: 1000 * 60 * 60 }); // 1 hour
useEffect(() => {
if (query.isSuccess) {
queryClient.invalidateQueries("cart");
setCartItems(query.data);
}
}, [query.isSuccess, query.data]);
const changeCart = (param) => {
changeMutation.mutate(param);
};
return { cartItems, changeCart };
};
export default useCustomCart;
* useQuery()를 이용할 때 1시간의 staleTime을 지정한 이유는 외부에서 어떤 영향으로 상품의 정보가 변경될 수 있기 때문에 자주는 아니지만 가끔은 장바구니 안에 있는 상품 정보를 다시 가져오기 위해서이다.
* components/menus/CartComponent는 리액트 쿼리와 리코일로 처리된 useCustomCart를 이용하고 리코일의 Seletor로 만든 총액(total)을 보여주도록 한다.
import useCustomLogin from "../../hooks/useCustomLogin";
import useCustomCart from "../../hooks/useCustomCart";
import CartItemComponent from "../cart/CartItemComponent";
import { useRecoilValue } from "recoil";
import { cartTotalState } from "../../atoms/cartState";
const CartComponent = () => {
const { isLogin, loginState } = useCustomLogin();
const { cartItems, changeCart } = useCustomCart();
const totalValue = useRecoilValue(cartTotalState);
return (
<div className="w-full">
{isLogin ? (
<div className="flex flex-col">
<div className="w-full flex">
<div className="font-extrabold text-2xl w-4/5">
{loginState.nickname}'s Cart
</div>
<div className="bg-orange-600 text-center text-white font-bold w-1/5 rounded-full m-1">
{cartItems.length}
</div>
</div>
<div>
<ul>
{cartItems.map((item) => (
<CartItemComponent
{...item}
key={item.cino}
changeCart={changeCart}
email={loginState.email}
/>
))}
</ul>
</div>
<div className="m-2 text-3xl ">TOTAL: {totalValue}</div>
</div>
) : (
<div></div>
)}
</div>
);
};
export default CartComponent;
* 브라우저를 이용해서 로그인된 상황에서는 장바구니의 내용물들이 출력되는지 확인하고 마지막에 총액(total)이 출력되는지 확인한다.
(3) 장바구니 아이템 추가
* 상품 조회 화면에서는 장바구니에 아이템을 추가하는 기능을 추가하기 위해서 useCustomLogin()과 useCustomCart()를 이용한다.
import React from "react";
import { getOne } from "../../api/productsApi";
import { API_SERVER_HOST } from "../../api/todoApi";
import useCustomMove from "../../hooks/useCustomMove";
import FetchingModal from "../common/FetchingModal";
import useCustomCart from "../../hooks/useCustomCart";
import useCustomLogin from "../../hooks/useCustomLogin";
import { useQuery } from "@tanstack/react-query";
const initState = {
pno: 0,
pname: "",
pdesc: "",
price: 0,
uploadFileNames: [],
};
const host = API_SERVER_HOST;
const ReadComponent = ({ pno }) => {
//화면 이동용 함수
const { moveToList, moveToModify } = useCustomMove();
const { loginState } = useCustomLogin();
const { cartItems, changeCart } = useCustomCart();
const { isFetching, data } = useQuery(["products", pno], () => getOne(pno), {
staleTime: 1000 * 10 * 60,
retry: 1,
});
const handleClickAddCart = () => {
let qty = 1;
const addedItem = cartItems.filter((item) => item.pno === parseInt(pno))[0];
if (addedItem) {
if (
window.confirm("이미 추가된 상품입니다. 추가하시겠습니까? ") === false
) {
return;
}
qty = addedItem.qty + 1;
}
changeCart({ email: loginState.email, pno: pno, qty: qty });
};
const product = data || initState;
return (
(...)
);
};
export default ReadComponent;
(4) 로그아웃 처리
* 로그인이나 로그아웃은 모두 useCustomLogin()을 이용해서 처리하고 있는 상황이지만, 로그아웃 시에는 리코일로 유지되는 장바구니 아이템들도 모두 삭제한다. 리코일의 useResetRecoilState()를 이용해서 장바구니 데이터들 역시 삭제하도록 한다.
import { Navigate, useNavigate, createSearchParams } from "react-router-dom";
import { useRecoilState, useResetRecoilState } from "recoil";
import signinState from "../atoms/signinState";
import { loginPost } from "../api/memberApi";
import { removeCookie, setCookie } from "../util/cookieUtil";
import { cartState } from "../atoms/cartState";
const useCustomLogin = () => {
const navigate = useNavigate();
const [loginState, setLoginState] = useRecoilState(signinState);
const resetState = useResetRecoilState(signinState);
const resetCartState = useResetRecoilState(cartState); // 장바구니 비우기
const isLogin = loginState.email ? true : false; // 로그인 여부
(...)
// 로그아웃 함수
const doLogout = () => {
removeCookie("member");
resetState();
resetCartState();
};
(...)
};
export default useCustomLogin;
[리덕스 툴킷 설정 지우기]
* 마지막으로 리덕스 툴킷을 사용하지 않도록 index.js를 수정해서 스토어 관련 설정을 삭제한다.
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import { RecoilRoot } from "recoil";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<RecoilRoot>
<App />
</RecoilRoot>
);
reportWebVitals();
* 기존에 리덕스 툴킷을 제거한 후에도 정상적으로 동작하는지 확인해 준다.
'SpringBoot_React 풀스택 프로젝트' 카테고리의 다른 글
(56) AWS Elastic Beanstalk 2 (0) | 2024.03.11 |
---|---|
(55) AWS Elastic Beanstalk 1 (0) | 2024.03.11 |
(53) 리액트 쿼리와 리코일 5 (0) | 2024.03.11 |
(52) 리액트 쿼리와 리코일 4 (0) | 2024.03.11 |
(51) 리액트 쿼리와 리코일 3 (0) | 2024.03.11 |