React 난 이렇게 배웠다[Redux-toolkit]

진건희·2024년 5월 4일
1
post-thumbnail
post-custom-banner

redux-toolkit이란

Redux를 더 사용하기 쉽게 만들기 위해 Redux에서 공식 라이브러리이다.

Redux의 문제점

redux-toolkit은 redux의 3가지 단점을 보완하기 위해 만들어졌다.

  • 저장소 구성의 복잡성(store 환경이 복잡)
  • 많은 패키지 필요성(의존성)
  • 한 작업 시 필요한 수 많은 코드양('보일러플레이트')

redux-toolkit의 4가지 특징

Simple

스토어 설정, 리듀서 생성, 불변성 업데이트 로직 사용을 편리하게 하는 기능 포함

Opitionated

스토어 설정에 관한 기본 설정 제공, 일반적으로 사용되는 redux addon이 내장

Powerful

Immer에 영감을 받아 '변경'로직으로 '불변성'로직 작성 가능, state 전체를 slice로 자동으로 만들 수 있음

Effective

적은 코드에 많은 작업을 수행 가능

주요 개념

configureStore

Redux 스토어를 설정하는 함수.
Redux Toolkit은 configureStore 함수를 통해 스토어를 생성하며, 기본적으로 Redux DevTools Extension과 함께 사용됨.

createSlice

Redux Toolkit의 핵심 기능 중 하나로, 리듀서와 액션 생성 함수를 한 번에 정의할 수 있게 해준다.
createSlice 함수를 사용하면 리듀서 함수와 액션 생성 함수를 하나의 파일에 함께 정의하여 관리할 수 있다.

immer

Redux Toolkit에서 상태를 변경할 때 불변성을 유지하는 것을 간편하게 만들어주는 라이브러리.
immer를 사용하면 복잡한 불변성 로직을 작성하지 않아도 된다.

Thunk middleware

비동기 작업을 처리하기 위한 Redux 미들웨어.
Redux Toolkit은 Thunk middleware를 기본으로 포함하고 있어서 비동기 작업을 쉽게 처리하는 게 가능하다.

주요 개념 해석 ↓

configureStore

스토어(store)를 만들 때 사용되는 함수.
Redux DevTools Extension 기능도 딸려옴.

createSlice

Redux Toolkit에서 중요 기능 중 하나.
리듀서액션 생성 함수를 한번에 선언 가능.
즉, 우리가 상태를 어떻게 변경할지를 정의하는 리듀서
상태를 변경할 때 사용할 액션들을 한번에 관리할 수 있는 거죠.

immer

상태를 변경할 때 불변성을 유지하는 게 쉬워지도록 도와주는 라이브러리.
Redux를 사용하면 상태를 변경할 때 매우 복잡한 로직을 작성해야 한다.
하지만 immer를 사용하면 더 간단하게 상태를 변경할 수 있게 된다.

Thunk middleware

이건 비동기 작업을 처리하기 위한 Redux 미들웨어이다.
비동기 작업은 우리가 어떤 작업을 요청한 후에 결과를 기다리는 것이다.
Redux Toolkit은 이 Thunk middleware를 기본으로 포함하여 비동기 작업을 편하게 할 수 있다.

미들 웨어(middleware)

미들웨어(Middleware)는 소프트웨어 시스템에서 입력과 출력 사이에 위치하여 데이터를 처리하고 변환하는 소프트웨어 구성 요소.
특히, 웹 개발에서는 서버와 클라이언트 사이에 위치하여 요청을 처리하고 응답을 생성하는 데 사용됨.

Redux에서는 액션과 리듀서 사이에서 동작하여 액션을 가로채고 추가적인 작업을 수행함.

  1. 액션이 디스패치를 함
  2. 미들웨어는 해당 액션을 가로챔
  3. 필요에 따라 액션을 변형하거나 추가적인 작업을 수행
  4. 리듀서에게 액션을 전달

많이 사용되는 미들웨어

Redux에서 많이 사용되는 미들웨어는
Redux Thunk, Redux Saga, Redux Observable이 있다.

Redux DevTools Extension

Redux 개발 도구의 한 종류.
Redux 애플리케이션의 상태를 시각적으로 디버깅하고 모니터링할 수 있게 해주는 브라우저 확장 프로그램.

  • Redux 스토어의 상태를 실시간으로 확인
  • 액션들의 로그를 볼 수 있음
  • 시간에 따른 상태의 변화를 추적

기능을 보자면
상태 시각화, 액션 로깅, 시간 여행, 실시간 업데이트
4가지이다.

상태 시각화
스토어의 현재 상태를 트리나 차트 형태로 시각화하여 볼 수 있다.

액션 로깅
디스패치된 액션들의 로그를 확인할 수 있어서 액션이 어떻게 처리되는지를 추적 가능.

시간 여행
상태의 이전 버전과 현재 버전을 비교, 이전 상태로 되돌아가거나 앞으로 나아갈 수 있는 기능을 제공.
디버깅을 용이하게 해줌.

실시간 업데이트
애플리케이션이 실행 중일 때 실시간으로 상태 변화를 모니터링 가능.
개발자가 애플리케이션의 동작을 쉽게 이해 가능하게 해줌.


사용법

4가지 단계로 나눈다.

단계

  1. 스토어 생성 : configureStore 함수를 사용하여 Redux 스토어를 생성.
  2. 슬라이스 생성 : createSlice 함수를 사용하여 리듀서와 액션 생성 함수를 한번에 사용하는 슬라이스를 생성.
  3. 액션 디스패치 : 생성된 액션 생성 함수를 사용하여 액션을 디스패치하여 상태를 변경.
  4. 구독 : 필요한 경우, 스토어의 상태 변경을 구독하여 변화를 감지하고 처리.

작동 방식

Redux Toolkit은 Redux의 기본 원리를 기반으로 하며, Redux의 여러 기능들을 보다 효율적으로 사용할 수 있도록 지원합.

  1. createSlice 함수를 사용하여 슬라이스를 생성하면 해당 슬라이스에 대한 리듀서와 액션 생성 함수가 자동으로 생성됨.
  2. immer 라이브러리를 사용하여 상태를 변경할 때 불변성을 유지하면서 코드를 작성 가능.
  3. Thunk middleware를 사용하여 비동기 작업을 처리.

대표 API

configureStore : Redux 스토어를 생성하는 함수.
createSlice : 리듀서와 액션 생성 함수를 함께 정의하여 슬라이스를 생성하는 함수.
createAsyncThunk : 비동기 작업을 처리하는 Thunk 액션 생성 함수를 생성하는 함수.
createReducer : 리듀서 함수를 생성하는 함수로, 복잡한 리듀서 로직을 분리하여 관리.

useSelector

스토어의 상태를 선택하는 데 사용되는 hook.
컴포넌트에서 필요한 상태를 선택하여 가져올 수 있다.

useDispatch

액션을 디스패치하는 데 사용되는 hook.
useDispatch를 사용하여 컴포넌트에서 액션을 디스패치할 수 있다.

실습

저번에 redux를 배웠을 때 만든 투두리스트 코드를 redux-toolkit으로 재개발 해보았다.

과정

createSlice
Redux Toolkit의 createSlice 함수를 사용하여 슬라이스를 생성.
슬라이스는 상태와 액션 생성자(reducers)를 함께 정의하여 관련된 로직을 묶어준다. 위 코드에서는 'todos' 슬라이스를 생성하고, 초기 상태와 할일을 추가하는 액션 생성자를 정의함.

코드

const todoSlice = createSlice({
  name: 'todos',
  initialState: {
    todos: []
  },
  reducers: {
    addTodo: (state, action) => {
      state.todos.push({ id: Date.now(), text: action.payload });
    }
  }
});

코드 역할
여기서 createSlice 함수가 사용되었다.
이 함수는 'todos'라는 이름을 가진 슬라이스를 생성하고,
초기 상태로 빈 배열을 가지며, addTodo라는 액션 생성자(reducer)를 선언하고 있다.

configureStore
Redux Toolkit의 configureStore 함수를 사용하여 스토어를 생성합.
이 함수는 슬라이스 리듀서를 인자로 받아 Redux 스토어를 생성함.
이 예제에서는 'todos' 슬라이스의 리듀서를 스토어에 등록합니다.

코드

const store = configureStore({
  reducer: todoSlice.reducer
});

코드 역할
여기서 configureStore 함수가 사용됨.
이 함수는 Redux 스토어를 설정하고, todoSlice.reducer를 스토어의 리듀서로 설정함.

Provider
리액트 애플리케이션에서 Redux 스토어를 사용할 수 있도록 제공하는 컴포넌트. Provider 컴포넌트는 최상위에 위치하여 앱 전체에 Redux 스토어를 제공.

코드

const App = () => {
  return (
    <Provider store={store}>
      <div>
        <h1>투두리스트</h1>
        <TodoList />
        <AddTodo />
      </div>
    </Provider>
  );
};

코드 역할
여기서는 Provider 컴포넌트가 사용되었다.
이 컴포넌트는 Redux 스토어를 앱에 제공하는 역할이다.

useSelector
React Redux의 useSelector 훅을 사용하여 스토어의 상태를 가져옴.
이 훅은 함수형 컴포넌트 내에서 사용되며=고, 스토어의 상태를 선택하여 컴포넌트에 제공함.
이 예제에서는 useSelector를 사용하여 투두리스트의 상태를 가져오는 역할.

코드

const todos = useSelector(state => state.todos);

코드 역할
여기서 useSelector 훅이 사용되었다.
이 훅을 사용하여 스토어의 상태인 todos를 가져왔다.

useDispatch
React Redux의 useDispatch 훅을 사용하여 액션을 디스패치한다.
이 훅은 함수형 컴포넌트 내에서 사용되고, 디스패치 함수를 가져와서 액션을 디스패치할 수 있다.
이 예제에서는 useDispatch를 사용하여 할일을 추가하는 액션을 디스패치 하는 역할.

코드

const dispatch = useDispatch();

코드 역할
여기서 useDispatch 훅이 사용되었다.
이 훅을 사용하여 액션을 디스패치하는 dispatch 함수를 가져왔다.

최종 코드

import React from 'react';
import { configureStore, createSlice } from '@reduxjs/toolkit';
import { Provider, useDispatch, useSelector } from 'react-redux';

// 슬라이스 생성
const todoSlice = createSlice({
  name: 'todos',
  initialState: {
    todos: []
  },
  reducers: {
    addTodo: (state, action) => {
      state.todos.push({ id: Date.now(), text: action.payload });
    }
  }
});

// 스토어 생성
const store = configureStore({
  reducer: todoSlice.reducer
});

// 투두리스트 컴포넌트
const TodoList = () => {
  // useSelector를 사용하여 스토어의 상태를 가져오기
  const todos = useSelector(state => state.todos);

  return (
    <ul>
      {todos.map(todo => (
        <li key={todo.id}>{todo.text}</li>
      ))}
    </ul>
  );
};

// 할일 추가 컴포넌트
const AddTodo = () => {
  const dispatch = useDispatch();

  // 할일 추가 함수
  const handleAddTodo = () => {
    const todoText = prompt('할 일을 입력하세요:');
    if (todoText) {
      dispatch(todoSlice.actions.addTodo(todoText));
    }
  };

  return (
    <button onClick={handleAddTodo}>할 일 추가</button>
  );
};

// 앱 컴포넌트
const App = () => {
  return (
    <Provider store={store}>
      <div>
        <h1>투두리스트</h1>
        <TodoList />
        <AddTodo />
      </div>
    </Provider>
  );
};

export default App;

결과

코드가 저번과 다르지만 생김새와 기능에 차이가 없다.
처음에는 제목인 투두리스트와 할 일 추가 버튼이 있고,

할 일 추가 버튼을 누르면 할 일의 제목을 적을 수 있는 alert가 뜬다.

alert에 제목을 적어주고 확인을 누르면 할 일이 추가 된다.

비교

redux-toolkit X

import React, { useState } from 'react';
import { createStore } from 'redux';

// 액션 타입 정의
const ADD_TODO = 'ADD_TODO';

// 액션 생성 함수
const addTodo = (text) => ({
  type: ADD_TODO,
  payload: { text }
});

// 리듀서 함수
const todoReducer = (state = { todos: [] }, action) => {
  switch(action.type) {
    case ADD_TODO:
      return {
        ...state,
        todos: [...state.todos, { id: Date.now(), text: action.payload.text }]
      };
    default:
      return state;
  }
};

// 스토어 생성
const store = createStore(todoReducer);

// 투두리스트 컴포넌트
const TodoList = ({ todos }) => (
  <ul>
    {todos.map(todo => (
      <li key={todo.id}>{todo.text}</li>
    ))}
  </ul>
);

// 앱 컴포넌트
const App = () => {
  // 투두리스트 상태 설정
  const [todos, setTodos] = useState([]);

  // 스토어 구독
  store.subscribe(() => {
    setTodos(store.getState().todos);
  });

  // 할일 추가 함수
  const handleAddTodo = () => {
    const todoText = prompt('할 일을 입력하세요:');
    if (todoText) {
      store.dispatch(addTodo(todoText));
    }
  };

  return (
    <div>
      <h1>투두리스트</h1>
      <TodoList todos={todos} />
      <button onClick={handleAddTodo}>할 일 추가</button>
    </div>
  );
};

export default App;

redux-toolkit O

import React from 'react';
import { configureStore, createSlice } from '@reduxjs/toolkit';
import { Provider, useDispatch, useSelector } from 'react-redux';

// 슬라이스 생성
const todoSlice = createSlice({
  name: 'todos',
  initialState: {
    todos: []
  },
  reducers: {
    addTodo: (state, action) => {
      state.todos.push({ id: Date.now(), text: action.payload });
    }
  }
});

// 스토어 생성
const store = configureStore({
  reducer: todoSlice.reducer
});

// 투두리스트 컴포넌트
const TodoList = () => {
  // useSelector를 사용하여 스토어의 상태를 가져오기
  const todos = useSelector(state => state.todos);

  return (
    <ul>
      {todos.map(todo => (
        <li key={todo.id}>{todo.text}</li>
      ))}
    </ul>
  );
};

// 할일 추가 컴포넌트
const AddTodo = () => {
  const dispatch = useDispatch();

  // 할일 추가 함수
  const handleAddTodo = () => {
    const todoText = prompt('할 일을 입력하세요:');
    if (todoText) {
      dispatch(todoSlice.actions.addTodo(todoText));
    }
  };

  return (
    <button onClick={handleAddTodo}>할 일 추가</button>
  );
};

// 앱 컴포넌트
const App = () => {
  return (
    <Provider store={store}>
      <div>
        <h1>투두리스트</h1>
        <TodoList />
        <AddTodo />
      </div>
    </Provider>
  );
};

export default App;

redux-toolkit을 사용하여 개선된 점들

1. 간결한 코드
redux-toolkit을 사용하면 코드가 더 간결해졌다.
createSlice 함수를 사용하여 액션과 리듀서를 한 번에 정의하고,
configureStore 함수로 스토어를 설정하는 등 많은 반복적인 작업을 줄임.

2. 불변성 유지
redux-toolkit은 내부적으로 Immer를 사용하여 불변성을 유지하게 되었음.
이로 인해 상태를 변경하는 코드를 작성할 때 더 간편하고 직관적으로 작성할 수 있게됨.

3. DevTools 통합
redux-toolkit은 기본적으로 Redux DevTools Extension과 함께 사용됨.
이를 통해 개발자는 애플리케이션의 상태 변화쉽게 추적하고 디버깅할 수 있다.

4. 간편한 비동기 작업 처리
redux-toolkit은 Thunk middleware를 기본으로 포함하고 있어서 비동기 작업을 쉽게 처리 가능하게 되었다.
이를 통해 네트워크 요청이나 비동기 작업을 처리하는 데 더 간편하고 일관된 방식으로 코드를 작성할 수 있다.

5. 성능 개선
redux-toolkit은 내부적으로 성능 최적화가 적용되어 있다.
이로 인해 더 효율적으로 상태 업데이트가 이루어지며, 애플리케이션의 성능을 향상시킬 수 있다.
이러한 이유로 redux-toolkit을 사용하면 더 효율적으로 Redux를 관리하고 개발할 수 있다.

profile
프론트엔드 공부 중인 GSM 학생입니다.
post-custom-banner

0개의 댓글