33일차 React_Redux, Redux Toolkit

정상희·2025년 4월 22일

코딩공부

목록 보기
41/60
post-thumbnail

현재 오즈코딩스쿨 강의를 통해 프론트엔드를 학습하고 있습니다.
본 포스트는 해당 강의에 대한 내용 정리를 목적으로 합니다.

1. Redux로 전역상태 관리하기

1) Redux

Redux는 자바스크립트 애플리케이션의 상태(state)를 효율적으로 관리하기 위한 상태 관리 라이브러리이다.

  • 모듈 설치
    npm install redux react-redux

2) Redux의 데이터 흐름

Redux는 Flux 아키텍처를 기반으로 한 구조를 따르며, 데이터가 단방향으로 흐른다.

View(UI)에서 사용자 행동 → Action을 생성하고 → Dispatch로 Action을 전달하면 →
Reducer가 현재 상태와 Action을 바탕으로 새 상태를 계산 → Store가 새로운 상태를 보관 →
View는 상태 변경을 감지하고 리렌더링

즉, "Action → Dispatch → Reducer → Store → View"의 순환 구조이다.


3) 주요 구성 요소

a. Action

상태를 어떻게 변화시킬 것인지에 대한 내용이 담겨있는 객체.

  • 액션 객체 : Action 객체는 상태에 어떤 변화가 일어나야 하는지를 설명하는 자바스크립트 객체이며,
    반드시 type 속성을 가지고 있으며, 그 외에 필요한 데이터는 payload 등으로 함께 전달할 수 있다.
const incrementAction = {
  type: 'INCREMENT',
};

const addTodoAction = {
  type: 'ADD_TODO',
  payload: {
    id: 1,
    text: 'Redux 공부하기',
  },
};

  • 액션 생성자 :Action 객체를 반환하는 함수이며, 코드의 재사용성과 가독성을 높이고, Action 생성을 일관되게 처리할 수 있도록 도와준다.
const increment = () => {
  return {
    type: 'INCREMENT',
  };
};

const addTodo = (id, text) => {
  return {
    type: 'ADD_TODO',
    payload: {
      id,
      text,
    },
  };
};

b. Dispatch

액션 객체를 Reducer로 넘겨주는 역할을 하는 함수

  • 액션 객체
// 액션 객체
const incrementAction = {
  type: 'INCREMENT',
};

// Dispatch로 전달
dispatch(incrementAction);

위 예시는 INCREMENT 타입의 액션 객체를 직접 만들어 dispatch에 전달하는 방식이다.


  • 액션 생성자
// 액션 생성자
const increment = () => {
  return {
    type: 'INCREMENT',
  };
};

// Dispatch로 전달
dispatch(increment());

위 예시는 increment()라는 액션 생성자를 통해 액션 객체를 생성하고, 그것을 dispatch에 전달하는 방식이다.


c. Reducer

리턴하는 값이 새로운 상태가 되는 함수

const reducer = (state = 초기값, action) => {
  switch (action.type) {
    case '액션타입':
      return 새로운_상태;
    default:
      return state;
  }
};

const counterReducer = (state = 0, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1;
    case 'DECREMENT':
      return state - 1;
    default:
      return state;
  }
};

이 Reducer는 숫자 상태를 관리하며, INCREMENT 액션이 오면 +1, DECREMENT가 오면 -1을 반환하는 구조이다.

Reducer의 특징

  • 순수 함수이다 → 동일한 입력(state, action)에 대해 항상 동일한 결과를 반환한다.
  • 직접 상태를 수정하지 않는다 → 항상 새로운 객체나 값을 반환해야 한다.
  • 여러 Reducer를 combineReducers()를 통해 하나로 합칠 수 있다.

combineReducers() 예시

import { combineReducers } from 'redux';

// 카운터 상태를 처리하는 리듀서
const counterReducer = (state = 0, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1;
    default:
      return state;
  }
};

// 로그인 상태를 처리하는 리듀서
const authReducer = (state = { isLoggedIn: false }, action) => {
  switch (action.type) {
    case 'LOGIN':
      return { isLoggedIn: true };
    case 'LOGOUT':
      return { isLoggedIn: false };
    default:
      return state;
  }
};

// 두 reducer를 결합
const rootReducer = combineReducers({
  counter: counterReducer,
  auth: authReducer,
});

위 예시에서 combineReducers()counterauth라는 상태 조각을 각각 담당하는 reducer들을 하나로 합쳐 rootReducer로 만든 것이다.

{
  counter: 0,
  auth: {
    isLoggedIn: false
  }
}

이 경우 Redux store의 전체 상태는 다음과 같이 구성된다.


d. Store

Redux의 전역 상태 저장소, createStore 함수에 Reducer를 전달해서 생성

import { createStore } from 'redux';

// 간단한 reducer
const counterReducer = (state = 0, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1;
    default:
      return state;
  }
};

// store 생성
const store = createStore(counterReducer);

// 상태 조회
console.log(store.getState()); // 0

// 액션 디스패치
store.dispatch({ type: 'INCREMENT' });

// 상태 다시 조회
console.log(store.getState()); // 1


4) useDispatch() 와 useSelector()의 역할

a. useDispatch()

컴포넌트에서 Action을 발생시키기 위한 함수를 제공

const dispatch = useDispatch();
dispatch({ type: 'INCREMENT' });

b. useSelector()

Store 안의 상태를 읽어오기 위한 함수

const count = useSelector((state) => state);

5) Redux-Thunk

Redux-Thunk는 비동기 작업을 처리하기 위한 미들웨어이다.

  • 기본 Redux는 동기적 Action만 처리 가능하다.
  • Thunk를 사용하면 Action 대신 함수를 Dispatch할 수 있고, 이 함수 안에서 비동기 작업(AJAX 요청 등)을 수행할 수 있다.
// 비동기 액션 생성 함수
const fetchData = () => {
  return async (dispatch) => {
    dispatch({ type: 'LOADING' });
    const res = await fetch('/api/data');
    const data = await res.json();
    dispatch({ type: 'SUCCESS', payload: data });
  };
};

Thunk는 redux-thunk 패키지를 설치하고 applyMiddleware()로 store에 연결해서 사용한다.

import { applyMiddleware, createStore } from 'redux';
import thunk from 'redux-thunk';

const store = createStore(reducer, applyMiddleware(thunk));
  • Redux-Thunk 모듈 설치
    npm install redux-thunk


2. Redux Toolkit으로 전역상태 관리하기

1) Redux Toolkit

Redux Toolkit은 Redux 공식 팀에서 제공하는 상태 관리 툴킷으로, 복잡하고 반복적인 Redux 코드를 더 간결하고 직관적으로 작성할 수 있도록 도와주는 라이브러리이다.
Redux의 표준 방식으로 권장되며, createSlice, configureStore, createAsyncThunk 등의 API를 제공한다.

즉, Redux Toolkit은 Redux 개발을 쉽게 만들어주는 도구 모음이다.

  • 모듈 설치
    npm install @reduxjs/toolkit react-redux


2) Redux와 차이점

구분ReduxRedux Toolkit
액션 정의수동 작성createSlice()로 자동 생성
리듀서 작성직접 작성createSlice()로 간단히 정의
상태 변경불변성 수동 처리Immer 내장으로 직관적인 코드 작성 가능
비동기 처리Thunk 등 수동 설정createAsyncThunk() 제공
개발자 도구별도 설정 필요configureStore()로 자동 연동

3) createSlice(), configureStore() 의 역할

a. createSlice()

createSlice()state, reducer, action creator를 한 번에 정의하는 함수이다.
이 함수로 상태 로직을 모듈 단위로 구성할 수 있다.

// counterSlice.js
import { createSlice } from '@reduxjs/toolkit';

const counterSlice = createSlice({
  name: 'counter',
  initialState: 0,
  reducers: {
    increment: (state) => state + 1,
    decrement: (state) => state - 1,
  }
});

export const { increment, decrement } = counterSlice.actions;
export default counterSlice.reducer;

정리하면, createSlice()는 상태값과 관련된 모든 것을 하나로 묶어서 정의할 수 있는 함수이다.


b. configureStore()

configureStore()는 Redux의 createStore()를 대체하는 함수이며,
reducer 설정, Redux DevTools 연동, 미들웨어 설정 등을 쉽게 할 수 있도록 도와준다.

// store.js
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from '../features/counter/counterSlice';

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

정리하면, configureStore()는 Redux의 스토어를 더 쉽게 생성하고 설정해주는 함수이다.


4)Redux Toolkit에서 Thunk 사용법

비동기 처리를 위해 Redux Toolkit에서는 createAsyncThunk()를 사용한다.
이 함수는 API 요청과 같은 비동기 작업을 액션처럼 정의할 수 있게 해준다.

createAsyncThunk()란?

createAsyncThunk()는 비동기 요청에 대해 pending, fulfilled, rejected의 세 가지 상태를 자동으로 생성해주는 함수이다.

// 예시: 비동기 사용자 정보 가져오기
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';

// Thunk 정의
export const fetchUser = createAsyncThunk(
  'user/fetchUser',
  async (userId) => {
    const response = await fetch(`/api/user/${userId}`);
    return await response.json();
  }
);

// Slice 정의
const userSlice = createSlice({
  name: 'user',
  initialState: { data: null, status: 'idle' },
  reducers: {},
  extraReducers: (builder) => {
    builder
      .addCase(fetchUser.pending, (state) => {
        state.status = 'loading';
      })
      .addCase(fetchUser.fulfilled, (state, action) => {
        state.data = action.payload;
        state.status = 'succeeded';
      })
      .addCase(fetchUser.rejected, (state) => {
        state.status = 'failed';
      });
  }
});

정리하면, createAsyncThunk()는 비동기 로직을 Redux 액션처럼 작성할 수 있게 해주는 도구이며,
자동으로 처리 상태를 관리할 수 있도록 도와주는 함수이다.

profile
UI/UX디자이너의 코딩 공부

0개의 댓글