[Next.js] 공식문서 : How Next.js Works

👾·2023년 6월 26일
0

Next.js

목록 보기
2/4
post-thumbnail

좀 더 심화된 Next.js 기능을 배우기 전에, Next.js가 어떻게 동작하는지에 대한 개념을 이해하면 도움이 될 것이다.

강의 초반에서, React가 애플리케이션을 만들고 구조화하는 방법에 대해 어떻게 상대적으로 이견이 없는지에 대해 이야기했다. - React로 애플리케이션을 만드는 방법에는 여러가지가 있다. Next.js는 애플리케이션을 구조화하는 프레임워크를 제공하며, 최종 애플리케이션과 개발 프로세스를 더 빠르게 만드는데 도움이 되도록 최적화한다.

다음 강의에서, 다음과 같은 다양한 단계에서 애플리케이션 코드에 어떤 일이 발생하는지 알아보자.

  • 코드가 실행되는 환경 : Development vs Production
  • 코드가 언제 실행되는지 : Build Time vs Runtime
  • 렌더링이 어디서 발생하는지 : Client vs Server

이제 이러한 개념들에 대해 좀 더 자세히 알아보고, Next.js가 백그라운드에서 수행하는 일부 프로세스에 대해 이야기해보자.

From Development to Production

Development and Production Envirnoments

환경(environment)은 코드가 실행되는 컨텍스트와 같이 생각하면 된다.

개발을 하면서, 개인 컴퓨터에 애플리케이션을 만들고 실행한다. Going to production은 애플리케이션을 배포할 수 있고 유저가 이용할 수 있게 만드는 과정이다.

How this applies to Next.js

Next.js는 애플리케이션의 개발 단계와 프로덕션 단계에 대한 기능을 모두 제공한다. 예를 들어:

  • 개발 단계에서, Next.js는 개발자와 애플리케이션을 개발하는 개발자 경험을 최적화한다. TypeScript, ESLint integration, Fast Refresh 등과 같이 개발자 경험 향상을 목표로하는 기능들이 제공된다.
  • 프로덕션 단계에서, Next.js는 최종 사용자와 애플리케이션을 사용하는 그들의 경험을 최적화한다. 성능과 접근성을 높이기 위해 코드를 변환하는 것을 목표로 한다.

각 환경은 다른 고려사항과 목표를 가지기 때문에, 애플리케이션을 개발에서 프로덕션으로 이동하려면 수행해야할 작업이 많다. 예를들어 애플리케이션 코드는 컴파일, 번들, 축소, 코드분할 되어야한다.

The Next.js Compiler

Next.js는 이러한 코드 변환과 기본 인프라의 대부분을 처리하여 애플리케이션이 쉽게 프로덕션으로 갈 수 있게 해준다.

이는 Next.js가 저수준 프로그래밍 언어인 Rust로 작성된 컴파일러와 컴파일, 최소화, 번들링 등에 사용할 수 있는 플랫폼인 SWC를 가지고 있기에 가능하다.

Compiling

What is Compiling?

개발자들은 JSX, TypeScript, 모던 자바스크립트와 같이 개발자 친화적인 언어로 코드를 작성한다. 이러한 언어들이 개발자의 효율과 자신감을 높여주지만, 브라우저가 그들을 이해하기 위해선 먼저 JavaScript로 컴파일되어야 한다.

컴파일이란 한 언어로 이루어진 코드를 다른 언어나 언어의 다른 버전으로 만드는 프로세스를 말한다.

Next.js에서, 개발 단계에서 컴파일은 코드를 수정할때 발생하고, production을 위해 애플리케이션을 준비하는 빌드 단계의 일부로 발생한다.

Minifying

개발자들은 사람의 가독성에 최적화된 코드를 작성한다. 코드에는 주석, 공백, 들여쓰기, 여러 줄 등 코드 실행에 필요하지 않은 추가 정보가 포함될 수 있다.

경량화는 코드의 기능은 유지한 채 불필요한 코드 포맷팅이나 주석을 제거하는 프로세스를 의미한다. 경량화의 목적은 파일 사이즈를 줄여 애플리케이션의 성능을 향상시키는 것이다.

Next.js에서, JavaScript와 CSS 파일은 production에서 자동으로 경량화된다.

Bundling

개발자들은 애플리케이션의 큰 부분을 개발하는데 사용하기 위해서 애플리케이션을 모듈, 컴포넌, 함수들로 분리한다. 이러한 내부 모듈과 third party 패키지를 export하고 import하는 것은 파일 종속성을 가진 복잡한 웹을 생성한다.

번들링은 유저가 웹페이지를 방문할 때 request 수를 줄이기 위해서, 웹의 종속성을 해결하고 브라우저에 최적화된 번들로 파일(또는 모듈)들을 합치는(또는 패키징)하는 과정이다.

Code Splitting

개발자들은 주로 다른 URL로 접근할 수 있는 여러 페이지로 애플리케이션을 분리한다. 각 페이지들은 애플리케이션의 고유한 entry point가 된다.

코드 스플리팅은 애플리케이션 번들을 각 entry point에서 필요한 더 작은 청크로 쪼개는 과정이다. 해당 페이지에 필요한 코드만 로딩하여 애플리케이션의 초기 로드 속도를 향상시키는 것을 목표로 한다.

Next.js는 자체적으로 코드 스플리팅을 지원한다. pages/ 디렉토리 내부의 각 파일은 빌드 단계에서 자동으로 각자의 JavaScript 번들로 코드 스플리팅 된다.

더 나아가:

  • 페이지 간의 공유되는 코드 역시 추후 네비게이션시에 같은 코드를 중복 다운로드하는 것을 피하기 위해 다른 번들로 분리된다.
  • 초기 페이지를 로드한 이후, Next.js는 유저가 이동할 가능성이 있는 다른 페이지들의 코드를 미리 로드할 수 있다.
  • 동적 import는 처음에 로드된 코드를 수동으로 분리하는 또 다른 방법이다.

Build Time vs Runtime

빌드 타임 (또는 빌드 단계)는 production을 위해 애플리케이션 코드를 준비하는 일련의 단계를 의미한다.

애플리케이션을 빌드할때 Next.js는 서버에 배포하고 유저가 사용할 준비가 된 배포-최적화된 파일로 코드를 변환한다. 이 파일은 다음을 포함한다:

  • 정적으로 생성된 페이지들을 위한 HTML 파일
  • 서버에서 페이지를 렌더링하기 위한 JavaScript 코드
  • 클라이언트에서 페이지를 인터렉티브하게 만들기 위한 JavaScript 코드
  • CSS 파일

런타임 (또는 request time)은 애플리케이션이 빌드 및 배포된 후, 유저 요청에 대한 응답으로 애플리케이션이 실행되는 기간을 나타낸다.

Client and Server

웹 애플리케이션에서, client는 애플리케이션 코드에 대한 요청을 서버에게 보내는, 유저 디바이스의 브라우저를 의미한다. 그리고 서버에서 받은 응답을 유저가 상호작용 할 수 있는 인터페이스로 변환한다.

서버는 애플리케이션 코드를 저장하고, 클라이언트로부터 요청을 받고, 연산을 수행하고, 적절한 응답을 반송하는 데이터 센터의 컴퓨터를 말한다.

Rerendering

What is Rendering?

React로 작성한 코드를 UI의 HTML 표현으로 변환하기 위해서 필수적인 작업이 있다. 이 과정을 렌더링이라 한다.

렌더링은 서버 또는 클라이언트에서 발생할 수 있다. 빌드 단계 전, 또는 런타임의 모든 요청에 대해 발생할 수 있다.

Next.js를 사용하면 서버 사이드 렌더링, 정적 사이트 생성, 클라이언트 사이드 렌더링 3가지 유형의 렌더링 방법을 사용할 수 있다.

Pre-Rendering

서버 사이드 렌더링과 정적 사이트 생성은 외부 데이터를 fetch하고 React 컴포넌트들을 HTML로 변환하는 결과물이 클라이언트로 전송되기 전에 발생하므로 Pre-Rendering이라고도 한다.

Client-Side Rendering vs. Pre-Rendering

표준 React 애플리케이션에서, 브라우저는 서버로부터 UI를 구성하기 위한 명령을 담은 JavaScript와 함께 빈 HTML shell을 받는다. 초기 렌더링이 유저의 디바이스에서 일어나기 때문에 클라이언트-사이드 렌더링이라 한다.

| Note: React의 useEffect()이나 useSWR과 같은 데이터 fetching hook을 통해 데이터 fetch하기로 선택하여 Next.js 애플리케이션의 특정 컴포넌트에 클라이언트 사이드 렌더링을 사용할 수 있다.

반대로, Next.js은 기본적으로 모든 페이지를 pre-render한다. Pre-rendering은 유저의 디바이스에서 자바스크립에 의해 모든것이 이루어지는 대신, 서버에서, 사전에, HTML이 생성됨을 의미한다.

실제로, 완전한 클라이언트-사이드 렌더링 앱에서, 유저는 렌더링이 완료될때까지 빈 페이지를 보게 될것이다. 유저가 구조화된 HTML을 볼 수 있는 pre-render앱과 비교하면:

두가지 타입의 pre-rendering에 대해 살펴보자:

Server-Side Rendering

서버-사이드 렌더링에서, 페이지의 HTML은 각 request마다 서버에서 생성된다. 그런 다음생성된 HTML, JSON 데이터, 페이지를 인터렉티브하게 만드는 JavaScript 명령들이 클라이언트에 전송된다.

클라이언트에서는, HTML은 React가 JSON 데이터와 JavaScript 명령들을 이용하여 컴포넌트들을 인터렉티브하게 만드는 동안(예를들어, 버튼에 이벤트 핸들러를 부착하는 것), HTML은 인터렉티브하지 않은 페이지를 빠르게 보여주는데 사용된다. 이 과정을 hydration이라 한다.

Next.js에서, getServerSideProps를 이용해 서버-사이드 렌더링 페이지를 선택할 수 있다.

| Note: React 18과 Next 12는 알파 버전의 React server component이 도입되었다. 서버 컴포넌트는 서버에서 완전히 렌더링되며, 렌더링을 위해 클라이언트 측 JavaScript가 필요하지 않다. 또한 서버 컴포넌트를 사용하면 개발자가 일부 로직을 서버에 유지하고, 해당 로직의 결과만 클라이언트에 전송할 수 있다. 이렇게 되면 클라이언트로 전송되는 번들 크기가 줄어들고, 클라이언트 측 렌더링 성능이 향상된다. 여기에서 React server component에 대해 자세히 알아봐라.

Static Site Generation

Static Site Generation에서, HTML은 서버에서 생성되지만, 서버 사이드 렌더링과는 다르게 런타임에는 서버가 없다. 대신, 컨텐츠는 애플리케이션이 배포될 때, 빌드 타임에 한번 생성되며, HTML은 CDN에 저장되어 각 요청마다 재사용된다.

Next.js에서, getStaticProps를 이용해 정적 페이지를 생성할 수 있다.

| Note: 사이트를 만들고 난 후, Incremental Static Regeneration을 사용하여 정적 페이지를 생성하거나 업데이트 할 수 있다. 즉, 데이터가 변경되더라도 전체 사이트를 rebuild할 필요가 없다.

Next.js의 장점은 정적 사이트 생성, 서버사이드 렌더링, 클라이언트 사이드 렌더링 등 페이지별로 이용사례에 가장 적합한 렌더링 방식을 선택할 수 있는 것이다. 특정 이용사례에 적합한 렌더링 방법에 대해 자세히 알아보려면 data fetching docs를 살펴봐라.

CDNs and the Edge

What is the Network?

애플리케이션 코드가 어디에 저장되고 네트워크에 배포된 후 어디서 실행되는지 아는 것은 도움이 된다. 네트워크를 리소스를 공유할 수 있는 컴퓨터(또는 서버)들의 연결이라고 생각할 수 있다. Next.js 애플리케이션의 경우, 애플리케이션 코드를 origin server, Content Delivery Networks (CDNs), Edge에 배포할 수 있다. 각각에 대해 살펴보자:

Origin Servers

앞에서 설명한 것처럼, 서버는 애플리케이션 코드의 origin 버전을 저장하고 실행하는 메인 컴퓨터를 의미한다.

이 서버를, CDN 서버Edge 서버와 같이 애플리케이션 코드가 배포될 수 있는 다른 곳과 구별하기 위해 origin 이라는 용어를 사용한다.

origin 서버가 요청을 받으면, 응답을 보내기 전에 일부 연산을 수행한다. 이 연산의 결과는 CDN (Content Delivery Network)로 옮겨질 수 있다.

Content Delivery Network

CDN은 전 세계의 여러 위치에 정적 콘텐츠(HTML과 이미지 파일 등)을 저장하고, 클라이언트와 origin 서버 사이에 위치한다. 새 요청이 들어오면, 유저와 가장 가까운 CDN 위치에서 캐시된 결과를 응답할 수 있다.

이렇게 하면 매 요청마다 연산이 일어날 필요가 없기 때문에 origin의 부하가 줄어든다. 또한 유저와 지리적으로 더 가까운 곳에서 응답이 오기 때문에 유저는 더 빠르게 응답받을 수 있다.

Next.js에서는, pre-rendering을 미리 수행할 수 있으므로, CDN은 작업의 정적 결과를 저장하는데 적합하다. - 컨텐츠 전달 속도가 더 빨라진다.

The Edge

Edge는 유저에게 가장 가까운 네트워크의 주변부 (또는 엣지)에 대한 일반화된 개념이다. CDN은 정적 컨텐츠를 네트워크의 주변(edge)에 저장하기 때문에 "Edge"의 일부로 여겨질 수 있다.

CDN과 유사하게, Edge 서버는 전 세계의 여러 위치에 배포된다. 그러나 정적 컨텐츠를 저장하는 CDN과는 달리, 일부 Edge 서버는 코드의 작은 스니펫을 실행할 수 있다.

이는 캐싱코드 실행 모두 유저에게 더 가까운 Edge에서 실행할 수 있음을 의미한다.

전통적으로 클라이언트-사이드 또는 서버-사이드에서 실행되었던 일부 작업을 Edge로 이동하면, 클라이언트로 전송되는 코드의 양이 줄어들고, 사용자 요청의 일부가 origin 서버로 되돌아가지 않아도 되므로 애플리케이션의 성능을 향상시킬 수 있다. - 따라서 latency가 줄어든다. Next.js를 이용한 Edge 예시를 여기서 확인해라.

Next.js에서는, 미들웨어로 Edge에서 코드를 실행할 수 있고, 곧 React Server Component로도 가능하다.

0개의 댓글