"use client";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { useState } from "react";
next.js에서는 컴포넌트 상단에 위 코드와 같이 컴포넌트의 최상단에 "use client" directive를 추가할 수 있다. 해당 directive를 추가한 컴포넌트는 Client Component로 작동하게 된다.
하지만 next.js에서 컴포넌트가 client component로 작동한다고 해서 SSR이 아닌 CSR로 작동한다는 것은 아니다(즉슨, 첫 렌더링은 서버에서 이루어진다). 컴포넌트 최상단에 "use client"이 존재한다는 것은 해당 컴포넌트에 hydration이 일어난다는 의미이다.
hydration의 무엇인지 설명하기에 앞서, 먼저 CSR과 SSR의 차이를 간단히 짚어보자.
CSR (Client-Side Rendering)
CSR 방식의 경우 초기에 페이지를 로드할 때, 하나의 빈 html 파일과 거대한 js 번들 파일을 서버로부터 전달받게 된다.
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
때문에 CSR 방식의 index.html 파일의 body 태그 내부에는 root 역할을 하는 div 태그만이 존재하며, 이후 페이지에 포함되어야 할 모든 컴포넌트들은 Critical rendering path 과정에서 js 파일의 해석과 함께 실제 렌더 트리에 추가된다.
React 프로젝트 내 사용되는 모듈(의존성, 컴포넌트 등등)들은 빌드 과정에서 하나의 거대한 js 파일로 번들링되기 때문에, 큰 용량으로 인해 초기 페이지 로딩에 있어 불리하다. (이 부분은 code splitting을 통해 해소가 가능하다)
또한 CSR 방식만을 채택하는 웹페이지는 하나의 html 파일만 존재해 SEO 최적화에 있어서도 불리하다.
SSR (Server-Side Rendering)
CSR은 클라이언트(브라우저)에서 렌더링이 발생한다면, SSR은 호스트(서버)에서 렌더링이 발생한다. 이는 즉슨, 이미 서버에서 렌더링을 마쳤으므로 DOM 조작을 위한 추가적인 js 파일이 필요하지 않을 뿐더러, 컴포넌트 별로 완성된 html 파일로 제공되어 SEO 최적화에도 유리하다.
그렇다면 SSR 방식이 완성된 정적 html만을 제공한다면, 브라우저 상에서 발생하는 interactive한 event들은 어떻게 처리할까?
이를 해결하기 위한 방식이 바로 이번 글의 핵심인 hydration이다.
Hydration
Hydration은 SSR로 제공된 컴포넌트에 interactive한 동작을 추가하기 위한 eventListener를 추가하는 과정이며, 이 과정을 위해 또 한번의 렌더링이 이루어진다.
즉, Hydration는 Server Side 단에서 렌더링 된 정적 페이지와 js 파일을 클라이언트에게 보낸 뒤, 클라이언트 단에서 HTML 코드와 js 코드를 서로 매칭 시키는 과정을 말한다.
오해하지 말아야 할 것은, 여기서 이루어지는 두 번째 렌더링에서는 paint 과정을 다시 거치지는 않는다는 것이다.
위에서 살펴 본 "use client" directive를 다시 떠올려보자, 컴포넌트의 최상단에 "use client"를 선언하게 된다면, 해당 컴포넌트는 client component가 되며 hydration을 적용한다는 의미가 된다.
여기서 오해하지 말아야 할 점이 있다.
"use client" directive를 선언한 Client Component도 여전히 SSR로 동작하며 서버에서 Pre-Rendering이 이루어진다는 것이다. Client Component로 동작한다고 해서 CSR로 동작하는 것이 아니라는 점을 알아두자.
"use client"의 역할은 해당 컴포넌트에서 동적인 이벤트가 이루어질 수 있으며, 이벤트를 처리하기 위한 추가적인 js 파일과 hydration이 필요하다고 명시해두는 선언부로 생각해 볼 수 있다.