Frontend/Today I Learned

[JS, redux] Redux Toolkit 기본 사용법

joycie416 2024. 8. 26. 20:44

오늘은 `Redux Toolkit`(`rtk`)의 아주 기초적인 사용법에 대해 정리해보려고 한다.

 

지난번 Redux 기본 사용법에서는 initial state, action value, action creator, reducer를 직접 작성해주어야 했는데, `rtk`에서는 action value를 별도로 작성하지 않아도 된다. 큰 틀은 redux과 크게 다르지 않지만 세부적인 부분에서 차이점이 있다.

 

Redux Toolkit 사용 기초

기본 설정

먼저 아래 명령어를 실행해 rtk를 설치해주자.

yarn add react-redux @reduxjs/toolkit

 

이후 `src`폴더에 `redux`폴더를 만든 후 `config` 폴더와 `slices` 폴더를 만들자.

먼저 `config/configStore.js`를 만들어 아래와 같이 작성하자.

// src/redux/config/configStore.js

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

const store = configureStore({ });

export default store;

 

store는 지난번 redux 게시글에서 설명한 store와 동일한 역할을 한다. `configStore`에 parameter로 주어진 `{}`에는 reducers를 키로 갖는 reducer object를 넣어줄 것이다. (redux에서 `createStore`를 import하면 취소선이 생겼을 것이다. `createStore` 대신 `rtk`에서 `configureStore`를 사용하면 된다.)

 

다음으로 redux에서 했던 것처럼 `main.jsx`의 App 컴포넌트를 `<Provider>`로 감싸주자.

// src/main.jsx

import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import App from './App.jsx'
import './index.css'
import {Provider} from "react-redux"
import store from './redux/config/configStore.js'

createRoot(document.getElementById('root')).render(
  <StrictMode>
    <Provider store={store}>
      {/* 모든 하위 컴포넌트에서는 store를 사용할 수 있음 */}
      <App />
    </Provider>
  </StrictMode>,
)

 

redux에서는 modules 폴더에 reducer를 만들기 위한 코드를 작성했지만 `rtk`를 사용할 때에는 slice를 만들어 사용한다.

// src/redux/slices/counterSlice.js

import { createSlice } from "@reduxjs/toolkit";

// initial state
const initialState = {
  number: 0,
};

const counterSlice = createSlice({
  name: 'counter',
  initialState,
  reducers: {
    plus: (state, action) => {
      return { number:state.number + action.payload }
    }
    minus: (state, action) => {
      return { number:state.number - action.payload }
    }
  }
})


// 액션크리에이터는 컴포넌트에서 사용하기 위해 export 하고
export const { plus, minus } = counterSlice.actions;
// reducer 는 configStore에 등록하기 위해 export default 합니다.
export default counterSlice.reducer;

 

이전 redux에서 작성한 modules/counter.js와 비교하면 코드가 꽤 단순해진 것을 알 수 있다. 특히 action value를 설정하지 않아도 되는 것이 큰 차이점이다.

 

Reducer 연결하기

이제 config 파일에 reducer를 연결하자.

// src/redux/config/configStore.js

import { configureStore } from "@reduxjs/toolkit";
import counterSlice from "../Slices/counterSlice";

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

export default store;

 

이때 다른 slice에서 만들어진 reducer들이 더 있다면 아래 예시처럼 모두 추가로 작성해주면 된다.

import { configureStore } from "@reduxjs/toolkit";
import counterSlice from "../Slices/counterSlice";
import xSlice from "../Slices/xSlice";
import ySlice from "../Slices/ySlice";

const store = configureStore({
  reducer: {
    counter: counterSlice,
    x: xSlice,
    y: ySlice,
  },
});

export default store;

 

이때 slice를 작성할 때 다른 slice의 reducer를 사용하게 되면 경고/에러가 발생하므로 구상할 때 주의해야 한다. (컴포넌트 내에서만 hook을 사용할 수 있다고 뜬다.)

 

이제 counter의 state와 reducer를 사용하는 방법에 대해 알아보자. Redux에서 했던 것처럼 원하는 컴포넌트에서 `useSelector`와 `useDispatch` hook을 사용하면 된다.

State 확인하기

`useSelector`를 사용해 store에 저장된 state를 조회할 수 있다.

 

// src/App.jsx

import './App.css'
import { useSelector } from 'react-redux'

function App() {

  const counterReducer = useSelector((state) => {
    return state.counter;
  })
  console.log('counterReducer :', counterReducer); // {number:0}

  return (
    <>
      {/* your codes */}
    </>
  )
}

export default App

 

Action 전달하기

`useDispatch`와 reducer를 import해 action을 전달할 수 있다.

// src/App.jsx

import { useState } from 'react'
import './App.css'
import { useDispatch, useSelector } from 'react-redux'
import { minus, plus } from './redux/slices/counterSlice'

function App() {
  const [count, setCount] = useState(0);

  const counterReducer = useSelector((state) => {
    return state.counter;
  })
  console.log('counterReducer :', counterReducer);

  const dispatch = useDispatch();
  // button의 action을 reducer에게 전달해줌

  return (
    <>
      {counterReducer.number}
      <input type='number' onChange={(e) => {
        setCount(+e.target.value);
      }} />
      <button onClick={() => {
        dispatch(minus(count))
      }}>
      	빼기
      </button>
      <button onClick={() => {
        dispatch(plus(count))
      }}>
      	더하기
      </button>
    </>
  )
}

export default App