React

[TIL/React] 2023/10/17

reference: https://www.npmjs.com/package/redux-persist > Redux-Persist โœ๏ธ ๋ฐฐ๊ฒฝ ๐ŸŸ  ํ”„๋กœ์ ํŠธ ์ง„ํ–‰ ์ค‘(=์‡ผํ•‘ ์นดํŠธ ๋ชฉ๋ก ํŽ˜์ด์ง€), ์ƒˆ๋กœ๊ณ ์นจ์— ์˜ํ•ด ๋ฐ์ดํ„ฐ๊ฐ€ ์ดˆ๊ธฐํ™”๋œ๋‹ค๋Š” ๋ฌธ์ œ์ ์„ ๋ฐœ๊ฒฌํ–ˆ๋‹ค. Redux-Per

2023๋…„ 10์›” 17์ผ3min read

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

Redux-Persist โœ๏ธ

๋ฐฐ๊ฒฝ ๐ŸŸ 

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

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

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

๊ตฌ์กฐ ๐ŸŸ 

code
// 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๋ผ๋Š” ๋ณ€์ˆ˜์— ํ• ๋‹นํ–ˆ๋‹ค.

code
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 ๊ด€๋ จ ๋กœ์ง โœ๏ธ

code
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๊นŒ์ง€ ์™„๋ฒฝํ•˜๊ฒŒ ๊ตฌ์„ฑํ•ด์„œ ์ตœ์ข…์ ์œผ๋กœ ์ •๋ฆฌํ•˜๋„๋ก ํ•˜๊ฒ ๋‹ค!

More to read

Amazon VPC

Amazon VPC Architecture ์ดํ•ดํ•˜๊ธฐ

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

'์›์‚ฌ์ดํŠธ'

ํ”„๋ก ํŠธ์—”๋“œ ๊ด€์ ์œผ๋กœ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์ดํ•ดํ•˜๊ธฐ

์˜ค๋žœ๋งŒ์— ๋ฐฉ๋ฒ•๋ก ์— ๊ด€ํ•œ ๊ธ€์„ ์“ฐ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ตœ๊ทผ ์ƒํ™ฉ์€ ์ด๋ ‡์Šต๋‹ˆ๋‹ค. SSAFY์—์„œ๋Š” ํ•˜๋ฃจ์— ์—„์ฒญ๋‚œ ์–‘์˜ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๋ฌธ์ œ๋“ค์„ ๊ณผ์ œ๋กœ ์ˆ˜ํ–‰ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๊ทธ ๊ณผ์ •์—์„œ, '๊ตฌํ˜„๋ ฅ'์ด ๋งค์šฐ ๋–จ์–ด์ง„๋‹ค๋Š” ์ƒ๊ฐ์ด ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. ์™„์ „ํžˆ ์–ด๋ ค์šด ๋ฌธ์ œ๋ผ๋ฉด '์•„์‰ฌ์›€'์ด๋ผ๋Š” ๊ฐ์ •์กฐ์ฐจ ๋А๋ผ์ง€

Subnet

VPC ์„ค๊ณ„์˜ ์‹œ์ž‘: IP์™€ Subnet

๋ฐ˜๋ณต๋˜๋Š” ๋ฃจํ‹ด ์†์—์„œ ์–ป์€ ์•ˆ์ •๊ฐ์„ ๋ฐœํŒ ์‚ผ์•„, ์ด์ œ๋Š” ๊ธฐ์ˆ ์  ์ŠคํŽ™ํŠธ๋Ÿผ์„ ๋„“ํžˆ๊ธฐ ์œ„ํ•œ ๊ฐœ์ธ ํ”„๋กœ์ ํŠธ์— ์ฐฉ์ˆ˜ํ•˜๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค.์ด๋ฒˆ ํ”„๋กœ์ ํŠธ์˜ ๋ชฉํ‘œ๋Š” ๋‹จ์ˆœํ•œ ํฌํŠธํด๋ฆฌ์˜ค ๊ตฌ์ถ•์„ ๋„˜์–ด, ์‹ค์ œ ์„œ๋น„์Šค ์ˆ˜์ค€์˜ ๋ธ”๋กœ๊ทธ ์‹œ์Šคํ…œ ๊ตฌํ˜„๊ณผ ๋‹ค๊ตญ์–ด ์ฒ˜๋ฆฌ ์ ์šฉ ๋“ฑ ์‹ค๋ฌด์— ๊ฐ€๊นŒ์šด ์—ญ๋Ÿ‰์„ ํ•œ ๋‹จ๊ณ„