[Next.js 14] 생각 없이 짠 코드 리팩토링 - Custom hook 편

houndhollis·2024년 9월 6일
1
post-custom-banner

😵 왜 이렇게? 내 프로젝트..

우선 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를 분리하여 더 깔끔하고 관리하기 쉽게 만들어 줬을 뿐만 아니라, 복잡한 로직을 숨기고, 컴포넌트에서는 필요한 데이터와 함수만 사용할 수 있게 해 한 번에 여러 컴포넌트에서 쉽게 재사용할 수 있다는 장점이 있는거 같습니다.

완벽한 사람이 없듯이 완벽한 코드는 없지만 조금은 괜찮은 코드를 작성하기 위해 조금 더 공부하겠습니다! 🙏

profile
한 줄 소개
post-custom-banner

0개의 댓글