๐จ TroubleShotting
Query data cannot be undefined
๋ผ๋ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ค.
๋ฌธ์ ๋ฐ์์ ๋ฐฐ๊ฒฝ - As Is
1. Next.js์ 14๋ฒ์ App-router๋ฅผ ์ฌ์ฉ ์ค์ด๋ค.
2. src > app > chat > page.tsx ํ์ผ์์ SideBar.tsx๋ผ๋ ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ๋ฅผ import ํ๊ณ ์์๋ค.
import SideBar from '@/components/chat/sidebar/SideBar';
const ChatPage = async ({ params }: { params: { chatroom_id: string } }) => {
...
return (
<main>
<HydrationBoundary state={dehydrate(queryClient)}>
<Suspense fallback={<ChatLoading />}>
...
<section className="lg:flex lg:max-w-96 max-sm:absolute max-sm:z-40 max-sm:bg-white min-h-[36rem] ">
<SideBar chatRoomId={chatRoomId} /> ---> ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ
</section>
...
3. SideBar.tsx(ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ - ์ดํ ์๋ต) ์์์๋ useSuspenseQuery๋ฅผ ์ฌ์ฉํ๊ณ ์์๊ณ , ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ChatPage.tsx์์ SideBar.tsx๋ฅผ <Suspense>๋ก ๊ฐ์ธ๊ณ ์์๋ค.
4. ๊ทธ๋ฌ๊ณ localhost:3000์์ ChatPage.tsx์์ ์๋ก๊ณ ์นจ์ ํ๋ฉด ์์ ์ค๋ฅ๊ฐ ๋ด๋ค.
5. ๊ทผ๋ฐ ์ด์ ๋ฌธ์ ๋ ์ ์ค๋ฅ๊ฐ ๋ธ๋ผ์ฐ์ ์ฝ์์๋ ๋จ์ง ์๊ณ ์๋ฒ ์ธก ํฐ๋ฏธ๋์ ๋ด๋ค๋ ๊ฒ๊ณผ, ๋ธ๋ผ์ฐ์ ์ฝ์์๋ ์๋ฌด ์ค๋ฅ๊ฐ ๋จ์ง ์์๊ณ ๋ฐ์ดํฐ๋ ์ ๋ฐ์์์ ธ์ ๋ ๋๋ง๋ ์ ๋์๋ค๋ ๊ฒ์ด๋ค.
์ฌ๊ธฐ์ ์๋ฌธ
SideBar.tsx๋ ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ์ด๊ณ ,
React-query๋ ํด๋ผ์ด์ธํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ธ๋ฐ
์ ์๋ฒ ์ธก ํฐ๋ฏธ๋์์ ์ค๋ฅ๊ฐ ๋๋๊ฐ?
ํฐ๋ฏธ๋์ ์ค๋ฅ๊ฐ ๋๋๋ฐ ๋ธ๋ผ์ฐ์ ์๋
์ ์ค๋ฅ ์์ด ๋ฐ์ดํฐ fetching์ด ์ ๋๋๊ฐ?
How(๊ณผ์ ) ?
2๊ฐ์ ์์ธ๊ฐ์ค์ ์ธ์๋ณด์๋ค.
์์ธ๊ฐ์ค1
Rendering: Client Components | Next.js
Learn how to use Client Components to render parts of your application on the client.
nextjs.org
๊ทธ ๊ณผ์ ์์ useSuspenseQuery๊ฐ ์คํ๋๊ธฐ ๋๋ฌธ์ด๋ค!
๊ทธ๋ผ ์ฌ๊ธฐ์ ๋ํ ๋ ์๋ฌธ์
(1) 'use client'๋ผ๊ณ ํด๋ Client ์ปดํฌ๋ํธ๊ฐ pre-render๋ก ์ธํด์ ๊ทธ ๋ก์ง๋ค์ด server์์ ๋ฏธ๋ฆฌ ์คํ์ด ๋๋ค๋ฉด, useSuspenseQuery ๋ฟ๋ง ์๋๋ผ useState๋ useEffect ๊ฐ์ React hook๋ค๋ ์คํ๋์ด์ผ ํ ํ
๋ฐ ์ ๊ฑ๋ค๋ค์ ๋ํ ์ค๋ฅ๋ ํฐ๋ฏธ๋์ ์ฐํ์ง ์๋์ง?
(2) ๊ทธ๋ ํ๋ฆฌ๋ ๋๋ง์ด ์์ธ์ด๋ผ๊ณ ์น์. ๊ทธ๋ผ ์ด์จ๋ ์๋ฒ ์ชฝ์์ ์ค๋ฅ๊ฐ ๋๋ค๋ ๊ฒ์ ๋น๋ ์์๋ ์ค๋ฅ๊ฐ ๋์ผ ํ ํ ๋ฐ ์ ๋น๋ ์์๋ ์ค๋ฅ๊ฐ ๋์ง ์๋์ง?
(3) ํ๋ฆฌ๋ ๋๋ง์ด ๋๋ ์ข์๋ฐ, pre-๋ ๋๋ง์ด ๋ง ๊ทธ๋๋ก "๋ฏธ๋ฆฌ ์๋ฒ ์ธก์์ ๋ ๋๋ง" ๋๋ ๊ฑฐ๋ผ๋ฉด ์ ํด๋ผ์ด์ธํธ์์ ํ๋ฒ ๋ ๋ ๋๋ง์ด ๋์ด useSuspenseQuery๊ฐ ์คํ๋๊ณ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์ค๋์ง? ์ฆ, ํ๋ฒ pre-๋ ๋๋ง์ผ๋ก ์คํ๋์ผ๋ฉด ๋์ง ์ ํด๋ผ์ด์ธํธ ์ธก์ ํ๋ฒ ๋ ์คํ๋๋์ง?
์์ธ๊ฐ์ค 2
์๋ ์ด๊ฑฐ ํ๋ฆฌ๋ ๋๋ง ๋๋ฌธ ์๋ ๊ฒ ๊ฐ์. ์ด๊ฑฐ Suspense ๋๋ฌธ์ธ ๊ฒ ๊ฐ์.
์๋๋ฉด ๋ค๋ฅธ ๊ณณ์์๋ useSuspenseQuery ๋ง๊ณ useQuery ์ผ๋๋ฐ ์ด๋ฐ ์ค๋ฅ ์ ๋.
์ด๊ฑฐ๋ด. ์ฌ๊ธฐ์ ๋งํ๊ธธ Suspense์ SSR์ด ๋ง๋๋ฉด ๋๋ Next.js์ ๋ฒ๊ทธ๋
SSR ํ๊ฒฝ์์ Suspense์ useSuspenseQuery๊ฐ ์ค์๋ํ๋ ๋ฌธ์ ํด๊ฒฐํ๊ธฐ
๊ณตํต์ ์ ํตํด ๋ฌธ์ ๋ฐ์ ์์ธ ํ์ํ๊ธฐ
velog.io
์ด๊ฒ์ ๋ํ ๋ ์๋ฌธ์
(1) Suspense์ SSR์ด ๋ง๋๋ฉด ์ ํํ ๋ญ ๋๋ฌธ์ ๊ทธ๋ฐ๊ฑด์ง?
(2) ์ค์ผ์ด ํ๋ฆฌ๋ ๋๋ง ์๋๋ผ๊ณ ์น์. ๊ทธ๋ผ Suspense๋ ๋ง์ฐฌ๊ฐ์ง๋ก ์ฒ์์ undefined๋ฅผ ๋ฐํํด์ ์ค๋ฅ๋ฅผ ๋ฑ์์ผ๋ฉด ๊ทธ๊ฑธ๋ก ๋์ด์ง ์ด๊ฒ๋ ์ ํด๋ผ์ด์ธํธ์์ ํ๋ฒ ๋ ์คํ๋๋์ง?
Why(์ด์ ) ?
๊ฒฐ๊ตญ ์ด์ ๋ ๋ ๊ฐ์ค์ ์งฌ๋ฝ์ด์๋ค.
Suspense๊ฐ pre-render์ ๋์์ด ๋๊ธฐ ๋๋ฌธ์ด๋ค. ์ฆ, SSR-Streaming์ ๋์์ด ๋๋ค.
์ ๋งํฌ์ Example ๋ถ๋ถ์ By using Suspense, you get the benefits of: 1. Streaming Server Rendering ์ ๋ณด๋ฉด
Suspense๋ฅผ ์ฌ์ฉํจ์ผ๋ก์จ ์๋ฒ์์ rendering ํ ์ ์๋ค๊ณ ๋์์๋ค.
(์ฌ๊ธฐ์ ์์ธ๊ฐ์ค1์ (1)์ ๋ต์ด ๋์จ๋ค. pre-render๊ฐ ๋๋ ๋์์ ๋ฐ๋ก ์๋ค. Suspense๋ ๊ทธ pre-render์ ๋์, ์ฆ ์๋ฒ ์ธก ์ฝ๋๊ฐ ๋๊ธฐ ๋๋ฌธ์ react-hook ๊ฐ์ client ์ฝ๋์ ๋ค๋ฅด๊ฒ ์๋ฒ์์ ์คํ๋๋ค.)
Suspense๋ useSuspenseQuery ๋ด queryFn๊ฐ ๋ฐํํด์ฃผ๋ Promise ๋ฐํ๊ฐ์ ์บ์นํด์, ๊ทธ ๊ฐ์ด pending์ด๋ฉด streamig์ ๊ธฐ๋ค๋ ค์ฃผ๊ณ , resolve๋ฉด Suspense ๋ฐ์ด๋๋ฆฌ ์์ ์ปดํฌ๋ํธ๋ฅผ ๋ ๋๋ง ํด์ค๋ค.
๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ์๋ฒ ์ธก์์ Suspense๋ฅผ ์ํ useSuspenseQuery๊ฐ ์คํ๋๋ ๊ฒ์ด์๊ณ , ๋ฌธ์ ๋ useSuspenseQuery์ queryFn(fetchChatData) ๋ด์์ ์๋ฒ ์ธก์์ ์คํ๋ ์์ ์ clientSupabase๋ฅผ ์ฌ์ฉํ๊ณ ์๊ธฐ ๋๋ฌธ์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ ๊ฒ์ด๋ค.
export const fetchChatData = async (chatRoomId: string) => {
console.log('aaaa', chatRoomId);
const { data: chatData, error: chatDataErr } = await clientSupabase ---> ํด๋ผ์ด์ธํธ ํจ์
.from('chatting_room')
.select('*')
.eq('chatting_room_id', chatRoomId)
.single();
if (chatDataErr) {
throw chatDataErr;
} else {
return chatData;
}
};
์์ธ๊ฐ์ค๋ค์ ์๋ฌธ์ ์ค ํ๋์ธ "์ ํ๋ฒ ๋ ์คํ๋๋์ง"๋ ์๋ง ์๋ฒ ์ธก์์ queryFn์ ์คํจํ์ ๋, ํด๋ผ์ด์ธํธ ์ธก์์ ์ฌ์คํํ๋ ์ต์ ์ด ์์ ๊ฒ ๊ฐ๋ค๋ ๊ฒฐ๋ก ์ ์ง์๋ค.
์์ธ์ ์ด์ ์์๋ค. ๊ทธ๋ผ ํด๊ฒฐ๋ฐฉ๋ฒ์?
what(๊ฒฐ๊ณผ) - To Be
์ด ์ธ ๊ฐ์ง์ ์๊ฒฌ์ด ๋์๋ค.
1. Next.js ๊ณต์๋ฌธ์์ lazy-loading > Skipping SSR ๋ถ๋ถ์ Client ์ปดํฌ๋ํธ์ pre-rendering์ ์ํ์ง ์์ผ๋ฉด, Server ์ปดํฌ๋ํธ์์ Suspense๋ก ์ฝ๋์คํ๋ฆฌํ ํ ๋ถ๋ถ = Client ์ปดํฌ๋ํธ(์ฌ๊ธฐ์ SideBar.tsx)๋ฅผ import ํ๋ ๊ฒ ์์ฒด๋ฅผ dynamic, {ssr: false} ์ต์ ์ ๊ฑธ์!
์๋๊ฒฐ๊ณผ -> ์ค๋ฅ ํด๊ฒฐ
// src > app > chat > .. > page.tsx
...
// import SideBar from '@/components/chat/sidebar/SideBar';
const SideBarWrapper = dynamic(() => import('@/components/chat/sidebar/SideBar'), { ssr: false });
const ChatPage = async ({ params }: { params: { chatroom_id: string } }) => {
...
return (
<main>
<HydrationBoundary state={dehydrate(queryClient)}>
<Suspense fallback={<ChatLoading />}>
<section className="lg:flex lg:max-w-96 max-sm:absolute max-sm:z-40 max-sm:bg-white min-h-[36rem] ">
<SideBarWrapper chatRoomId={chatRoomId} /> --> SideBar๋ฅผ SideBarWrapper๋ก ๋ณ๊ฒฝ
</section>
...
2. ๊ทธ๊ฒ๋ณด๋ค๋ pre-render๋ฅผ ํ์ฉํด์ ๋ฏธ๋ฆฌ data๋ฅผ ์ ๋ฐ์์ค๋ฉด ๋ก๋ฉ ์๋๋ ๋นจ๋ฆฌ์ง ๊ฒ ๊ฐ์๋ฐ, ๊ทธ๋ฅ queryFn ๋ด์ ClientSupabase๋ฅผ window์ type์ ๋ฐ๋ผ์ server ์ธก์์๋ serverSupabse() ์ฌ์ฉํ๊ณ client ์ธก์์๋ clientSupabase() ์ฌ์ฉํ๋๋ก ์กฐ๊ฑด๋ฌธ ๊ฑธ์ด๋ณด์!
์๋๊ฒฐ๊ณผ -> ๋ค๋ฅธ ์ค๋ฅ ๋ฐ์
const getSupabase = () => {
if (typeof window === undefined) {
const supabase = serverSupabase();
return supabase;
} else {
return clientSupabase;
}
};
export const fetchChatData = async (chatRoomId: string) => {
console.log('aaaa', chatRoomId);
const { data: chatData, error: chatDataErr } = await getSupabase()
.from('chatting_room')
.select('*')
.eq('chatting_room_id', chatRoomId)
.single();
if (chatDataErr) {
throw chatDataErr;
} else {
return chatData;
}
};
ํ์ง๋ง
์ด๋ ๊ฒ ํ๋ฉด ๋ค์๊ณผ ๊ฐ์ ๋ค๋ฅธ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ค.
serverSupabase() ํจ์๋ ์ค์ง server ์ปดํฌ๋ํธ์์๋ง ์ฌ์ฉ๊ฐ๋ฅํ๋ฐ, ์ฐ๋ฆฌ๋ ์ง๊ธ fetchChatData๋ผ๋ useSuspenseQuery์ queryFn์ client ์ปดํฌ๋ํธ์์ ์ฌ์ฉํ๊ณ ์์ด์ ์ธ ๊ฒ ๊ฐ๋ค.
๐ก ์๋กญ๊ฒ ์๊ฒ๋ ์
Nest.js์ app-router์์๋ "use client"๋ฅผ ์ฌ์ฉํ๋ค๊ณ ํด์ ์์ ํ CSR์ ์๋ ์ ์๋ค.
์๋ํ๋ฉด? pre-rendering์ด ๋๊ธฐ ๋๋ฌธ์ด๋ค.
SSR๊ณผ CSR์ hybrid ์ ๋๋ผ๊ณ ํ ์ ์์ ๊ฒ ๊ฐ๋ค.
'TIL' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
2024.05.29 #Next.js #Server-Action1 (1) | 2024.05.31 |
---|---|
2024.05.28 #Next.js #How are Client Components Rendered? #์ถ๋ก (0) | 2024.05.29 |
2024.04.26 TIL #useSuspenseQuery #useSuspenseQueries #useMemo (0) | 2024.05.17 |
2024.04.22 TIL #์ฑํ ๋ฐฉ_๋๊ฐ๊ธฐ #๋๋_๋๊ฐ๋๋ฐ_๋๋? #type_์ง์ (0) | 2024.05.11 |
2024.04.19 TIL #์ฑํ #๋ง์ง๋ง_๋ฉ์ธ์ง_๊ธฐ์ต #์ฌ๊ธฐ๊น์ง_์ฝ์์ต๋๋ค (0) | 2024.05.09 |