본문 바로가기
sns 프로젝트

상태관리 도입해서 전역 변수 관리하기(Redux, React Query)

by 우주속공간 2024. 1. 21.

상태 관리를 도입했다. 프로젝트가 계속 진행되면서 전역적으로 사용해야하는 변수들이 생겨났기 때문이다. 

예를 들어 로그인 유무를 알 수 있는 isLogin과 같은 변수는 전역적으로 관리하여 로그인 상태일때는 home이나 다른 페이지를 보여주고 로그인을 해야하는 상태에는 Auth 화면을 보여준다. 

 

상태관리는 redux, react- query 2개를 사용했다. 처음에는 redux를 사용했다가 다양한 기능과 백엔드 시스템과의 연동을 쉽게 구현할 수 있는 react-query를 도입하여 사용해보았다. 

 

이번 글에서는 리덕스 기본 흐름과 도입 과정, 실제 코드에 대해서 작성해볼려고 한다. 

 

리덕스 도입 


 

리덕스 기본 흐름  

 

특정한 액션이 유발(dispatch)되면 /  이에 대응하는 reducer가 변화한 상태 state값을 반환하고 / 이것은 전역 스토어에 반영하게 된다.

redux 작동방식  / 출처 : https://www.datoybi.com/20220930TIL/

 

리덕스 사용 과정  

1. 리덕스 설치

npm install @reduxjs/toolkit react-redux

 

2. Store 파일 생성 

 

리덕스를 설치한 후 변수를 관리할 store 파일을 만들어준다. redux toolkit의 `configureStore` 를 사용하여 store를 설정한다. 

redux를 사용하기 위해서는 Index 파일에 provider로 app을 감싸줘야한다. 

 

// store.ts
import { configureStore } from '@reduxjs/toolkit';

export const store = configureStore({
  reducer: {
    // 여기에 리듀서를 추가합니다.
  },
});

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

export default store;

 

3. Provider 설정

 

애플리케이션의 최상위 컴포넌트를 Provider로 감싸고, 이를 통해 store를 React 컴포넌트에 연결한다.

 

// index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { store } from './store';
import App from './App';

ReactDOM.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>,
  document.getElementById('root')
);

 

3. 리듀서 생성 및 연결

 

react toolkit의 createSlice를 사용하여 리듀서를 생성하고 store.ts 파일에서 리듀서를 연결한다. 

 

// slices/exampleSlice.ts
import { createSlice, PayloadAction } from '@reduxjs/toolkit';

interface ExampleState {
  value: number;
}

const initialState: ExampleState = {
  value: 0,
};

export const exampleSlice = createSlice({
  name: 'example',
  initialState,
  reducers: {
    increment: (state) => {
      state.value += 1;
    },
    decrement: (state) => {
      state.value -= 1;
    },
    incrementByAmount: (state, action: PayloadAction<number>) => {
      state.value += action.payload;
    },
  },
});

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

 

// store.ts
import { configureStore } from '@reduxjs/toolkit';
import exampleReducer from './slices/exampleSlice';

export const store = configureStore({
  reducer: {
    example: exampleReducer,
  },
});

// 이하 동일...

 

 

 

실제 사용 코드  

로그인 유무를 알 수 있는 isLogin 변수를 전역 변수로 사용하기 위해서 isLogin 변수를 변경할 수 있는 리듀서를 만들었다. 

 

store.ts

import { combineReducers, configureStore, createSlice } from "@reduxjs/toolkit";
import IsLoginSlice from "./createSlice/IsLoginSlice";


export const store = configureStore({
  reducer: {
    changeIsLogin: IsLoginSlice,
  },
});
export type RootState = ReturnType<typeof store.getState>;


export default store;

 

isLoginSlice.tsx

import type { PayloadAction } from "@reduxjs/toolkit";
import { createSlice } from "@reduxjs/toolkit";
interface isLoginState {
  isLogin: boolean;
}

const initialState = { isLogin: false } as isLoginState;
export const isLoginSlice = createSlice({
  name: "setIsLogin",
  initialState,
  reducers: {
    changeState(state, action: PayloadAction<boolean>) {
      state.isLogin = action.payload;
    },
  },
});

export const { changeState } = isLoginSlice.actions;
export default isLoginSlice.reducer;

 

login.tsx

    const onLogin = (data: any) => {
    customAxios
      .post("/login", {
        email: data.email,
        password: data.password,
      })
      .then(async (response) => {
        const { accessToken } = response.data;
        
        // API 요청하는 콜마다 헤더에 accessToken 담아 보내도록 설정
        customAxios.defaults.headers.common[
          "Authorization"
        ] = `Bearer ${accessToken}`;

        alert("로그인 성공");
        
        //로그인 성공시 isLogin변수 true로 변경
        dispatch(changeState(true));
        socket.emit("login", { email: data.email, socketID: socket.id });
      })

      .catch((error) => {
        // ... 에러 처리
      });
  };

 

 

마지막으로 isLogin변수에 따라서 라우팅 변경

 

router.tsx

function AppRouter() {
  const isLogin = useSelector( 
    (state: RootState) => state.changeIsLogin.isLogin
  );
  return (
    <Routes>
      {isLogin ? (
        <>
          <Route path="/" element={<Tweets />} />
          <Route path="/explore" element={<Explore />} />
          <Route path="tag/:tagId" element={<Tag />} />
          <Route path="/profile" element={<Profile />} />
          <Route path="tag" element={<Tag />} />
          <Route path="message" element={<Message />} />
          <Route path="bookmark" element={<Bookmark />} />
        </>
      ) : (
        <>
          <Route path="/" element={<Auth />} />
          <Route path="/explore" element={<Navigate to={"/"} />} />
          <Route path="tag/:tagId" element={<Navigate to={"/"} />} />
          <Route path="/profile" element={<Navigate to={"/"} />} />
          <Route path="tag" element={<Navigate to={"/"} />} />
          <Route path="message" element={<Navigate to={"/"} />} />
          <Route path="bookmark" element={<Navigate to={"/"} />} />
        </>
      )}
    </Routes>
  );
}