Skip to main content
  1. Guides 리스트/
  2. Next.js 답게 개발하기: 앱 라우터의 설계 원리와 실전 가이드/

Next.js 서버 컴포넌트 데이터 fetching 이제 선택이 아닌 필수인 이유

·724 words·4 mins·
Next.js 답게 개발하기: 앱 라우터의 설계 원리와 실전 가이드 - This article is part of a series.
Part 1: This Article

결론부터 말씀드리면, 데이터 페칭은 이제 ‘서버 컴포넌트(Server Components)‘에서 처리하는 것이 좋은데요.

클라이언트 컴포넌트(Client Components)에서 데이터를 가져오는 방식은 더 이상 권장되지 않는 베스트 프랙티스입니다.

기존 방식의 문제점
#

과거 리액트(React) 컴포넌트는 주로 클라이언트 사이드에서 모든 것을 처리하는 것을 전제로 했거든요.

그래서 클라이언트 환경에서의 데이터 페칭을 위한 ‘SWR’이나 ‘리액트 쿼리(React Query)’, ‘아폴로 클라이언트(Apollo Client)’ 같은 훌륭한 라이브러리나 구현 패턴이 정말 많습니다.

하지만 클라이언트 사이드에서 직접 데이터를 가져오는 방식은 여러 가지 명확한 단점을 가지고 있는데요.

어떤 문제들이 있는지 하나씩 짚어보겠습니다.

성능과 설계의 상충 관계
#

클라이언트와 서버 간의 통신은 물리적 거리나 불안정한 네트워크 환경 때문에 느려지는 경우가 많거든요.

그래서 성능만 생각하면 통신 횟수를 최소화하는 게 가장 이상적인 방법입니다.

그런데 통신 횟수를 줄이는 것과 단순하고 좋은 설계는 서로 상충하는 경우가 많은데요.

예를 들어 ‘REST API’에서 통신 횟수를 줄이려고 하다 보면, 너무 많은 책임을 가진 ‘갓 에이피아이(God API)‘가 만들어지기 쉽고, 이건 결국 유지보수의 어려움이나 API 자체의 성능 문제로 이어집니다.

반대로 책임을 잘게 나눈 API는 소위 ‘채티 에이피아이(Chatty API)’, 즉 수다스러운 API가 되어 통신 횟수가 급증하고 데이터 페칭 워터폴이 발생하기 쉬운데요.

이건 결국 웹 애플리케이션의 성능을 저하시키는 주된 원인이 됩니다.

복잡한 구현 비용
#

클라이언트 사이드 데이터 페칭은 대부분의 경우, 리액트에서도 권장하듯 캐시 기능이 내장된 서드파티 라이브러리를 사용하게 되는데요.

동시에 요청을 보내는 API는 공용 네트워크에 노출되어야 하므로, 훨씬 더 견고한 보안 대책이 필수입니다.

결국 라이브러리 학습, 책임 설계, API 보안 강화 등 수많은 개발 비용이 추가로 발생한다는 문제가 있거든요.

이건 프로젝트의 속도를 늦추는 아주 큰 장벽입니다.

늘어나는 번들 사이즈
#

클라이언트에서 데이터를 페칭하려면 서드파티 라이브러리, 데이터 페칭 로직, 유효성 검사 코드 등 정말 많은 코드가 번들 파일에 포함되어 클라이언트로 전송되는데요.

심지어 특정 조건에서만 사용되는 에러 처리 UI 같은 코드들까지 번들에 포함되는 경우가 대부분입니다.

Next.js의 해답 서버 컴포넌트
#

리액트 팀은 앞서 언급된 문제들을 개별적인 이슈로 보지 않았거든요.

근본적으로 리액트가 서버를 충분히 잘 활용하지 못하고 있다는 점이 진짜 문제라고 보고, 이를 해결하기 위한 아키텍처를 내놓았습니다.

그 결과물이 바로 ‘리액트 서버 컴포넌트(React Server Components, RSC)‘인데요.

Next.js는 이 RSC를 완벽하게 지원하며, 데이터 페칭은 ‘서버 컴포넌트’에서 수행하는 것을 베스트 프랙티스로 삼고 있습니다.

“use server"에 대한 흔한 오해
#

‘서버 컴포넌트에는 “use server"가 필요하다’는 오해를 종종 볼 수 있는데요.

이건 명백히 잘못된 정보입니다.

‘“use server”‘는 함수를 ‘서버 액션(Server Actions)‘으로 만들어 클라이언트에서 호출할 수 있게 해주는 지시어거든요.

서버 컴포넌트 자체를 지정하기 위한 것이 절대 아닙니다.

데이터 페칭을 서버 컴포넌트에서 수행하면 다음과 같은 압도적인 장점들을 얻을 수 있습니다.

1. 빠른 백엔드 접근 속도
#

Next.js 서버와 API 서버 간의 통신은 대부분의 경우 아주 빠르고 안정적인데요.

특히 API 서버가 같은 네트워크나 동일한 데이터 센터 내에 있다면 그 속도는 비교할 수 없을 정도로 빠릅니다.

설령 API 서버가 외부에 있더라도, 보통은 수도권 내의 고속 네트워크망을 통해 통신하기 때문에 클라이언트 환경과는 비교할 수 없이 안정적이고 빠르다고 볼 수 있거든요.

이건 사용자 경험에 직접적인 영향을 미치는 아주 중요한 장점입니다.

2. 간결하고 안전한 구현
#

서버 컴포넌트는 비동기 함수(async/await)를 완벽하게 지원해서, 별도의 라이브러리 없이도 데이터 페칭을 아주 간단하게 구현할 수 있는데요.

export async function ProductTitle({ id }) {
  const res = await fetch(`https://dummyjson.com/products/${id}`);
  const product = await res.json();

  return <div>{product.title}</div>;
}

이런 간결한 코드가 가능한 이유는 서버 컴포넌트가 서버 측에서 단 한 번만 렌더링되기 때문입니다.

기존처럼 클라이언트에서 여러 번 렌더링될 상황을 굳이 고려할 필요가 없는 거거든요.

또한 데이터 페칭이 서버에서만 실행되므로, API를 굳이 공용 네트워크에 공개할 필요가 없습니다.

백엔드 API를 프라이빗 네트워크 안에서만 접근하도록 구성하면, 보안 리스크와 관리 비용을 획기적으로 줄일 수 있습니다.

3. 번들 사이즈 감소
#

서버 컴포넌트의 실행 결과는 HTML이나 ‘RSC 페이로드(Payload)’ 형태로 클라이언트에 전달되는데요.

덕분에 앞서 언급했던 서드파티 라이브러리, 데이터 페칭 로직, 에러 처리 UI 같은 코드들이 클라이언트 번들에 전혀 포함되지 않습니다.

물론 단점도 존재합니다
#

사용자 인터랙션과 데이터 페칭
#

사용자의 특정 행동(클릭, 입력 등)에 반응해서 데이터를 가져와야 하는 경우는 서버 컴포넌트만으로 처리하기가 까다로운데요.

이 부분에 대한 자세한 내용은 이후 챕터에서 다시 다루겠습니다.

GraphQL과의 미묘한 관계
#

RSC 환경에서 ‘그래프큐엘(GraphQL)‘을 함께 사용하는 것은 장점보다 단점이 더 클 수 있거든요.

그래프큐엘은 앞서 말한 ‘성능과 설계의 상충 관계’ 문제를 해결해주는 강력한 도구이지만, RSC 역시 같은 문제를 해결하기 위해 등장했습니다.

즉, RSC를 사용하면 그래프큐엘의 가장 큰 장점 하나가 사라지는 셈인데요.

오히려 현재로서는 RSC와 그래프큐엘을 함께 잘 사용하기 위한 노하우나 라이브러리가 부족해서, 구현 비용과 번들 사이즈만 늘어나는 결과로 이어질 수 있습니다.

RSC는 GraphQL의 정신적 후계자
#

흥미로운 점은 RSC에 대한 첫 RFC(의견 수렴 문서)를 제안한 사람이 바로 ‘릴레이(Relay)‘의 초기 개발자 중 한 명이자 그래프큐엘 전문가인 ‘조 사보나(Joe Savona)‘라는 사실인데요.

이것은 RSC가 그래프큐엘이 가진 장점과 풀어야 할 숙제들을 충분히 고민한 끝에 설계되었다는 것을 의미합니다.

어떤 면에서는 RSC를 그래프큐엘의 정신을 이어받은 ‘정신적 후계자’라고도 볼 수 있는 거거든요.

이게 바로 리액트가 바라보는 데이터 페칭의 미래입니다.

Next.js 답게 개발하기: 앱 라우터의 설계 원리와 실전 가이드 - This article is part of a series.
Part 1: This Article