React Server Components(RSC)는 개발자가 서버에서 실행될 수 있는 React 컴포넌트로 서버 사이드 렌더링 애플리케이션을 구축할 수 있게 해주는 React의 새로운 기능입니다. 서버가 데이터 페칭(Data fetching)과 렌더링을 처리하는 동안 클라이언트는 상호작용에 집중할 수 있으므로, 더 나은 성능과 향상된 사용자 경험을 제공할 수 있다는 약속을 담고 있습니다. 또한 애플리케이션 구조를 잡는 방식을 변화시켜, 데이터 페칭과 렌더링 로직을 동일한 컴포넌트에 함께 배치(Colocate)할 수 있게 해줍니다.
한동안 RSC는 Next.js에서만 사용할 수 있었지만, 이제 다른 번들러와 프레임워크들도 이를 지원하기 시작했습니다. 반가운 소식은 Parcel이 버전 2.9.0에서 RSC 지원을 추가했다는 점입니다.
이 글에서는 간단한 작업 관리(Task management) 애플리케이션을 구축하며 Parcel과 함께 RSC를 사용하는 방법을 살펴보겠습니다.
서버 설정 (The Server Setup)
Parcel에서 새 페이지를 선언하려면 서버 프레임워크에서 새 라우트(Route)를 선언해야 합니다. 예를 들어 Express를 사용하는 경우는 다음과 같습니다.
console.log("Server listening on port http://localhost:3000");
});
About 페이지
여기서 특별히 새로운 것은 없습니다. 예상하시다시피 React 컴포넌트가 서버 측에서 렌더링되므로, 브라우저는 대부분 HTML 마크업(HTML 1.5kB, CSS 10kB)과 작은 런타임(일반 파일 5.0kB 및 페이지 전용 파일 약 65kB 등 두 개의 파일)을 수신하여 렌더링 프로세스 속도를 높입니다.
위에서 언급한 파일들을 보여주는 브라우저 개발자 도구 네트워크 탭
서버 컴포넌트와 데이터 페칭 (Server Components And Data Fetching)
RSC는 데이터베이스나 API에서 데이터를 가져와야 할 때 흥미로워집니다. 서버 컴포넌트는 이제 서버 측 함수를 호출하여 데이터를 기다릴(await) 수 있습니다. 다음은 데이터베이스에서 작업을 가져오는 작업 목록 페이지의 예시입니다.
기존 Parcel 코드를 수정하여 페이지를 로드하는 동안 링크에 data-loading 속성을 추가했습니다. 이를 통해 사용자에게 자신의 동작이 처리되고 있음을 즉시 알릴 수 있습니다.
All 링크는 활성 상태이고 Completed 링크는 로딩 상태인 작업 페이지
Parcel 문서에서 설명하듯이, 이 방식은 여전히 전체 페이지 콘텐츠를 교체하므로 페이지 콘텐츠를 더 세밀하게 업데이트하려면 라우팅 라이브러리에 의존해야 합니다. 이 글을 쓰는 시점에 React Router가 RSC에 대한 실험적 지원을 막 발표했지만, 아직 시도해 보지는 않았습니다.
서버 함수를 이용한 데이터 변경 (Mutating Data With Server Functions)
데이터를 변경(Mutation)할 때 상황이 흥미로워집니다. RSC를 사용하면 "use server;" 지시문이 있는 파일에서 서버 함수를 가져와 직접 호출할 수 있습니다. 내부적으로는 실제로 fetch 요청이 발생하며, 이로 인해 클라이언트 코드와 서버 코드 사이의 경계가 모호해집니다.
이것으로도 잘 작동하지만, 사용자 경험은 더 개선될 여지가 있습니다. 사용자는 새로 삽입된 작업을 보기 위해 서버 응답을 기다려야 합니다. 하지만 낙관적 업데이트(Optimistic updates)를 사용하면 더 나은 경험을 제공할 수 있습니다!
낙관적 업데이트 추가하기 (Adding Optimistic Updates)
이 용어가 생소한 분들을 위해 설명하자면, 데이터 변경(새 작업을 생성하는 서버로의 POST 요청)이 호출되는 동안 UI에서 그 결과를 즉시 가짜로 보여주는 것을 의미합니다. 대부분의 경우 서버 호출은 성공할 것이며, 사용자는 업데이트된 UI를 더 빨리 보게 됩니다. 서버 호출이 실패할 경우 UI 변경 사항을 롤백(Rollback)하면 됩니다.
이제 사용자가 새 작업을 추가할 때 UI가 즉시 업데이트되어 더욱 매끄러운 경험을 제공합니다.
풀스택 컴포넌트의 부상 (The Rise of Full-Stack Components)
React Server Components는 프론트엔드와 백엔드 로직을 모두 포함하는 새로운 유형의 재사용 가능한 컴포넌트를 가능하게 합니다. 좋아요 수를 표시하고 사용자가 아이템에 좋아요를 누를 수 있게 하며, 서버에서 좋아요 수를 증가시키는 데이터 변경 로직까지 포함된 <LikeButton> 컴포넌트를 생각해 보십시오.
이러한 컴포넌트는 데이터 페칭 및 변경 로직을 중복해서 작성하지 않고도 애플리케이션의 여러 곳에서 사용될 수 있습니다. 이를 때때로 *풀스택 컴포넌트(Full-Stack Components)*라고 부릅니다. 저는 이 용어를 Remix(및 react-router에도 적용됨)의 맥락에서 이 패턴을 설명하는 Kent C. Dodds의 글에서 빌려왔지만, 아이디어는 RSC에서도 거의 동일합니다.
우리는 <TaskCheckbox>를 <TaskListItem> 컴포넌트와 단일 작업을 수정하는 다른 페이지 모두에서 사용할 수 있습니다.
결론 (Conclusion)
명확성을 위해 스타일링과 필수적이지 않은 코드 대부분을 제거했지만, 이 예제 애플리케이션의 전체 소스 코드는 GitHub에서 확인할 수 있습니다: marmelab/parcel-rsc-app.
React Server Components는 React로 서버 렌더링 애플리케이션을 구축하는 강력한 방법입니다. 컴포넌트에서 직접 데이터를 가져오고 백엔드 함수를 더 자연스러운 방식으로 호출할 수 있게 해주어, 데이터 흐름에 대한 멘탈 모델(mental model)을 더 쉽게 세울 수 있게 하고 복잡한 상태 관리의 필요성을 줄여줍니다. 이는 애플리케이션이 빠르고 반응성이 좋으면서도 훌륭한 사용자 경험을 제공하도록 보장합니다.
반가운 소식은 RSC가 더 이상 Next.js와 같은 특정 프레임워크에 종속되지 않으며, Parcel과 같은 다른 번들러와도 함께 사용될 수 있다는 점입니다. 하지만 프로덕션 수준의 애플리케이션을 구축하려면 라우팅, 낙관적 업데이트, 캐싱과 같은 다른 고려 사항들도 해결해야 할 것입니다.