πŸ’» Redux-Toolkit

waterglassesΒ·2022λ…„ 6μ›” 26일
0

TIL

λͺ©λ‘ 보기
34/50
post-thumbnail

⚠️ μ •λ¦¬ν•œ λ‚΄μš©μ€ μ˜€νƒ€λ‚˜ 잘λͺ»λœ 정보가 μžˆμ„ 수 μžˆμŠ΅λ‹ˆλ‹€. λŒ“κΈ€λ‘œ μ•Œλ €μ£Όμ‹œλ©΄ κ°μ‚¬ν•˜κ² μŠ΅λ‹ˆλ‹€.

RTK(Redux Tool kit)

Redux

λ¨Όμ € λ¦¬λ•μŠ€λŠ” Flux μ•„ν‚€ν…μ²˜μ˜ κ΅¬ν˜„μ²΄λ‘œ λŒ€ν˜• MVC μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ—μ„œ μ’…μ’… λ‚˜νƒ€λ‚˜λŠ” 데이터 κ°„ μ˜μ‘΄μ„± 이슈λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄ κ³ μ•ˆλ˜μ—ˆλ‹€.

λ¦¬λ•μŠ€λ₯Ό μ‚¬μš©ν•˜λŠ” κ΅¬μ‘°μ—μ„œλŠ” μ „μ—­ μƒνƒœλ₯Ό 전보 ν•˜λ‚˜μ˜ μ €μž₯μ†Œ(store) μ•ˆμ— μžˆλŠ” 객체 νŠΈλ¦¬μ— μ €μž₯ν•˜λ©°, μƒνƒœλ₯Ό λ³€κ²½ν•˜λŠ” 것은 μ–΄λ–€ 일이 μ„œμˆ ν•˜λŠ” 객체인 μ•‘μ…˜(action)을 λ‚΄λ³΄λ‚΄λŠ”(dispatch) 것이 μœ μΌν•œ 방법이닀.
그리고 전체 μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ˜ μƒνƒœλ₯Ό μ–΄λ–»κ²Œ 변경할지 λͺ…μ‹œν•˜κ²Œ μœ„ν•΄μ„œλŠ” λ¦¬λ“€μ„œ(reducer)의 μž‘μ„±μ΄ ν•„μš”ν•˜λ‹€.

이 λͺ¨λ“  μ„€κ³„λŠ” 데이터가 단방ν–₯으둜 흐λ₯Έλ‹€λŠ” μ „μ œν•˜μ— λ°μ΄ν„°μ˜ 일관성을 ν–₯μƒμ‹œν‚€κ³  버그 λ°œμƒ 원인을 더 μ‰½κ²Œ νŒŒμ•…ν•˜λ €λŠ” μ˜λ„μ—μ„œ μΆœλ°œν–ˆμŒμ„ μ•Œ 수 μžˆλ‹€.

λ¦¬λ•μŠ€ μ‹€ν–‰ ꡬ쑰

RTKλŠ” μ™œ λ§Œλ“€μ–΄μ‘Œμ„κΉŒ?

λ¦¬λ•μŠ€λ₯Ό 더 μ‰½κ²Œ μ‚¬μš©ν•˜κΈ° μœ„ν•΄μ„œ! λ§Œλ“€μ–΄μ‘Œλ‹€.

λ¦¬λ•μŠ€μ—λŠ” μ•„λž˜μ™€ 같은 λ¬Έμ œκ°€ μžˆμ—ˆλ‹€κ³  ν•œλ‹€.

  1. λ¦¬λ•μŠ€ μŠ€ν† μ–΄ ν™˜κ²½ 섀정은 λ„ˆλ¬΄ λ³΅μž‘ν•˜λ‹€.
  2. λ¦¬λ•μŠ€λ₯Ό μœ μš©ν•˜κ²Œ μ‚¬μš©ν•˜λ €λ©΄ λ§Žμ€ νŒ¨ν‚€μ§€λ₯Ό μΆ”κ°€ν•΄μ•Όν•œλ‹€.
  3. λ¦¬λ•μŠ€λŠ” λ³΄μΌλŸ¬ν”Œλ ˆμ΄νŠΈ, 즉 μ–΄λ–€ 일을 ν•˜κΈ° μœ„ν•΄ κΌ­ μž‘μ„±ν•΄μ•Ό ν•˜λŠ” μ½”λ“œλ₯Ό λ„ˆλ¬΄ 많이 μš”κ΅¬ν•œλ‹€.

RTKλŠ” λ¦¬λ•μŠ€λ₯Ό μž‘μ„±ν•˜λŠ” ν‘œμ€€ 방식이 되기 μœ„ν•œ μ˜λ„λ‘œ λ§Œλ“€μ–΄μ‘Œλ‹€κ³  ν•œλ‹€.

RTK의 μ£Όμš” API

1. configureStore()

  • μŠ€ν† μ–΄λ₯Ό κ΅¬μ„±ν•˜λŠ” ν•¨μˆ˜
  • λ¦¬λ•μŠ€ μ½”μ–΄ 라이브러리의 ν‘œμ€€ ν•¨μˆ˜μΈ createStoreλ₯Ό μΆ”μƒν™”ν•œ 것이닀.
  • λ¦¬λ•μŠ€μ˜ λ²ˆκ±°λ‘œμ€ κΈ°λ³Έ μ„€μ • 과정을 μžλ™ν™”ν•˜μ˜€λ‹€.
import { configureStore } from '@reduxjs/toolkit'

import rootReducer from './reducers'

const store = configureStore({ reducer: rootReducer })

μœ„μ²˜λŸΌ μ„ μ–Έν•˜λ©΄ κΈ°λ³Έ λ―Έλ“€μ›¨μ–΄λ‘œ redux-thunkλ₯Ό μΆ”κ°€ν•˜κ³  개발 ν™˜κ²½μ—μ„œ λ¦¬λ•μŠ€ 개발자 도ꡬλ₯Ό ν™œμ„±ν™”ν•΄μ€€λ‹€.

2. createReducer()

  • μƒνƒœμ— λ³€ν™”λ₯Ό μΌμœΌν‚€λŠ” λ¦¬λ“€μ„œ ν•¨μˆ˜λ₯Ό μƒμ„±ν•˜λŠ” μœ ν‹Έ ν•¨μˆ˜
  • λ‚΄λΆ€μ μœΌλ‘œ immer 라이브러리λ₯Ό μ‚¬μš©ν•˜μ—¬ λΆˆλ³€ μ—…λ°μ΄νŠΈκ°€ 이루어지도둝 λ‘œμ§μ„ κ°„λ‹¨νžˆ ν•  수 μžˆλ‹€.
const todosReducer = createReducer(state = [], (builder) => {
  builder.addCase('UPDATE_VALUE', (state, action) => {
    const {someId, someValue} = action.payload;

    state.first.second[someId].fourth = someValue;
  })
})

build callback ν‘œκΈ°λ²•

  • createReducer의 콜백 ν•¨μˆ˜ 인자둜 μ£Όμ–΄μ§€λŠ” builder κ°μ²΄λŠ” addCase, addMatcher, addDefaultCaseλΌλŠ” λ©”μ„œλ“œλ₯Ό μ œκ³΅ν•œλ‹€.

각 λΌμΈλ§ˆλ‹€ λΉŒλ” λ©”μ„œλ“œλ₯Ό λ‚˜λˆ„μ–΄ ν˜ΈμΆœν•˜κ±°λ‚˜ 체이닝 ν˜•νƒœλ‘œ μž‘μ„±ν•˜λ©΄ λœλ‹€.

  • addCase: μ•‘μ…˜ νƒ€μž…κ³Ό λ§΅ν•‘λ˜λŠ” μΌ€μ΄μŠ€ λ¦¬λ“€μ„œλ₯Ό μΆ”κ°€ν•˜μ—¬ μ•‘μ…˜μ„ μ²˜λ¦¬ν•œλ‹€.
  • addMatcher: μƒˆλ‘œ λ“€μ–΄μ˜€λŠ” λͺ¨λ“  μ•‘μ…˜μ— λŒ€ν•΄μ„œ 주어진 νŒ¨ν„΄κ³Ό μΌμΉ˜ν•˜λŠ”μ§€ ν™•μΈν•˜κ³  λ¦¬λ“€μ„œλ₯Ό μ‹€ν–‰ν•œλ‹€.
  • addDefaultCase:addCaseλ‚˜ addMatcher도 μ‹€ν–‰λ˜μ§€ μ•Šμ•˜λ‹€λ©΄ μ‹€ν–‰λœλ‹€.

3. createAction()

  • μ•‘μ…˜ νƒ€μž… μƒμˆ˜μ™€ μ•‘μ…˜ μƒμ„±μž ν•¨μˆ˜λ₯Ό λΆ„λ¦¬ν•˜μ—¬ μ„ μ–Έν•˜λ˜ 방법을 κ²°ν•©ν•˜μ—¬ μΆ”μƒν™”ν•˜μ˜€λ‹€.

// AFTER
import { createAction } from '@reduxjs/toolkit'

const increment = createAction('counter/increment')

const action = increment(3)
// { type: 'counter/increment', payload: 3 }

4. createSlice()

  • createAction,createReducerν•¨μˆ˜κ°€ λ‚΄λΆ€μ μœΌλ‘œ μ‚¬μš©λ˜λ©° createSlice에 μ„ μ–Έλœ 슬라이슀 이름을 λ”°λΌμ„œ λ¦¬λ“€μ„œμ™€ 그리고 그것에 μƒμ‘ν•˜λŠ” μ•‘μ…˜ μƒμ„±μžμ™€ μ•‘μ…˜ νƒ€μž…μ„ μžλ™μœΌλ‘œ μƒμ„±ν•œλ‹€.
  • λ”°λΌμ„œ createSliceλ₯Ό μ‚¬μš©ν•˜λ©΄ createAction,createReducerλ₯Ό μž‘μ„±ν•  ν•„μš”κ°€ μ—†λ‹€.
import { createSlice, PayloadAction } from '@reduxjs/toolkit'

interface Item {
  id: string
  text: string
}

const todosSlice = createSlice({
  name: 'todos',
  initialState: [] as Item[],
  reducers: {    
    addTodo: {
      reducer: (state, action: PayloadAction) => {
        state.push(action.payload)
      },
      prepare: (text: string) => {
        const id = nanoid()
        return { payload: { id, text } }
      },
    },
  },
})

const { actions, reducer } = todosSlice
export const { addTodo } = actions

export default reducer 
  • μ•‘μ…˜ μ°¨μž…μ€ 슬라이슀 이름을 μ ‘λ‘μ–΄λ‘œ μ‚¬μš©ν•΄μ„œ μžλ™ μƒμ„±λœλ‹€. -> 'todos/addTodo'
  • 이에 μƒμ‘ν•˜λŠ” μ•‘μ…˜ νƒ€μž…μ„ 가진 μ•‘μ…˜μ΄ λ””μŠ€νŒ¨μΉ˜ 되면 λ¦¬λ“€μ„œκ°€ μ‹€ν–‰λ©λ‹ˆλ‹€.

5. createAsyncThunk

  • createAction의 비동기 버전을 μœ„ν•΄μ„œ μ œμ•ˆλ˜μ—ˆλ‹€.
  • μ•‘μ…˜ νƒ€μž… λ¬Έμžμ—΄κ³Ό ν”„λ‘œλ―ΈμŠ€λ₯Ό λ°˜ν™˜ν•˜λŠ” 콜백 ν•¨μˆ˜λ₯Ό 인자둜 λ°›μ•„μ„œ 주어진 μ•‘μ…˜ νƒ€μž…μ„ μ ‘λ‘μ–΄λ‘œ μ‚¬μš©ν•˜λŠ” ν”„λ‘œλ―ΈμŠ€ 생λͺ… μ£ΌκΈ° 기반의 μ•‘μ…˜ νƒ€μž…μ„ μƒμ„±ν•œλ‹€.

import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'
import { userAPI } from './userAPI'

const fetchUserById = createAsyncThunk(
  'users/fetchByIdStatus',
  async (userId, thunkAPI) => {
    const response = await userAPI.fetchById(userId)

    return response.data
  }
)

const usersSlice = createSlice({
  name: 'users',
  initialState: { entities: [], loading: 'idle' },
  reducers: {},
  extraReducers: (builder) => {
    builder
      .addCase(fetchUserById.pending, (state) => {})
      .addCase(fetchUserById.fulfilled, (state, action) => {
	      state.entities.push(action.payload)
      })
      .addCase(fetchUserById.rejected, (state) => {})
  },
})

dispatch(fetchUserById(123))
  • κΌ­ μ„œλ²„μ™€ 톡신이 μ΄λ£¨μ–΄μ§€λŠ” κ΅¬κ°„μ—μ„œλ§Œ μ‚¬μš©λ˜λŠ” 것은 μ•„λ‹ˆκ³  λΉ„μ¦ˆλ‹ˆμŠ€ λ‘œμ§μ„ 비동기 ν˜•νƒœλ‘œ κ΅¬ν˜„ν•  λ•Œ μ‘μš©ν•  μˆ˜λ„ μžˆλ‹€.

πŸ”₯ λŠλ‚€μ 

reduxλ₯Ό λ°°μš°λ©΄μ„œ 이것저것 μ„€μ •ν•  것, λ§Œλ“€μ–΄μ•Ό ν•  νŒŒμΌλ“€μ΄ λ„ˆλ¬΄ λ§Žμ•˜λŠ”λ° Redux Toolkit을 μ μš©ν•˜λ©΄μ„œ 둜직이 많이 κ°„λ‹¨ν•΄μ§€λŠ” 것을 λ³Ό 수 μžˆμ—ˆλ‹€.

ν™”ν•΄ νŒ€μ—μ„œ rtk에 λŒ€ν•΄ μžμ„Ένžˆ 정리해 놓은 글을 μ°Έκ³ ν•˜μ˜€λŠ”λ° μžμ„Ένžˆ μ •λ¦¬ν•΄μ£Όμ…”μ„œ 이해가 μ’€ μ‰½κ²Œ 된 것 κ°™λ‹€.

이제 redux-toolkitμ΄λž‘ redux-thunk μ‹€μŠ΅ν•˜λ©΄μ„œ κ³΅λΆ€ν•œ λ‚΄μš©μ„ μ •λ¦¬ν•˜λŸ¬ κ°€κ² μŠ΅λ‹ˆλ‹€!!

Refer

profile
맀 μˆœκ°„ μ„±μž₯ν•˜λŠ” κ°œλ°œμžκ°€ 되렀고 λ…Έλ ₯ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.

0개의 λŒ“κΈ€