본문 바로가기
C.W.K.
Stream
Lesson 01 of 09 · published

서버 컴포넌트에서 바로 데이터 가져오기

~20 min · fetch, Server Component, dedupe

Level 0호기심
0 XP0/68 lessons0/11 achievements
0/120 XP to next level120 XP to go0% complete

데이터 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 계약을 추가해.

Code

직접 fetch·tsx
// app/posts/page.tsx
export default async function PostsPage() {
  const res = await fetch('https://api.example.com/posts', {
    next: { revalidate: 60 },
  });
  const posts = await res.json();
  return <PostList posts={posts} />;
}
DB 직접 질의·tsx
import { db } from '@/lib/db';

export default async function UsersPage() {
  const users = await db.user.findMany({
    include: { posts: true },
    orderBy: { createdAt: 'desc' },
  });
  return <UserTable users={users} />;
}
각 잎에서 fetch하고 동일 호출은 한 번만 실행·tsx
async function Header()  { const me = await fetch('/me');     return <Nav user={await me.json()} />; }
async function Sidebar() { const me = await fetch('/me');     return <UserCard user={await me.json()} />; }
// 둘 다 /me 부름; 프레임워크가 렌더링마다 한 번만 fetch해.

External links

Exercise

초기 서버 목록을 위해 React Query나 SWR을 쓰는 페이지를 골라 서버 컴포넌트 fetch로 바꿔. 해당 화면에서 불필요해진 클라이언트 의존성도 제거해.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.