본문 바로가기
JavaScript/Next.js

[Next.js] Next.js Routing과 Hydronation, 'use Client' 알아보기

by junvely 2024. 3. 15.

[Next.js] Next.js Routing과 Hydronation, 'use Client' 알아보기

Next.js에 대해 공부해보면서 알게된 내용들을 간단히 정리해 보고자 한다.

먼저 React는 라이브러리이고, Next.js는 프레임워크이다. 학습하면서 차이가 있었던 것은 리액트는 라이브러리 이기 때문에 폴더 구조, 이름, 라우팅, CSS, 하나부터 열까지 모든 선택과 결정을 내가 해야 했었다. 어떻게 보면 하나 하나 전부 고민해야 하고 여러가지를 공부하고 선택해야 하는 번거로움이 있었지만 내가 원하는 대로, 필요할 때 원하는 패키지와 개발 방식으로 자유롭게 개발할 수 있다는 장점이 있었다. Next.js같은 경우는 프레임워크기 때문에 그들이 정해놓은 룰과 규칙이 있었고, 라우팅 방식이나 폴더구조, 파일이름, CSS까지 그 규칙을 준수해야지만 개발이 가능했다. 하지만 나보다 더 멋진 개발팀이 짜놓은 규칙과 틀 안에서 보다 편리해진 기능으로 개발한다는 것이 어떻게 보면 개인적으로 안심도 되고 리액트에 비해 편리해진 기능들도 많았다. Next.js 프레임워크는 현재 리액트 메타 프레임워크 1위로 급부상할만큼 많은 인기를 끌고 있지만 한편으로는 13,14 등 최신 버전이 아직 베타 수준이고 아직 불안정하다는 여론도 많은 것 같다. 

Next.js 13,14 버전 이후의 app 디렉토리가 등장하면서 편리해진 기능들이 많은 것같다. 어떤 사이드 이펙트가 있을지는 아직 잘 모르겠지만.... 그래도 계속해서 새로운 시도를 해보고 시야를 넓히는건 개발에 큰 도움이 될테니 차근차근 배우면서 간단히 정리해 보고자 한다.


1. Next.js 설치(수동 설치)

1. create next-app을 사용하지 않고 간단히 프로젝트를 생성해 본다. react, react-dom, next를 최신버전으로 설치해 준다.

npm install react@latest react-dom@latest next@latest

2. package.json에 "dev" : "next dev" 추가

"scripts": {
    "dev": "next dev",
    ...

3. src폴더 생성/app폴더/ 내부에 page.tsx 생성(13버전 app 디렉토리)

4. npm run dev로 앱을 실행시키면, typescript를 따로 설치하지 않아도 앱 실행과 동시에 typescript 패키지와, 폴더 구조까지 생성해 준다.

npm run dev

 

2. Router

Next.js에서 라우팅은 파일시스템을 통해 폴더 구조대로 url이 생성된다.

13 이전 버전에서는 pages구조를 사용하여 index.tsx, home.tsx 등 파일명으로 라우팅이 됐었다. -> /  또는 /home 으로 생성됨

├── /pages
│   ├── api
│   │     └── hello.ts
│   ├── _app_.tsx
│   ├── _document.tsx
│   ├── index.tsx  ----> /
│   └── page1.tsx  ----> /age1

13버전의 app 디렉토리 구조를 사용할 때는 aap/폴더명/page.tsx 방식으로 라우팅 한다. -> /폴더명 으로 생성됨

├── /app
│   ├── api(API Routes 지원 여부 불확실)
│   ├── page1 ----> /page1
│   │  ├──── page2  ----> /page1/page2
│   │  │    └── page.tsx
│   │  ├─── page.tsx //page명은 반드시 page.tsx 필수
│   │  ├─── layout.tsx
│   ├── layout.tsx
│   └── page.tsx

이때 폴더명으로 라우팅이 되며, 폴더 내부 페이지명은 반드시 page.tsx로 설정해 주어야 라우팅이 가능하다.

참고로 app 디렉토리로 구성할 경우에도, 모든 컴포넌트들은 서버 컴포넌트로 렌더링된다(RSC:React Server Component) 

 

3. CSR vs SSR 

먼저 SSR과 CSR에 대한 이해가 부족하다면 다음 포스팅을 통해 공부하고 오도록 하자.

 

CSR(Client Side Rendering)과 SSR(Server Side Rendering)의 차이점

CSR(Client Side Rendering)과 SSR(Server Side Rendering)의 차이점 1. 브라우저 렌더링 브라우저가 서버로부터 요청해 받은 내용을 브라우저 화면(View)에 표시해주는 작업을 말한다. Chorme 같은 경우, 브라우저

junvelee.tistory.com

 

CSR(Client Side Rendering)의 단점

1) 사용자 경험성이 떨어진다 : CSR은 HTML문서가 비어있고, JS에 의해 HTML요소가 추가되는 형태이다. -> 초기 렌더링이나 새로고침 시 유저가 페이지를 처음 봤을 땐 빈 화면을 보게된다. JS가 다운로드되고 실행되면 그제서야 화면이 보이기 때문에 초기 렌더링이 오래걸린다. -> 만약 사용자의 네트워크가 느릴 경우, JS파일이 다운로드되고 실행될 때 까지 기다려야만 한다.

2) SEO(검색엔진)에 취약 하다 : 구글 검색엔진은 빈 HTML을 보게 되므로 정보를 얻을 수 없다(구글 검색엔진은 간혹 JS를 실행시킨다고 하지만 대부분의 검색엔진은 JS까지 실행시키지 않고 HTML를 분석한다.)

이와같은 CSR의 단점으로 최근 SSR의 필요성이 높아진것 같다.

SSR(Server Side Rendering)의 장점

1) SEO에 유리 : SSR은 서버에서 렌더링하여 완성된 HTML을 클라이언트에 전달한다. 이 때문에 모든 내용을 HTML이 가지고 있어 SEO에 유리하다. 

2) 초기 렌더링 빠름 : 네트워크가 느리거나, JS가 실행되기 전에도 이미 사용자는 HTML을 통해 웹 페이지 내용을 확인할 수 있다. 초기 렌더링 시에도 유저가 빈화면을 볼 일이 없기 때문에 초기렌더링이 빠르다. 즉 모든컴포넌트 UI를 빌드하는데 JS에 의존적이지 않다.(다만 동적인 반응은 JS가 다운로드 된 후 일어나기 때문에 격차는 발생할 수 있다)

Next.js의 경우 SSR이지만 그 단점을 좀 더 보완시켰다. 아래에서 확인해 보자.

 

4. Hydration

SSR의 경우 모든 페이지를 서버에서 렌더링 하기 때문에 서버로부터 완성된 HTML을 받아 온다. 때문에  페이지 이동 시 새 페이지를 다시 요청하여 로딩 속도가 오래걸리고 깜빡임이 발생했다.

Next.js에서는 Hydration과정을 통해 이를 줄일수 있도록 하였다. 간단히 요약하면 다음과 같다.

사용자가 url 접속 -> 서버에서 렌더링된 HTML 전달 -> 클라이언트에서 HTML을 받아 바로 사용자에게 보여줌
-> 이와 함께 프레임워크가 즉시 JS를 load해서 초기 HTML 위에 React 컴포넌트로 초기화 시킴 -> React app을 생성함

Hydration은 단순한 HTML문서를 -> React app으로 초기화하는 작업을 뜻하며 클라이언트에서 이루어진다. 이 과정을 통해, 사용자는 초기 렌더링 시 빠르게 HTML문서를 확인 가능하면서도, 클라이언트 쪽에서 즉시 JS를 리액트 컴포넌트로 초기화하여 -> 페이지 이동시에도 reload없이 CSR과 같이 부드럽게 페이지를 이동시킬 수 있다. SSR의 단점이었던 페이지 이동시 느린 로딩 속도를 보완한 것이다.

 

5. 'use Client'

이전까지는 Next.js에서 모든 컴포넌트가 서버 컴포넌트(RSC)였기 때문에 서버에서 렌더링 되고, 또 모든 컴포넌트가 클라이언트 쪽에 JS번들로 전달되어 Hydration 되었다. 이말은 곧 클라이언트쪽에 넘겨지는 JS파일들이 많다는 뜻이고, 그만큼 페이지 이동시에도 (서버에서 새 페이지 렌더링 + 받는 JS파일이 많음) 로딩 속도가 많이 빠르지 못한 부분이 있었다.

 ❗주의) 클라이언트 컴포넌트 또한 서버에서 렌더링되는 것은 같다. 다만 클라이언트 쪽에도 JS를 전달하여 렌더링 -> Hydration 된다.(클라이언트 컴포넌트라고 해서 클라이언트에서만 렌더링되는 것이 아니라 '둘 다'에서 렌더링 된다.)

Next.js 13버전에서도 app 디렉토리를 구성하면 기본적으로 모두 서버 컴포넌트가 되며 서버에서 렌더링 된다. 하지만 이번에 'use Client'가 등장하면서 선택적으로 클라이언트 컴포넌트(RCC)로 만들어 특정 요소만 클라이언트에 전달하여 Hydrate 할 수 있게 되었다.

즉, 오직 "use Client"를 선언한 클라이언트 컴포넌트만 클라이언트 쪽에(전달된다)서 Hydrate되는 것이다. 

이로 인해 달라진 점은, 서버 컴포넌트와 클라이언트를 선택적으로 적용 가능하게 되었고, JS 동적인 기능이 필요한 컴포넌트만 클라이언트 컴포넌트로 선언하여 서버 -> 클라이언트로 JS번들을 전달하여 Hydration(JS로 컴포넌트화)시킨다. 이렇게 하면 서버 컴포넌트는 더 이상 클라이언트에 JS번들이 전달되지 않고(서버 컴포넌트는 동적인 기능이 없기때문에 클라이언트에 전달될 필요가 없다), 클라이언트가 받는 JS번들은 오직 클라이언트 컴포넌트밖에 없기 때문에 이전에 비해 전달되는 JS파일들이 적고 페이지 로딩 속도가 더 빨라진다(페이지 이동, 새로고침 시 로딩 속도 문제 보완).

+ 어떤 컴포넌트를 클라이언트 컴포넌트('use Client')로 선언할지는 고민하지 않아도 된다. onClick, useState 등의 hooks, Link 등 reload, 동적인 동작을 할 경우 자동으로 'use Client'를 선언해야 한다고 에러를 띄워준다. 

+ 서버 컴포넌트와 클라이언트 컴포넌트가 나뉘어짐으로서 서버컴포넌트는 더이상 클라이언트로 전달되지 않기 때문에 보안성이 높다.(API key, DB와 통신 가능 등)

 

6. 'Client Component' 내부에 'Server Component' ?

그렇다면 서버 컴포넌트와 클라이언트 컴포넌트를 섞어서 사용할 경우, 클라이언트 컴포넌트는 클라이언트로 전달된다고 했는데 클라이언트 컴포넌트가 서버 컴포넌트를 감싸고 있을 경우에는 어떻게 될까? 의문이 들 것이다.

기본적으로 클라이언트 컴포넌트의 자식은 모두 클라이언트 컴포넌트 모듈로 간주되어 클라이언트로 같이 전달된다. 하지만 이번에 변경되어 서버 컴포넌트를 클라이언트 컴포넌트의 props으로 전달하여 자식으로 배치시키면 가능하다고 한다.

자세한 내용은 공식문서를 참조하자.

 

<참조>

공식문서 및 nomadcoders의 Next.js 시작하기 강의로 공부한 후 정리한 내용입니다.