Redux
는 리액트에서 가장 많이 사용되고 있는 상태관리 라이브러리이다.
Redux
를 사용하게 되면 상태를 글로벌하게 관리할 수 있고 효율적으로 관리할 수가 있다.
이를 이용해서 간단하게 로그인을 구현하는 포스트를 작성하고자 한다.
위의 이미지와 같이 사용자가 로그인을 하게 되면 Header부분에 로그인
이 아닌 마이페이지
버튼이 보이게 된다.
이를 위해선 사용자가 로그인을 했는지 하지 않았는지 판단할 수 있는 데이터가 필요하고, 해당 데이터는 Redux
를 통해 관리하게 된다.
Redex
를 준비하기에 앞서 데이터를 영구적으로 보관하기 위한 모듈이 필요하다.
사용자가 새로고침을 하게되면 스토어에 있는 정보는 사라지기 때문에 이를 방지하기 위해 redux-persist
이용한다.
redux-persist
는 데이터를 local storage
나 session storage
에 저장하는 모듈이다.
session storage
는 페이지를 새로고침해도 데이터가 남아 있고,local storage
는 브라우저를 껐다 켜도 데이터가 남아 있다.
다음과 같이 local storage
에 데이터를 저장할 수 있다.
import storage from 'redux-persist/lib/storage';
이제 상태를 관리할 로직을 작성해보자.
먼저, 리액트 프로젝트 내에 module
이라는 디렉토리를 생성해 준 뒤, index.js
와 user.js
를 생성해보자.
user.js
에는 로그인에 관련된 액션 타입과 리듀서를 작성할 예정이고,index.js
에는store
를 생성하는 역할을 할 예정이다.
로그인에 관련한 리듀서는 다음과 같이 코드를 작성할 수가 있다.
// src/module/user.js
// Redux에서 관리할 초기 상태
const initialState = {
user: {},
}
// 액션 타입
const SET_USER = 'SET_USER';
// 액션 함수
export const getToken = data => ({ type: SET_USER, data });
// 리듀서
export default function user(state = initialState, action) {
switch(action.type) {
case SET_USER:
return {
...state,
user: action.data
}
default:
return state;
}
}
리듀서와 redux-persist
와 combine
해서 store
를 생성하는 코드는 다음과 같이 작성할 수 있다.
import { combineReducers } from 'redux'
import { persistReducer } from 'redux-persist';
import user from './user'
import storage from 'redux-persist/lib/storage';
const persistConfig = {
key: "root",
storage, // local storage에 저장.
whitelist: ["user"],
}
const rootReducer = combineReducers({ user });
export default persistReducer(persistConfig, rootReducer)
위에서 리듀서와 스토어의 코드를 작성했다면 리액트 프로젝트에 적용시켜주면 된다.
// index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import rootReducer from './modules';
import { persistStore } from 'redux-persist';
import { PersistGate } from 'redux-persist/integration/react';
const root = ReactDOM.createRoot(document.getElementById('root'));
const store = createStore(rootReducer);
const persistor = persistStore(store);
root.render(
<React.StrictMode>
<Provider store={store}>
<PersistGate persistor={persistor}>
<App />
</PersistGate>
</Provider>
</React.StrictMode>
);
export default store;
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
이제 사용자가 로그인을 했을 때에 토큰 값을 Redux
를 통해 관리할 수 있게 됐다.
이를 Redux
에서 관리하는 상태값을 조회하여 확인하거나 API 요청 시에 확인하여 요청해야 한다.
맨 위의 이미지를 보면 사용자가 로그인을 하면 로그인
UI가 아닌 마이페이지
UI가 나오는 것을 확인할 수가 있다.
Header 부분에서 사용자의 토큰 값을 글로벌로 확인하여 검증하는 것이다.
이처럼 Redux
를 통해 관리되는 상태값을 조회할 수 있는 함수가 useSelector
이다.
다음과 같이 useSelector
를 사용할 수가 있다.
import { useSelector } from 'react-redux';
export const Nav => () => {
const { user } = useSelector(state => state.user);
return(
<>
...
<div>
<input type="button" value={user.token ? "마이페이지" : "로그인"} />
</div>
...
</>
)
}
Axios interceptors는 request
와 response
가 then
과 catch
에 의해 처리되기 전에 전역적으로 가로채게 허용해 주는 Axios 라이브러리이다.
예를 들어, 사용자가 어떤 액션을 취하는 API를 호출할 때 매번 로그인을 확인하는 로직을 작성한다면 코드의 길이가 엄청나게 길어짐과 동시에 유시보수에도 어려움을 줄 것이다.
필자는 이것을 Axios interceptors를 이용해 HTTP Authorizaition 요청 헤더에 JWT-Token
을 보내서 해당 API에 요청하기 전에 검증시키는 로직을 작성할 것이다.
다음과 같이 코드를 작성할 수가 있다.
import axios from "axios";
import store from '../index'
const baseURL = '...'
const baseInstance = axios.create({
baseURL,
header: {
Authorization: ''
},
})
baseInstance.interceptors.request.use((config) => {
const token = store.getState().user.user.token;
try {
if (token) {
config.headers['Authorization'] = `JWT ${token}`
}
return config;
} catch (error) {
console.error(error)
}
})
baseInstance.interceptors.response.use((data) => data)
export const apiRequest = {
...
}