Epic React - React Server Component (2)

김동하·2일 전
0

react

목록 보기
24/25
post-thumbnail

Server Component 에 대해

1편에서 RSC에 대해 대략적으로 살펴봤다면, 다음으로 Server Component에 대해 알아보자.

1. Async Components

이제, RSC가 서버에서 실행되므로, 각 컴포넌트 내부에서 직접 데이터 요청이 가능하다.

가령, 목록 api에서 조회하던 데이터를 직접 컴포넌트에서도 조회할 수 있게 되는 것인데 GET /rsc/:shipId?를 살펴보면

여기서 getShip이나 searchShip은 DB 조회를 모방한 함수로써, shipData에 접근하여 데이터를 조회한다. (네트워크 지연 같은 상황도 모방)

이제 getShip를 서버 컴포넌트에서 호출할 수 있는 것이다.

1.1 /server/app.js

위에서 언급한대로 GET api에서 (흉내낸)DB 조회 함수를 지운다.

1.2 /client/app.js

그렇다면 이제 App.js도 데이터 조회의 결과를 props로 받을 필요 없으니 ship, shipResults를 props에서 제거한다.

1.3 /client/SearchResults.js

SearchResults에서 직접 DB를 조회하자.

SearchResults 컴포넌트는 검색 결과에 따라서 리스트가 나오는 좌측 부분이다.

컴포넌트에 async 키워드를 붙이고 DB 접근 함수를 호출하면 된다.

마찬가지로 Detail 컴포넌트에서도 직접 조회가 가능하다. Detail은 우측에 이미지가 나오는 부분이다.

동일하게 DB 직접 조회로 코드를 변경했다.

2. Streaming

이제 우리는 데이터가 필요한 컴포넌트에서 각각 DB 조회를 하여 데이터를 패칭할 수 있도록 변경했다.

하지만, 문제가 생겼는데 이 중 하나라도 데이터 페칭이 지연되면 전체 앱이 느리게 렌더링 된다는 것이다.

searchShip에 딜레이를 추가하고 다시 앱을 실행시켜보면

searchShip가 완료되기 전까지 전체 앱이 렌더링되지 않는 것을 확인할 수 있다.

서버 컴포넌트가 서버에서 독립적으로 async로 데이터 요청을 해서 병렬인가? 싶지만 사실 병렬이 아니다.

RSC는 기본적으로 전체 리액트 트리 완료 후 RSC payload 스트리밍을 하려고 하기 때문에 모든 컴포넌트가 준비될 때까지 기다리는 것이다.

이 문제를 해결하기 위해 Suspense를 사용해야 한다. Suspense를 통해 먼저 준비된 부분부터 스트리밍 가능하다.

2.1 Suspense

SearchResultsShipDetails 각각 Suspense를 해주자.

번들러가 없기 때문에 createElement를 직접 사용해서 조금 난해해보이지만 createElement(Component, props, child1, child2, child3...) 라는 걸 인지하면

<Suspense fallback={<SearchResultsFallback />}>
	<SearchResults shipId={sihpId} search={search} />
<Suspense/> 

와 동일하다.

이제 다시 앱을 확인하기 전에 RCS Payload가 어떻게 스트리밍 되는지 api를 먼저 확인해보자.

2.1.1 Suspense with 스트리밍

renderToPipeableStream가 반환하는 pipe 에서 RSC Payload를 어떻게 스트리밍하는지 보자.

해당 api인 http://localhost:4000/rsc/3ba8aa65ffe6c를 브라우저에서 보면 Payload 청크가 3번에 나눠서 스트리밍되는 것을 알 수 있다.

호출하자마자 처음부터 있는 가장 큰 청크인 이니셜 청크는 Root에 대한 청크다.

그 다음 스트리밍되는 청크가 딜레이가 없는 Detail 컴포넌트고

마지막으로 4초 뒤에 만들어지는 청크가 delay 4초를 추가한 SearchResults 컴포넌트다

그럼 앱을 실행시켜서 화면 동작을 확인해보자.

RCS Payload 스트리밍에서 본 것처럼 데이터가 준비되는 순서로 스트리밍 하고 지연된 컴포넌트만 Suspense에 걸리는 것을 확인할 수 있다. 서버 컴포넌트와 Suspense 조합으로 Cascading waterfalls 해결할 수 있다.

3. Server Context

1편에서 말했듯 RSC에선 상태 관리를 하지 못 한다. stateless니, 메모리니 이런 이유들을 제쳐두고서라도 느낌적으로 서버에선 hook 같은 기능을 사용하지 못할 것 같다.

하지만 컨텍스트로 상태를 관리하고 싶은 충동이 들 때도 있다.

가령, shipId, search같은 파라미터을 props가 아니라 전역 컨텍스트로 관리한다면 프롭스 드릴링을 줄일 수 있을 텐데 말이다!

그래서 사용할 수 있는 Node.js의 기능으로 AsyncLocalStorage가 있다.

3.1 AsyncLocalStorage

AsyncLocalStorage는 마치 클라이언트 훅에 useContext처럼 컨텍스트를 공유할 수 있는 기능이다.

AsyncLocalStorage 인스턴스를 하나 만들고 이제 api의 파라미터를 컨텍스트 안에 추가하면 된다.

run의 콜백 안에서는 모든 컴포넌트가 동일한 컨텍스트에 접근 할 수 있다.

이렇게 첫 번째 인자에 공유할 데이터를 명시하고, 콜백 안에서 pipe를 실행시키면 된다.

3.1 Client

이제 App에서 받던 props가 없으니 클라이언트 코드를 수정하면 된다.

getStore를 통해 서버에서 받은 파라미터를 공유할 수 있게 된다. 동일하게 shipID, search를 소비했던 컴포넌트들도 props가 아니라 AsyncLocalStorage로 컴포넌트에서 직접 조회하도록 수정하면 된다.(생략)

4. 정리

이로써 RSC가 해결하고자 했던 Cascading waterfallsProp drilling 가 서버 컴포넌트로 해결되었다. 서버 컴포넌트는 이만 마무리하고 클라이언트 컴포넌트를 살펴보자. (AsyncLocalStorage 신기하다)

참고 : epic-react

profile
프론트엔드 개발

0개의 댓글