Jieunny์˜ ๋ธ”๋กœ๊ทธ

S3) Unit 4. [React] ์ƒํƒœ ๊ด€๋ฆฌ ๋ณธ๋ฌธ

CodeStates/learning contents

S3) Unit 4. [React] ์ƒํƒœ ๊ด€๋ฆฌ

Jieunny 2023. 2. 23. 11:00

๐Ÿ“ฃ  ์ „์—ญ ์ƒํƒœ ๊ด€๋ฆฌ

โœ”๏ธ ์ƒํƒœ : UI ์— ๋™์ ์œผ๋กœ ํ‘œํ˜„๋  ๋ฐ์ดํ„ฐ

โžฐ ์ƒํƒœ๋ฅผ ๋‹ค๋ฃฐ ๋•Œ๋Š” Side Effect ๋ฅผ ๊ณ ๋ คํ•ด์•ผ ํ•œ๋‹ค!

โžฐ Side Effect : ํ•จ์ˆ˜ ๋˜๋Š” ์ปดํฌ๋„ŒํŠธ์˜ ์ž…๋ ฅ ์™ธ์—๋„ ํ•จ์ˆ˜์˜ ๊ฒฐ๊ณผ์— ์˜ํ–ฅ์„ ๋ฏธ์น˜๋Š” ์š”์ธ

    - ๋„คํŠธ์›Œํฌ ์š”์ฒญ (๋ฐฑ์—”๋“œ API ์š”์ฒญ)

 

โœ”๏ธ ๋กœ์ปฌ ์ƒํƒœ : ํŠน์ • ์ปดํฌ๋„ŒํŠธ ์•ˆ์—์„œ๋งŒ ๊ด€๋ฆฌ๋˜๋Š” ์ƒํƒœ

โžฐ ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ์™€ ๋ฐ์ดํ„ฐ๋ฅผ ๊ณต์œ ํ•˜์ง€ ์•Š๋Š” ํผ ๋ฐ์ดํ„ฐ๋Š” ๋Œ€๋ถ€๋ถ„ ๋กœ์ปฌ ์ƒํƒœ์ด๋‹ค.

โžฐ input box, select box ๋“ฑ๊ณผ ๊ฐ™์ด ์ž…๋ ฅ๊ฐ’์„ ๋ฐ›๋Š” ๊ฒฝ์šฐ

 

โœ”๏ธ ์ „์—ญ ์ƒํƒœ : ํ”„๋กœ๋•ํŠธ ์ „์ฒด ๋˜๋Š” ์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ์—์„œ ๊ด€๋ฆฌ๋˜๋Š” ์ƒํƒœ

โžฐ ์„œ๋กœ ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋™์ผํ•œ ์ƒํƒœ๋ฅผ ๋‹ค๋ฃฌ๋‹ค๋ฉด, ์ด ์ถœ์ฒ˜๋Š” ์˜ค์ง ํ•œ ๊ณณ์ด์–ด์•ผ ํ•œ๋‹ค.

โžฐ ์ด ์ถœ์ฒ˜๊ฐ€ '์ „์—ญ ๊ณต๊ฐ„' ์ด ๋œ๋‹ค.

โžฐ ํ…Œ๋งˆ ์„ค์ •, ๊ตญ์ œํ™” ์„ค์ • ๋“ฑ์ด ์ „์—ญ ์ƒํƒœ์˜ ๋Œ€ํ‘œ์ ์ธ ์˜ˆ์‹œ์ด๋‹ค.

๋”๋ณด๊ธฐ

๐Ÿšจ ๋ฐ์ดํ„ฐ ๋ฌด๊ฒฐ์„ฑ์„ ์œ„ํ•ด, ๋™์ผํ•œ ๋ฐ์ดํ„ฐ๋Š” ํ•ญ์ƒ ๊ฐ™์€ ๊ณณ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ง€๊ณ  ์˜ค๋„๋ก ํ•˜์ž!!

๐Ÿšจ Single source of truth ์›์น™์€ ํ”„๋ก ํŠธ์—”๋“œ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋‹ค์–‘ํ•œ ๊ณณ์—์„œ ์–ธ๊ธ‰๋˜๋Š” ์›์น™

 

โœ”๏ธ ์ƒํƒœ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ํ•ด๊ฒฐํ•ด์ฃผ๋Š” ๋ฌธ์ œ์ 

โžฐ ์ „์—ญ ์ƒํƒœ๋ฅผ ์œ„ํ•œ ์ €์žฅ์†Œ๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

โžฐ props drilling ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•œ๋‹ค.

โž• props drilling

๋”๋ณด๊ธฐ

โžฐ ์ค‘๊ฐ„์— ์กด์žฌํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๋“ค์€ ๊ตณ์ด ํ•„์š”ํ•œ state๊ฐ€ ์•„๋‹˜์—๋„, ์•„๋ž˜ ์ปดํฌ๋„ŒํŠธ์— ์ „๋‹ฌํ•ด์ฃผ๊ธฐ ์œ„ํ•ด์„œ state๋ฅผ ์ „๋‹ฌ๋ฐ›์•„์•ผ ํ•˜๋Š” ๊ฒƒ


๐Ÿ“ฃ  Props Drilling ์˜ ๋ฌธ์ œ์ 

โœ”๏ธ ๊ทœ๋ชจ๊ฐ€ ์ปค์ง€๊ณ , ๊ตฌ์กฐ๊ฐ€ ๋ณต์žกํ•ด์ง€๋ฉด์„œ props์˜ ์ „๋‹ฌ ๊ณผ์ •์ด ๋Š˜์–ด๋‚œ๋‹ค๋ฉด ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

โžฐ ์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ์ด ๋งค์šฐ ๋‚˜๋น ์ง„๋‹ค.

โžฐ ์ฝ”๋“œ์˜ ์œ ์ง€๋ณด์ˆ˜ ๋˜ํ•œ ํž˜๋“ค์–ด์ง€๊ฒŒ ๋œ๋‹ค.

โžฐ state ๋ณ€๊ฒฝ์‹œ Props ์ „๋‹ฌ ๊ณผ์ •์—์„œ ๋ถˆํ•„์š”ํ•˜๊ฒŒ ๊ด€์—ฌ๋œ ์ปดํฌ๋„ŒํŠธ๋“ค ๋˜ํ•œ ๋ฆฌ๋ Œ๋”๋ง์ด ๋ฐœ์ƒํ•œ๋‹ค -> ์›น์„ฑ๋Šฅ์— ์•…์˜ํ–ฅ์„ ์ค€๋‹ค.

 

๐Ÿ“ฃ  ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•

โœ”๏ธ ์ปดํฌ๋„ŒํŠธ์™€ ๊ด€๋ จ์žˆ๋Š” state๋Š” ๋  ์ˆ˜ ์žˆ์œผ๋ฉด ๊ฐ€๊นŒ์ด ์œ ์ง€ํ•˜๋Š” ๋ฐฉ๋ฒ• 

โœ”๏ธ ์ƒํƒœ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•


๐Ÿ“ฃ  Redux

โœ”๏ธ Redux : ์ „์—ญ ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ์ €์žฅ์†Œ์ธ Store๋ฅผ ์ œ๊ณตํ•˜๋Š” ์ƒํƒœ ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

 

๐Ÿ“ฃ  Redux์˜ ๊ตฌ์กฐ

 

โœ”๏ธ Redux์˜ ์ƒํƒœ ๊ด€๋ฆฌ ์ˆœ์„œ

1. ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋˜์–ด์•ผ ํ•˜๋Š” ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด, ๋ณ€๊ฒฝ๋  ์ƒํƒœ์— ๋Œ€ํ•œ ์ •๋ณด๊ฐ€ ๋‹ด๊ธด Action ๊ฐ์ฒด๊ฐ€ ์ƒ์„ฑ๋œ๋‹ค.

2. ์ด Action ๊ฐ์ฒด๋Š” Dispatch ํ•จ์ˆ˜์˜ ์ธ์ž๋กœ ์ „๋‹ฌ๋œ๋‹ค.

3. Dispatch ํ•จ์ˆ˜๋Š” Action ๊ฐ์ฒด๋ฅผ Reducer ํ•จ์ˆ˜๋กœ ์ „๋‹ฌํ•ด์ค€๋‹ค.

4. Reducer ํ•จ์ˆ˜๋Š” Action ๊ฐ์ฒด์˜ ๊ฐ’์„ ํ™•์ธํ•˜๊ณ , ๊ทธ ๊ฐ’์— ๋”ฐ๋ผ ์ „์—ญ ์ƒํƒœ ์ €์žฅ์†Œ Store์˜ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•œ๋‹ค.

6. ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด, ๋ฆฌ์•กํŠธ๋Š” ํ™”๋ฉด์„ ๋‹ค์‹œ ๋ Œ๋”๋ง ํ•œ๋‹ค.

 

โžฐ Action -> Dispatch -> Reducer -> Store ์ˆœ์„œ๋กœ ๋ฐ์ดํ„ฐ๊ฐ€ ๋‹จ๋ฐฉํ–ฅ์œผ๋กœ ํ๋ฅด๊ฒŒ ๋œ๋‹ค.

 

๐Ÿ“ฃ  Store

โœ”๏ธ ์ƒํƒœ๊ฐ€ ๊ด€๋ฆฌ๋˜๋Š” ์˜ค์ง ํ•˜๋‚˜๋ฟ์ธ ์ €์žฅ์†Œ ์—ญํ• ์„ ํ•œ๋‹ค.

โžฐ Redux ์•ฑ์˜ state๊ฐ€ ์ €์žฅ๋˜์–ด ์žˆ๋Š” ๊ณต๊ฐ„

import { createStore } from 'redux';

const store = createStore(rootReducer);

โžฐ createStore ๋ฉ”์„œ๋“œ๋ฅผ ํ™œ์šฉํ•ด์„œ Reducer๋ฅผ ์—ฐ๊ฒฐํ•ด์„œ Store๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

 

๐Ÿ“ฃ  Reducer

โœ”๏ธ Dispatch์—๊ฒŒ์„œ ์ „๋‹ฌ๋ฐ›์€ Action ๊ฐ์ฒด์˜ type ๊ฐ’์— ๋”ฐ๋ผ์„œ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝ์‹œํ‚ค๋Š” ํ•จ์ˆ˜

โžฐ Reducer๋Š” '์ˆœ์ˆ˜ ํ•จ์ˆ˜' ์—ฌ์•ผ ํ•œ๋‹ค.

const count = 1

// Reducer๋ฅผ ์ƒ์„ฑํ•  ๋•Œ์—๋Š” ์ดˆ๊ธฐ ์ƒํƒœ๋ฅผ ์ธ์ž๋กœ ์š”๊ตฌํ•ฉ๋‹ˆ๋‹ค.
const counterReducer = (state = count, action) => {

  // Action ๊ฐ์ฒด์˜ type ๊ฐ’์— ๋”ฐ๋ผ ๋ถ„๊ธฐํ•˜๋Š” switch ์กฐ๊ฑด๋ฌธ์ž…๋‹ˆ๋‹ค.
  switch (action.type) {

    //action === 'INCREASE'์ผ ๊ฒฝ์šฐ
    case 'INCREASE':
			return state + 1

    // action === 'DECREASE'์ผ ๊ฒฝ์šฐ
    case 'DECREASE':
			return state - 1

    // action === 'SET_NUMBER'์ผ ๊ฒฝ์šฐ
    case 'SET_NUMBER':
			return action.payload

    // ํ•ด๋‹น ๋˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์—†์„ ๋• ๊ธฐ์กด ์ƒํƒœ๋ฅผ ๊ทธ๋Œ€๋กœ ๋ฆฌํ„ด
    default:
      return state;
	}
}
// Reducer๊ฐ€ ๋ฆฌํ„ดํ•˜๋Š” ๊ฐ’์ด ์ƒˆ๋กœ์šด ์ƒํƒœ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

 

โžฐ ์—ฌ๋Ÿฌ ๊ฐœ์˜ Reducer๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ, Redux์˜ combineReducers ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ํ•˜๋‚˜์˜ Reducer๋กœ ํ•ฉ์ณ์ค„ ์ˆ˜ ์žˆ๋‹ค.

import { combineReducers } from 'redux';

const rootReducer = combineReducers({
  counterReducer,
  anyReducer,
  ...
});

 

๐Ÿ“ฃ  Action

โœ”๏ธ ์–ด๋–ค ์•ก์…˜์„ ์ทจํ•  ๊ฒƒ์ธ์ง€ ์ •ํ•ด ๋†“์€ ๊ฐ์ฒด

// payload๊ฐ€ ํ•„์š” ์—†๋Š” ๊ฒฝ์šฐ
{ type: 'INCREASE' }

// payload๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ
{ type: 'SET_NUMBER', payload: 5 }

โžฐ type์€ ํ•ด๋‹น Action ๊ฐ์ฒด๊ฐ€ ์–ด๋–ค ๋™์ž‘์„ ํ•˜๋Š”์ง€ ๋ช…์‹œํ•ด์ฃผ๋Š” ์—ญํ• ์„ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํ•„์ˆ˜๋กœ ์ง€์ •ํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.

โžฐ ๋Œ€๋ฌธ์ž์™€ Snake Case๋กœ ์ž‘์„ฑํ•˜๋ฉฐ, ํ•„์š”์— ๋”ฐ๋ผ payload๋ฅผ ์ž‘์„ฑํ•ด์„œ ๊ตฌ์ฒด์ ์ธ ๊ฐ’์„ ์ „๋‹ฌํ•œ๋‹ค.

 

โžฐ ๋ณดํ†ต Action์„ ์ง์ ‘ ์ž‘์„ฑํ•˜๊ธฐ๋ณด๋‹ค๋Š” Action ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด ์‚ฌ์šฉํ•œ๋‹ค -> ์•ก์…˜ ์ƒ์„ฑ์ž

// payload๊ฐ€ ํ•„์š” ์—†๋Š” ๊ฒฝ์šฐ
const increase = () => {
  return {
    type: 'INCREASE'
  }
}

// payload๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ
const setNumber = (num) => {
  return {
    type: 'SET_NUMBER',
    payload: num
  }
}

 

๐Ÿ“ฃ  Dispatch

โœ”๏ธ Reducer๋กœ Action์„ ์ „๋‹ฌํ•ด์ฃผ๋Š” ํ•จ์ˆ˜

// Action ๊ฐ์ฒด๋ฅผ ์ง์ ‘ ์ž‘์„ฑํ•˜๋Š” ๊ฒฝ์šฐ
dispatch( { type: 'INCREASE' } );
dispatch( { type: 'SET_NUMBER', payload: 5 } );

// ์•ก์…˜ ์ƒ์„ฑ์ž(Action Creator)๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ
dispatch( increase() );
dispatch( setNumber(5) );

โžฐ Action ๊ฐ์ฒด๋ฅผ ์ „๋‹ฌ๋ฐ›์€ Dispatch ํ•จ์ˆ˜๋Š” Reducer๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.

 

๐Ÿ“ฃ  Redux Hooks

โœ”๏ธ React-Redux์—์„œ Redux๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋Š” Hooks ๋ฉ”์„œ๋“œ๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

 

1๏ธโƒฃ useDispatch()

โœ”๏ธ Action ๊ฐ์ฒด๋ฅผ Reducer๋กœ ์ „๋‹ฌํ•ด ์ฃผ๋Š” Dispatch ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฉ”์„œ๋“œ

import { useDispatch } from 'react-redux'

const dispatch = useDispatch()
dispatch( increase() )
console.log(counter) // 2

dispatch( setNumber(5) )
console.log(counter) // 5

 

2๏ธโƒฃ useSelector()

โœ”๏ธ ์ปดํฌ๋„ŒํŠธ์™€ state๋ฅผ ์—ฐ๊ฒฐํ•˜์—ฌ Redux์˜ state์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๋ฉ”์„œ๋“œ

// Redux Hooks ๋ฉ”์„œ๋“œ๋Š” 'redux'๊ฐ€ ์•„๋‹ˆ๋ผ 'react-redux'์—์„œ ๋ถˆ๋Ÿฌ์˜ต๋‹ˆ๋‹ค.
import { useSelector } from 'react-redux'
const counter = useSelector(state => state)
console.log(counter) // 1

 

๐Ÿ“ฃ  Redux ์˜ ์„ธ ๊ฐ€์ง€ ์›์น™

1๏ธโƒฃ Single source of truth

โžฐ ๋™์ผํ•œ ๋ฐ์ดํ„ฐ๋Š” ํ•ญ์ƒ ๊ฐ™์€ ๊ณณ์—์„œ ๊ฐ€์ง€๊ณ  ์™€์•ผ ํ•œ๋‹ค.

โžฐ Redux์—๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๋Š” Store๋ผ๋Š” ๋‹จ ํ•˜๋‚˜๋ฟ์ธ ๊ณต๊ฐ„์ด ์žˆ์Œ๊ณผ ์—ฐ๊ฒฐ ๋˜๋Š” ์›์น™

 

2๏ธโƒฃ State is read-only

โžฐ ๋ฆฌ์•กํŠธ์˜ state ์ฒ˜๋Ÿผ Redux์˜ ์ƒํƒœ๋„ ์ง์ ‘ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†์Œ์„ ์˜๋ฏธํ•œ๋‹ค.

โžฐ Action ๊ฐ์ฒด๊ฐ€ ์žˆ์–ด์•ผ๋งŒ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Œ๊ณผ ์—ฐ๊ฒฐ ๋˜๋Š” ์›์น™

 

3๏ธโƒฃ Changes are made with pure functions

โžฐ ๋ณ€๊ฒฝ์€ ์ˆœ์ˆ˜ ํ•จ์ˆ˜๋กœ๋งŒ ๊ฐ€๋Šฅํ•˜๋‹ค.

โžฐ ์ƒํƒœ๊ฐ€ ์—‰๋šฑํ•œ ๊ฐ’์œผ๋กœ ๋ณ€๊ฒฝ๋˜๋Š” ์ผ์ด ์—†๋„๋ก ์ˆœ์ˆ˜ ํ•จ์ˆ˜๋กœ ์ž‘์„ฑ๋˜์–ด์•ผํ•˜๋Š” Reducer ์™€ ์—ฐ๊ฒฐ๋˜๋Š” ์›์น™


๐Ÿ“š ์ฐธ๊ณ ํ•  ๋งŒํ•œ ๋ ˆํผ๋Ÿฐ์Šค ๋งํฌ

robinwieruch ๋ธ”๋กœ๊ทธ: Redux

FLUX ํŒจํ„ด ๊ณต์‹ ๋ฌธ์„œ