[TIL/React] 2023/10/17

์›๋ฏผ๊ด€ยท2023๋…„ 10์›” 17์ผ

[TIL]

๋ชฉ๋ก ๋ณด๊ธฐ
126/201

reference: https://www.npmjs.com/package/redux-persist

Redux-Persist โœ๏ธ

๋ฐฐ๊ฒฝ ๐ŸŸ 

ํ”„๋กœ์ ํŠธ ์ง„ํ–‰ ์ค‘(=์‡ผํ•‘ ์นดํŠธ ๋ชฉ๋ก ํŽ˜์ด์ง€), ์ƒˆ๋กœ๊ณ ์นจ์— ์˜ํ•ด ๋ฐ์ดํ„ฐ๊ฐ€ ์ดˆ๊ธฐํ™”๋œ๋‹ค๋Š” ๋ฌธ์ œ์ ์„ ๋ฐœ๊ฒฌํ–ˆ๋‹ค. Redux-Persist๋Š” ๋ธŒ๋ผ์šฐ์ €์— ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•  ์ˆ˜ ์žˆ๋„๋ก ๋•๋Š”๋‹ค.

์ด์ปค๋จธ์Šค ์‚ฌ์ดํŠธ์˜ '์žฅ๋ฐ”๊ตฌ๋‹ˆ ๋ชฉ๋ก' ๋˜๋Š” '๋‹คํฌ๋ชจ๋“œ' ๋“ฑ, ์œ ์ €์˜ ๊ฐœ์ธ์ •๋ณด ์œ ์ถœ์— ์˜ํ–ฅ์„ ๋ฏธ์น˜๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ์•„๋‹ˆ๋ผ๋ฉด, ๋ธŒ๋ผ์šฐ์ €์— ํ•ด๋‹น ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜์—ฌ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋” ์œ ๋ฆฌํ•˜๊ฒ ๋‹ค.

๋”ฐ๋ผ์„œ ๋ณธ TIL์—์„œ๋Š” Redux-Persist๊ฐ€ ์ œ๊ณตํ•˜๋Š” API ๋ช‡ ๊ฐ€์ง€๋ฅผ ์‚ดํŽด๋ณด๊ณ  ๊ธฐ์ดˆ์ ์ธ ์‚ฌ์šฉ๋ฒ•์— ๋Œ€ํ•ด ๊ธฐ์ˆ ํ•˜๊ฒ ๋‹ค. ์ถ”๊ฐ€์ ์œผ๋กœ ์–ด์ œ๊นŒ์ง€ ์‹œ๋„ํ•œ shopping cart์— ๋Œ€ํ•œ ์ „๋ฐ˜์ ์ธ ๋กœ์ง์„ ์ •๋ฆฌํ•˜๋„๋ก ํ•˜๊ฒ ๋‹ค.

๊ตฌ์กฐ ๐ŸŸ 

// configureStore.js
 
import { createStore } from 'redux'
import { persistStore, persistReducer } from 'redux-persist'
import storage from 'redux-persist/lib/storage' // defaults to localStorage for web
 
import rootReducer from './reducers'
 
const persistConfig = {
  key: 'root',
  storage,
}
 
const persistedReducer = persistReducer(persistConfig, rootReducer)
 
export default () => {
  let store = createStore(persistedReducer)
  let persistor = persistStore(store)
  return { store, persistor }
}

์œ„ ์ฝ”๋“œ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ํ”„๋กœ์ ํŠธ์˜ store.js ํŒŒ์ผ์— ์ž…๋ ฅ๋  ๋‚ด์šฉ์ด๋‹ค. API์— ๋Œ€ํ•ด์„œ๋Š” ์ฐจํ›„ ์‚ดํŽด๋ณผ ์˜ˆ์ •์ด๋‹ˆ, ๊ตฌ์กฐ๋ถ€ํ„ฐ ํ™•์ธํ•˜๊ฒ ๋‹ค.

persistConfig๋ผ๋Š” ๊ฐ์ฒด๊ฐ€ ์„ ์–ธ๋˜์–ด ์žˆ๊ณ  ๋‚ด๋ถ€์—๋Š” 'key:'root''์™€ 'storage'๊ฐ€ ์žˆ๋‹ค. ์ด ๊ฐ์ฒด๋ฅผ persistReducer๋ผ๋Š” ํ•จ์ˆ˜์— ์ฒซ ๋ฒˆ์งธ ์ธ์ž๋กœ ์ „๋‹ฌํ•˜๊ณ  ์žˆ๋‹ค. ๋‘ ๋ฒˆ์งธ ์ธ์ž์—๋Š” rootReducer๊ฐ€ ์žˆ๋‹ค.

persistReducer ํ•จ์ˆ˜๋ฅผ persistedReducer์— ์ €์žฅํ•˜๊ณ , persistedReducer๋ฅผ createStore ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด store์— ํ• ๋‹นํ•œ ๋ชจ์Šต์ด๋‹ค.

์ถ”๊ฐ€์ ์œผ๋กœ persistStore๋ผ๋Š” ํ•จ์ˆ˜์— ์œ„ store๋ฅผ ์ธ์ž๋กœ ์ „๋‹ฌํ•˜์—ฌ persistor๋ผ๋Š” ๋ณ€์ˆ˜์— ํ• ๋‹นํ–ˆ๋‹ค.

import { PersistGate } from 'redux-persist/integration/react'
 
// ... normal setup, create store and persistor, import components etc.
 
const App = () => {
  return (
    <Provider store={store}>
      <PersistGate loading={null} persistor={persistor}>
        <RootComponent />
      </PersistGate>
    </Provider>
  );
};

๋ฃจํŠธ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์œ„ ์ฝ”๋“œ์ฒ˜๋Ÿผ PersistGate๋กœ ๊ฐ์‹ธ๋ฉด ์ตœ์ข…์ ์œผ๋กœ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ธŒ๋ผ์šฐ์ €์— ์ €์žฅ๋œ๋‹ค.

API ๐ŸŸ 

persistReducer() ๐ŸŸข

persistReducer()๋Š” ๋‘ ๊ฐœ์˜ ์ธ์ž(=config, store)๋ฅผ ๋ฐ›๋Š”๋‹ค.

config obj

config ๊ฐ์ฒด๋Š” Redux-Persist์— ๋Œ€ํ•œ ์„ค์ •์„ ํฌํ•จํ•˜๋Š” ๊ฐ์ฒด๋‹ค. key๋Š” Redux ์ƒํƒœ๋ฅผ ์‹๋ณ„ํ•  ๋•Œ ์‚ฌ์šฉํ•  key๋‹ค. storage๋Š” ์ €์žฅ์†Œ ์œ ํ˜•(=๋กœ์ปฌ ์Šคํ† ๋ฆฌ์ง€, ์„ธ์…˜ ์Šคํ† ๋ฆฌ์ง€)์„ ์˜๋ฏธํ•œ๋‹ค.

localStorage์— ์ €์žฅํ•˜๊ณ  ์‹ถ์œผ๋ฉด import storage from 'redux-persist/lib/storage', session Storage์— ์ €์žฅํ•˜๊ณ  ์‹ถ์œผ๋ฉด import storageSession from 'redux-persist/lib/storage/session'๋ฅผ ์ž‘์„ฑํ•˜๋ฉด ๋œ๋‹ค.

์š”์•ฝํ•˜์ž๋ฉด persistReducer()๋Š” Redux-Persist์— ๋Œ€ํ•œ ์„ค์ •์„ ํฌํ•จํ•˜๋Š” ๊ฐ์ฒด์ธ config๋ฅผ ์ „๋‹ฌ๋ฐ›์•„์„œ, Redux ์ƒํƒœ๋ฅผ ์ง€์†์ ์œผ๋กœ ์ €์žฅํ•˜๊ณ  ๋ณต์›ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ์ƒˆ๋กœ์šด reducer๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ๋‹ค.

persistStore() ๐ŸŸข

persistReducer()๋ฅผ ํ†ตํ•ด persist์— ๋Œ€ํ•œ ์„ค์ •์ด ์ ์šฉ๋œ reducer๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค. persistStore()๋Š” ํ•ด๋‹น ๋‚ด์šฉ์„ ๋‹ด์•„์„œ, persist์— ๋Œ€ํ•œ ์„ค์ •์ด ์ ์šฉ๋œ store๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. 'persistStore(store)'๋Š” persistor ๊ฐ์ฒด๋‹ค.

persistor obj ๐ŸŸข

persistor ๊ฐ์ฒด๋Š” Redux Persist์˜ persistStore ํ•จ์ˆ˜๋กœ๋ถ€ํ„ฐ ๋ฐ˜ํ™˜๋˜๋ฉฐ, ์ƒํƒœ๋ฅผ ์˜๊ตฌ์ ์œผ๋กœ ์ €์žฅํ•˜๊ณ  ๋ณต์›ํ•˜๊ธฐ ์œ„ํ•œ ์—ฌ๋Ÿฌ ์œ ์šฉํ•œ ๋ฉ”์„œ๋“œ๋ฅผ ์ œ๊ณตํ•œ๋‹ค. purge, flush, pause, persist๊ฐ€ ์žˆ๋Š”๋ฐ ๋ณธ ๊ธ€์—์„œ๋Š” ์ž์„ธํžˆ ๋‹ค๋ฃจ์ง€ ์•Š๋„๋ก ํ•˜๊ฒ ๋‹ค. ์œ ์šฉํ•œ ๋ฉ”์„œ๋“œ๊ฐ€ ์žˆ๋‹ค๋Š” ์ ๋งŒ ์•Œ์•„๋‘๊ณ  ๋„˜์–ด๊ฐ€์ž.

total ๊ด€๋ จ ๋กœ์ง โœ๏ธ

import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import { splitPrice } from "../utils/commonModule";

export const getCartData = createAsyncThunk(
  "cart/getCartData",
  async (action) => {
    console.log(action);
    try {
      return action;
    } catch (error) {
      console.error("Error: ", error);
      throw error;
    }
  }
);

export const deleteCardData = createAsyncThunk(
  "cart/deleteCardData",
  async (productId) => {
    try {
      return productId;
    } catch (error) {
      console.error("Error: ", error);
      throw error;
    }
  }
);

export const updateTotalPriceAndQuantity = createAsyncThunk(
  "cart/updateTotalPriceAndQuantity",
  async (_, { getState }) => {
    const state = getState();
    const { cartProduct } = state.cart;

    const { totalPrice, totalQuantity } = cartProduct.reduce(
      (acc, item) => {
        acc.totalPrice += splitPrice(item.price) * item.count;
        acc.totalQuantity += item.count;
        return acc;
      },
      { totalPrice: 0, totalQuantity: 0 }
    );

    return { totalPrice, totalQuantity };
  }
);

const cartSlice = createSlice({
  name: "cart",
  initialState: {
    cartProduct: [],
    totalPrice: 0,
    totalQuantity: 0,
  },
  reducers: {},
  extraReducers: (builder) => {
    builder.addCase(getCartData.fulfilled, (state, action) => {
      const { category, id, count } = action.payload;
      const existingProductIndex = state.cartProduct.findIndex(
        (product) => product.category === category && product.id === id
      );
      if (existingProductIndex !== -1) {
        state.cartProduct[existingProductIndex].count += count;
      } else {
        state.cartProduct.push(action.payload);
      }
    });

    builder.addCase(deleteCardData.fulfilled, (state, action) => {
      const { id, category } = action.payload;
      const deleteIndex = state.cartProduct.findIndex(
        (product) => product.category === category && product.id === id
      );
      if (deleteIndex !== -1) {
        state.cartProduct.splice(deleteIndex, 1);
      }
    });

    builder.addCase(updateTotalPriceAndQuantity.fulfilled, (state, action) => {
      const { totalPrice, totalQuantity } = action.payload;
      state.totalPrice = totalPrice;
      state.totalQuantity = totalQuantity;
    });
  },
});

export default cartSlice.reducer;

์ด์ œ ์ง„์งœ ์™„๋ฒฝํ•˜๊ฒŒ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ–ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋‚ด์ผ์ด ์‹œํ—˜์ด๊ธฐ์—...

view๊นŒ์ง€ ์™„๋ฒฝํ•˜๊ฒŒ ๊ตฌ์„ฑํ•ด์„œ ์ตœ์ข…์ ์œผ๋กœ ์ •๋ฆฌํ•˜๋„๋ก ํ•˜๊ฒ ๋‹ค!

profile
Write a little every day, without hope, without despair โœ๏ธ

0๊ฐœ์˜ ๋Œ“๊ธ€