[React+Spring] Slice 를 이용한 장바구니 API 연동

HJ·2024년 2월 6일
0

React+Spring

목록 보기
10/11

아래 내용은 코드로 배우는 React with 스프링부트 API서버 강의와 함께 설명이 부족한 부분에 대해서 조사하여 추가한 내용입니다.


API 연결 ~ 상태 이용


1. API 작성

export const getAllCartItems = async() => {
    const res = await jwtAxios.get(`${prefix}/items`);
    return res.data;
}

2. createAsyncThunk 액션 생성

export const getCartItemsAsync = createAsyncThunk('getCartItemsAsync', () => {
    return getAllCartItems();
});

3. extraReducer 를 통한 상태 업데이트

const cartSlice = createSlice({
    ...
    extraReducers: (builder) => {
        builder
            .addCase(getCartItemsAsync.fulfilled, (state, action) => {
                console.log("getCartItemsAsync fulfilled action = ", action);
                return action.payload;
            })
            ...
    }
})

export default cartSlice.reducer;   // 반환은 reducer 로

4. store.js 에 등록

export default configureStore({
    reducer: { 
        "cartSlice" : cartSlice
    }
})

5. Thunk 액션 호출을 통한 상태 업데이트

function CartComponent() {
    const {isLogin, loginState} = useLogin();
    const dispatch = useDispatch();

    useEffect(() => {
        if(isLogin) {
            dispatch(getCartItemsAsync());
        }
    }, [isLogin]);
    ...
}

6. 업데이트 된 상태 가져오기

function CartComponent() {
    const cartItems = useSelector(state => state.cartSlice);
    ...
}

[ Hook 으로 작성하기 ]

const useCart = () => {
    const cartItems = useSelector(state => state.cartSlice);
    const dispatch = useDispatch();

    const getCart = () => {
        dispatch(getCartItemsAsync());
    }

    const changeCart = (cartItem) => {
        dispatch(postChangeCartItemAsync());
    }

    return {cartItems, getCart, changeCart}
}

export default useCart;

위의 5번과 6번은 useDispatch 를 이용하여 액션을 호출하고, useSelector 를 통해 상태를 가져와서 사용하고 있습니다. 이 두 가지 기능을 하나의 Hook 을 작성하여 보다 편리하게 사용하게 합니다.

따라서 위의 4, 5번에 해당하는 코드를 아래처럼 줄여서 사용할 수 있습니다.

function CartComponent() {
    const { isLogin } = useLogin();
    const {cartItems, getCart} = useCart();

    useEffect(() => {
        if (isLogin) {
            getCart();
        }
    }, [isLogin])
}




CartItem 변경


[ 사이드바로 변경 ]

1. 수정 호출 함수 전달

function CartComponent() {
    const { cartItems, getCart, changeCart } = useCart();
    ...
    return (
        <ul> {cartItems.map(item => 
            <CartItemComponent {...item} 
            key={item.cartItemNo} 
            changeCart={changeCart} 
            email={loginState.email}/>)}
        </ul>
    )
    ...
}

+, - 버튼을 통해 수량 변경을 했을 때 수정 API 를 호출하기 위해 useCart 훅에 정의해놓은 changeCart 를 CartItemComponent 에 넘겨줍니다.

2. 클릭 이벤트 설정

function CartItemComponent({ cartItemNo, itemName, price, itemId, quantity, imageFile, changeCart, email }) {
  const handleClickQty = (amount) => {
    changeCart({
      cartItemNo: cartItemNo,
      itemId: itemId,
      quantity: quantity + amount,
      email: email
    })
  }

  return (
    <li key={cartItemNo} className="border-2 w-full">
    ...
        <button className="m-1 p-1 text-2xl bg-orange-500 w-8 rounded-lg"
              onClick={() => handleClickQty(1)}>+ </button>
    ...
  )
}

버튼 클릭 시, CartItem 객체를 만들고, 이를 전달받은 변경 함수의 파라미터로 하여 호출합니다. changeCartdispatch 를 통해 Thunk 액션을 디스패치 하는 함수입니다.

해당 호출이 종료되면 extraReducer 에서 action.payload 를 반환하고, 반환된 값으로 상태가 변경되어 실시간으로 CartItemComponent 에서 보이는 수량도 변경되게 됩니다.



[ ReadComponent 에서 추가 ]

function ReadComponent({ itemId }) {
    ...
    const {cartItems, changeCart} = useCart();
    const {loginState} = useLogin();

    const handleClickAdd = () => {
        let quantity = 1;
        const addedItem = cartItems.filter(item => item.itemId === parseInt(itemId))[0];    // 이미 추가된 아이템
        if(addedItem) {
            if(window.confirm("이미 추가된 상품입니다. 수량을 추가할까요?") === false) {
                return;
            }
            quantity = addedItem.quantity + 1;
        } else {
            if(window.confirm("상품을 추가할까요?") === false) {
                return;
            }
        }
        changeCart({email: loginState.email, quantity: quantity, itemId: itemId});
    }

    return (...);
}

filter 를 통해 현재 보고있는 페이지의 상품이 장바구니에 있는지 판단합니다. 그 후 추가한다고 하면 수량을 증가시켜 호출합니다.

상품을 최초에 추가하는 경우에도 이미 서버에서 CartItemNo 가 없는 경우에 대한 처리를 했기 때문에 동일하게 changeCart 를 호출해도 상관없습니다.

0개의 댓글