(React) 쇼핑몰 만들기 - redux, 장바구니 기능 구현

고민지·2022년 7월 25일
1

React

목록 보기
22/25
post-thumbnail

routes 폴더 안에 Cart.js 파일을 생성하여 다음 코드를 작성한다.

import { Table } from "react-bootstrap";

function Cart() {
  return (
    <div>
      <Table responsive="md">
        <thead>
          <tr>
            <th>#</th>
            <th>상품명</th>
            <th>수량</th>
            <th>변경하기</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>1</td>
            <td>Table cell</td>
            <td>Table cell</td>
            <td>Table cell</td>
          </tr>
          <tr>
            <td>2</td>
            <td>Table cell</td>
            <td>Table cell</td>
            <td>Table cell</td>
          </tr>
          <tr>
            <td>3</td>
            <td>Table cell</td>
            <td>Table cell</td>
            <td>Table cell</td>
          </tr>
        </tbody>
      </Table>
    </div>
  );
}

export default Cart;

기본 테이블 레이아웃이다.

App.js 파일에 적당한 위치에 다음 코드를 추가한다.

.
.
import Cart from "./routes/Cart";
.
.
<Route path="/cart" element={<Cart />} />

이제 /cart 페이지로 접속하면 테이블 구조를 볼 수 있다.

redux 설치

근데 설치하기 전에 package.json 파일을 열어서 "react" ,"react-dom" 항목의 버전을 확인합시다.
이거 두개가 18.1.x 이상이면 사용가능하다.
npm install @reduxjs/toolkit react-redux

store.js 에서 state 등록/사용하는 방법

import { configureStore, createSlice } from "@reduxjs/toolkit";

const user = createSlice({
  name: "user",
  initialState: "kim",
});

export default configureStore({
  reducer: {
    user: user.reducer,
  },
});
name: "user"

👉 state 이름

initialState: "kim"

👉 state 초기값

 reducer: {
    user: user.reducer,
  }

👉 state 등록

state 사용할 Component 안에서 다음 코드를 적으면 store.js 안에 있는 모든 state 를 사용할 수 있다.

const store = useSelector((state) => {
    return state;
  });

redux state 수정하는 방법

  1. state 변경해주는 함수 만들기
  2. export
  3. dispatch(1번에서 만듬 함수())

이제 기본적인 redux 사용법을 배웠으니 실제로 장바구니에 데이터를 담아보고 수량을 증가시키는 버튼을 만들어보자.

아래는 장바구니 데이터를 직접 적어서 넣고 수량(count) state 변경까지 적용한 코드이다.

(store.js)

/* eslint-disable */

import { configureStore, createSlice } from "@reduxjs/toolkit";

const user = createSlice({
  name: "user",
  initialState: { name: "kim", age: 21 },
});

const cart = createSlice({
  name: "cart",
  initialState: [
    { id: 0, name: "White and Black", count: 2 },
    { id: 2, name: "Grey Yordan", count: 1 },
  ],
  reducers: {
    changeCount(state, action) {
      const i = state.findIndex((product) => product.id === action.payload);
      state[i].count += 1;
    },
  },
});

export const { changeCount } = cart.actions;

export default configureStore({
  reducer: {
    user: user.reducer,
    cart: cart.reducer,
  },
});
 changeCount(state, index)

👉 state는 현재 state 값이고 index는 아래 코드의 dispatch(변수)의 변수값이다.

아래는 테이블에 state를 가져와서 데이터바인딩 하는 코드이다.

(Cart.js)

/* eslint-disable */

import { Table } from "react-bootstrap";
import { useDispatch, useSelector } from "react-redux";
import { changeCount } from "../store";

function Cart() {
  const state = useSelector((state) => state);
  const dispatch = useDispatch();

  return (
    <div>
      <h5>{state.user.name}님의 장바구니</h5>
      <Table responsive="md">
        <thead>
          <tr>
            <th>#</th>
            <th>상품명</th>
            <th>수량</th>
            <th>변경하기</th>
          </tr>
        </thead>
        <tbody>
          {state.cart.map((item, index) => {
            return (
              <tr key={index}>
                <td>{item.id}</td>
                <td>{item.name}</td>
                <td>{item.count}</td>
                <td>
                  <button
                    onClick={() => {
                      dispatch(changeCount(item.id));
                    }}
                  >
                    +
                  </button>
                </td>
              </tr>
            );
          })}
        </tbody>
      </Table>
    </div>
  );
}

export default Cart;

+버튼을 누르면 그 상품의 수량이 증가하는 걸 확인할 수 있다.

이제 상품상세화면에서 장바구니 버튼을 클릭할 때 장바구니에 상품을 담는 기능과 장바구니 페이지에서 물품을 삭제하는 기능을 구현해보자.

우선 cart state 관련 코드가 길어질 것 같으니 store.js에서 cart 부분은 따로 빼자.
store 폴더를 만들고 그 안에 cartSlice.js 파일을 만든다.
다음 코드를 넣는다.

import { createSlice } from "@reduxjs/toolkit";

const cart = createSlice({
  name: "cart",
  initialState: [
    { id: 0, name: "White and Black", count: 2 },
    { id: 2, name: "Grey Yordan", count: 1 },
  ],
  reducers: {
    changeCount(state, action) {
      const i = state.findIndex((product) => product.id === action.payload);
      state[i].count += 1;
    },
    addItem(state, action) {
      const pd = {
        id: action.payload.id,
        name: action.payload.title,
        count: 1,
      };
      state.push(pd);
    },
    deleteItem(state, action) {
      const i = state.findIndex((product) => product.id === action.payload);
      state.splice(i, 1);
    },
  },
});

export const { changeCount, addItem, deleteItem } = cart.actions;
export default cart;

addItem, deleteItem 모두 state 변경하는 과정 그대로 하면 된다.

Detail.js 에서 장바구니 버튼 부분을 아래처럼 수정해준다.

<button
	className="btn btn-danger"
    onClick={() => {
    	const index = state.cart.findIndex(
        	(pd) => pd.id === shoe[0].id
        );
        if (index < 0) {
        	dispatch(addItem(shoe[0]));
        } else {
        	dispatch(changeCount(shoe[0].id));
            }
        }}
>
장바구니
</button>

Cart.js 도 다음과 같이 수정해주자.

.
.
 <tbody>
          {state.cart.map((item, index) => {
            return (
              <tr key={index}>
                <td>{item.id}</td>
                <td>{item.name}</td>
                <td>{item.count}</td>
                <td>
                  <button
                    onClick={() => {
                      dispatch(changeCount(item.id));
                    }}
                  >
                    +
                  </button>
                </td>
                <td>
                  <button
                    onClick={() => {
                      dispatch(deleteItem(item.id));
                    }}
                  >
                    x
                  </button>
                </td>
              </tr>
            );
          })}
        </tbody>
.
.

import 할 건 찾아서 하면 된다.
장바구니에 이미 있는 상품이면 수량을 추가해주고 없는 상품일때만 상품을 새로 장바구니에 추가해준다고 생각하고 코드를 짰다.

지금까지 페이지들을 모두 연동하기 위해 몇가지 수정을 좀 해주자.

(App.js)

.
.
return (
    <div className="App">
      <Navbar bg="dark" variant="dark">
        <Container>
          <Navbar.Brand
            onClick={() => {
              navigate("/");
            }}
          >
            My shop
          </Navbar.Brand>
          <Nav className="me-auto">
            <NavDropdown title="About" id="navbarScrollingDropdown">
              <NavDropdown.Item
                onClick={() => {
                  navigate("/about/member");
                }}
              >
                member
              </NavDropdown.Item>
              <NavDropdown.Item
                onClick={() => {
                  navigate("/about/location");
                }}
              >
                location
              </NavDropdown.Item>
            </NavDropdown>
            <Nav.Link
              onClick={() => {
                navigate("/cart");
              }}
            >
              Cart
            </Nav.Link>
          </Nav>
        </Container>
      </Navbar>

      <Routes>
        <Route path="/" element={<Home shoes={shoes} />} />
        <Route path="/detail/:id" element={<Detail shoes={shoes} />} />
        <Route path="/cart" element={<Cart />} />
        <Route path="/about" element={<About />}>
          <Route path="member" element={<div>멤버정보</div>} />
          <Route path="location" element={<div>위치정보</div>} />
        </Route>
        <Route path="*" element={<div>없는 페이지입니다.</div>} />
      </Routes>
      {btnShow == true ? <button onClick={btnMore}>더보기</button> : null}
      {loading == true ? <p>로딩중입니다.</p> : null}
    </div>
  );
.
.
(Shoe.js)

.
.
 <img
	src={"https://codingapple1.github.io/shop/shoes" + i + ".jpg"}
    width="80%"
    onClick={() => {
    	console.log("home");
        navigate(`/detail/${item.id}`);
    }}
/>
.
.

이제 지금까지 구현한 기능들을 모두 페이지로 이동하고 서로 연동되는 것을 확인할 수 있게 되었다.


profile
도전 성취 성장을 향한 개발자

0개의 댓글