2024-07-11 supabase를 클라이언트-서버 컴포넌트에서 사용할 때 클라이언트를 계속 생성해야 하는가?
Next.js는 서버 컴포넌트와 클라이언트 컴포넌트 둘 다 지원하는 프레임워크이다.
보통 React.js를 배우고 나서 Next.js로 넘어 가게 될 텐데 매우 혼란스러운 개념이 생긴다.
리액트는 기본적으로 클라이언트 컴포넌트이기에, 내가 지금 구현한 페이지의 렌더링 방식이 어떤 것인지 고민할 필요가 없어서 리액트를 배우는 과정에서도 이를 다루진 않는다.
하지만 SSG를 만들어내서 SEO에 유리한 서버 컴포넌트도 작성할 수 있는 Next.js로 넘어 오면서, 기존 리액트에서 쓰던 방식을 그대로 써도 되는 게 있고 안 되는 것이 생기면서 초심자 입장에서는 머리가 복잡해진다.
그 중 하나가 supabase를 세팅하는 부분이 상당히 복잡해졌다는 것인데, 클라이언트, 서버, 추가로 미들웨어까지 세팅을 해주어야 한다.
무슨 말을 하고 있는 거냐면,
const supabase = createClient();
리액트에서는 supabaseClient.ts 파일에서 위와 비슷한 인스턴스를 한 번만 작성해주고, 저것을 사용하는 곳에서는 Import만 시켜주면 되었었는데, 넥스트의 서버 컴포넌트에서는 할 때마다 생성해주어야 한다.
그냥 그러라고 하니까 그렇게 했는데, 문제는 넥스트에서 매번 서버 컴포넌트만 쓰는 것이 아니라 페이지 자체는 서버 컴포넌트로 하고, 페이지에서 포함하고 있는 칠드런 컴포넌트들은 클라이언트 컴포넌트로 만들어서 import 하는 방식으로 개발을 많이 하게 되는데,
서버 컴포넌트에서 저렇게 인스턴스를 쓸 때마다 만들었으니, 클라이언트 컴포넌트에서도 그렇게 쓰면 되는 거라고 넘겼다.
그런데 알고 보니, 클라이언트 컴포넌트에서는 저 인스턴스를 계속 호출할 필요가 없었다. 물론 호출을 쓸 때마다 해도 메모리 누수가 발생하진 않는다고 한다.
Do I need to create a new client for every route?
On the client, createBrowserClient already uses a singleton pattern, so you only ever create one instance, no matter how many times you call your createClient function.
On the server, it basically configures a fetch call. You need to reconfigure the fetch call anew for every request to your server, because you need the cookies from the request.
- Supabase 공식문서 발췌
명확하게 공식문서에 설명이 되어 있다.
서버 컴포넌트에서는 계속해서 클라이언트를 생성해주어야 한다. 이유는 서버 컴포넌트에서 supabase로 통신을 요청할 때 매번 새로운 쿠키를 생성해야 하기 때문이라고 한다.
이런 컴포넌트가 수 백 개가 된다면 심각한 메모리 누수가 발생할 것 같지만, 이는 매우 가벼운 작업이라고 걱정 말라고 말하고 있다.
그리고 클라이언트 컴포넌트에서는 매번 호출해도 괜찮다고 설명하고 있다. 이 말이 애매해서 헷갈렸다. 하지 말라는 건지 하라는 건지 애매하지만 결론은 해도 되고 말아도 되고라는 뜻인 것이다.
인스턴스를 계속 생성하면 불필요한 메모리 누수가 발생할 것 같지만, 저 인스턴스를 생성하기 위해 사용 하는 createBrowserClient라는 팩토리 함수는 싱글톤 패턴을 사용하기 때문에 여러 번 호출해도 단 하나의 인스턴스만 생성되도록 보장하는 디자인 패턴으로 제작하였다고 자랑하고 있다.
import { createBrowserClient } from '@supabase/ssr'
export function createClient() {
return createBrowserClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
)
}
위 코드스니펫이 supabase 에서 제공하는 createClient 함수 생성 스니펫인데, 리액트를 배우고 막 넘어 온 입장에서는 인스턴스가 export 되지 않아서 어찌 해야 할 지 골치가 아픈 부분이다.
supabase에서 기본 코드스니펫을 저렇게 적용하고 있으니, 팀 프로젝트를 진행 해보면 클라이언트 컴포넌트 여기저기서 똑같은 인스턴스 생성 함수를 수십번 사용하고 있는데, 사용해도 괜찮다고 하니 사용하는 것이지만, 사용해야 되는 줄 알고 사용했던 나는 공식문서에서 이 정보를 뒤늦게 접하여 '모르고' 썼던 것이었다.
import { Database } from '@/types/supabase'
import { createBrowserClient } from '@supabase/ssr'
export function createClient() {
return createBrowserClient<Database>(
process.env.NEXT_PUBLIC_SUPABASE_URL! as string,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY! as string
)
}
const supabase = createClient();
export default supabase;
그런데 나는 위 코드처럼 사용하는 것이 좋겠다는 부트캠프 동기의 말씀에 동감한다.
짧지만 같은 코드를 계속 컴포넌트마다 쓰는 것은 메모리 누수는 없다고 하니 넘기더라도 같은 코드를 계속 반복하는 것이 불필요한 보일러 플레이트를 늘려나가는 것이기 때문이다.
저렇게 셋업 해놓으면 필요한 곳에서는 import 하기만 하면 되기 때문에 저 방법이 좋다고 생각한다.
supabase 프로젝트를 자주 셋업 하시는 독자께서는 한 번 고민해보시길 바란다.
'Programing > TIL' 카테고리의 다른 글
2024-07-12 Next.js 팀프로젝트 회고(readme) (0) | 2024.07.13 |
---|---|
2024-07-11 Next.js 마이페이지 구현 로직 (0) | 2024.07.12 |
2024-07-08 Next.js 마이페이지 구현하기 (1) (0) | 2024.07.09 |
2023-07-03 Next.js + TanStack Query 포켓몬 도감 만들기 (2) | 2024.07.04 |
2024-06-26 TypeScript로 국가 선택 앱 만들기 (2) (0) | 2024.06.26 |
댓글