우선 Custom hook 을 살펴보자 Custom hook 은 컴포넌트를 따로 분리하지 않고도 기본 제공 Hook (useState, useEffect 등
) 조합하여 재사용 가능한 로직을 만드는 방법입니다. Custom Hook의 함수 이름은 반드시 use 로 시작해야 하고 js 함수처럼 동작하면서 내부에서 React의 상태나 라이프사이클 관련 기능들을 사용할수 있어야 합니다.
하지만....
useKakaoLogin 이라고 지었던 내 훅은 사실 handleKaKaoLogin 에 더욱 적합한 로직이였다,
"use client";
import { createBrowserSupabaseClient } from "utils/supabase/client";
export const useKaKaoLogin = async () => {
const supabase = await createBrowserSupabaseClient();
const { data, error } = await supabase.auth.signInWithOAuth({
provider: "kakao",
options: {
redirectTo: process.env.NEXT_PUBLIC_VERCEL_URL
? `https://daily-connection.vercel.app/auth/callback`
: "http://localhost:3000/auth/callback",
},
});
if (error) {
alert(error.message);
}
};
기존 로직을 보면 그냥 로그인을 시도하는 함수일 뿐, hook 처럼 제대로 활용해 보이진 않았습니다 그래서 이번에 리팩토링을 하였습니다 🧐
"use client";
import { useCallback, useState } from "react";
import { createBrowserSupabaseClient } from "utils/supabase/client";
export const useKaKaoLogin = () => {
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
const supabase = createBrowserSupabaseClient();
const loginAuthKakao = useCallback(async () => {
setError(null);
setIsLoading(true);
try {
const { error } = await supabase.auth.signInWithOAuth({
provider: "kakao",
options: {
redirectTo: process.env.NEXT_PUBLIC_VERCEL_URL
? `https://daily-connection.vercel.app/auth/callback`
: "http://localhost:3000/auth/callback",
},
});
if (error) {
throw error;
}
} catch (error) {
setError(error?.message);
} finally {
setIsLoading(false);
}
}, [supabase]);
if (error) {
alert(error.message);
}
return { loginAuthKakao, isLoading, error };
};
원래 이런 형태로 만들어야 햇었는데 뭔가 대충한 느낌이 가득해서 반성중입니다 😭
이제는 LoginButton Component
를 만들어서
"use client";
import Image from "next/image";
import KaKaoLoginButton from "../../public/kakao_login_large_wide.png";
import { useKaKaoLogin } from "hooks/useKakaoLogin";
import { ClipLoader } from "react-spinners";
type LoginButtonProp = {
isCustom?: boolean;
};
export default function LoginButton({ isCustom = false }: LoginButtonProp) {
const { loginAuthKakao, isLoading, error } = useKaKaoLogin();
if (isCustom) {
return (
<button
onClick={loginAuthKakao}
disabled={isLoading}
className="bg-black text-white rounded flex items-center justify-center text-[12px] w-[48px] h-[32px] px-2 "
>
{isLoading ? <ClipLoader size={16} color="white" /> : "로그인"}
{error && <p className="text-[14px] text-red-500">{error}</p>}
</button>
);
}
return // 생략
이렇게 하면 확실히 컴포넌트에서 로직과 UI를 분리하여 더 깔끔하고 관리하기 쉽게 만들어 줬을 뿐만 아니라, 복잡한 로직을 숨기고, 컴포넌트에서는 필요한 데이터와 함수만 사용할 수 있게 해 한 번에 여러 컴포넌트에서 쉽게 재사용할 수 있다는 장점이 있는거 같습니다.
완벽한 사람이 없듯이 완벽한 코드는 없지만 조금은 괜찮은 코드를 작성하기 위해 조금 더 공부하겠습니다! 🙏