JWT 인증
React에서 JWT 인증 방법은 아래 링크에서 확인할 수 있다.
https://5xjin.github.io/blog/install_hugo/
해당 링크에 이어서 인증 라우터를 구현할 예정이다.
JWT 인증 라우터 구현
JWT 인증 라우터를 구현함으로써 인증된 사용자는 로그인 페이지로 이동하지 못하게, 인증되지 못한 사용자는 인증 페이지로 이동하지 못하게 할 예정이다.
인증되었을 때와 인증되지 못하였을 때 총 2가지 조건이 있으므로 만들 라우터는 아래와 같다.
- PublicRouter
- PrivateRouter
JWT 체크 컴포넌트
JWT 인증 여부, 즉 JWT 발급 여부
를 확인하는 컴포넌트이다.
이전 게시글에서 JWT를 웹 브라우저의 쿠키
와 redux
에 저장해 놓았으므로 이를 이용하여 검증하고, 해당 검증 값을 반환할 예정이다.
나는 해당 컴포넌트를 만들기 위해 CheckToken.js
파일을 만들었다.
CheckToken Component
./auth/CheckToken.js
export function CheckToken(key) {
const [isAuth, setIsAuth] = useState('Loaded');
const { authenticated, expireTime } = useSelector(state => state.token);
const refreshToken = getCookieToken();
const dispatch = useDispatch();
useEffect(()=> {
const checkAuthToken = async () => {
if (refreshToken===undefined) {
dispatch(DELETE_TOKEN());
setIsAuth('Failed');
} else {
if (authenticated && new Date().getTime() < expireTime){
setIsAuth('Success');
} else {
const response = await requestToken(refreshToken);
if (response.status) {
const token = response.json.access_token;
dispatch(SET_TOKEN(token));
setIsAuth('Success');
} else {
dispatch(DELETE_TOKEN());
removeCookieToken();
setIsAuth('Failed');
}
}
}
};
checkAuthToken();
}, [refreshToken, dispatch, key]);
return {
isAuth
};
}
웹 브라우저의 쿠키
에 토큰이 저장되어 있는지 확인한다. 만일 존재하지 않는다면Failed
를 반환한다.- 쿠키에 refresh 토큰이 저장되어 있다면 redux 내에도 저장되었는지 확인한다.
- 만일 리덕스 내에 저장되어 있지 않거나, 저장되었더라도 토큰이 만료되었다면 새로운 access 토큰을 요청한다.
- 새로운 access 토큰을 정상적으로 발급 받았거나 만료되지 않은 access 토큰이 리덕스에 존재한다면
Success
를 반환한다. - access 토큰에 요청에 대한 응답이 올바르지 않다면
Failed
를 반환한다.
PrivateRouter
위에서 기술한 CheckToken
컴포넌트의 응답값을 이용하여 JWT 인증 라우터를 아래와 같이 구현한다.
./routes/PrivateRoute.js
export default function PrivateRoute() {
const location = useLocation();
const { isAuth } = CheckToken(location.key);
if (isAuth === 'Failed') {
return (
<Navigate to="/user/login" />
)
} else if (isAuth==='Loading') {
return <LoadingModal />
}
return <Outlet />
}
App.js
function App() {
return (
<Router>
<Routes>
<Route element={<PrivateRoute />}>
<Route path="/" element={<Home />} />
<Route path="/logout" element={<Logout />} />
</Route>
<Route path="/user/login" element={ <Login /> } />
</Routes>
</Router>
);
}
- 중첩 라우트를 활용하여 인증된 사용자만 접근 가능한 페이지는
PrivateRoute
로 한 번 감쌌다. - 해당 path에 대해 접근하기 전에
PrivateRoute
를 통해 인증 여부를 한 번 검증한다. - 검증 여부에 따라 실패 하면 로그인 페이지로, 성공하면
Oulet
을 통해 path와 일치하는 페이지로 이동한다.
PublicRouter
PrivateRoute
결과 값을 반대로 이용하여 똑같이 작성하면 되지만 Route로 감싸는 방법이 아닌 살짝 다른 방법을 사용해 보기로 했다.
./routes/PublicRoute.js
export default function PublicRoute({ children }) {
const location = useLocation();
const { isAuth } = CheckToken(location.key);
if (isAuth === 'Success') {
return (
<Navigate to="/" />
)
} else if (isAuth==='Loading') {
return <LoadingModal />
}
return children
}
App.js
function App() {
return (
<Router>
<Routes>
<Route element={<PrivateRoute />}>
<Route path="/" element={<Home />} />
<Route path="/logout" element={<Logout />} />
</Route>
<Route path="/user/login"
element={
<PublicRoute>
<Login />
</PublicRoute>} />
</Routes>
</Router>
);
}
PrivateRoute
와 차이점은 Route로 감싼 게 아닌 children 요소로 직접 지정했다는 점이다.- 자식 요소로 지정하여 넣어주었기 때문에 props로 자식 요소가 될 컴포넌트를 받아야 한다.
- 이후 인증 여부에 따라 children 컴포넌트 반환을 통해 해당 컴포넌트를 렌더링 할지, 홈으로 이동할지 여부가 결정된다.