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 컴포넌트 반환을 통해 해당 컴포넌트를 렌더링 할지, 홈으로 이동할지 여부가 결정된다.