프로젝트를 진행하다보면 페이지 접근을 제한해야 할 때가 있다.
로그인을 한 유저만 페이지에 접근할 수 있게 하거나 특정 역할을 가진 유저만 페이지에 접근할 수 있도록 하는 것이 필요하다.
이번 포스팅에서는 로그인을 한 유저만 메인 페이지에 접속할 수 있고 로그인을 하지 않은 사람이 메인 페이지에 접속하려고 하면 로그인 페이지로 리다이렉트하는 기능을 구현해보려고 한다.
첫 번째 시도 (useEffect)
우선 메인 페이지에 접근하려는 사람이 로그인을 했는지 확인하기 위한 api를 생성했다.
api/v1/users/myself로 쿠키와 함께 요청을 보냈을 때 상태 코드 200번과 함께 내 정보를 받으면 로그인을 한 사람이고 상태 코드 401번과 함께 권한이 없다는 응답을 받으면 로그인을 하지 않은 상태이다.
이 api 응답에 따라서 상태 코드 401번이 응답으로 왔을 때 로그인 페이지로 강제 이동시키면 된다.
이를 위한 useOnlyAuthenticated라는 커스텀 훅을 생성했다.
1. useEffect
마운트 될 때 서버에 요청을 보내기 위함
2. useNavigate
페이지 이동 훅
3. axios
비동기 요청
import { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import axios from 'axios';
/**
* 인증되지 않은 유저라면 리다이렉트
* @param {string} redirectUrl 만약 유저 정보를 가져올 수 없을 때 이동할 url
*/
export const useOnlyAuthenticated = (redirectUrl) => {
const navigate = useNavigate();
useEffect(() => {
axios.get(`${process.env.REACT_APP_SERVER_URL}/api/v1/users/myself`)
.catch(error => {
if (error.response && error.response.status === 401) {
navigate(redirectUrl);
}
});
}, []);
};
그리고 위 커스텀 훅을 인증되지 않은 사용자의 접근을 제한할 컴포넌트 내부에서 호출하면 된다.
...
const LogInAndJoin = () => {
useOnlyNotAuthenticated('/rooms');
return (
<div>
...
</div>
)
}
...
처음에는 이 방법으로 페이지 접근 제한을 구현했다.
페이지 접근 제한이 잘 작동했지만 UI에서 약간의 문제가 발생했다.
useEffect의 경우 렌더링이 완료된 다음에 내부 콜백 함수를 실행하기 때문에 인증을 확인하기 위해 api 요청을 보내는 과정은 렌더링 이후에 발생하는 것이었다.
그래서 인증되지 않은 사용자가 접근 제한 페이지에 접근할 때 접근 제한 페이지의 일부가 렌더링 된 다음에 페이지 강제 이동이 발생했다.
중요 정보가 노출되지는 않았지만 접근 제한 페이지의 UI 일부가 노출되는 것과 깜빡이는 것이 UX 상 좋지는 않을 것이다.
즉 렌더링 되기 전에 인증 여부를 판단하고 페이지를 강제 이동하는 과정이 필요했다.
이를 위해 protected route 방식을 적용했다.
두 번째 시도 (protected route)
깜빡임 현상을 해결하기 위해 protected route 방식을 사용했다.
경로에 따라 컴포넌트를 렌더링 하기 전에 요청을 받아서 페이지 강제 이동을 구현하면 된다.
접근 제한 페이지를 ProtectedRoute로 감싸고 인증에 성공했을 때만 접근 제한 페이지를 렌더링 하도록 ProtectedRoute를 구현해보자.
<ProtectedRoute.jsx>
import { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import axios from "axios";
export const ProtectedRoute = ({ children, redirectUrl }) => {
// api 응답 성공 여부
const [isSuccess, setIsSuccess] = useState(false);
const navigate = useNavigate();
useEffect(() => {
axios.get(`${process.env.REACT_APP_SERVER_URL}/api/v1/users/myself`)
.then(()=>{
// 요청이 성공적으로 오면
setIsSuccess(true);
})
.catch((error)=>{
navigate(redirectUrl);
})
}, []);
// 응답이 완료된 후 자식 컴포넌트를 렌더링한다.
if (isSuccess){
return children;
}
};
이제 접근 제한이 필요한 페이지에 ProtectedRoute를 감싸보자.
<기존 router>
...
const Router = () => {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<LogInAndJoin />}></Route>
<Route path="/rooms" element={<Main />}></Route>
<Route path="/rooms/:roomId" element={<Room />}></Route>
</Routes>
</BrowserRouter>
);
};
export default Router;
<ProtectedRoute 적용 Router>
...
const Router = () => {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<LogInAndJoin />}></Route>
<Route path="/rooms" element={
<ProtectedRoute redirectUrl = '/'>
<Main />
</ProtectedRoute>
}></Route>
<Route path="/rooms/:roomId" element={
<ProtectedRoute redirectUrl = '/'>
<Room />
</ProtectedRoute>
}></Route>
</Routes>
</BrowserRouter>
);
};
export default Router;
'공부 > 프론트' 카테고리의 다른 글
[React] 리액트에서 public에 이미지 불러오기 (2) | 2024.06.21 |
---|---|
[React] redux 사용법 (0) | 2024.04.26 |
[React] useReducer와 Context로 상태값 관리 (0) | 2024.04.22 |
[html/css/js] 테이블 태그 안에 답글 기능 구현 (0) | 2024.04.06 |
[html/css] 테이블 꾸미기 (모서리 둥글게) (0) | 2024.04.03 |
댓글