TIL

2024.04.08 TIL #user_data #์ „์—ญ๊ด€๋ฆฌ #Zustand vs #Tanstack_Query #ํŒ€ํšŒ์˜ #๊ธฐ์ˆ ์ ์˜์‚ฌ๊ฒฐ์ •

inz1234 2024. 5. 7. 20:56

๐Ÿ“Œ Task TODOLIST

- [x] user ๋ฐ์ดํ„ฐ ์ „์—ญ๊ด€๋ฆฌ


โœจ ๊ฐœ๋ฐœ ๋‚ด์šฉ

1. user ๋ฐ์ดํ„ฐ ์ „์—ญ๊ด€๋ฆฌ

 

Why(์ด์œ ) ?

user ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋งŽ๊ธฐ ๋•Œ๋ฌธ์—, ์ „์—ญ์ ์œผ๋กœ ๊ด€๋ฆฌํ•  ํ•„์š”๊ฐ€ ์žˆ์—ˆ๋‹ค.

 

How(๊ณผ์ •) ?

  (1) RootLayout.tsx์—์„œ ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๋“ค์˜ ๊ฐ€์žฅ ์ƒ๋‹จ์— InitUser.tsx ์ปดํฌ๋„ŒํŠธ ์ƒ์„ฑ 

  (2) useEffect ๋‚ด์— userData๋ฅผ ๋ฐ›์•„์˜ค๋Š” ๋น„๋™๊ธฐ๋กœ์ง + ๊ทธ ๋ฐ์ดํ„ฐ์— ๋”ฐ๋ผ ๋กœ๊ทธ์ธ ์ƒํƒœ๊นŒ์ง€

  (3) userData์™€ isLoggedIn ์ƒํƒœ ๋ชจ๋‘ Zustand๋กœ ์ „์—ญ๊ด€๋ฆฌ

 

what(๊ฒฐ๊ณผ) 

const InitUser = () => {
  const { isLoggedIn, setIsLoggedIn, setUser } = userStore((state) => state);

  useEffect(() => {
    // isLoggedIn ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋˜๊ฑฐ๋‚˜, ์ƒˆ๋กœ๊ณ ์นจ ํ–ˆ์„๋•Œ ์Ž„์…˜ ์œ ์ง€ ์ค‘์ธ์ง€ ์—ฌ๋ถ€์— ๋”ฐ๋ผ ์‹คํ–‰๋˜๋Š” ํ•จ์ˆ˜
    // (์ฐธ๊ณ : isLoggedIn ์ƒํƒœ๋Š” ๋กœ๊ทธ์ธ/๋กœ๊ทธ์•„์›ƒ ์‹œ์—๋งŒ ๋ณ€๊ฒฝ๋จ = ๋กœ๊ทธ์ธ/๋กœ๊ทธ์•„์›ƒ์— ๋ชจ๋“  ํ˜ธ์ถœ์ด ์ข…์†๋˜๋„๋ก)
    const fetchUserData = async () => {
      const {
        data: { user }
      } = await clientSupabase.auth.getUser();
      // ์Ž„์…˜ ์œ ์ง€์ค‘์ด๋ฉด
      if (user) {
        setIsLoggedIn(true); // ์ƒˆ๋กœ๊ณ ์นจํ•ด๋„ isLoggedIn ์ƒํƒœ true๋กœ ์œ ์ง€

        // ์Ž„์…˜ ์œ ์ง€์ค‘์ผ ๋•Œ๋งŒ supabase DB์— ์š”์ฒญํ•˜๋„๋ก(์„ฑ๋Šฅ์  ํšจ์œจ์„ฑ)
        const { data: userData } = await clientSupabase.from('users').select('*').eq('user_id', String(user?.id));
        if (userData && userData[0]) {
          setUser(userData[0]);
        }
      } else {
        // ์ด ๋•Œ๋Š” ์ƒˆ๋กœ๊ณ ์นจ ์‹œ๋ฅผ ๊ณ ๋ คํ•˜์—ฌ isLoggedIn์„ false๋กœ ํ•˜์ง€ ์•Š์•„๋„ ๋จ
        // -> isLoggedIn์˜ ์ดˆ๊ธฐ๊ฐ’ ์ž์ฒด๊ฐ€ false์ด๊ธฐ ๋•Œ๋ฌธ์— ์ƒˆ๋กœ๊ณ ์นจํ•˜๋ฉด ์ž๋™์œผ๋กœ false
        // ๋กœ๊ทธ์•„์›ƒํ•˜๋ฉด(= ์Ž„์…˜ ์œ ์ง€ ์ค‘์ด ์•„๋‹ˆ๋ฉด) ์œ ์ €๋ฐ์ดํ„ฐ null๋กœ ๋ณ€๊ฒฝ
        setUser(null);
      }
    };
    fetchUserData();
  }, [isLoggedIn, setIsLoggedIn]);
  return <></>;
};

export default InitUser;

 

๐Ÿšจ  TroubleShotting

์ด๋Œ€๋กœ ํ–ˆ๋”๋‹ˆ.. 

๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ ์‹œ 2๊ฐ€์ง€ ๋‹จ์ ์ด ์ƒ๊ฒผ๋‹ค.

1. ๋ถˆํ•„์š”ํ•œ ์„œ๋ฒ„์š”์ฒญ

2. ๋ฐ์ดํ„ฐ ์ด์ค‘๊ด€๋ฆฌ

 

Why(์ด์œ )?

๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ ์‹œ

๋ณ€๊ฒฝ ์„œ๋ฒ„์š”์ฒญ(1ํšŒ ์š”์ฒญ) -> Zustand๋กœ - setUserData, setIsLoggedIn ->  ๋ฆฌ๋ Œ๋”๋ง ๋ฐœ์ƒ -> ๋‹ค์‹œ ์ตœ์ƒ์œ„ ์ปดํฌ๋„ŒํŠธ์˜ useEffect ๋‚ด ์„œ๋ฒ„์š”์ฒญ ๋กœ์ง์‹คํ–‰(2ํšŒ ์š”์ฒญ)

๋ผ๋Š” ๊ณผ์ •์œผ๋กœ ํ•œ ๋ฒˆ์˜ ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ ์‹œ,

1) 2๋ฒˆ์˜ ์„œ๋ฒ„์š”์ฒญ

2) ์™ธ๋ถ€ ์„œ๋ฒ„์˜ ๋ฐ์ดํ„ฐ ์—…๋ฐ์ดํŠธ + ๋‚ด๋ถ€ Zustand store ๋ฐ์ดํ„ฐ๋„ ์—…๋ฐ์ดํŠธ(setState) = ๋ฐ์ดํ„ฐ ์ด์ค‘๊ด€๋ฆฌ

๋ผ๋Š” ์œ„์˜ 2๊ฐ€์ง€ ๋‹จ์ ์ด ๋ฐœ์ƒํ•œ๋‹ค.

 

How(๊ณผ์ •) ?

์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์šฐ๋ฆฌ ํŒ€์€ useEffect + Zustand ์กฐํ•ฉ์„ ๊ณผ๊ฐํžˆ ๋ฒ„๋ฆฌ๊ณ ,

- ํ•˜๋‚˜์˜ ์ฟผ๋ฆฌ ํ‚ค๋กœ ๋™์ผํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์ „์—ญ์ ์œผ๋กœ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๊ณ 

- ํ•œ ์ปดํฌ๋„ŒํŠธ์—์„œ ์ด๋ฏธ ๋ฐ์ดํ„ฐ ์š”์ฒญ์„ ํ•ด์„œ ์บ์‹œ๋ฐ์ดํ„ฐ๊ฐ€ ์ƒ๊ธฐ๋ฉด ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋™์ผํ•œ ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•ด ์„œ๋ฒ„์š”์ฒญํ•  ํ•„์š”๊ฐ€ ์—†๋Š” = ์ฆ‰, ๋ถˆํ•„์š”ํ•œ ์„œ๋ฒ„์š”์ฒญ์„ ๊ฐ์†Œ์‹œํ‚ค๋Š”

Tanstack-Query๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ๋กœ ํ•œ๋‹ค. 

 

what(๊ฒฐ๊ณผ) ?

+) ์ถ”๊ฐ€์ ์œผ๋กœ userData๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋‹ค๋ฅธ ๋ฐ์ดํ„ฐ๋ฅผ ์š”์ฒญํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๋“ค์ด ์›Œ๋‚™ ๋งŽ์•„์„œ userData ๋˜ํ•œ prefetch + dyfydration ํ•˜์—ฌ RootLayout.tsx ์—์„œ ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ๋“ค์— ๋„˜๊ฒจ์ฃผ๊ธฐ๋กœ ํ–ˆ๋‹ค.

export default async function RootLayout({
  children
}: Readonly<{
  children: React.ReactNode;
}>) {
  const setUser = async () => {
    const supabase = serverSupabase();
    // ์œ ์ € ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ
    const {
      data: { user },
      error
    } = await supabase.auth.getUser();
    if (!user || error) throw error;
    const { data: userData, error: userDataErr } = await supabase
      .from('users')
      .select('*')
      .eq('user_id', String((user as User).id))
      .single();
    if (userDataErr || !userData) {
      console.error(userDataErr.message);
    } else {
      return userData;
    }
  };
  const queryClient = new QueryClient();

  await queryClient.prefetchQuery({  ----> prefetch
    queryKey: [USER_DATA_QUERY_KEY],
    queryFn: setUser
  });
  return (
    <html lang="ko">
      <body className={inter.className}>
        <NextProvider>
          <QueryProvider>
            <HydrationBoundary state={dehydrate(queryClient)}>  ---> dehydration
              <Suspense>
                <ToastContainer position="top-right" limit={1} closeButton={false} autoClose={4000} />
                <ValidationModal />
                <NavBar />
                {children}
                <ReactQueryDevtools initialIsOpen={false} />
              </Suspense>
              <Footer />
            </HydrationBoundary>
          </QueryProvider>
        </NextProvider>
      </body>
    </html>
  );
}

 

ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ๋“ค์—์„œ์˜ ์‚ฌ์šฉ: userData์™€ isLoggedIn ์ƒํƒœ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ์ปค์Šคํ…€ ํ›… ์ƒ์„ฑ ๋ฐ ์‚ฌ์šฉ

export const useGetUserDataQuery = () => {
  const { data, isSuccess } = useSuspenseQuery({
    queryKey: [USER_DATA_QUERY_KEY],
    queryFn: () => fetchUserData()
  });

  const isLoggedIn = data ? true : false;

  return { data, isSuccess, isLoggedIn };
};

 

// ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ
const NavBarContents = () => {
  const { data: user, isLoggedIn } = useGetUserDataQuery();
  
  ... ์ดํ•˜ ์ƒ๋žต

๐Ÿ“ธ ์Šคํฌ๋ฆฐ์ƒท

๐Ÿ“š ๋ ˆํผ๋Ÿฐ์Šค

https://tanstack.com/query/latest/docs/framework/react/guides/ssr