데이터 hook부터 연결할 필요가 없어
서버 컴포넌트는 결과를 표시하는 바로 그 자리에서 fetch()나 DB 클라이언트를 호출할 수 있어. 프레임워크가 Promise를 기다린 뒤 HTML을 만들기 때문에 useEffect, isLoading 상태, 첫 렌더 뒤의 클라이언트 요청을 기본값으로 깔지 않아도 돼.
읽는 책임을 사용하는 곳 가까이에 둬
페이지 꼭대기에서 모든 데이터를 모아 세 층 아래까지 prop으로 내리면 컴포넌트 경계가 데이터 배관이 돼 버려. 각 서버 컴포넌트가 자기 소스를 읽게 해. 같은 렌더 패스에서 URL과 옵션이 같은 fetch()는 중복 제거되므로 형제 둘이 요청해도 실제 네트워크 호출은 하나야.
클라이언트 라이브러리를 없애는 만능 규칙은 아니야
“서버에서 한 번 읽고 한 번 그린다”는 흔한 경우에는 React Query, SWR, Pages Router의 getServerSideProps를 대신할 수 있어. 하지만 실시간 polling, 무한 스크롤, 오프라인 캐시처럼 브라우저 수명과 함께 움직이는 데이터에는 클라이언트 도구가 여전히 가치가 있어. 필요 없는 화면에 그 무게를 기본 장비처럼 얹지 않는다는 뜻이야.
첫 화면에 한 번 필요한 데이터인지, 사용자가 머무는 동안 계속 바뀌어야 하는 데이터인지 구분해. 전자는 서버 컴포넌트에서 읽고, polling·무한 스크롤·오프라인 상태가 필요한 후자만 클라이언트 캐시로 넘겨. 초기 데이터와 이후 갱신을 섞어야 한다면 서버 결과를 초기값으로 재사용해 중복 요청을 막아.
페이지에서 직접 fetch하지 말라는 말은 요청을 모두 잎에 흩뿌리라는 뜻도 아니야. 같은 권한·캐시·오류 정책을 가진 데이터는 서버 함수로 묶고, 컴포넌트는 그 계약을 호출해. 위치의 가까움과 정책의 일관성을 동시에 지켜야 해.
초기 목록을 클라이언트 effect와 서버 컴포넌트로 각각 구현해 HTML 첫 응답, 요청 횟수, JavaScript 양을 비교해. 그다음 polling과 무한 스크롤을 추가해 어느 시점부터 클라이언트 캐시가 실제 가치를 갖는지 경계를 기록해. 서버에서 읽은 초기값을 클라이언트 캐시에 넘길 때 같은 데이터를 즉시 다시 요청하지 않는지 확인해. 경계를 잘 나눴는데 hydration 직후 중복 fetch가 나가면 이점의 일부를 스스로 지우는 셈이야.
내부 서버 화면이 자기 데이터베이스를 읽을 수 있는데 공개 API를 한 번 더 거치면 직렬화와 인증 경계가 늘어. 외부 소비자가 실제로 있을 때만 URL 계약을 추가해.