React && Redux

SANGKU OHยท2020๋…„ 9์›” 20์ผ
0
post-thumbnail

Redux

๐Ÿšจํ•„์ž๋Š” ์ด์ „ ํ”„๋กœ์ ํŠธ์—์„œ props ์ง€์˜ฅ์„ ๋ง› ๋ณด์•˜๋‹ค.
ํ•ด๋‹น ๋ฐฉ๋ฒ•์œผ๋กœ ๋ฌด๋ ค 3๋‹จ๊ณ„๋ฅผ ๋‚ด๋ ค๊ฐ”๋‹ค...๐Ÿ’ฆ

Redux๊ฐ€ ํ•˜๋Š” ์ผ

๋ฉ€๋ฆฌ ๋–จ์–ด์ง„ ์ปดํฌ๋„ŒํŠธ๋ผ๋ฆฌ ์ƒํƒœ๊ฐ’์„ ๊ตํ™˜ํ•  ๋•Œ ๋ณต์žกํ•ด์ง€๋Š” ๋ฐ์ดํ„ฐ ํ๋ฆ„์ด ์ „์—ญ ์Šคํ† ์–ด์˜ ๊ฐœ์ž…์„ ํ†ตํ•ด ๊ฐ„๊ฒฐํ•˜๊ฒŒ ๋งŒ๋“ ๋‹ค. ์ฆ‰, ์ปดํฌ๋„ŒํŠธ์™€ ์Šคํ† ์–ด๊ฐ€ 1:1 ๊ด€๊ณ„๋ฅผ ๋งบ๊ฒŒ ๋จ

Rule

์ „์ฒด ์ƒํƒœ๊ฐ’์„ ํ•˜๋‚˜์˜ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ฐ์ฒด๋กœ ํ‘œํ˜„

  • ํ•˜๋‚˜์˜ ๊ฐ์ฒด๋ฅผ ์ง๋ ฌํ™”(serialize)ํ•ด์„œ ์„œ๋ฒ„์™€ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ํ”„๋กœ๊ทธ๋žจ์˜ ์ „์ฒด ์ƒํƒœ๊ฐ’์„ ์ฃผ๊ณ  ๋ฐ›์„ ์ˆ˜ ์žˆ์Œ.
  • ํ”„๋กœ๊ทธ๋žจ์ด ํŠน์ •ํ•œ ์ƒํƒœ์— ์žˆ์„ ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ๋ฒ„๊ทธ๋ฅผ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ๊ทธ ์ƒํƒฏ๊ฐ’์„ ์ €์žฅํ•œ ํ›„ ๋ฐ˜๋ณตํ•ด์„œ ์žฌํ˜„ํ•  ์ˆ˜ ์žˆ์Œ.
  • ์ตœ๊ทผ์˜ ์ƒํƒฏ๊ฐ’์„ ๋ฒ„๋ฆฌ์ง€ ์•Š๊ณ  ์ €์žฅํ•ด ๋†“์œผ๋ฉด ์‹คํ–‰ ์ทจ์†Œ(undo)์™€ ๋‹ค์‹œ ์‹คํ–‰(redo) ๊ธฐ๋Šฅ์„ ์‰ฝ๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Œ. (๋ฌดํ•œ ์Šคํฌ๋กค ์‹œ ์Šคํฌ๋กค ์œ„์น˜ ์ €์žฅ โ†’ ํŽ˜์ด์ง€๋ฅผ ์ด๋™ํ•ด๋„ ๊ฐ™์€ ์œ„์น˜๋กœ ๋Œ์•„์˜ด)

    ๐ŸšจNotice
    - ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์œ„ํ•œ ๋ฐ์ดํ„ฐ, ๋ฌธ์ž์—ด ์ž…๋ ฅ์ฐฝ์˜ ํ˜„์žฌ ์ƒํƒœ๊ฐ’์€ ์ปดํฌ๋„ŒํŠธ์—์„œ ๊ด€๋ฆฌํ•˜๋Š”๊ฒŒ ๋” ๋‚˜์Œ.
    - ์ „์ฒด ์ƒํƒฏ๊ฐ’์„ ๋ฆฌ๋•์Šค๋กœ ๊ด€๋ฆฌํ•˜๋ฉด ์‹œ๊ฐ„ ์—ฌํ–‰ ๋“ฑ์˜ ๊ธฐ๋Šฅ์„ ์‰ฝ๊ฒŒ ๊ตฌํ˜„ ๊ฐ€๋Šฅ but,
    ํ•ด๋‹น ๊ธฐ๋Šฅ์ด ํ•„์š”ํ•˜์ง€ ์•Š๋‹ค๋ฉด, ํ”„๋กœ๊ทธ๋žจ์˜ ์ผ๋ถ€ ์ƒํƒœ๋งŒ ๋ฆฌ๋•์Šค๋ฅผ ํ™œ์šฉํ•ด๋„ ์ข‹์Œ.

์ƒํƒœ๊ฐ’์€ ์ฝ๊ธฐ ์ „์šฉ์˜ ๋ถˆ๋ณ€ ๊ฐ์ฒด๋กœ ๊ด€๋ฆฌํ•œ๋‹ค

๐ŸšจNotice
๋ฆฌ๋•์Šค์˜ state๋ฅผ ์ˆ˜์ •ํ•˜๋Š” ์œ ์ผํ•œ ๋ฐฉ๋ฒ•! => action ๊ฐ์ฒด์™€ ํ•จ๊ป˜ dispatch๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ
(aka.setState)

  • dispatch ๋ฉ”์†Œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋œ ์ˆœ์„œ๋Œ€๋กœ ๋ฆฌ๋•์Šค ๋‚ด๋ถ€์—์„œ ๋ณ€๊ฒฝ => ๋ณ€๊ฒฝ๋œ ์ˆœ์„œ๋ฅผ ์‰ฝ๊ฒŒ ์ดํ•ด
  • action ๊ฐ์ฒด๋Š” ํ‰๋ฒ”ํ•œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ฐ์ฒด => ์ž…๋ ฅ๋œ ์ˆœ์„œ ์ €์žฅํ•˜๊ณ  ๋‚˜์ค‘์— ๊ทธ ๊ณผ์ •์„ ์‰ฝ๊ฒŒ ์žฌํ˜„

์˜ค์ง ์ˆœ์ˆ˜ ํ•จ์ˆ˜์— ์˜ํ•ด์„œ๋งŒ ์ƒํƒœ๊ฐ’์„ ๋ณ€๊ฒฝํ•ด์•ผ ํ•œ๋‹ค

๐ŸšจNotice
reducer๋Š” preState ์™€ action ๊ฐ์ฒด๋ฅผ ์ž…๋ ฅ์œผ๋กœ ๋ฐ›์•„ ์ƒˆ๋กœ์šด state๋ฅผ ๋งŒ๋“œ๋Š” ์ˆœ์ˆ˜ ํ•จ์ˆ˜

  • fetch ๋“ฑ์˜ ๋น„๋™๊ธฐ ๋กœ์ง, input์— ๋‹ค๋ฅธ return => unit test ๋ถˆ๊ฐ€
  • ์ˆœ์ˆ˜ ํ•จ์ˆ˜๋Š” ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ธฐ ์‰ฝ๋‹ค! => ๊ฐ™์€ ์ž…๋ ฅ์— ๋Œ€ํ•ด ๊ฐ™์€ ์ถœ๋ ฅ

Core Concept

action

export const addCart = (item) => { // ์•ก์…˜ "์ƒ์„ฑ ํ•จ์ˆ˜"
  return {
    type: "ADD_ITEM", // ์•ก์…˜ "๊ฐ์ฒด"
    payload: item,
  };
};
  • ์•ก์…˜์€ type attr์„ ๊ฐ€์ง„ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ฐ์ฒด
  • ์•ก์…˜ ์ƒ์„ฑํ•จ์ˆ˜๋Š” ๊ทธ ์•ก์…˜ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๋Š” ์—ญํ• ์„ ํ•˜๋Š” ํ•จ์ˆ˜
    - ์•ก์…˜ ๊ฐ์ฒด๋ฅผ dispatch ๋ฉ”์†Œ๋“œ์— ๋„ฃ์–ด์„œ ํ˜ธ์ถœ(useDispatch)
    - ์•ก์…˜ ์ƒ์„ฑํ•จ์ˆ˜๊ฐ€ ์ƒ์„ฑํ•œ ์•ก์…˜ ๊ฐ์ฒด๋Š” reducer๋ฅผ ๊ฑฐ์ณ store๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๊ฒŒ ๋จ!**

useDispatch

const dispatch = useDispatch()

- store์˜ ๋‚ด์žฅ ํ•จ์ˆ˜, store์— ์•ก์…˜๊ฐ์ฒด๋ฅผ ์ „๋‹ฌํ•˜๋Š” ํ•จ์ˆ˜

  • dispatch์˜ ์ธ์ž๋กœ๋Š” ์•ก์…˜ ๊ฐ์ฒด๋ฅผ ์ „๋‹ฌ
  • dispatch๊ฐ€ ์‹คํ–‰๋˜๋ฉด ์•ก์…˜ ๊ฐ์ฒด๋Š” reducer๋กœ ์ „๋‹ฌ๋˜๊ณ , reducer ๋‚ด์— ๋ฏธ๋ฆฌ ์ •์˜ํ•ด๋‘” ์กฐ๊ฑด๋ฌธ๊ณผ action.type์— ๋”ฐ๋ผ store๊ฐ€ ์—…๋ฐ์ดํŠธ ๋จ
    - store(๋ถ€๋ชจ), ์ปดํฌ๋„ŒํŠธ(์ž์‹)๊ฐ„์— setํ•จ์ˆ˜ ๋‚ด๋ ค์ฃผ๊ธฐ๋กœ ์ƒ๊ฐํ•ด๋ณด์ž!
const [state, setState] = useState() // ์—ฌ๊ธฐ์„œ state๋Š” store

<FooComponent setState={setState} /> // ์—ฌ๊ธฐ์„œ FooComponent๋Š” ๋‚ด๊ฐ€ ์ปจํŠธ๋กคํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ

reducer

state, action) => nextState // state(ํ˜น์€ store)์™€ action ๊ฐ์ฒด๋ฅผ ๋ฐ›๊ณ  ๋‹ค์Œ state ๋ฆฌํ„ด

// cartReducers.js
export default function cartReducer(state = INITIAL_STATE, action) {
  switch (action.type) {
    case "ADD_ITEM":
      return [...state, action.payload]; // ์Šคํ† ์–ด์˜ ์ด์ „ ์ƒํƒœ์— ์ƒˆ๋กœ์šด item์„ ์ถ”๊ฐ€
		case "DELETE_ITEM":
			return [...action.payload]
    default:
      return state; // ํ•ด๋‹น ์‚ฌํ•ญ ์—†์œผ๋ฉด ์ด์ „ ์ƒํƒœ๋ฅผ ๊ทธ๋Œ€๋กœ ๋ฆฌํ„ด
  }
}

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

export default combineReducers({ cartReducer });ํฌ๋„ŒํŠธ

- reducer๋Š” ์•ก์…˜์ด ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ์ƒˆ๋กœ์šด ์ƒํƒœ๊ฐ’์„ ๋งŒ๋“œ๋Š” ํ•จ์ˆ˜

  • ์ž˜ ๋ชจ๋ฅด๊ฒ ๋‹ค๋ฉด reducer๋Š” store์˜ key๊ฐ’์ด๋ผ๊ณ  ์ƒ๊ฐํ•ด๋ณด์ž!

store

import React from "react";
import ReactDOM from "react-dom";
import Routes from "./Routes";

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")
);

- store๋Š” redux์˜ ์ƒํƒœ๊ฐ’์„ ๊ฐ€์ง€๋Š” ๋‹จ์ผ ๊ฐ์ฒด์ด๋ฉฐ, ํ”„๋กœ์ ํŠธ ์–ด๋””๋“  ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค!(provider)

  • action => ๋ฏธ๋“ค์›จ์–ด => reducer => storeUpdate => componentUpdata

useSelector

const items = useSelector((store) => store.cartReducer);
  • useSelector๋ฅผ ํ†ตํ•ด store์˜ ํŠน์ • ๋‚ด์šฉ์„ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค
    - reducer = store์˜ key
  • styled compoenets์˜ ThemeProvider ์™€ ๊ฐ™์€ ์›๋ฆฌ์ด๋‹ค!
// index.js
<ThemeProvider>
	<Routes />
</ThemeProvider>

// app.js
const foo = styled.div`
	color: ${props => props.theme.wecodeBlue}
`
  • useSelector๋Š” ์—„๊ฒฉํ•œ ๋น„๊ต(===)๋ฅผ ํ•˜์—ฌ, ์ฐธ์กฐ๊ฐ€ ๋‹ฌ๋ผ์ง€๋ฉด ๋ฌด์กฐ๊ฑด ์—…๋ฐ์ดํŠธ๋ฅผ ํ•œ๋‹ค
  • useSelector์—๋Š” ๋‘ ๋ฒˆ์งธ ์ธ์ž๋กœ ๋™์ผ์„ฑ์„ ๊ฒ€์‚ฌํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ๋„ฃ์–ด์ค„ ์ˆ˜ ์žˆ๋‹ค.
profile
Prof.Google์„ ํ†ตํ•ด ํ•„์š”ํ•œ ์ •๋ณด๋ฅผ ์ด ๊ณณ์— insert ๐Ÿธ

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