ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 아마존 E-commerce 클론 -8) add to Cart 버튼 기능 만들고 redux store에 카트정보 넣어서 활용하기
    NODE.JS 2021. 5. 27. 13:23

     

     

     

    먼저 재고가 0개 이상이라면 상품의 선택란을 표기하는 부분이 생기도록 해보자. 

    productscreen.js를 다음과 같이 설정해준다. 

    import React,{useEffect, useState} from 'react'
    
    
        const [qty, setQty] = useState(1);
    
    
    
    
                                </li>
                                {product.countInStock > 0 && (
                                    <>
                                        <li>
                                            <div className="row">
                                                <div>Qty</div>
                                                <div>
                                                    <select value={qty} onChange={(e)=> setQty(e.target.value)}>
                                                        {[...Array(product.countInStock).keys()].map(
                                                            (x)=>(<option key={x+1}value={x+1}>{x+1}</option>
                                                        )
                                                        )}
                                                    </select>
                                                </div>
                                            </div>
                                        </li>
                                        <li>
                                            <button onClick={addToCartHandler} className="primary block">
                                                Add to Cart
                                            </button>
                                        </li>
                                        </>
                                )}
                            </ul>

     

     

    addToCarthandler함수도 설정해주자.

        const addToCartHandler = () =>{
            props.history.push(`/cart/${productId}?qty=${qty}`);
    
        }

     

     

     

    클릭하면 페이지를 이동하도록 설정했으니, 

    cartscreen을 만들어준다. 

    import React from 'react'
    
    function CartScree(props) {
        const productId = props.match.params.id
        const qty = props.location.search? Number(props.location.search.split('=')[1]):1
    
        return (
            <div>
                <h1>Cart Screen</h1>
                <p>ADD TO CART : ProductId:{productId} Qty : {qty}</p>
            </div>
        )
    }
    
    export default CartScree
    

     

     

    스크린을만들었으니 App.js에 추가해준다. 

    import CartScreen from './screens/CartScreen';
    
    function App() {
      return (
        <BrowserRouter >
       
                  <Route path="/cart/:id?" component={CartScreen} exact></Route>
    
        </BrowserRouter>
      );
    }
    
    export default App;
    

     

     

     

    상품 선택가능

     

    카트페이지로 가면 몇개가 추가되었는지 아이디와 함께 학인할 수 있다. 

     

    마지막으로 index.css에서 스타일링을 해보자. 

    원래 버튼만 설정했던 것을 범위를 넓혀서 모두 적용되도록 할 것이다. 

    
    input, select, textarea,button{
      padding:1rem;
      border-radius: 0.5rem;
      border:0.1rem #a4a4a4 solid;
      font-family: Helvetica, Arial, sans-serif;
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
    
    }
    
    button{
      background-color: #f8f8f8;
      cursor:pointer
    }
    input:hover, 
    select:hover, 
    textarea:hover,
    button:hover{
      border: 0.1rem #404040 solid;
    }
    
    button.primary{
      background-color: #f0c040;
    }
    button.block{
      width:100%;
    }

     

     

    이제 리덕스 스토어에 카트 정보도 넣어서 이용하도록 하자. 

    우선 상수를 추가해주자. 

    constants>cartConstants를 만들어준다. 

    export const CART_ADD_ITEM = 'CART_ADD_ITEM'

     

     

    그다음 액션을 생성해준다. 

    actions>cartActions.js

    import Axios from "axios";
    import { CART_ADD_ITEM } from "../constants/cartConstants";
    
    export const addToCart = (productId, qty)=> async(dispatch, getState)=>{
        const {data} =await Axios.get(`/api/products/${productId}`);
        dispatch({
            type:CART_ADD_ITEM,
            payload:{
                name:data.name,
                image:data.image,
                price:data.price,
                countInStock:data.countInStock,
                product:data._id,
                qty,
            }
        })
    }

     

     

     

    리듀서를 생성해준다. 

    reducers>cartReducers.js

    이미 아이템이 있으면 그 아이템을 이용하고 없으면 새로운 아이템을 추가해서 돌려준다. 

    import { CART_ADD_ITEM } from "../constants/cartConstants";
    
    export const cartReducer = (state={cartItems:[]}, action)=>{
        switch(action.type){
            case CART_ADD_ITEM:
                const item = action.payload;
                const existItem = state.cartItems.find(x=>x.product===item.product)
                if(existItem){
                    return {
                        ...state,
                        cartItems:state.cartItems.map(x=>x.product===existItem.product? item:x)
                    }
                }else{
                    return {...state, cartItems:[...state.cartItems, item]};
                }
            default: 
                return state;
        }
    }

    store.js에 다음과 같이 추가해준다. 

     

    import{combineReducers, createStore, compose, applyMiddleware} from 'redux';
    import thunk from 'redux-thunk'
    import { cartReducer } from './reducers/cartReducers';
    import { productDetailsReducer, productListReducer } from './reducers/productReducers';
    
    
    const initialState = {};
    
    const reducer = combineReducers({
        productList: productListReducer,
        productDetails : productDetailsReducer,
        cart:cartReducer,
        
    })
    const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__||compose;
    
    const store = createStore(reducer, initialState, composeEnhancer(applyMiddleware(thunk)));
    
    export default store;

    카트 스크린에서 다음과 같이 디스패치를 추가해준다. 

    import React, { useEffect } from 'react'
    import {useDispatch} from 'react-redux'
    import { addToCart } from '../actions/cartActions'
    
    
    function CartScree(props) {
        const productId = props.match.params.id
        const qty = props.location.search? Number(props.location.search.split('=')[1]):1
    
    
        const  dispatch = useDispatch()
        useEffect(()=>{
            if(productId){
                dispatch(addToCart(productId, qty))
            }
        },[dispatch, productId, qty])
    
        return (
            <div>
                <h1>Cart Screen</h1>
                <p>ADD TO CART : ProductId:{productId} Qty : {qty}</p>
            </div>
        )
    }
    
    export default CartScree
    

     

     

     

    마지막으로 component의 Product.js, App.js에서 

    react-router-dom의 Link를 이용해서 

     a 를 모두 LInk, Link to 로 변경해주자. 

    import React from 'react'
    import Rating from './Rating';
    import {Link} from 'react-router-dom'
    
    function Product(props) {
    
        const{product} = props;
    
        return (
            <div key={product._id} className="card">
            <Link to={`/product/${product._id}`}>
                <img className="medium" src={product.image} alt={product.name}/>
            </Link>
            <div className="card-body">
               <Link to={`/product/${product._id}`}>
                    <h2>{product.name}</h2>
                </Link>
                <Rating rating ={product.rating} numReviews={product.numReviews}/>
                <div className="price">
                    ${product.price}
                </div>
            </div>
            </div>
        )
    }
    
    export default Product
    
    import React from 'react';
    import  {BrowserRouter,Route} from 'react-router-dom'
    import ProductScreen from './screens/ProductScreen';
    import HomeScreen from './screens/HomeScreen';
    import CartScreen from './screens/CartScreen';
    import {Link} from 'react-router-dom'
    
    function App() {
      return (
        <BrowserRouter >
        <div className="grid-container">
              <header className="row">
                <div>
                  <Link className="brand" href="/">amazona</Link>
                </div>
                <div>
                  <Link to="/cart">Cart</Link>
                  <Link to="/signin">Sign In</Link>
                </div>
              </header>
              <main>
                  <Route path="/" component={HomeScreen} exact></Route>
                  <Route path="/product/:id" component={ProductScreen} exact></Route>
                  <Route path="/cart/:id?" component={CartScreen} exact></Route>
              </main>
              <footer className="row center">All right reserved</footer>
            </div>
        </BrowserRouter>
      );
    }
    
    export default App;
    

     

     

     

     

     

     

    add to cart 버튼을 누르면 다음과 같이 리덕스에 추가된 것을 확인할 수 있다. 

     

     

     

    다른 아이템을 추가하면 카트 아이템 리스트가 변경된 것을 확인할 수 있다. 

     

     

    우리가 만약 카트저장을 해놓고 랜딩페이지에가서 다시화면을 리프레시 하면 카트아이템이 빈 배열로 되는데, 

    이는 우리가 로컬 저장소에 저장하지않았기 때문이다. 

     

    action에 가서 다음과 같이 수정해준다. 

    import Axios from "axios";
    import { CART_ADD_ITEM } from "../constants/cartConstants";
    
    export const addToCart = (productId, qty)=> async(dispatch, getState)=>{
        const {data} =await Axios.get(`/api/products/${productId}`);
        dispatch({
            type:CART_ADD_ITEM,
            payload:{
                name:data.name,
                image:data.image,
                price:data.price,
                countInStock:data.countInStock,
                product:data._id,
                qty,
            }
        });
        localStorage.setItem('cartItems', JSON.stringify(getState().cart.cartItems));
    }

     

    store.js에 가서 다음과 같이 작성해준다. 

     

    const initialState = {
        cart:{
            cartItems: localStorage.getItem('cartItems')? JSON.parse(localStorage.getItem('cartItems')):[]
        }
    };
    

     

     

    다시 랜딩페이지로 가도 사라지지 않은 것을 확인할 수 있다. 
    리프레시 해도 사라지지 않고 state가 남아있다. 

     

    이제 카트 상단부분에 카트에 몇개가 있는지 보이도록하는 기능을 추가해보자. 

     

    App.js에 다음과 같이 작성해준다. 

    import { useSelector } from 'react-redux';
    
    function App() {
    
      const cart = useSelector(state=> state.cart);
      const {cartItems} = cart;
      
      
      return (
    
                <div>
                  <Link to="/cart">Cart
                  {cartItems.length>0 &&(
                    <span className="badge">{cartItems.length}</span>
                  )}
                  </Link>
    
              </header>

     

     

    index.css로 가서 디자인해준다. 

    a.brand {
      color: #ffffff;
      font-size: 3rem;
      font-weight: bold;
    }
    
    .badge{
      background-color: #f02020;
      color: white;
      border-radius: 50%;
      padding: 0.2rem 0.7rem;
      font-size: 1.4rem;
      margin-left: 0.2rem;
    }

     

     

     

     

     

     

     

     

     

     

Designed by Tistory.