토큰을 로컬 스토리지에 저장을 해서 변수에 넣은 뒤에 변수를 활용한 삼항 연산자를 했는데, 직접 새로고침을 해야하지만 렌더링이 되면서 인식을 하면서 로그인/로그아웃이 바뀌었다. 내 상상(?)으로는 자동으로 바꿔져야하는데 그렇지 못해서 구글링을 했다.
그 결과 스택오버플로우(stack overflow)에 나와 비슷한 문제가 있는 분이 질문을 올리신 것을 찾을 수 있었다.
const insertedToken = localStorage.getItem('access_token');
return (
<RecoilRoot>
<NavUl>
<NavLi>고객센터</NavLi>
<NavLi>관심상품</NavLi>
<NavLi>마이페이지</NavLi>
{insertedToken ? (
<NavLi onClick={logoutToken}>로그아웃</NavLi>
) : (
<NavLi onClick={() => navigate('/login')}>로그인</NavLi>
)}
</NavUl>
</RecoilRoot>
);
이 글을 통해서 결국 문제를 전역변수에 토큰을 담아서 관리를 해줘서 변하면 자동으로 렌더링이 되게끔 해주게끔 코드를 작성해야함을 알게 되었다. 나는 그저 로컬 스토리지에서 값을 빼와서 그것을 변수에 담아서 그것에 값이 담겼는지 아닌지에 따라서 상태가 변화게끔 하니, state값등의 변화가 생기지 않으니 렌더링이 일어나지 않는 것이다.
결국 이러한 문제점을 해결하기 위해서는 context API를 써줘야 했다. 그래서 이와 관련해서 공부를 하고 있었는데 월요일에 스프린트 미팅 때 우리 팀 멘토이신 도현님이 context API보다는 리코일(Recoil)을 써보라고 조언을 주셔서 다시 리코일에 대해서 공부를 했다.
[Recoil 공식문서]
https://recoiljs.org/ko/docs/introduction/getting-started/
RecoilRoot
recoil 상태를 사용하는 컴포넌트는 부모 트리 어딘가에 나타나는 RecoilRoot 가 필요하다. 루트 컴포넌트가 RecoilRoot를 넣기에 가장 좋은 장소다.
Atom
Atom은 상태(state)의 일부를 나타낸다. Atoms는 어떤 컴포넌트에서나 읽고 쓸 수 있다. atom의 값을 읽는 컴포넌트들은 암묵적으로 atom을 구독한다. 그래서 atom에 어떤 변화가 있으면 그 atom을 구독하는 모든 컴포넌트들이 재 렌더링 되는 결과가 발생할 것이다.
const textState = atom({
key: 'textState', // unique ID (with respect to other atoms/selectors)
default: '', // default value (aka initial value)
});
공식문서를 보면서 리코일을 적용을 해봤는데, 작동을 여전히 작동을 하지가 않았다.
[Routes.js 파일]
const Router = () => {
return (
<BrowserRouter>
<RecoilRoot>
<Nav />
</RecoilRoot>
<Routes>
<Route path="/" element={<Main />} />
<Route path="/products/:id" element={<ProductDetail />} />
<Route path="/login" element={<Login />} />
</Routes>
<Footer />
</BrowserRouter>
);
};
[Routes.js 파일]
const Login = () => {
let params = new URLSearchParams(document.location.search);
let code = params.get('code');
const navigate = useNavigate();
const [tokenData, setTokenData] = useState('');
const getToken = async () => {
try {
await fetch('https://kauth.kakao.com/oauth/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8',
},
body: qs.stringify({
grant_type: 'authorization_code',
client_id: restApiKey,
redirect_uri: redirectUrl,
code: code,
client_secret: clientSecret,
}),
})
.then(res => res.json())
.then(data => setTokenData(data.access_token));
} catch (err) {
console.error(err);
}
};
const sendToken = async () => {
try {
await fetch('http://10.58.6.151:8000/users/login', {
method: 'GET',
headers: { Authorization: tokenData },
})
.then(res => res.json())
.then(data => {
localStorage.setItem('access_token', data.access_token)})
.then(navigate('/'));
} catch (err) {
console.error(err);
}
};
useEffect(() => {
getToken();
if (tokenData) {
sendToken();
}
});
[login.js 파일]
const insertedToken = localStorage.getItem('access_token');
const Nav = () => {
const navigate = useNavigate();
const [token, setToken] = useRecoilState(tokenState);
const logoutToken = () => {
setToken(insertedToken);
localStorage.removeItem('access_token');
};
return (
<RecoilRoot>
<NavUl>
<NavLi>고객센터</NavLi>
<NavLi>관심상품</NavLi>
<NavLi>마이페이지</NavLi>
{token ? (
<NavLi onClick={logoutToken}>로그아웃</NavLi>
) : (
<NavLi onClick={() => navigate('/login')}>로그인</NavLi>
)}
</NavUl>
</RecoilRoot>
);
};
몇시간 고민을 해도 Recoil에 대한 이해의 부족으로 내가 잘못 쓰고 있음을 깨닫는거 말고는 답을 알 수가 없어서 팀원에게 도움을 요청하였고, 개념부터 다시 이해를 하고 로직을 다시 세워보았다.