Redux 예제 - 장바구니

유휘찬·2020년 11월 22일
1
post-thumbnail

컴포넌트간 값의 전달을 props 로만 하다보면 복잡도가 상승하여 머리가 지끈해지게 됩니다. 이럴 때 redux 를 도입하면 이러한 복잡성을 해결할 수 있습니다.

작업 파일

Redux 를 공부하기 위해 간단한 예제를 만들었습니다. 상품을 장바구니에 담으면 nav 바에 장바구니 아이콘 옆 숫자가 늘어나고, 장바구니 페이지에서 상품 삭제 아이콘을 누르면 상품이 곧바로 삭제되며 nav 바의 숫자 또한 줄어들게 하는 것을 목표로 하였습니다.

원하시는 분은 아래 주소에서 repo 를 clone 받아 공부하셔도 좋습니다.

https://github.com/chan-97/Redux-market

완성된 모습

폴더 구조

src
|
|-store
  |
  |-actions
  |  |
  |  |-index.js
  |
  |-reducers
    |
    |-index.js
    |-cartReducer.js

준비 사항

라우터까지는 구현이 되어있으니 npm install 하시고 시작하시면 됩니다.
npm i redux react-redux 도 설치해주세요.

actions

actions 폴더의 index.js 에서 액션을 생성하는 액션 생성 함수를 정의해 줍니다.

  • 액션은 type 이라는 속성 값을 가진 js 객체입니다.
  • 액션 생성함수는 그 액션 객체를 생성하는 역할을 하는 js 함수입니다.
  • 액션 객체를 dispatch메서드에 넣어서 호출합니다. (useDispatch)
  • 액션 생성함수가 생성한 액션 객체는 reducer 를 거쳐 store 를 업데이트 하게 됩니다.
// store -> actions -> index.js

export const addCart = (item) => {
	return {
    	type: "ADD_ITEM",
        payload: item
    }
}
// type 이라는 속성을 가진 액션을 생성하는 addCart 는 액션 생성 함수입니다.
// 이름만 봐도 장바구니에 담는 역할을 할 것 같습니다.

export const deleteCart = (items) => {
	return {
    	type: "DELETE_ITEM",
        payload: items
    }
}

reducers

reducers 폴더의 cartReducer.js 파일에서 장바구니와 관련된 reducer 함수를 작성해 줍니다. 프로젝트가 크다면 서로 다른 주제별로 reducer 를 만들 수 있습니다. 이렇게 다른 주제의 reducer 들은 결국 rootReducer 로 모입니다.

// store -> reducers -> cartReducer.js

const cartReducer = (state = [], action) => {
	switch (action.type) {
    	case "ADD_ITEM":
        	return [...state, action.payload];
        case "DELETE_ITEM":
        	return [...action.payload];
        default:
        	return state;
    }
}

export default cartReducer;
// store -> reducers -> index.js
// 이곳이 rootReducer 입니다.

import { combineReducers } from "redux";
import cartReducer from "./cartReducer";

export default combineReducers({ cartReducer });

// redux 에서 제공하는 combineReducers 를 통해 주제별로 나눈 조각 reducer (여기서는 cartReducer 만 존재) 들을 rootReducer 로 모아줍니다.

이렇게 모아진 rootReducer 는 전체 파일을 렌더하고 있는 index.js 에서 사용할 수 있습니다.

// src -> index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import Routes from './Routes';
import reportWebVitals from './reportWebVitals';

import { Provider } from "react-redux";
import { createStore } from "redux";
import rootReducer from "./store/reducers/"

const store = createStore(rootReducer);

ReactDOM.render(
    <Provider store={store}>
      <Routes />
    </Provider>
  ,
  document.getElementById('root')
);

reportWebVitals();

// redux 에서 제공하는 createStore 틍해 우리가 만든 rootReducer 를 import 받아와 store 라는 // 이름으로 저장합니다.

// react-redux 에서 제공하는 Provider 를 통해 프로젝트 전역에서 바라볼 수 있도록 해줍니다.

기능 구현

useDispatch

ProductItem.js 컴포넌트에너 useDispatch 와 actions 에서 작성해 둔 액션 생성함수를 import 받아와 사용합니다. 현재 ProductItem 컴포넌트는 부모 컴포넌트에서 map 을 돌리고 있습니다. props 로 내려오는 선택한 item 에 관한 데이터를 "장바구니 담기" 버튼을 클릭하면 액션 생성 함수 addCart 의 인자로 넘겨줍니다.

아래와 같이 action 으로 값을 올려줄 때에는 useDispatch 라는 훅을 사용합니다.

이렇게 하면 장바구니에 담기로 선택한 아이템에 관한 데이터가 actions 의 addCart 를 거쳐 reducer 에서 switch 문을 만납니다. switch 문에서는 action 의 type 이 "ADD_CART" 라면 기존의 state 에 전달 받은 데이터 payload 를 추가시킵니다.

이렇게 store 가 업데이트되면 해당 store 를 구독하고 있는 컴포넌트의 값은 저절로 바뀝니다.

useSelector

현재 상품 리스트 페이지에서 각 상품들을 장바구니에 담으면 store 에 하나씩 업데이트 됩니다. 이렇게 업데이트 된 store 를 구독하는 법을 알아봅시다. store 를 구독하려면 useSelector 라는 훅이 필요합니다.

useSelector 를 사용하여 store 에 접근하여 store 의 조각인 cartReducer 즉, 장바구니 담기 버튼을 클릭할 때마다 업데이트 되는 장바구니에 담긴 상품들의 배열을 cart 라는 변수에 담아 아래에서 map 을 돌려주면서 장바구니를 구현합니다.

현재 actions 의 addCart 라는 액션 생성함수를 이용해서 장바구니에 상품을 추가하는 기능을 구현하였습니다. 이와 같은 원리로 Nav 컴포넌트도 store 를 구독하게 한다면 store 가 업데이트 될 떄마다 장바구니 갯수가 자동으로 업데이트 됩니다.

장바구니의 상품을 삭제하는 기능 역시 같은 원리로 filter 를 사용한다면 어렵지 않게 구현할 수 있습니다.

profile
tenacity

0개의 댓글