\(@^0^@)/

[알리미 6] 프로젝트 업그레이드 2 : Router v6, 401 Error, RefreshToken 본문

프로젝트&웨비나 회고/개인 프로젝트

[알리미 6] 프로젝트 업그레이드 2 : Router v6, 401 Error, RefreshToken

minjuuu 2022. 9. 16. 20:37
728x90

Router v6 + 권한 처리

1. 로그인 후 권한에 맞지 않는 route에 접근하게 될 경우 : unauthorized 페이지를 보여줌
2. 로그인 전 다른 route에 접근하게 될 경우 : login 페이지를 보여줌

위의 조건에 부합하는 로직을 작성하였음.
전역 상태를 관리하고 있는 recoil의 useRecoilState을 불러와 auth로 선언 후, 

const RequireAuth = ({ allowedRoles }) => {
	const auth = useRecoilState(Auth);
	const location = useLocation();
};


App컴포넌트에서 User, Manager, Admin에 대해 상수화 선언한다.
ROLES에 맞게 권한을 주기 위해서, RequireAuth 컴포넌트의 props에 각각의 역할을 넘겨준다.

const ROLES = {
    User: 2001,
    Manager: 1984,
    Admin: 5150,
};


function App() {
    return (
        <Routes>
            <Route element={<RequireAuth allowedRoles={[ROLES.User]} />}>
                <Route path='/' element={<Home />} />
            </Route>
            <Route element={<RequireAuth allowedRoles={[ROLES.Manager]} />}>
                <Route path='manager' element={<Manager />} />
            </Route>
            <Route element={<RequireAuth allowedRoles={[ROLES.Admin]} />}>
                <Route path='admin' element={<Admin />} />
            </Route>
        </Routes>
    );
}

Recoil의 useRecoilState속 Auth를 가져와, auth에 대입해준다.
auth를 console에 찍어보면, 배열 형태로 찍혀서 0번째 index만을 가져오는 로직을 구현하였다.
(아무리 생각해도 배열 형태로 찍히는 건 이상한 것 같은데.. docs로는 잘 모르겠어서 조만간 각 잡고 찾아볼 예정)

auth의 0번째 index에 내가 원하는 role이 있다면, 제대로 동작하는 것으로 true를 반환하여 Outlet을 보여준다.

그렇지 않고, false이지만 auth안에 user가 있는 (즉, 권한은 없지만 로그인은 되어있는) 상태라면, unauthorized 컴포넌트를 보여준다.

그렇지 않고, 로그인도 되어있지 않은 상태라면 login 컴포넌트를 보여준다.

const RequireAuth = ({ allowedRoles }) => {
	const auth = useRecoilState(Auth);
	const location = useLocation();

	return auth[0]?.roles?.find((role) => allowedRoles?.includes(role))
    ? (<Outlet />) 
    : auth[0]?.user 
      ? (<Navigate to='/unauthorized' state={{ from: location }} replace />) 
      : (<Navigate to='/login' state={{ from: location }} replace />);
};

Axios Error 401 (Unauthorized)

admin 페이지에만 회원가입을 한 모든 사용자의 정보를 보여주는 것을 구현하기 위해 Axios로 users api를 불러오는 와중에 401 에러가 발생하였다.

401 (Unauthorized) 에러는 말 그대로 내가 해당 서버를 불러올 권한이 없다는 의미.
role이 admin인 아이디로 로그인해서, admin만 들어올 수 있는 페이지에 사용자 리스트를 불러오는 컴포넌트를 만들고,
해당 api를 불러오는데 왜 권한이 없다는 에러가 날까? 의문이었다.

네트워크 탭에도 Unauthorized라는 말뿐, 별다른 힌트를 얻을 수가 없었다ㅠ (저 단어가 제일 중요한 건지도 모르고..!)
server단에서 코드를 살펴보고 썬더 클라이언트로 테스트를 해보던 중, 무엇이 문제인지 알 수 있었다.

해당 api를 불러올 때, token을 같이 넘겨주지 않아서 발생하는 에러였던 것...
server단에다가 jwt가 있어야만 users list를 불러올 수 있다는 로직을 작성하고 잠시 까먹었었다ㅠ

const USER_URL = "/users";

const getUsersFetch = async (token) => {
	const res = await axios.get(USER_URL, {
		headers: { Authorization: `Bearer ${token}` },
		withCredentials: true,
	});
	return res.data;
};

이렇게 token을 넣어주니, users list가 잘 받아와 지는 것을 볼 수 있다.

error해결은 늘 알고 나면 허무하다.. 내가 왜 이 부분을 놓쳤지? 못 봤지? 하는 것들이 대부분이기 때문에...
하지만 지금 내 위치가 무수한 error를 발생시키면서 본인이 경험했던 error들을 경험으로, 같은 error 발생을 방지하거나 조금 더 빠르게 해결할 수 있도록 성장하는 것이 아닌가 싶다.


useRefreshToken Hook

refreshToken을 새로 받아올 수 있는 api call을 생성.
기존에 존재하는 auth 데이터 안의 accessToken에, 새롭게 받아온 accessToken을 덮어 씌운  return 하는 custom hook.

여전히 useRecoilState를 사용할 때 useState형식으로 표현해야 하는데, setAuth만 필요하고 auth는 필요 없는 경우에 어떻게 해야 할지 모르겠는 상태.. 우선 다른 나머지 기능들을 구현 후 다시 생각해 볼 예정.

const useRefreshToken = () => {
	const [auth, setAuth] = useRecoilState(useAuth);
	console.log(auth);

	const refresh = async () => {
		const res = await axios.get("/refresh", {
			withCredentials: true,
		});
		setAuth((prev) => {
			console.log(JSON.stringify(prev.accessToken));
			console.log(res.data.accessToken);
			return { ...prev, accessToken: res.data.accessToken };
		});
		return res.data.accessToken;
	};

	return refresh;
};

빨간 박스의 토큰이 기존에 존재했던 토큰이고, 초록 박스의 토큰이 이번 hook으로 인해 새롭게 생성된 토큰이다.
제대로 잘 작동하는 것을 볼 수 있음.


 

728x90