React-query, SWR을 사용해야하는 이유. 엄격 모드
카카오 로그인을 구현하기 위해서 코드를 작성하고 있는데, 예상치 못한 부분에서 오류가 났습니다.
카카오톡 로그인은 위와 같은 과정으로 구현할 수 있습니다.
카카오 로그인에 대한 자세한 내용은 아래 공식문서를 참조하세요!
Kakao Developers
카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.
developers.kakao.com
오류가 왜 났는지 결론부터 말씀드리자면, 인가코드로 토큰을 요청하는데 Strict-Mode 때문에, fetch 요청이 두 번 이루어 지게 되면서 오류가 난 것이었습니다.
const Auth = () => {
const params = useSearchParams();
const code = params.get("code");
const router = useRouter();
useEffect(()=>{
const asyncRequest = async () => {
const res = await (
await fetch("/api/kakaoToken", {
method: "POST",
body: JSON.stringify({ code }),
})
).json();
if (res.ok) {
router.push("/message");
}
if (res && !res.ok) {
router.push("/");
toast.error(res.error);
}
};
asyncRequest();
},[])
return (
<div className="py-5 px-10 flex flex-col gap-2 items-center backdrop-blur-lg rounded-lg">
<p className="text-lg font-semibold ">로그인 중입니다...</p>
<RiLoader2Line size={30} className="animate-spin" />
</div>
);
};
위 코드는 제가 인가코드를 이용하여 로그인을 요청하는 부분을 짠 코드입니다.
보시면 fetch API를 이용하여 서버에 인가코드를 보냈고, 서버에서 외부 서버인 카카오 서버로 토큰을 요청하게 되어 로그인을 완료하게 합니다.
하지만 위 코드로는 로그인이 정상적으로 이루어지지 않게 되었습니다.
❓ 로그인에 실패한 이유?
서론에서 말씀 드렸다시피, 로그인에 실패한 이유는 바로 React 환경에서는 기본적으로 Strict Mode가 적용되어 있기 때문입니다. Strict 모드 즉 엄격 모드는 두 번 실행하는(Double-Invoke) 방식으로 부작용을 방지합니다.
따라서 렌더링 시 함수가 두 번 실행되는 Double-Invoke 때문에, 인가 코드를 보내는 요청이 두 번 발생하게 되면서 오류가 발생했던 것입니다.
카카오 서버의 인가 코드는 1회만 사용할 수 있는데, 두 번 요청하게 되면서 서버에서 예상치 못한 Error 혹은 예상치 못한 값을 응답받게 됩니다.
React-query, SWR 라이브러리를 사용하여 서버 데이터를 요청하게 되면, 엄격 모드에서도 한 번 작동하게 됩니다.
따라서 서버데이터를, 특히나 외부 서버 데이터를 다루는 경우에는 필수적으로 React-query, SWR과 같은 서버 데이터 관리 툴을 사용하는 것이 필수적이겠네요!
❓Strict Mode를 해제하면 되지 않을까?
Strict 모드를 사용하지 않는다는 것은 굉장히 위험합니다.
Strict 즉 엄격 모드를 사용하지 않는다면, 실수의 가능성이 매우 높아지고, 사용하지 않을 경우 의도하지 않은 결과를 낳을 수 있습니다. 특히나 유연성이 높은 JS의 경우에는 더더욱 그렇겠지요.
아래는 리액트의 Strict 모드 공식 문서입니다.
Strict 모드 – React
A JavaScript library for building user interfaces
ko.legacy.reactjs.org
따라서 Strict Mode를 사용하지 않는 것 보다, React-query, SWR과 같은 라이브러리를 사용하는 것이 좋겠네요!
다음은 수정 코드 입니다.
const Auth = () => {
const params = useSearchParams();
const code = params.get("code");
const router = useRouter();
if (!code) {
router.push("/");
toast.error("something wrong");
}
const { data } = useSWR(["kakaoToken"], () => getToken(code!));
useEffect(() => {
if (data && data.ok) {
router.push("/message");
}
if (data && !data.ok) {
router.push("/");
toast.error(data.error);
}
}, [data, router]);
return (
<div className="py-5 px-10 flex flex-col gap-2 items-center backdrop-blur-lg rounded-lg">
<p className="text-lg font-semibold ">로그인 중입니다...</p>
<RiLoader2Line size={30} className="animate-spin" />
</div>
);
};