이번 글에서는 React 18 버전부터 도입된 개념인 React Server Component 에 대해 정리한다.
이번에 침하하 clone 과 쇼핑몰 사이트 개발을 하면서 공부 겸 Next 14 를 사용했었다.
회사에서 사용할 때는 Next를 써보지 않않고, 공부할 때는 제일 최신버전인 14 버전으로 공부해서 page router 가 아닌 app router 기반으로 Next 개발을 시작했다.
저번주에 면접을 진행하면서 관련 질문을 받은 만큼 이번에는 Next 13 이상 버전의 app router 의 가장 큰 특징인 React Server Component 를 정리해보려고 한다.
RSC(React Server Component)
단순히 이름만 보았을 땐 이름에 Server 가 들어가기 때문에 SSR(Server Side rendering)에 관련된 컴포넌트인가? 생각할 수 있다. 결론을 바로 얘기하자면 RSC 와 SSR은 다르다. 어떻게 다른지 알아보기 위해 먼저 CSR 과 SSR을 비교하면서 SSR 개념부터 잡아보자.
CSR vs SSR
CSR(Client Side Rendering) 과 SSR(Server Side Rendering) 은 단순하게 보면 이름에서 알 수 있듯이 렌더링하는 방식에 있어서 client(브라우저) 에서 렌더링을 하는지, server에서 렌더링을 하는지에 대해 차이가 있다고 볼 수 있다.
CSR
위 사진을 보면 서버에서 응답이 오면 자바스크립트를 로드하고 실행이 완료될 때까지 유저는 화면을 확인할 수 없다.
기본적으로 SPA(Single Page Application) 이기 때문에 CSR 방식은 하나의 html 껍데기를 받고 로드된 자바스크립트를 실행하면서 화면을 동적으로 그리기 때문에 자바스크립트가 실행되기 전까지 화면을 볼 수 없다.
이는 초기에 자바스크립트를 받기 위한 시간이 필요하기 때문에 초기 로딩 속도가 느린 대신 이후에는 빠른 화면 전환이나 인터렉션이 가능하다는 장점이 있다.
또한, 서버에서는 하나의 정적파일만 제공하면 되므로 부하가 감소하는 장점이 있는 반면, 자바스크립트가 실행되기전까지 html에 내용이 없기 때문에 SEO(검색엔진최적화)가 취약하다는 단점도 있다.
SSR
SSR 은 위 사진을 보면 서버에서 껍데기 html 이 아닌 렌더링할 준비가 된 즉, 내용이 채워진 상태의 html 을 응답하기 때문에 브라우저는 이를 받으면 일단 html을 화면에 렌더링하고, 자바스크립트를 로드받고 실행하면서 html에 연결하는데 이를 hydration 이라고 한다.
따라서 SSR 은 초기 로딩 속도가 빠르며, 완성된 html 이 오기 때문에 SEO에도 CSR 보다 유리하다. 하지만 요청에 대해 서버에서 새로 렌더링 해야하므로 서버에 부하가 있을 수 있고, 먼저 html을 보여주고 이 후에 hydration 하기 때문에 사용자와의 상호작용하는 데 딜레이가 있게 된다.
그래서 RSC 는..?
이제 SSR에 대해 알아봤으니 먼저 위 RSC 와 SSR은 다르다. 라는 것에 대해 설명할 수 있을 거 같다.
SSR은 위에서 설명했듯이 서버에서 완성된 html을 응답할 뿐 결국 사용자와 상호 작용할 수 있도록 하는 hydration 과정이 있기 때문에 자바스크립트를 로드받고 실행해야한다. 하지만 RSC 는 서버에서 한번 실행되고 렌더링된 내용이 클라이언트로 전송되고 그 상태로 변하지 않는다. 그래서 유저 인터렉션도 할 수 없고 리렌더링도 되지 않는다. 그렇기 때문에 서버 컴포넌트는 번들에 포함되지도 않는다. 따라서 서버 컴포넌트에서 사용되는 라이브러리 역시도 번들에 포함되지 않아 번들 사이즈를 줄일 수 있다.
이렇게 SSR과 비교하며 RSC 의 특징이자 장점으로 제로 번들 사이즈가 있다는 것을 알았다. 이번에는 기존 컴포넌트인 RCC 와 비교하며 RSC 의 특징들을 좀 더 알아보자.
RSC 는 위에도 언급했듯 서버에서 실행되고, 응답되면 변하지 않기 때문에 useState, useEffect 와 같은 state 와 effect 에 관련된 hook을 사용할 수 없다. 같은 이유로, DOM과 같은 브라우저 api 역시 사용할 수 없다.
하지만 서버에서 실행되기 때문에 데이터베이스, 파일시스템과 같은 server 데이터에 직접 접근하고 사용할 수 있다. RSC를 소개하는 영상을 보아도 기존 컴포넌트의 api를 fetch 하는 데 생기는 문제점(api over-fetching, waterfall 문제 등등)들을 해결하기 위해 RSC가 나온 것으로 보인다.
실제 영상에서 예시로 들었던 demo 코드를 보면 아래처럼 리액트에서 직접 db에 접근하는 코드를 확인할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
export default async function NoteList({searchText}) {
// const notes = await (await fetch('http://localhost:4000/notes')).json();
// WARNING: This is for demo purposes only.
// We don't encourage this in real apps. There are far safer ways to access
// data in a real application!
const notes = (
await db.query(
`select * from notes where title ilike $1 order by id desc`,
['%' + searchText + '%']
)
).rows;
// Now let's see how the Suspense boundary above lets us not block on this.
// await fetch('http://localhost:4000/sleep/3000');
return notes.length > 0 ? (
<ul className="notes-list">
{notes.map((note) => (
<li key={note.id}>
<SidebarNote note={note} />
</li>
))}
</ul>
) : (
<div className="notes-empty">
{searchText
? `Couldn't find any notes titled "${searchText}".`
: 'No notes created yet!'}{' '}
</div>
);
}
정리
RSC는 말 그대로 서버에서 실행되는 컴포넌트를 의미하고, 그로인해 아래와 같은 특징과 장점이 있다고 정리할 수 있을 거 같다.
특징
- 서버에서 실행
- 유저 인터렉션 불가능
- useState, useEffect 등 hook 사용 불가
- 브라우저 API 사용 불가
- 데이터베이스 접근, 파일시스템과 같은 server 기능 사용 가능
장점
- 서버 리소스에 자유로운 접근
- 제로 번들 사이즈 컴포넌트
- 자동 코드 분할
- 서버 컴포넌트에서 import 되는 모든 클라이언트 컴포넌트를 code splitting 포인트로 간주해 React.lay 로 명시하지 않아도 됨.
마치며..
최근 Nextjs 14 으로 개발을 하고 저번주 면접을 보면서 관련 질문도 받아서 서버 컴포넌트에 관해 정리를 해보았다. app router가 서버 컴포넌트를 기반으로 구현된 거 라는 것만 대충 알고 있었는데, 이번 기회에 알아보니 꽤 도움이 됐던 거 같다. 처음에 리액트 공식문서에 있는 영상을 보았을 땐, 영어로 되어 있어 막막했는데 다른 분들이 한글로 정리를 잘 해주신 것들을 많이 참고해서 조금은 이해가 되었던 거 같다.
서버 컴포넌트가 나오면서 앞으로는 컴포넌트를 분리할 때 해당 컴포넌트가 서버 컴포넌트가 적합한 지 클라이언트 컴포넌트가 적합한 지 고민을 열심히 해야할 거 같고 그러한 고민들을 통해 좀 더 명확한 기준을 가지고 컴포넌트의 역할을 나눌 수 있을 거 같다는 생각이 들었다. 그리고 영상에도 나왔지만 서버 컴포넌트는 기본적으로 api fetching을 위해 나온 거 같은데, 그렇다면 기존에 사용하던 react-query 같은 api fetching 라이브러리들은 어떻게 활용될지 아니면 서버 컴포넌트의 server action 에 대체가 될 지 이런것들도 궁금해졌다. 나는 침하하 clone을 하면서 별 생각 없이 react-query를 썼던 거 같은데, 이 후 개발을 할 때에는 server action도 한번 사용해보고 싶다.
끝으로.. 저번주는 면접을 핑계(?)로 개발도 많이 못했으니 이제 내일부터 다시 열심히 해보려고 한다!!! 화이팅 ✋
참고 링크
- https://react.dev/blog/2020/12/21/data-fetching-with-react-server-components
- https://tech.kakaopay.com/post/react-server-components/
- https://velog.io/@2ast/React-%EC%84%9C%EB%B2%84-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8React-Server-Component%EC%97%90-%EB%8C%80%ED%95%9C-%EA%B3%A0%EC%B0%B0
- https://velog.io/@9rganizedchaos/React-Server-Components%EB%A5%BC-%EC%9D%B4%ED%95%B4%ED%95%B4%EB%B3%B4%EC%9E%90
- https://velog.io/@cheal3/%EC%84%9C%EB%B2%84%EC%82%AC%EC%9D%B4%EB%93%9C-%EB%A0%8C%EB%8D%94%EB%A7%81-SSR-%EA%B3%BC-%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8-%EC%82%AC%EC%9D%B4%EB%93%9C-%EB%A0%8C%EB%8D%94%EB%A7%81-CSR-%EC%97%90-%EB%8C%80%ED%95%B4

