프로그래밍 언어/React

[React] 상태 관리 Redux 라이브러리 사용하여 slice, store, dispatch 설정

이로률 2025. 3. 7. 11:20

1. Redux와 React-Redux 설치 

npm install @reduxjs/toolkit react-redux

 

- typescript인 경우 추가로 @types/react-redux 설치

npm install @types/react-redux

 

2. Slice 생성 (userSlice.ts)

2-1. Redux Toolkit을 사용한 상태 관리(slice) 설정

- 로그인 후 id와 token을 저장 

 

1) UserState 인터페이스 정의

interface UserState {
  id: string | null;
  token: string | null;
}

2) initialState 초기 상태 설정

const initialState: UserState = {
  id: null,
  token: null
};

3) createSlice를 사용한 상태 관리 설정

const userSlice = createSlice({
  name: 'user', 
  initialState, 
  reducers: { ... }
});

 

  • createSlice는 리듀서(reducer)와 액션(action)을 한 번에 생성하는 함수
  • name: 'user': 이 상태의 이름을 "user"로 설정
  • initialState: 초기 상태를 설정
  • reducers: 상태를 변경하는 함수(액션 생성)

2-2. 상태 변경을 위한 리듀서 함수 (reducers) 정의

1) setUser : 로그인 정보 저장

setUser: (state, action: PayloadAction<{ id: string; token: string }>) => {
  state.id = action.payload.id;
  state.token = action.payload.token;
}

2) resetUser : 로그아웃 시 상태 초기화

resetUser: (state) => {
  state.id = null;
  state.token = null;
}

 

2-3. 전체 코드 

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

interface UserState {
  id: string | null;
  token: string | null;
}

const initialState: UserState = {
  id: null,
  token: null
};

const userSlice = createSlice({
  name: 'user',
  initialState,
  reducers: {
    setUser: (state, action: PayloadAction<{ id: string; token: string }>) => {
      state.id = action.payload.id;
      state.token = action.payload.token;
    },
    resetUser: (state) => {
      state.id = null;
      state.token = null;
    }
  }
});

export const { setUser, resetUser } = userSlice.actions;
export default userSlice.reducer;

 

3. Store 설정 (store.ts)

- userSlice를 store에 등록

 

3-1. Redux 스토어 생성

const store = configureStore({
  reducer: {
    user: userReducer,
  },
});

 

3-2. RootState 타입 정의

export type RootState = ReturnType<typeof store.getState>;

 

  • store.getState()는 현재 Redux의 전체 상태를 반환하는 함수.
  • ReturnType<typeof store.getState> → store.getState()의 반환 타입을 자동으로 가져와 RootState 타입으로 정의.
  • 이후 Redux 상태를 사용할 때 TypeScript가 타입을 추론할 수 있도록 함.

- 사용 예시

const user = useSelector((state: RootState) => state.user);

 

3-3. AppDispatch 타입 정의

export type AppDispatch = typeof store.dispatch;

 

 

  • store.dispatch의 타입을 가져와 AppDispatch로 정의.
  • Redux에서 dispatch()를 사용할 때, 올바른 액션을 전달하도록 TypeScript가 검사할 수 있도록 함.

3-4. 전체코드

import { configureStore } from '@reduxjs/toolkit';
import userReducer from './userSlice';

const store = configureStore({
  reducer: {
    user: userReducer,
  },
});

export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;

export default store;

 

4. main.tsx에 Provider 추가 

- 기존 코드

import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import App from './App.tsx'

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <App />
  </StrictMode>,
)

 

- 수정된 코드 

import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import App from './App.tsx'
import { Provider } from 'react-redux'
import store from '@store/store'

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </StrictMode>,
)

 

5. 컴포넌트에서 활용 (예시)

import { useDispatch, useSelector } from "react-redux";
import { setUser, resetUser } from "./userSlice";
import { RootState } from "./store"; // Redux store 타입

const Login = () => {
  const dispatch = useDispatch();
  const user = useSelector((state: RootState) => state.user);

  const handleLogin = () => {
    dispatch(setUser({ id: "user123", token: "abcd1234" }));
  };

  const handleLogout = () => {
    dispatch(resetUser());
  };

  return (
    <div>
      <h2>로그인 상태: {user.id ? `ID: ${user.id}` : "로그아웃됨"}</h2>
      <button onClick={handleLogin}>로그인</button>
      <button onClick={handleLogout}>로그아웃</button>
    </div>
  );
};

export default Login;

 

* Redux store 는 새로고침 시 저장된 데이터들이 사라진다. 

새로고침 시에도 사라지지 않고 저장되길 원한다면 로컬 스토리지를 사용하거나 Redux Persist 사용하여 구현하면 된다.