느려도 한걸음씩

Pokemon Card Flip - 5. reduxToolkit을 이용한 앱 상태 관리 본문

토이프로젝트/Pokemon Card Flip

Pokemon Card Flip - 5. reduxToolkit을 이용한 앱 상태 관리

hoj0806 2025. 3. 15. 18:13

현재 내가 만드는 앱은 메인화면, 난이도 선택, 게임 등 여러 상태들에 따라 사용자에게 다른 컨텐츠를 보여줘야했다

 

그래서 상태에 따라 조건부 렌더링을 통해 다른 컴포넌트를 렌더링 하는 방식과 그 상태값을 reduxToolkit으로 저장 및 관리하기로 했다

 

 

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

interface modeType {
  mode: string;
}

const initialState: modeType = { mode: "" };

const modeSlice = createSlice({
  name: "modeSlice",
  initialState,
  reducers: {
    setMode: (state, action: PayloadAction<string>) => {
      state.mode = action.payload;
    },
  },
});

export default modeSlice;
export const { setMode } = modeSlice.actions;
export const selectMode = (state: RootState) => state.modeSlice.mode;

 

  • modeSlice는 앱의 현재 모드(main, game 등) 를 관리한다.
  • setMode 액션을 통해 원하는 모드로 변경할 수 있다.
  • selectMode는 useSelector에서 사용하기 위해 mode 값을 반환하는 selector 함수이다.

 

 

 

 

import { configureStore } from "@reduxjs/toolkit";
import modeSlice from "./slice/modeSlice";

export const store = configureStore({
  reducer: {
    modeSlice: modeSlice.reducer,
  },
});

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

 

  • configureStore를 사용해 Redux Store를 생성하고, modeSlice를 등록했다.
  • RootState는 store.getState()의 반환 타입을 자동으로 추론하여 Redux 상태의 타입을 고정하는 역할을 한다.
  • AppDispatch는 Redux의 dispatch 타입을 가져와 타입 안전한 dispatch 사용을 지원한다.

 

 

 

import { AppDispatch } from "../store";
import { useDispatch } from "react-redux";

export const useAppDispatch: () => AppDispatch = useDispatch;

 

  • useAppDispatch는 useDispatch를 기반으로 AppDispatch 타입을 적용한 커스텀 훅이다.
  • 이를 사용하면 dispatch(setMode("main"))처럼 타입 안전한 dispatch 호출이 가능하다.

 

 

 

import { useSelector } from "react-redux";
import { RootState } from "../store";
import { TypedUseSelectorHook } from "react-redux";

export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;

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

 

  • useAppSelector는 useSelector에 RootState 타입을 적용한 커스텀 훅이다.
  • 이를 통해 Redux 상태를 가져올 때 타입 추론이 자동으로 적용되어 state.modeSlice.mode가 string으로 인식된다.

 

 

 

import "./App.css";
import { useAppDispatch } from "./hooks/useAppDispatch";
import { useAppSelector } from "./hooks/useAppSelector";
import { selectMode, setMode } from "./slice/modeSlice";

const App = () => {
  const currentMode = useAppSelector(selectMode);
  const dispatch = useAppDispatch();

  const mainButtonHandler = () => {
    dispatch(setMode("main"));
  };

  const gameButtonHandler = () => {
    dispatch(setMode("game"));
  };

  return (
    <div className='flex flex-col items-center gap-4 mt-10'>
      {currentMode === "main" && (
        <h1 className='text-2xl font-bold'>메인 화면 입니다</h1>
      )}
      {currentMode === "game" && (
        <h1 className='text-2xl font-bold'>게임 화면 입니다</h1>
      )}

      <div className='flex gap-4'>
        <button
          onClick={mainButtonHandler}
          className='px-4 py-2 bg-blue-500 text-white font-semibold rounded-lg hover:bg-blue-700 transition-all'
        >
          메인으로
        </button>

        <button
          onClick={gameButtonHandler}
          className='px-4 py-2 bg-green-500 text-white font-semibold rounded-lg hover:bg-green-700 transition-all'
        >
          게임하기
        </button>
      </div>
    </div>
  );
};

export default App;

 

 

  • useAppSelector(selectMode)를 사용해 modeSlice.mode 상태를 가져온다.
  • useAppDispatch()를 사용해 setMode("main"), setMode("game") 액션을 디스패치하여 상태를 변경한다.
  • 현재 상태에 따라 다른 화면(메인 화면, 게임 화면)을 조건부 렌더링한다.

 

 

적용된 모습