240819 React Redux 1

์‹ ํ•œ๋ณ„ยท2024๋…„ 8์›” 19์ผ

๐Ÿ’ช ๋งค์ผ ๊ธฐ๋ก

๋ชฉ๋ก ๋ณด๊ธฐ
61/97

01. Redux ์†Œ๊ฐœ

(1) ๋ฆฌ๋•์Šค๊ฐ€ ํ•„์š”ํ•œ ์ด์œ 

1. useState๊ฐ€ ๋ถˆํŽธํ•ด์„œ

๐Ÿ“ข State๋ฅผ ๊ณต์œ ํ•˜๊ณ ์ž ํ• ๋•Œ ๋ถ€-๋ชจ ๊ด€๊ณ„๊ฐ€ ์•„๋‹ˆ์–ด๋„ ๋˜๊ณ , ์ค‘๊ฐ„์— ๋ถˆํ•„์š”ํ•œ ์ „๋‹ฌ์ฑ…์ด ํ•„์š”์—†๋‹ค!

  • `
  1. ์ปดํฌ๋„ŒํŠธ์—์„œ ์ปดํฌ๋„ŒํŠธ๋กœ State๋ฅผ ๋ณด๋‚ด๊ธฐ์œ„ํ•ด์„œ๋Š” ๋ฐ˜๋“œ์‹œ ๋ถ€-๋ชจ ๊ด€๊ณ„๊ฐ€ ๋˜์–ด์•ผ ํ•จ
  2. ์กฐ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์—์„œ ์†์ž ์ปดํฌ๋„ŒํŠธ๋กœ ๊ฐ’์„ ๋ณด๋‚ด๊ณ ์ž ํ• ๋•Œ๋„ **๋ฐ˜๋“œ์‹œ ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ฑฐ์ณ์•ผ๋งŒ ํ•œ๋‹ค. ๊ทธ ๊ฐ’์ด ๋ถ€๋ชจ์—๊ฒŒ ํ•„์š”์—†๋Š” ๊ฐ’์ด์–ด๋„
  3. ์ž์‹ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ๋กœ ๊ฐ’์„ ๋ณด๋‚ผ ์ˆ˜ ์—†๋‹ค.

2. Global state์™€ Local state

a. Local state ?

  • ์ปดํฌ๋„ŒํŠธ๋‹จ์œ„์—์„œ useState๋ฅผ ์ด์šฉํ•ด์„œ ์ƒ์„ฑํ•œ state

b. Global state ?

  • ์ค‘์•™ state ๊ด€๋ฆฌ์†Œ ์—์„œ ์ƒ์„ฑํ•œ state
  • ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์–ด๋””์— ์žˆ๋“  ์ƒ๊ด€์—†์ด state๋ฅผ ๋ถˆ๋Ÿฌ์™€์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์ด๋Ÿฌํ•œ ๊ฐ’๋“ค์„ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์„ ์ „์—ญ ์ƒํƒœ ๊ด€๋ฆฌ ๋ผ๊ณ  ํ•œ๋‹ค.

3. Context API๊ฐ€ ์žˆ๋Š”๋ฐ ๊ตณ์ด Redux?

a. Context API

  • React ์ž์ฒด์ œ๊ณต ์ „์—ญ ์ƒํƒœ ๊ด€๋ฆฌ ๋„๊ตฌ
  • ๋ณต์žกํ•œ ์ƒํƒœ ๊ด€๋ฆฌ, ๋Œ€๊ทœ๋ชจ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์ ํ•ฉํ•˜์ง€ ์•Š์Œ
  • Provide ํ•˜์œ„์˜ ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ฆฌ๋žœ๋”๋ง -> ๋ณต์žกํ•œ ์ตœ์ ํ™”

b. Redux

  • ์„ฑ๋Šฅ์ตœ์ ํ™” ์ƒํƒœ ๋ณ€๊ฒฝ ์‹œ ๊ด€๋ จ๋œ ์ปดํฌ๋„ŒํŠธ๋งŒ ์„ ํƒ์ ์œผ๋กœ ์—…๋ฐ์ดํŠธ -> ์„ ํƒ๊ด€๋ฆฌ ์šฉ์ด
  • ์ƒํƒœ ๋กœ์ง์˜ ์ค‘์•™ํ™”, ์ผ๊ด€์„ฑ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ƒํƒœ๋ฅผ ํ•˜๋‚˜์˜ ์ €์žฅ์†Œ(store)์— ์ €์žฅ -> ์ผ๊ด€์„ฑ, ์˜ˆ์ธก ๊ฐ€๋Šฅํ•œ ์ƒํƒœ ๋ณ€๊ฒฝ
    ๋ฆฌ๋“€์„œ(Reducers)์— ์˜ํ•ด ์ฒ˜๋ฆฌ -> ๋””๋ฒ„๊น…, ํ…Œ์ŠคํŒ… ์šฉ์ด
  • ๋ฏธ๋“ค์›จ์–ด์™€ ๊ฐœ๋ฐœ๋„๊ตฌ ๋‹ค์–‘ํ•œ ๋ฏธ๋“ค์›จ์–ด ์ง€์›. ๋น„๋™๊ธฐ, ๋กœ๊น…, ์ƒํƒœ ๋ณ€๊ฒฝ์— ๋Œ€ํ•œ ์ถ”๊ฐ€ ์ฒ˜๋ฆฌ๋“ฑ ๋ณต์žกํ•œ ๊ธฐ๋Šฅ ๊ตฌํ˜„
    Redux Devtools๋ฅผ ํ†ตํ•ด ์ƒํƒœ ๋ณ€ํ™”๋ฅผ ์‹œ๊ฐ์ ์œผ๋กœ ๋ชจ๋‹ˆํ„ฐ๋ง, ๋กค๋ฐฑ ๊ฐ€๋Šฅ

(2) ๋ฆฌ๋•์Šค๋ž€?

์ค‘์•™ state ๊ด€๋ฆฌ์†Œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋„์™€์ฃผ๋Š” ํŒจํ‚ค์ง€(๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ)

02. Redux ์„ค์ •

(1) ๋ฆฌ๋•์Šค ์„ค์ •

1. ๋ฆฌ๋•์Šค ์„ค์น˜

yarn add redux react-redux
์•„๋ž˜์™€ ๊ฐ™์€ ์˜๋ฏธ

yarn add redux
yarn add react-redux

2. ํด๋” ๊ตฌ์กฐ ์ƒ์„ฑ

  1. src ํด๋” ์•ˆ์— redux ํด๋”๋ฅผ ์ƒ์„ฑ
  2. redux ํด๋” ์•ˆ์— config, modules ํด๋”๋ฅผ ์ƒ์„ฑ
  3. config ํด๋” ์•ˆ์— configStore.jsํŒŒ์ผ์„ ์ƒ์„ฑ
  • redux : ๋ฆฌ๋•์Šค์™€ ๊ด€๋ จ๋œ ์ฝ”๋“œ๋ฅผ ๋ชจ๋‘ ๋ชจ์•„ ๋†“์„ ํด๋”
  • config : ๋ฆฌ๋•์Šค ์„ค์ •๊ณผ ๊ด€๋ จ๋œ ํŒŒ์ผ๋“ค์„ ๋†“์„ ํด๋”
  • configStore : โ€œ์ค‘์•™ state ๊ด€๋ฆฌ์†Œ" ์ธ Store๋ฅผ ๋งŒ๋“œ๋Š” ์„ค์ • ์ฝ”๋“œ๋“ค์ด ์žˆ๋Š” ํŒŒ์ผ
  • modules : State๋“ค์˜ ๊ทธ๋ฃน. ์˜ˆ๋ฅผ ๋“ค์–ด ํˆฌ๋‘๋ฆฌ์ŠคํŠธ๋ฅผ ๋งŒ๋“ ๋‹ค๊ณ  ํ•œ๋‹ค๋ฉด, ํˆฌ๋‘๋ฆฌ์ŠคํŠธ์— ํ•„์š”ํ•œ state๋“ค์ด ๋ชจ๋‘ ๋ชจ์—ฌ์žˆ์„ todos.js๋ฅผ ์ƒ์„ฑํ•˜๋Š”๋ฐ, ์ด todos.js ํŒŒ์ผ์ด ๊ณง ํ•˜๋‚˜์˜ ๋ชจ๋“ˆ์ด ๋จ

(2) ์„ค์ •์ฝ”๋“œ ์ž‘์„ฑ

1. src/configStore.js

src/configStore.js

import { createStore } from "redux";
import { combineReducers } from "redux";

/*
1. createStore()
๋ฆฌ๋•์Šค์˜ ๊ฐ€์žฅ ํ•ต์‹ฌ์ด ๋˜๋Š” ์Šคํ† ์–ด๋ฅผ ๋งŒ๋“œ๋Š” ๋ฉ”์†Œ๋“œ(ํ•จ์ˆ˜) ์ž…๋‹ˆ๋‹ค. 
๋ฆฌ๋•์Šค๋Š” ๋‹จ์ผ ์Šคํ† ์–ด๋กœ ๋ชจ๋“  ์ƒํƒœ ํŠธ๋ฆฌ๋ฅผ ๊ด€๋ฆฌํ•œ๋‹ค๊ณ  ์„ค๋ช…ํ•ด ๋“œ๋ ธ์ฃ ? 
๋ฆฌ๋•์Šค๋ฅผ ์‚ฌ์šฉํ•  ์‹œ creatorStore๋ฅผ ํ˜ธ์ถœํ•  ์ผ์€ ํ•œ ๋ฒˆ๋ฐ–์— ์—†์„ ๊ฑฐ์˜ˆ์š”.
*/

/*
2. combineReducers()
๋ฆฌ๋•์Šค๋Š” action โ€”> dispatch โ€”> reducer ์ˆœ์œผ๋กœ ๋™์ž‘ํ•œ๋‹ค๊ณ  ๋ง์”€๋“œ๋ ธ์ฃ ? 
์ด๋•Œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๋ณต์žกํ•ด์ง€๊ฒŒ ๋˜๋ฉด reducer ๋ถ€๋ถ„์„ ์—ฌ๋Ÿฌ ๊ฐœ๋กœ ๋‚˜๋ˆ ์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. 
combineReducers์€ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ๋…๋ฆฝ์ ์ธ reducer์˜ ๋ฐ˜ํ™˜ ๊ฐ’์„ ํ•˜๋‚˜์˜ ์ƒํƒœ ๊ฐ์ฒด๋กœ ๋งŒ๋“ค์–ด์ค๋‹ˆ๋‹ค.
*/

const rootReducer = combineReducers({}); 
const store = createStore(rootReducer); 

export default store; 

index.js

// ์›๋ž˜๋ถ€ํ„ฐ ์žˆ๋˜ ์ฝ”๋“œ
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import reportWebVitals from "./reportWebVitals";

// ์šฐ๋ฆฌ๊ฐ€ ์ถ”๊ฐ€ํ•  ์ฝ”๋“œ
import store from "./redux/config/configStore";
import { Provider } from "react-redux";

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(

	//App์„ Provider๋กœ ๊ฐ์‹ธ์ฃผ๊ณ , configStore์—์„œ export default ํ•œ store๋ฅผ ๋„ฃ์–ด์ค๋‹ˆ๋‹ค.
  <Provider store={store}> 
    <App />
  </Provider>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

(3) ๋ชจ๋“ˆ ๋งŒ๋“ค๊ธฐ

์นด์šดํ„ฐ ํ”„๋กœ๊ทธ๋žจ ๋งŒ๋“ค๊ธฐ ๋ ˆ์ธ ๊ณ 

1. ์ฒซ ๋ชจ๋“ˆ ๋งŒ๋“ค๊ธฐ

๋ชจ๋“ˆ? state์˜ ๊ทธ๋ฃน.

  1. modulesํด๋”์— counter.js ํŒŒ์ผ ์ƒ์„ฑ
  2. ์ฝ”๋“œ ์ž‘์„ฑ
// src/redux/modules/counter.js

// ์ดˆ๊ธฐ ์ƒํƒœ๊ฐ’
const initialState = {
  number: 0,
};

// ๋ฆฌ๋“€์„œ
const counter = (state = initialState, action) => {
  switch (action.type) {
    default:
      return state;
  }
};

// ๋ชจ๋“ˆํŒŒ์ผ์—์„œ๋Š” ๋ฆฌ๋“€์„œ๋ฅผ export default ํ•œ๋‹ค.
export default counter;

(4) ๋ชจ๋“ˆ์˜ ๊ตฌ์„ฑ์š”์†Œ ์‚ดํŽด๋ณด๊ธฐ

1. initialState === ์ดˆ๊ธฐ ์ƒํƒœ๊ฐ’

// ์ดˆ๊ธฐ ์ƒํƒœ๊ฐ’

// ์ดˆ๊ธฐ ์ƒํƒœ๊ฐ’
const initialState = {
  number: 0,
};

์ดˆ๊ธฐ๊ฐ’์€ { } (๊ฐ์ฒด) ์ด๊ณ , ๊ทธ ์•ˆ์— number ๋ผ๋Š” ๋ณ€์ˆ˜์— ์ดˆ๊ธฐ๊ฐ’ 0์„ ํ• ๋‹นํ•ด์ค€ ๊ฒƒ
์ดˆ๊ธฐ๊ฐ’์€ ๊ผญ ๊ฐ์ฒด๊ฐ€ ์•„๋‹ˆ์–ด๋„ ๋œ๋‹ค. ๋ฐฐ์—ด, ์›์‹œ๋ฐ์ดํ„ฐ ์ „๋ถ€ OK
๊ฐ์ฒด์—๋„ ์—ฌ๋Ÿฌ๊ฐœ์˜ ๋ณ€์ˆ˜๋ฅผ ๋„ฃ์–ด์ค„ ์ˆ˜ ์žˆ์Œ

// ์ดˆ๊ธฐ๊ฐ’์ด 0
const initialState = 0;

// ์ดˆ๊ธฐ๊ฐ’์ด 0์ด ์žˆ๋Š” ๋ฐฐ์—ด 
const initialState = [0];

// ์ดˆ๊ธฐ๊ฐ’์ด number = 0, name = '์งฑ๊ตฌ'์ธ ๊ฐ์ฒด
const initialState = {
	number: 0,
	name: '์งฑ๊ตฌ'
};
// useState ๋ฒ„์ „ 
const [number, setNumber] = useState(0) // < ์ด๊ฑฐ๋ž‘ ๊ฐ™๋‹ค

useState๋ฅผ ์‚ฌ์šฉํ–ˆ์„ ๋•Œ ๊ด„ํ˜ธ ์•ˆ์— ์ดˆ๊ธฐ๊ฐ’์„ ์ง€์ •ํ•ด์ฃผ๋˜ ๊ฒƒ๊ณผ ๊ฐ™์Œ

2.Reducer === ๋ณ€ํ™”๋ฅผ ์ผ์œผํ‚ค๋Š” ํ•จ์ˆ˜

๋ณ€ํ™”๋ฅผ ์ผ์œผํ‚ค๋Š” ํ•จ์ˆ˜ ๋‹ค

// ๋ฆฌ๋“€์„œ 
const counter = (state = initialState, action) => {
  switch (action.type) {
    default:
      return state;
  }
};

์•„๋ž˜๋Š” useState ๋ฒ„์ „.

// useState ๋ฒ„์ „ 

const onClickHandler = () => {
	setNumber(number + 1); // setState๋ฅผ ์ด์šฉํ•ด์„œ state ๋ณ€๊ฒฝ
}

useState ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ๊ฐ’์„ ๋ฐ”๊พธ๊ณ  ์‹ถ์œผ๋ฉด setNumber๋ฅผ ์‚ฌ์šฉํ–ˆ๋‹ค.
๋ฆฌ๋•์Šค์—์„œ๋Š” ๋ฆฌ๋“€์Šค๊ฐ€ ์ด ์—ญํ• ์„ ํ•œ๋‹ค.

// src/redux/modules/counter.js


// counter ๋ฆฌ๋“€์„œ
const counter = (state = initialState, action) => {
  switch (action.type) {
    default:
      return state;
  }
};

export default counter; // ์—ฌ๊ธฐ

๋ฆฌ๋“€์„œ์˜ ์ธ์ž์— ์žˆ๋Š” (state = intialState, action)
์ฒซ๋ฒˆ์งธ ์ž๋ฆฌ์—์„œ๋Š” state๋ฅผ, ๋‘๋ฒˆ์งธ ์ž๋ฆฌ์—์„œ๋Š” action ์ด๋ผ๋Š” ๊ฒƒ์„ ๊บผ๋‚ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
๐Ÿ’ก state = intialState ์ฒ˜๋Ÿผ state์— initialState๋ฅผ ํ• ๋‹นํ•ด์ค˜์•ผ ํ•˜๋Š” ๊ฒƒ๋งŒ ๊ธฐ์–ต.

3. ์นด์šดํ„ฐ ๋ชจ๋“ˆ์„ ์Šคํ† ์–ด์— ์—ฐ๊ฒฐ

๋งŒ๋“  ๋ชจ๋“ˆ์„ ์Šคํ† ์–ด์— ์—ฐ๊ฒฐ ์‹œ์ผœ์•ผ ํ•œ๋‹ค! ์•„์ง์€ state๋ฅผ ์Šคํ† ์–ด์—์„œ ๊บผ๋‚ผ ์ˆ˜ ์—†๋Š” ์ƒํƒœ

configStore.js ์—์„œ ์•„๋ž˜ ์ฝ”๋“œ ์ถ”๊ฐ€

// src/redux/config/configStore.js


// ์›๋ž˜ ์žˆ๋˜ ์ฝ”๋“œ
import { createStore } from "redux";
import { combineReducers } from "redux";

// ์ƒˆ๋กญ๊ฒŒ ์ถ”๊ฐ€ํ•œ ๋ถ€๋ถ„
import counter from "../modules/counter";

const rootReducer = combineReducers({
  counter: counter, // <-- ์ƒˆ๋กญ๊ฒŒ ์ถ”๊ฐ€ํ•œ ๋ถ€๋ถ„
});
const store = createStore(rootReducer);

export default store;

(5) ์Šคํ† ์–ด์™€ ๋ชจ๋“ˆ ์—ฐ๊ฒฐ ํ™•์ธ

1. useSelector = ์Šคํ† ์–ด ์กฐํšŒ

๐Ÿ“ข ์ปดํฌ๋„ŒํŠธ์—์„œ ์Šคํ† ์–ด๋ฅผ ์ง์ ‘ ์กฐํšŒํ•  ๋•Œ react-redux์—์„œ ์ œ๊ณตํ•˜๋Š” useSelector ๋ผ๋Š” ํ›…์„ ์‚ฌ์šฉ

// 1. store์—์„œ ๊บผ๋‚ธ ๊ฐ’์„ ํ• ๋‹น ํ•  ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•ฉ๋‹ˆ๋‹ค.
const number = 

// 2. useSelector()๋ฅผ ๋ณ€์ˆ˜์— ํ• ๋‹นํ•ด์ค๋‹ˆ๋‹ค.
const number = useSelector() 

// 3. useSelector์˜ ์ธ์ž์— ํ™”์‚ดํ‘œ ํ•จ์ˆ˜๋ฅผ ๋„ฃ์–ด์ค๋‹ˆ๋‹ค.
const number = useSelector( ()=>{} )

// 4. ํ™”์‚ดํ‘œ ํ•จ์ˆ˜์˜ ์ธ์ž์—์„œ ๊ฐ’์„ ๊บผ๋‚ด return ํ•ฉ๋‹ˆ๋‹ค. 
// ์šฐ๋ฆฌ๊ฐ€ useSelector๋ฅผ ์ฒ˜์Œ ์‚ฌ์šฉํ•ด๋ณด๋Š” ๊ฒƒ์ด๋‹ˆ, state๊ฐ€ ์–ด๋–ค ๊ฒƒ์ธ์ง€ ์ฝ˜์†”๋กœ ํ™•์ธํ•ด๋ณผ๊นŒ์š”?
const number = useSelector((state) => {
	console.log(state)
	return state
});

App.js

// src/App.js

import React from "react";
import { useSelector } from "react-redux"; // import ํ•ด์ฃผ์„ธ์š”.

const App = () => {
  const counterStore = useSelector((state) => state); // ์ถ”๊ฐ€ํ•ด์ฃผ์„ธ์š”.
  console.log(counterStore); // ์Šคํ† ์–ด๋ฅผ ์กฐํšŒํ•ด๋ณผ๊นŒ์š”?

  return <div></div>;
}

export default App;

๋ธŒ๋ผ์šฐ์ €๋ฅผ ์ผœ๊ณ , ์ฝ˜์†”์„ ๋ณด๋ฉด ์•„๋ž˜ ์ด๋ฏธ์ง€์ฒ˜๋Ÿผ ๊ฐ์ฒด๊ฐ€ ๋ณด์ด๊ณ , ๊ทธ ์•ˆ์— counter ๋ผ๋Š” ๊ฐ’์ด ์žˆ๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“  counter ๋ผ๋Š” ๋ชจ๋“ˆ์˜ state๊ฐ€ ๋ณด์ด๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ™”์‚ดํ‘œ ํ•จ์ˆ˜์—์„œ ๊บผ๋‚ธ state๋ผ๋Š” ์ธ์ž๋Š” ๋ชจ๋“  ๋ฆฌ๋•์Šค ๋ชจ๋“ˆ์˜ ์ตœ์ƒ์œ„ ๊ฐ์ฒด๋ฅผ ์˜๋ฏธํ•˜๋Š” Root State์ธ ๊ฒƒ

์ปดํฌ๋„ŒํŠธ์—์„œ number๋ผ๋Š” ๊ฐ’์„ ์‚ฌ์šฉํ•˜๊ณ ์ž ํ•œ๋‹ค๋ฉด ์•„๋ž˜ ์ฝ”๋“œ์ฒ˜๋Ÿผ ๊บผ๋‚ด์„œ ์‚ฌ์šฉ

const number = useSelector(state => state.counter.number); // 0

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