상태관리 도입해서 전역 변수 관리하기(Redux, React Query)
상태 관리를 도입했다. 프로젝트가 계속 진행되면서 전역적으로 사용해야하는 변수들이 생겨났기 때문이다.
예를 들어 로그인 유무를 알 수 있는 isLogin과 같은 변수는 전역적으로 관리하여 로그인 상태일때는 home이나 다른 페이지를 보여주고 로그인을 해야하는 상태에는 Auth 화면을 보여준다.
상태관리는 redux, react- query 2개를 사용했다. 처음에는 redux를 사용했다가 다양한 기능과 백엔드 시스템과의 연동을 쉽게 구현할 수 있는 react-query를 도입하여 사용해보았다.
이번 글에서는 리덕스 기본 흐름과 도입 과정, 실제 코드에 대해서 작성해볼려고 한다.
리덕스 도입
리덕스 기본 흐름
특정한 액션이 유발(dispatch)되면 / 이에 대응하는 reducer가 변화한 상태 state값을 반환하고 / 이것은 전역 스토어에 반영하게 된다.
리덕스 사용 과정
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>
);
}