Dong _ hwa 2023. 5. 19. 00:16

Redux ๋ฅผ ์˜ˆ์ „์— ๋ณต์Šตํ–ˆ๋˜ ๊ฒŒ์‹œ๊ธ€์ด ์žˆ๋‹ค. ์‹ฌ์ง€์–ด ์ด๊ฒƒ๋„ ์ƒ๋‹นํžˆ ์˜ค๋ž˜๋œ...
์ •ํ™•ํ•˜๊ฒŒ! ๊ฐœ๋…์„ ์•Œ์•„๊ฐ€์•ผ ํˆดํ‚ท์ด๊ณ  ๋ญ๊ณ  ๋” ์‰ฝ๋‹ค๊ณ ๋“ค ํ•˜๋Š” zustand, recoil ๋“ฑ๋“ฑ ํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™์•„์„œ ๋‹ค์‹œ ์ฒ˜์Œ๋ถ€ํ„ฐ ์ •๋ฆฌ ์‹œ์ž‘.
์ด์ œ ๊ทธ๋ƒฅ ~ ์ด๋ ‡๊ฒŒ ์“ฐ๋Š” ๊ฑฐ๋ผ~ ๋‹ค๋“ค ์ด๋ ‡๊ฒŒ ์“ฐ๋‹ˆ๊นŒ~ ๊ฐ€ ์•„๋‹ˆ๊ณ  ์ œ๋Œ€๋กœ ์ข€ ์•Œ๊ณ  ์จ๋ณด์ž ..

  • Store ๏ผš์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๊ณณ, ์ฆ‰ ์ €์žฅ์†Œ
  • Reducer ๏ผš์ƒํƒœ๋ฅผ ๋ณ€ํ™”์‹œํ‚ค๋Š” ๊ณณ, ์ฆ‰ ๋ณ€์ˆ˜๋ฅผ ์ €์žฅํ•˜๋Š” ๊ณณ
  • Actions ๏ผš์ƒํƒœ๋ฅผ ๋ณ€ํ™”์‹œํ‚ค๋Š” ํ•จ์ˆ˜, ์•ก์…˜์„ ์ทจํ•˜๋Š” ๊ณณ

npm install @reduxjs/toolkit react-redux

๋‚˜ ์ด๊ฑฐ ์„ค์น˜ ์•ˆ ํ•˜๊ณ  ์•„๋‹ˆ ์™œ ๋‚˜ ์•ˆ ๋ ๊นŒ..? ํ•œ ์  ์žˆ์Œ ใ… _ใ… 
์ด๊ฑฐ ์„ค์น˜ ์•ˆํ•˜๋ฉด ์˜ค๋ฅ˜ ๋‚ฌ๋‹ค๊ณ  ๋ง๋„ ์•ˆ ํ•ด์ฃผ๋‹ˆ(ํ˜ธ์˜๊ฐ€ ๊ณ„์† ๋˜๋ฉด ๋‘˜๋ฆฌ์ธ ์ค„ ์•ˆ๋‹ค..) ๊ผญ ๊ผญ ์„ค์น˜ํ•˜๊ณ  ์ง„ํ–‰ํ•˜์Ÿˆ..



Counter ์‹ค์Šต

์ด์ œ ๋‚จ๋“ค ๋‹ค ํ•œ๋‹ค๋Š” counter ์‹ค์Šต์„ ํ•ด๋ณด์ž

์Šคํ† ์–ด ์ƒ์„ฑ

src/app/store.js

์ผ๋‹จ store๋ฅผ ์ƒ์„ฑํ•˜๊ณ , ๊ทธ ์•ˆ์— { configureStore } API๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค.

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

const store = configureStore({
  reducer: {},
});

export default store;

์ด ๊ณผ์ •์—์„œ Redux ์ €์žฅ์†Œ๊ฐ€ ์ƒ์„ฑ๋˜๊ณ ,
์ €์žฅ์†Œ๋ฅผ ๊ฒ€์‚ฌํ•  ์ˆ˜ ์žˆ๋„๋ก Redux DevTools ํ™•์žฅ๋„ ์ž๋™์œผ๋กœ ๊ตฌ์„ฑ๋œ๋‹ค. (์ด๊ฒŒ ๋ฆฌ๋•์Šค์™€ ๋ฆฌ๋•์Šค ํˆดํ‚ท์˜ ์ฐจ์ด!)

 

configureStore

configureStore๏ผš๊ธฐ์กด createStore์™€ ๋‹ฌ๋ฆฌ, ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์ธ์ž ๋Œ€์‹ ์— ์ด๋ฆ„์ด ์ง€์ •๋œ ํ•˜๋‚˜์˜ object๋ฅผ ๋ฐ›๊ณ , reducer์„ ๋„˜๊ฒจ ์ค€๋‹ค.



 

์Šคํ† ์–ด ์ œ๊ณต

index.js ํŒŒ์ผ์—์„œ Store, Provider ๋ฅผ import ํ•ด์˜ค๊ณ  <App />์„ <Provider>๋กœ ๊ฐ์‹ธ store๋ฅผ props๋กœ ์ „๋‹ฌํ•ด์ค€๋‹ค. (importํ•ด์˜จ store๋ฅผ Provider ์ปดํฌ๋„ŒํŠธ์˜ ์†์„ฑ๊ฐ’์œผ๋กœ ์„ค์ •)

index.js

import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'
import App from './App'
import store  from './app/store'
import { Provider } from 'react-redux'

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

์—ฌ๊ธฐ์„œ ์ค‘๊ด„ํ˜ธ ์˜ค๋ฅ˜๊ฐ€ ์žˆ์—ˆ๋Š”๋ฐ, ์—ฌ๊ธฐ๋Š” ๋‚˜์ค‘์— ํ•œ ํฌ์ŠคํŒ…์— ์ ์–ด ๋†“์œผ๋ ค๊ตฌ ํ•จ.

 

 

์Šฌ๋ผ์ด์Šค ์ƒ์„ฑ

Slice๋ฅผ ๋งŒ๋“ค์–ด์ค˜์„œ action, reducer์„ ๋™์‹œ์— ์จ์ค€๋‹ค.
counterSlice.js ํŒŒ์ผ์„ ๋งŒ๋“ค์–ด์„œ, { createSlice } API๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค.
์Šฌ๋ผ์ด์Šค๋ฅผ ์ƒ์„ฑํ•˜๋ ค๋ฉด ์Šฌ๋ผ์ด์Šค๋ฅผ ์‹๋ณ„ํ•˜๊ธฐ ์œ„ํ•œ ๋ฌธ์ž์—ด ์ด๋ฆ„, ์ดˆ๊ธฐ ์ƒํƒœ ๊ฐ’, ์ƒํƒœ ์—…๋ฐ์ดํŠธ ๋ฐฉ๋ฒ•์„ ์ •์˜ํ•˜๋Š” ํ•˜๋‚˜ ์ด์ƒ์˜ ๋ฆฌ๋“€์„œ ํ•จ์ˆ˜๊ฐ€ ํ•„์š”ํ•˜๋‹ค.
์Šฌ๋ผ์ด์Šค๊ฐ€ ์ƒ์„ฑ๋˜๋ฉด ์ƒ์„ฑ๋œ Redux ์•ก์…˜ ์ƒ์„ฑ์ž์™€ ์ „์ฒด ์Šฌ๋ผ์ด์Šค์— ๋Œ€ํ•œ ๋ฆฌ๋“€์„œ ๊ธฐ๋Šฅ์„ ๋‚ด๋ณด๋‚ผ ์ˆ˜ ์žˆ๋‹ค.

src/features/counter/counterSlice.js

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

const initialState = {
  value: 0,
};

export const counterSlice = createSlice({
  name: "counter",
  initialState,
  reducers: {
    increment: (state) => {
      state.value += 1;
    },
    decrement: (state) => {
      state.value -= 1;
    },
    incrementByAmount: (state, action) => {
      state.value += action.payload;
    },
  },
});

export const { increment, decrement, incrementByAmount } = counterSlice.actions;

export default counterSlice.reducer;

initialState๋Š” state ์ดˆ๊ธฐ๊ฐ’
reducers๋Š” setState๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ํŽธํ•˜๋‹ค.

increment, decrement, incrementByAmount๊ฐ™์€ ํ•จ์ˆ˜๋ฅผ action์ด๋ผ๊ณ  ํ•œ๋‹ค.

state์™€ action ์ด ๋‘๊ฐœ์˜ ์ธ์ž๋ฅผ ๋ฐ›์•„์˜จ๋‹ค.
state๋Š” ํ˜„์žฌ state๊ฐ’์ด๊ณ ,
action์€ reducer ํ•จ์ˆ˜๋ฅผ ๊ฑฐ์ณ ์ƒˆ๋กœ์šด state๋ฅผ ๋ฆฌํ„ด ํ•œ๋‹ค.

์—ฌ๊ธฐ์„œ reducer๋ฅผ ๋ฆฌํ„ดํ•จ์œผ๋กœ์จ
์Šฌ๋ผ์ด์Šค์— ์ œ๊ณต๋œ ์ดˆ๊ธฐ ์ƒํƒœ ๊ฐ’์— ๋Œ€ํ•œ ์•ก์„ธ์Šค๋ฅผ ์ œ๊ณตํ•˜๊ณ , ์ง€์—ฐ ์ƒํƒœ ์ดˆ๊ธฐํ™”๊ฐ€ ์ œ๊ณต๋˜๋ฉด ํ˜ธ์ถœ๋˜๊ณ  ์ƒˆ๋กœ์šด ๊ฐ’์ด ๋ฐ˜ํ™˜๋œ๋‹ค.

 

createSlice

createSlice

  • name : ํ•ด๋‹น ๋ชจ๋“ˆ์˜ ์ด๋ฆ„์„ ์ž‘์„ฑ
  • initialState : ํ•ด๋‹น ๋ชจ๋“ˆ์˜ ์ดˆ๊ธฐ๊ฐ’ ์„ธํŒ…
  • reducers : ๋ฆฌ๋“€์„œ๋ฅผ ์ž‘์„ฑ. ์ด๋•Œ ํ•ด๋‹น ๋ฆฌ๋“€์„œ์˜ ํ‚ค๊ฐ’์œผ๋กœ ์•ก์…˜ํ•จ์ˆ˜๊ฐ€ ์ž๋™์œผ๋กœ ์ƒ์„ฑ
  • extraReducers : ์•ก์…˜ํ•จ์ˆ˜๊ฐ€ ์ž๋™์œผ๋กœ ์ƒ์„ฑ๋˜์ง€ ์•Š๋Š” ๋ณ„๋„์˜ ์•ก์…˜ํ•จ์ˆ˜๊ฐ€ ์กด์žฌํ•˜๋Š” ๋ฆฌ๋“€์„œ๋ฅผ ์ •์˜

Redux Toolkit์˜ createSlice ๋ฐ createReducer API๋Š” ์˜ฌ๋ฐ”๋ฅธ ๋ณ€๊ฒฝ ๋ถˆ๊ฐ€๋Šฅํ•œ ์—…๋ฐ์ดํŠธ๊ฐ€ "๋ณ€๊ฒฝ๋˜๋Š”" ์—…๋ฐ์ดํŠธ ๋กœ์ง์„ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๊ฒŒํ•œ๋‹ค. (์ด๊ฒƒ๋„ ๋ฆฌ๋•์Šค์™€์˜ ์ฐจ์ด)





์Šคํ† ์–ด์— ์Šฌ๋ผ์ด์Šค ๋ฆฌ๋“€์„œ ์ถ”๊ฐ€

import { configureStore } from "@reduxjs/toolkit";
import counterReducer from "../features/counter/counterSlice";

const store = configureStore({
  reducer: {
    counter: counterReducer,
  },
});

export default store;

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





๋ฆฌ๋•์Šค ์ƒํƒœ ๋ฐ ์ž‘์—…

์ด์ œ React-Redux Hook๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ React ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ Redux ์ €์žฅ์†Œ์™€ ์ƒํ˜ธ ์ž‘์šฉํ•˜๋„๋ก ํ•  ์ˆ˜ ์žˆ๋‹ค.
useSelector๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ €์žฅ์†Œ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ๊ณ 
useDispatch๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ž‘์—…์„ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋‹ค.

๋‚ด๋ถ€์— <Counter> ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ์žˆ๋Š” Counter.js ํŒŒ์ผ์„ ๋งŒ๋“  ๋‹ค์Œ ํ•ด๋‹น ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ App.js๋กœ ๊ฐ€์ ธ์˜ค๊ณ  <App> ๋‚ด๋ถ€์—์„œ ๋ Œ๋”๋ง.

src/features/counter/Counter.js

import React from "react";
import { useSelector, useDispatch } from "react-redux";
import { decrement, increment } from "./counterSlice";

export function Counter() {
  const count = useSelector((state) => state.counter.value);
  const dispatch = useDispatch();

  return (
    <div>
      <div style={{ margin: "40px" }}>
        <button
          aria-label="Increment value"
          onClick={() => dispatch(increment())}
        >
          Increment
        </button>
        <span>{count}</span>
        <button
          aria-label="Decrement value"
          onClick={() => dispatch(decrement())}
        >
          Decrement
        </button>
      </div>
    </div>
  );
}

useDispatch๋Š” state๋ณ€๊ฒฝ ํ•จ์ˆ˜ ์‚ฌ์šฉ ํ›„ ์ „๋‹ฌํ•ด์ฃผ๊ธฐ ์œ„ํ•ด import
useSelector์€ store.js์—์„œ ์ƒํƒœ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด import ํ•œ ๊ฒƒ์ด๋‹ค.

๊ทธ๋ฆฌ๊ณ  counterSlice์—์„œ ๋งŒ๋“  state๋ณ€๊ฒฝ ํ•จ์ˆ˜๋“ค์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ import ํ•ด์ฃผ์—ˆ๋‹ค.

dispatch ๋ณ€์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด์„œ useDispatch๋ฅผ ๋‹ด์•„์ฃผ๊ณ 
dispatch(state๋ณ€๊ฒฝํ•จ์ˆ˜()) ํ•ด์„œ ์‚ฌ์šฉํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

counterSlice์—์„œ ๋งŒ๋“ 
increment(state, action) -> action์ž๋ฆฌ์—
dispatch(increment(action์ž๋ฆฌ))๋ฅผ ๋ฐ›์•„์„œ useDispatch๋ฅผ ํ†ตํ•ด ์ „๋‹ฌํ•ด์ค€๋‹ค.

๊ทธ๋ฆฌ๊ณ  ๊ผญ action์ธ์ž๋ฅผ ๋ฐ›์•„์˜ฌ๋•Œ action.payload๋ฅผ ๋ถ™์—ฌ ์จ์ฃผ์–ด์•ผ ํ•œ๋‹ค.

์ด์ œ "์ฆ๊ฐ€" ๋ฐ "๊ฐ์†Œ" ๋ฒ„ํŠผ์„ ํด๋ฆญํ•  ๋•Œ๋งˆ๋‹ค

  • ํ•ด๋‹น Redux ์ž‘์—…์ด ์Šคํ† ์–ด๋กœ ๋ฐœ์†ก๋œ๋‹ค.
  • ์นด์šดํ„ฐ ์Šฌ๋ผ์ด์Šค ๋ฆฌ๋“€์„œ๋Š” ์ž‘์—…์„ ๋ณด๊ณ  ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•œ๋‹ค.
  • <Counter> ๊ตฌ์„ฑ ์š”์†Œ๋Š” ์ €์žฅ์†Œ์˜ ์ƒˆ ์ƒํƒœ ๊ฐ’์„ ๋ณด๊ณ  ์ƒˆ ๋ฐ์ดํ„ฐ๋กœ ์ž์ฒด๋ฅผ ๋‹ค์‹œ ๋ Œ๋”๋งํ•œ๋‹ค.
  console.log("count");
  console.log(count);



 

์—ฌ๋Ÿฌ ๊ฐœ์˜ ๋ฆฌ๋“€์„œ ๋งŒ๋“ค๊ธฐ

store์— reducer๋ฅผ ์—ฌ๋Ÿฌ ๊ฐœ ๋„ฃ๊ณ  ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์€ ์–ด๋–ป๊ฒŒ ํ•˜๋Š”์ง€ ๊ถ๊ธˆํ•ด์„œ
๋˜ ๋”ฐ๋กœ ์ง„ํ–‰ํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค ใ…Ž_ใ…Ž

 

์ˆซ์ž ์ƒ‰์ƒ ๋ฐ”๊พธ๊ธฐ ๐ŸŽจ

features ํด๋”์— ChangeColor.js ํŒŒ์ผ์„ ๋งŒ๋“ ๋‹ค.

src/features/color/ChangeColor.js

import React, { useState } from "react";

function ChangeColor() {
  const [color, setColor] = useState("");
  return (
    <div>
      <input
        type="text"
        onChange={(e) => {
          setColor(e.target.value);
        }}
      />
      <button>CHANGE COLOR</button>
    </div>
  );
}

export default ChangeColor;



๋ฆฌ๋“€์„œ ์ถ”๊ฐ€

theme.js๋ผ๋Š” ํŒŒ์ผ์„ ๋งŒ๋“ ๋‹ค.

src/features/color/theme.js

import { createSlice } from '@reduxjs/toolkit'

// initialState๋Š” ๋ฆฌ๋“€์„œ๋ฅผ ์“ฐ๋‹ค๋ณด๋ฉด ์ž์ฃผ ๊ฑด๋“ค๊ฒŒ ๋˜๋Š”๋ฐ, ๋งค๋ฒˆ ์จ์ฃผ๋‹ค ๋ณด๋ฉด ๊ท€์ฐฎ์•„์„œ ๋”ฐ๋กœ ๋นผ์ฃผ๋Š” ์ž‘์—…
// ์—ฌ๊ธฐ๋Š” ์–ด์ฐจํ”ผ ํ•˜๋‚˜๋ฐ–์— ์—†์ง€๋งŒ, ๊ฐ์ฒด๊ฐ€ ๋งŽ์„ ๋•Œ๋Š” ์œ ์šฉํ•˜๋‹ค.
// string์„ ๋„˜๊ธธ ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— ""
const initialStateValue = "";

export const themeSlice = createSlice({
    name: "theme",
    initialState: { value: initialStateValue },
    reducers: {
        changeColor: ( state, action ) => {
            state.value = action.payload
        },
    },
});

export const { changeColor } = themeSlice.actions;

export default themeSlice.reducer;



์Šคํ† ์–ด์— ์ถ”๊ฐ€

src/app/store.js

import { configureStore } from '@reduxjs/toolkit'
import counterReducer from "../features/counter/counterSlice";
import themeReducer from "../features/color/theme";

export default configureStore({
    reducer:{
        counter: counterReducer,,
        theme: themeReducer
    }
})



์ƒ‰ ๋ณ€๊ฒฝ ๊ธฐ๋Šฅ ์ถ”๊ฐ€

์ˆซ์ž์˜ ์ƒ‰์ด ๋ฐ”๋€Œ๊ฒŒ ํ•˜๊ณ  ์‹ถ์œผ๋‹ˆ div์— ์Šคํƒ€์ผ์„ ํ•˜๋‚˜ ์ค˜์„œ ์ธํ’‹์˜ ๊ฐ’์ด css ์š”์†Œ๋กœ ๋“ค์–ด๊ฐ€๊ฒŒ ํ•ด์•ผ ํ•œ๋‹ค.

์ผ๋‹จ ChangeColor ์—์„œ ๋ฒ„ํŠผ ๊ธฐ๋Šฅ + dispatch (๋‚˜ ์ˆ˜์ •ํ–ˆ๋”ฐ!) ์ถ”๊ฐ€.

src/features/color/ChangeColor.js

import React, { useState } from "react";
import { useDispatch } from "react-redux";
import { changeColor } from "./theme";

function ChangeColor() {
  const [color, setColor] = useState("");
  const dispatch = useDispatch();
  console.log(color);

  return (
    <div>
      <input
        type="text"
        onChange={(e) => {
          setColor(e.target.value);
        }}
      />
      <button
        onClick={() => {
          dispatch(changeColor(color));
        }}
      >
        CHANGE COLOR
      </button>
    </div>
  );
}

export default ChangeColor;

์ด๊ฑด ์ด์ œ App.js์— ์ถ”๊ฐ€ํ•ด์ฃผ๊ณ ,

App.js

import React from "react";
import ChangeColor from "./features/color/ChangeColor";
import { Counter } from "./features/counter/Counter";

function App() {
  return (
    <div>
      <Counter />
      <ChangeColor />
    </div>
  );
}

export default App;

Counter ์— ์žˆ๋Š” ์• ๋“ค ์ƒ‰์ƒ์„ ๋ฐ”๊ฟ€ ๊ฑฐ๋‹ˆ๊น,
์•„๊นŒ Counter์— ์ถ”๊ฐ€ํ•ด์ค€๋‹ค.

src/features/counter/Counter.js

...

const themeColor = useSelector((state) => state.theme.value);
...



  return (
    <div>
      <div style={{ margin: "40px", color: themeColor }}>
        ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ๋ณด์ž.
        
        ...
      </div>

input ์ฐฝ์— css์—์„œ ์‚ฌ์šฉํ•˜๋Š” ์ƒ‰์ƒ๋ช…์„ ์•„๋ฌด๊ฑฐ๋‚˜ ์ ์œผ๋ฉด ๊ทธ๊ฑธ๋กœ ๋ฐ”๋€๋‹ค.