Redux Toolkit 도입해보기 - 1 with React, TypeScript

hyeji Han·2023년 3월 27일
0

React에서 보통 상태관리 툴로 Redux를 사용하는데 Redux에는 액션함수, 리듀서, 스토어 등으로 작성하는 코드가 많고 가독성이 좋지 않다.
그래서 Redux-Toolkit을 공부하며 프로젝트에 도입해보려한다!

Redux-Toolkit 패키지 설치하기

다음과 같이 설치해준다.

npm install @recuxjs/toolkit react-redux

만약 프로젝트에 버전 7.2.3 이하의 버전이라면 다음 코드를 추가한다.

npm install @types/react-redux

Redux-Toolkit 주요 API

Slice란 기능별로 store를 나누는 것이다.
예를 들어 쇼핑몰 프로젝트에서 진행한다면 1)회원가입, 2)로그인, 3)장바구니 등의 기능으로 나루 수 있다. 따라서 state를 이런 기능으로 나누는 것이다.
Slice 파일들은 보통 src/features 폴더에 저장한다.

createSlice()

Slice를 만들 때 사용하는 함수가 createSlice()이다.
slice의 state의 타입을 우선 정의해야한다. interface로 타입을 정한 후에 initialState의 값에 초기값을 정한다.

import { createSlice } from '@reduxjs/toolkit'
import type { PayloadAction } from '@reduxjs/toolkit'

interface CounterState {
  value: number
}

const initialState : CounterState = { value: 0 } 

아래 코드는 간단하게 카운트를 증감하는 예제이다.
여기서 createSlice 함수는 파라미터에 1)name, 2)initialState, 2) reducers 로 총 3개를 작성하면 된다.

1) name : createSlice 함수를 통해서 위에서 말한 것과 같은 기능마다 state를 나누는 slice를 생성할 수 있다. name은 이런 slice의 중복을 피하기 위해 정하는 고육한 이름 값이다.

2) initialState : 상태관리에 사용되는 type이다. interface로 타입을 지정하고 initialState의 값을 초기화하는데 카운터이기에 value를 0으로 설정했다.

3) reducers : 액션타입에 따라 상태변화를 처리하는 함수를 정의 한다. 함수의 이름은 dispatch로 부르는 액션함수의 이름이고 함수 내부에서는 state의 상태를 변경한다. 그리고 reducer에서 payload를 인자로 받는 경우 아래와 같이 action.payload의 타입을 정의해준다.

src/features/counterSlice.ts

import { createSlice } from '@reduxjs/toolkit'
import type { PayloadAction } from '@reduxjs/toolkit'

interface CounterState {
  value: number
}

const initialState : CounterState = { value: 0 } 

const counterSlice = createSlice({
  name: 'counter',
  initialState,
  reducers: {
    increment: state => {
        state.value += 1
      },
      decrement: state => {
        state.value -= 1
      },
      incrementByAmount: (state, action: PayloadAction<number>) => {
        state.value += action.payload
      }
})

configureStore()

이러한 기능별로 나눈 slice 안의 reducer를 configureStore에 등록시킨다.

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

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

컴포넌트에서 action 사용하기

컴포넌트에서 action을 사용한다면 'react-redux'의 useDispatch를 사용해서 action을 dispatch하면 그에 따른 reducer가 실행된다.

import { useDispatch } from 'react-redux';
import { increment } from '../features/counter/counterSlice'

function App() {
  const dispatch = useDispatch();
  
  // ...
  return (
    <div>
      <button onClick={() => dispatch(increment())}>increment</button>
    </div>
  )

컴포넌트에서 State에 접근하기 (JS)

컴포넌트에서 store에 저장된 state에 접근하고 싶다면 'react-redux'의 useSelector을 사용해주면 된다. useSelector에 store자체를 넘기고 이 store를 통해 slice의 state에 접근할 수 있다.

import { useSelector } from 'react-redux';

function App() {
  const { count } = useSelector((store) => store.counter);
  // ...
}

hooks.ts (TS에서 useSelector와 useDispatch사용하기)

타입스크립트를 사용하여 컴포넌트에서 useSelector와 useDispatch를 사용하기 위해서는 타입스크립트 버전으로 바꿔줘야 한다.
이런 타입화 과정을 매번 컴포넌트에서 하면 비효율적이기 때문에 hooks.ts라는 파일안에 pre-typed 버전을 만들어주었다.

import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'
import type { RootState, AppDispatch } from './store'

export const useAppDispatch: () => AppDispatch = useDispatch
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector

위와 같이 hooks.ts 파일을 만든 후 컴포넌트에서 dispatch와 selector을 사용할 수 있다.

import { useAppSelector, useAppDispatch } from '../hooks'
import { decrement, increment } from './store/counterSlice'

export function Counter() {
  const count = useAppSelector(state => state.counter.value)
  const dispatch = useAppDispatch()
  
  return (
    <div>
      <div>
        <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>
  )
}

App 전역 Provider에 등록하기

위에서 작성한 store를 전역에 사용가능하도록 App의 시작점인 index.tsx파일에 Provider로 등록해준다.

import {Provider} from "react-redux"
import {store} from './store/store'

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

다음에는 Redux-Toolkit을 활용하여 비동기 처리를 해보려고 한다!

profile
갓생 살아보자고! 아쟛

0개의 댓글