NextJS 이해 해보기_2 - Server Component

박재현·2024년 6월 8일
2

NEXT.JS

목록 보기
10/17
post-thumbnail

NextJS App Route와 React18 버전부터 지극히 개인적인 내생각에 가장 많이 변한 부분이 바로 React Server Component이지 않을까 생각한다.

관련해서 그냥 얼렁뚱땅 써보고 "아~ 이런식이구나?" 이런것 보다 제대로 찾아봐야할 필요성을 느꼈다.

일단 Server Component가 뭔지 알아보기 이전에 React 18 이전과 이후의 렌더링을 먼저 간략하게 살펴볼 필요가 있다고 생각한다.


렌더링(Rendering)이란?

렌더링이란 결국 코드를 사용자가 볼 수 있는 UI로 만드는걸 의미한다.
따라서 React에서의 렌더링은 코드로 작성한 React Component들을 HTML로 만드는 과정이다.

React 18 이전의 렌더링

1. CSR (Client Side Rendering)

React 18이 나오기 전, 리액트 컴포넌트의 모든 렌더링은 클라이언트 즉, 사용자의 브라우저에서 이루어졌다.

  • 서버는 React로 작성한 JavaScript 코드 뭉텅이(이를 번들링이라고 한다)들을 브라우저에게 응답(Response) 한다.
  • 브라우저는 응답받은 JavaScript 파일을 해석한 다음 HTML파일로 렌더링하고, 인터랙션이 필요한 Event Listener와 같은 코드들을 장착했다.

2. SSR (Server Side Rendering)

NextJS가 등장하면서 아래와 같은 방식의 SSR을 쉽게 구현할 수 있게 되었다.

  • NextJS를 통해서 서버단에서 1)페이지 별로 코드를 쪼갠 후(Code Split), 2) HTML을 렌더링한 다음, 3) 브라우저에게 응답
  • 브라우저는 전달 받은 1)HTML을 화면에 보여주고, 2)추가로 내려받은 JS코드들을 연결하는 Hydration 과정을 거친다.

하지만 이때도 기본적으로 브라우저에서 렌더링이 되는 CSR이 기본이었고, 서버에서 렌더링되는 SSR을 적용하려면 getServerSideProps() 함수와 같은 추가적인 코드 작업이 필요했다.

React 18 이후의 렌더링

React 18 버전부터 Server Component가 도입되었고 NextJS 13, 즉 App Route 부터 이를 완벽하게 지원한다.

따라서 NextJS App Route 부터는 아래 두가지 형식으로 컴포넌트를 만들 수 있다.

Server Component

  • 서버에서 HTML을 렌더링해서 브라우저에게 응답한다.
  • Hydration 과정은 없다.

Client Component

  • 서버에서 기본적은 HTML은 렌러링해 브라우저에게 응답한다.
  • Hydration(JS Interactivity)만 브라우저에서 이루어진다.

기본적은 HTML 자체는 모두 서버에서 렌더링된다!

즉 Client Component라고 할지라도 렌더링은 서버에서 이루어진다, 다만 Hydration은 브라우저에서 일어난다.

React18 이전 버전과 NextJS Page Route를 사용해본적이 없다보니 Client Component와 CSR이 개인적으로 많이 혼동이 왔었다...

즉, Event Listener(클릭 이벤트 등등), Browser API(local storage 등등), React Hooks(useState, useEffect 등등)은 브라우저에서만 작동하는 클라이언트단 JS코드이고, 브라우저에서 Hydration 과정을 거치는 Client Component 에서만 정의될 수 있다.

따라서 Server Component 에서는 사용이 불가능하다!

관련해서 본격적으로 Server Component에 대해서 알아보자.


Server Component

React Server Component를 사용하면 서버에서 UI를 구성하고 또한 선택적으로 Cached도 할 수 있다.

NextJS에서는 크게 3가지의 다른 서버 렌더링 전략을 가지고 있다.

  • Static Rendering
  • Dynamic Rendering
  • Streaming

Server Rendering의 이점

서버에서 렌더링 작업을 수행하면 아래와 같이 몇가지 이점이 있다.
(내 생각에 이점 정도가 아니라 그냥 사기같다 ㅋㅋ)

데이터 패칭

API를 사용해서 데이터를 얻어오는 Date Fetching을 Server Component 코드에서 바로 사용할 수 있다.

이를 통해 렌더링에 필요한 데이터를 가져오는 데 걸리는 시간과 클라이언트가 수행해야 하는 요청(Request) 수를 줄여 성능을 향상할 수 있다.

보안

Server Component는 API Keys 혹은 Token과 같이 민감한 정보 또는 민감한 로직을 클라이언트에게 노출될 위험없이 서버에 보관할 수 있다.

캐싱

서버에서 렌더링하면 결과를 Cached하고 이후에 발생되는 요청 및 사용자 전체에게 재사용할 수 있다.

따라서 성능을 향상시킬 수 있고 동시에 각각의 요청에 따른 데이터 페칭과 렌더링에 들어가는 비용을 아낄 수 있다.

성능

Server Comoponent는 추가적인 성능 최적화가 가능하다.

Server Component를 사용하면 Interactive하지 않은 부분들은 미리 렌더링이 되어있기 때문에 User가 다운로드 받는 JavaScript의 양이 줄어들게 된다.

따라서 User의 Browser에서 JavaScript 다운로드, 파싱, 실행의 부담이 줄어들기 때문에 인터넷 성능이 좋지 않은 단말이나 환경에서도 유리하다.

초기화면(FCP)

First Contectful Paint(FCP)
FCP는 사용자가 볼 수 있는 가장 첫번째 UI를 의미한다.
예를 들어서 구글에 "일론머스크" 라고 검색하면 가장 처음 볼 수 있는 화면은 아마도 상단의 Google 로고와 Serach Bar가 될것이다.

서버에서는 HTML을 생성할 수 있고, 이렇게 생성한 HTML은 사용자가 화면을 바로 볼 수 있게 해준다.

근데 이때 사용자는 어떠한 로딩이나, JS를 다운로드 받아서 실행하는 단계가 필요없이 즉각적으로 볼 수 있다. 와우!

검색엔진, SNS엔진 최적화

서버에서 미리 렌더링된 HTML은 검색 엔진 봇이 페이지를 확인하고 소셜 네트워크 봇이 페이지에 대한 썸네일을 생성하는 데 사용될 수 있다.

한마디로 Bot이 서버에서 미리 렌더링된 HTML을 분석할 수 있다라는소리!

스트리밍

Server Component를 사용하면 렌더링 작업을 청크로 분할하고 준비가 되면 클라이언트로 스트리밍할 수 있다.

이를 통해 사용자는 전체 페이지가 서버에서 렌더링될 때까지 기다리지 않고도 페이지의 일부를 더 일찍 볼 수 있다.

여기서 말하는 스트리밍의 의미는 결국 유저가 SSR처럼 모든 페이지가 다 렌더링 될 때 까지 기다리는게 아니라, 완료된 부분부분의 화면을 연속해서 끊기지 않고 볼 수 있다는 의미로 보인다.


NextJS에서 Server Component 사용하기

NextJS App Route에서는 기본적으로 Server Component를 사용한다.

따라서 자동적으로 추가적인 작업 없이 서버 렌더링을 구현할수 있고 필요에 따라서 Client Component를 사용할 수 있다.


Server Component는 어떻게 렌더링 될까?

서버에서 Next.js는 React의 API를 사용하여 렌더링을 조정하는데, 렌더링 작업은 개별 경로 세그먼트와 서스펜스 경계를 기준으로 청크단위로 나뉜다.

그리고 청크는 아래 두단계를 거쳐서 렌더링된다.

  1. 리액트는 Server Component를 Reacr Server Component Payload(RSC Payload)라는 특수 데이터 형식으로 렌더링 한다.

  2. NextJS는 RSC Payload 및 Client Component JavaScript의 설명(지침?)을 토대로 서버에서 HTML을 렌더링 한다.

하... 역시 가장 시급한 공부는 리액트가 아니라 영어다 ㅋㅋ

여튼 이렇게 되면 클라이언트 에서는

  1. 인터렉티브 하지 않은 HTML 페이지를 바로 화면에 보여주는데, 이는 초기 페이지 로딩에만 해당된다. (그도 그럴것이 서버에서 렌더링한 HTML이기 때문에 사용자와 상호작용하는 부분은 제외되는게 맞다.)

  2. 리액트 Server Components Payload는 클라이언트 및 Server Component 트리를 조정하고 DOM을 업데이트 하는데 사용된다.

  3. Hydration되고 Application을 인터렉티브 하게 만든다.

React Server Component Payload(RSC)가 뭐임?

RSC Payload는 렌더링된 리액트 서버 컴포넌트 트리의 압축된 표현이다. 브라우저의 DOM을 업데이트하기 위해서 리액트에서 사용된다.

  • 서버 컴포넌트의 렌더링 결과
  • 클라이언트 컴포넌트가 랜더링 되어야 하는 위치와 해당 JS 파일에 대한 참조를 표시
  • 서버 컴포넌트에서 클라이언트 컴포넌트로 전달되는 props들

서버 렌더링 전략

서버를 렌더링하는 전략에는 3가지가 있는데, Static, Dynamic, Streaming이 있다.

1. 정적 렌더링 (기본)

정적 렌더링은 빌드할때 렌더링 된다. 아니면 data revalidation 이후에 백그라운드에서 렌더링이 된다.

이렇게 렌더링된 HTML은 CDN에 의해서 Cached가 되고, 이 최적화를 통해서 사용자와 서버간에 렌더링 결과를 공유할 수 있다. (걍 대충 HTML을 미리 만들어 뒀으니가 그거 그냥 갖다주면 되니까 새로 만들 필요 없어서 Cached되었단 의미)

정적렌더링은 페이지를 보는 사용자에게 Dependency가 없거나, 아니면 빌드할때 필요한 외부 데이터를 알 수 있는 페이지에 유리하다. Ex) 블로그 포스팅, 제품 페이지

2. 동적 렌더링

동적 렌더링을 사용하면 요청때마다 각각 사용자에대한 경로가 렌더링 된다.

동적렌더링은 쿠키(Cookies)나 URL의 search params를 사용하는(그니까 /products/[id]와 같은 동적 URL)경우, 즉 사용자에 특화된 페이지를 보여주거나 사용자 마다 다른 페이지를 보여줘야 할때 아주 좋다.

동적 렌더링으로 전환

렌더링하는 동안 "Dynamic Function" 또는 "uncached data 요청"이 발견되면 NextJS는 동적 렌더링으로 전환하게 된다.

즉, Cached되지 않은 데이터가 필요하거나(아마도 새로운 값으로 업데이트 되었겠지) 아니면 Dynamic함수가 사용되는걸 발견하면 정적 렌더링으로는 대응할 수 없으니 자동으로 동적 렌더링으로 바꾼다는 소리로 이해했다.

위 사진과 같이 Dynamic Function도 없고 데이터가 이미 캐싱된 상태라면 정적 렌더링으로 대응이 가능하지만, 둘 중 하나라도 만족하지 못하면 새로운 값을 보여줘야 하기때문에 동적 렌더링으로 자동으로 전환 해야한다.

근데 여기서 어썸한건 무엇이냐!!?

바로 NextJS는 아주 똑똑해서 지가 알아서 사용한 API를 기반으로 각각의 페이지에 맞춰서 가장 적합한 방법으로 렌더링 전략을 세워준다는거다.

따라서 코드를 작성하는 사람 입장에서 정적 렌더링이니 동적 렌더링이니 선택을할 필요가 없다.

다만 특정 데이터를 Caching하거나 또는 Cached된 데이터를 Update하기 위해서 Revalidate 하는 부분에 대해서 신경써줘야 한다.

Dynamic Function이 뭐임?

Dynamic Function은 사용자의 쿠키나, 현재 Request의 헤더 또는 URL의 search params와 같이, 페이지 요청시에만 알 수 있는 정보에 의존하는 경우를 말한다.

따라서 현재 페이지를 보고있는 User의 정보가 필요하거나, API에게 Header를 포함한 Request를 날리거나, URL에 변수가 포함되어서 URL을 파싱해야하는 경우(/products/[id])를 예로 들수있다.

  • cookies() and headers(): 서버 컴포넌트에서 이를 사용하면 전체 라우트가(현재 보고있는 URL의 화면) 동적 렌더링으로 선택 된다.
  • searchParams: searchParams props를 페이지(page.tsx 파일)에서 사용하면 유저로부터 요청이 들어오면(즉 페이지를 접속하면) 동적 렌더링으로 선택된다.

이러한 기능 중 하나라도 사용하면 유저로 부터 요청받은 경로 전체가 동적 렌더링으로 된다. (hostname://products/[id])

3. 스트리밍

스트리밍을 사용하면 서버에서 UI를 점진적으로 렌더링할 수 있다. 작업은 여러 단위로 분할되어서 준비가 완료되면 클라이언트(유저)로 스트리밍 된다.

이를통해서 사용자는 전체 콘텐츠의 렌더링이 완료되기 전에 페이지의 일부를 즉시 볼 수 있다.

스트리밍은 NextJS App Route에 기본적으로 내장된 기능이다.

이는 초기 페이지 로딩 성능뿐 아니라 전체 경로 렌더링을 차단하는 외부 데이터를 갖고오는 느린 작업으로 부터 의존하는 UI를 개선하는데 도움이 된다.

그니까 이게 무슨말일까??

SSR + getServerSideProps()를 한번 상상해보자.

서버가 바빠서 사용자에게 보여주고 싶은 외부 데이터를 처리하는데 너무 오래걸린다면, 그 오래걸리는 동안 사용자는 아무것도 보지못하고 서버가 일을 다 할때까지 기다려야한다.

또, CSR의 경우를 생각해보자.

사용자에게 이번달에 전세계에서 개봉할 영화와 한국에서 개봉할 영화를 보여주고 싶다고 가정하자.

그러면 fetch를 통해서 API로 부터 이번달에 전세계에서 개봉할 영화들과 한국에서 개봉할 영화를 받아와야하는데, 받아올 동안 유저는 로딩화면만 계속 봐야 한다는 이야기다.

하지만 이런것들을 서버 컴포넌트와 스트리밍이 만나면 개선할 수 있다는 소리다!!!

어떻게?

먼저 SSR의 경우는 서버에서 일을 다 끝낼때까지 계속 기다려야 하지만, 스트리밍은 서버에서 일을 다 끝낼때까지 기다리는게 아니라, 부분 부분적으로 완료가된것들을 바로바로 보여준다는 소리다.

또 CSR의 경우는 Suspense 혹은 loading.tsx를 이용해서 하나의 페이지에서 "한국에서 개봉할 영화"와 "전세계에서 개봉할 영화"를 동시에 받아오기 시작하고.

먼저 다 받아온 데이터를 일단 보여주고, 나머지 데이터도 완료가 되면 보여준다는 소리다.

즉 하나의 페이지 내부에서도 부분적으로 먼저 준비가된 것들을 보여줄 수 있다는 소리다.

Suspense를 사용해서 보여줘도 되고, Loading.tsx를 사용해도 될거같다.


영어로 작성된 서버 컴포넌트 공식 문서를 나름대로 이해한 내용에 대해서 정리했다.

당장 자바스크립트보다 영어공부가 더 필요하다는걸 느낀다..ㅋㅋ

원문으로 보는게 더 직관적인데, 이걸 한글로 다시 적으려니 표현이 애매한 부분들이 많은것 같다.

profile
기술만 좋은 S급이 아니라, 태도가 좋은 A급이 되자

2개의 댓글

comment-user-thumbnail
2024년 6월 9일

아 재현님 정리 재밌어요. 정리 또 기다릴게요 호호

1개의 답글

관련 채용 정보