나의 NEXT.JS 입문기
사실 NextJS는 예전에 수강한 리액트 강의에서 블로그 만들기 수준으로 대강 다뤄본 경험은 있었지만 아직 제대로 공부해본 적은 없었다. 마침 진행하던 프로젝트도 끝난 상황이라 잘됐다 생각하여 시작해보기로 마음 먹었다.
따로 강의를 사서 볼까도 했지만, 워낙 NextJS팀의 공식문서가 잘 되어있다는 말을 많이 들었다. 이미 직접 프로젝트로 만들어보며 '우와!' 하는 것은 많이 해봤기 때문에 이번엔 동작 원리부터 제대로 공부해보려고 한다. 최근에 버전 13으로 올라가며 변한 부분도 많다고 들었는데 반영이 안 된 강의가 대부분인 것 같기도 했다.
아마 나만 볼 기록이 될 가능성이 크지만... 누군가 보신다면 부족한 공식문서 번역 + 제가 다시 이해하여 부족한 번역에 첨언한 내용이라고 생각해주시면 감사하겠습니다.
https://nextjs.org/learn/foundations/how-nextjs-works
Learn | Next.js
Production grade React applications that scale. The world’s leading companies use Next.js by Vercel to build pre-rendered applications, static websites, and more.
nextjs.org
Next.js는 어떻게 동작하는가?
당연한 이야기지만, 기능들을 배우기 전에 그것이 어떻게 동작하는지 이해하는 것은 큰 도움이 된다.
React는 웹 어플리케이션 프레임워크중에서 자유도가 아주 높은 것으로 유명하다. 실제로 React 팀 공식문서에도 '프레임워크'가 아닌 '라이브러리'라고 명시되어 있다.
때문에 개발자들은 presenter-container 디자인 패턴이나 아토믹 디자인 패턴 등 여러 디자인 패턴들을 공유하고 사용해왔다. 이렇게 자유도가 높은 것은 장점으로 보일 수도 있지만 협업이 많고 빠른 앱 구성이 필요한 개발자들에게는 단점일 수 있다. 또한 입문한지 얼마 안 된 개발자에게는 혼란을 줄 수도 있다. 실제로 Presenter-container 디자인 패턴의 창시자인 Dan Abramov는 '이해 없이 패턴을 맹목적으로 강제하는 경향이 있다'고 직접 지적하며 모던 리액트에서는 해당 패턴을 권장하지 않는다고 말하기도 했다.
Next.js는 빠르게 어플리케이션을 만들 수 있는 프레임워크를 제공하여 이러한 리액트의 단점아닌 단점을 보완해준다.
Development & Production Environments
Development는 어플리케이션이 나의(개발자의) 컴퓨터에서 실행되고 있는 환경이다. Production은 어플리케이션이 배포되어 유저에게 직접 사용되는 환경이다. NextJS에서 위의 두 환경은 조금 다르게 작동한다.
NextJS에서는 어떻게 적용될까?
Development Stage
개발 단계에서 Next.js는 개발자가 애플리케이션을 구축하는 경험을 위해 최적화한다. 타입스크립트와 ESLint, Fast Refresh 등과 같이 DX(Developer EXperience)를 높여준다.
Production Stage
프로덕션 단계에서는 최종 사용자가 애플리케이션을 사용하는 경험을 위해 최적화한다. 최적의 성능을 발휘하기위해 빌드단계에서 코드를 변환한다. 이를 위해 컴파일, 번들, 경량화(minify) 그리고 코드 분리를 사용한다.
Next.JS에서는 이러한 목적을 빠르고 쉽게 달성할 수 있도록 저수준 프로그래밍 언어인 Rust와 번들링 플랫폼인 SWC를 이용한 컴파일러를 사용한다.
컴파일링이 무엇인가?
거의 대부분의 (리액트) 개발자들을 효율성과 안정성을 높이기 위해 JSX와 Typescript, 최신 버전의 모던 자바스크립트 등의 개발자 친화적인 언어로 코드를 작성한다. 이러한 언어들은 생산성을 높여주고 편리하지만, 웹 생태계의 특성상 많은 브라우저에서 동작하기 위해서는 낮은 버전의 자바스크립트로 컴파일 되어야한다.
여기서 컴파일은 한 언어로 코드를 받아 다른 언어(또는 다른 버전의 언어)로 출력하는 것을 의미한다. Typescript -> Javascript가 대표적인 컴파일 과정이다.
Next.js에서 컴파일은 개발 과정에서 코드를 수정할 때 실시간으로 이루어진다. 또한 빌드 과정의 일부에서도 이루어진다.
경량화(Minification)는 무엇인가?
코드를 작성할 때 중요한 덕목 중 하나는 가독성이 좋은 코드를 쓰는 것이다. 하지만 이러한 코드는 어쩔 수 없이 실행할 때는 불필요한 요소들(주석, 여백, 들여쓰기 등)이 많이 포함된다. 경량화는 이렇게 불필요한 코드 구조와 주석을 삭제하는 과정이다. 물론 코드의 기능에는 전혀 문제 없는 형식이다. 이는 파일 사이즈를 줄여 애플리케이션의 성능을 실제로 향상한다. Next.js는 프로덕션 단계에서 CSS파일과 Javasript 파일을 자동으로 경량화한다.
번들링은 무엇인가?
개발자들은 하나의 거대한 애플리케이션을 빌드하기 위해 여러 개의 모듈과 컴포넌트, 함수들로 나누어 작성한다. 외부의 써드파티 패키지가 아니더라도 이렇게 내부의 여러 모듈로 나누어져 있는 것은 복잡한 종속성을 만들어 성능을 저하시킨다.
번들링(Bundling)은 이렇게 여러 개의 모듈(또는 파일)로 되어있는 애플리케이션을 브라우저에 최적화된 번들로 병합(또는 패키징)하여 유저의 파일 요청을 줄여주고 성능을 향상하는 과정이다.
Code splitting은 무엇인가?
Code splitting (코드분할) - MDN Web Docs 용어 사전: 웹 용어 정의 | MDN
코드분할은 번들한 여러 코드 혹은 컴포넌트를 분리하는 것입니다. 이렇게하면 필요에 따라 특정한 컴포넌트만 로딩하거나, 병렬로 로딩할 수 있습니다.
developer.mozilla.org
코드분할은 번들한 여러 코드 혹은 컴포넌트를 분리하는 것입니다. 이렇게하면 필요에 따라 특정한 컴포넌트만 로딩하거나, 병렬로 로딩할 수 있습니다. - MDN Doc
번들은 분명 좋은 기술이지만 점점 웹 애플리케이션이 커지다 보니 문제가 생겼다. 첫 화면을 띄우는데 너무 시간이 오래 걸리게 된 것이다. 이는 유저 경험을 명백히 해치는 문제였다.
코드 분할은 이러한 문제를 해결하기 위해 등장했다. 앱을 하나의 거대한 번들로 묶는 대신 각 진입점(entry point, 보통의 웹페이지에서는 다른 URL)별로 작게 나누어 번들링한 것이다. 이는 애플리케이션의 초기 로딩 시간을 개선한다.
React에서는 코드 분할을 위해 동적 import 구문이나 React.lazy 등의 별도의 구문을 이용해 개발자가 직접 구현해야 한다. 하지만 Next.js는 자동으로 `pages/` 경로에 있는 파일들을 분할하여 번들링한다. 또한 첫 페이지 로드 후, 사용자가 탐색할 가능성이 있는 다른 페이지를 pre-loading(사전 로딩)하는 것도 가능하다.
물론 여러 페이지에서 공유하는 코드는 별도의 다른 번들로 분할된다.
빌드와 런타임
Build Time
빌드 타임은 애플리케이션 코드가 실제 제품화를 위해 준비되는 단계이다. Next.js는 빌드 과정에서 최적화된 제품으로 만들기 위해 코드를 변형한다. 이때 다음의 파일들이 포함된다.
- 정적으로 생성된 HTML 파일(Next.js에서는 index.html을 따로 작성하지 않아도 된다!)
- 서버에서 렌더링될 JavaScript 코드
- 클라이언트에서 웹페이지를 동적으로 만들 JavaScript 코드
- CSS 파일
Run Time
런타임은 애플리케이션이 빌드되고 배포된 후, 유저의 요청대로 실행되는 단계를 의미한다.
Client & Server
클라이언트는 실제 유저가 사용하는 디바이스의 브라우저를 의미한다. 브라우저(클라이언트)는 서버로 애플리케이션 코드를 요청하여 받는다. 또한 유저의 동작(조작)에 따라 상호작용할 수 있게 서버에 응답을 보낸다.
서버는 애플리케이션 코드가 저장되어있는 데이터 센터를 의미한다. 서버는 클라이언트가 보낸 요청을 받거나 필요한 사항을 계산(computation)하고, 적절한 응답을 보내준다.
렌더링은 무엇인가?
React 애플리케이션이라면 필수로 고려해야하는 작업이 바로 렌더링이다. 렌더링은 React로 작성된 UI를 HTML(DOM)으로 그려주는 과정이다. 렌더링은 서버나 클라이언트에서 이루어질 수 있고, 빌드 타임에 미리 이루어지거나 런타임에 요청에 따라 실시간으로 이루어 질 수도 있다. Next.js는 클라이언트 사이드 렌더링 뿐만 아니라 서버사이드 렌더링, 정적사이트 생성을 사용할 수 있다.
Pre-Rendering
Server-Side Rendering(SSR)과 Static Site Generation(정적 사이트 생성)은 클라이언트에 코드가 전달되기 전에 필요한 요소를 가져오고 리액트 컴포넌트를 HTML로 변환하기 때문에 Pre-Rendering(사전 랜더링)이라고도 한다.
Client-Side Rendering(CSR) vs. Pre-Rendering
일반적인 React 애플리케이션에서 브라우저는 UI를 구성하기 위한 Javascript 파일과 (root div만 존재하는)빈 HTML 파일을 받는다. 이 파일을 받고 나서야 렌더링(UI를 HTML로 그리기)을 시작하기 때문에 이를 클라이언트 사이드 렌더링(CSR)이라고 부른다. CSR 방식은 어쩔 수 없이 유저가 처음 접속했을 때 잠시라도 빈 화면을 볼 수 밖에 없게 한다.
이와 달리 Next.js는 위에서 언급한 pre-rendering을 기본으로 한다.
Server-Side Rendering(SSR)
서버사이드 렌더링(SSR)은 어쩌면 Next.js가 널리 사용된 가장 큰 이유가 아닐까 생각한다. SSR에서는 각 요청마다 서버에서 HTML 페이지를 생성한다. 클라이언트에서 HTML은 정적인 페이지를 빠르게 보여주며, React는 JSON 데이터와 Javascript를 이용해 컴포넌트를 동적으로 만든다(이벤트 핸들러를 붙이는 등). Next.js에서 `getServerSideProps`를 사용하면 SSR을 이용할 페이지를 선택할 수 있다.
Note: React 18과 Next 12 버전부터는 React Server Components의 알파버전을 사용할 수 있다. 서버 컴포넌트는 서버에서 완전히 렌더링되며 렌더링을 위한 클라이언트 Javascript코드는 불필요하게 된다. 게다가 개발자는 서버에 일부 로직을 유지하고 클라이언트에는 계산의 결과만을 보낼 수도 있다. 이는 클라이언트로 전송되는 번들의 크기를 줄이고 CSR의 성능을 향상시킬 것이다. 해당 기능은 아직 React에서 stable하게 배포되지는 않았다.
Static Site Generation(SSG)
정적 사이트 생성(SSG)을 사용하면 HTML이 서버에서 생성되는 것은 맞지만, SSR과 달리 런타임 때 서버가 계속 존재하는 것은 아니다. 대신 콘텐츠는 빌드 타임 때 한 번 생성되며, HTML은 CDN(Content-Delivery-Network)에 저장되어 요청 때마다 재사용된다. Next.js에서 정적 사이트 생성은 `getStaticProps`를 이용해 사용할 수 있다.
Next.js의 장점은 정적 사이트 생성과 SSR, CSR 중 각 페이지별로 가장 적합한 렌더링 방법을 선택할 수 있다는 것이다.
네트워크란 무엇인가?
애플리케이션이 배포될 때 어디에 저장되고 어디서 동작하는지 이해하는 것은 중요하다. 네트워크는 자원(데이터)을 공유하기 위해 연결된 컴퓨터(또는 서버)들의 집합이라고 생각할 수 있다. Next.js 애플리케이션에서 코드는 Origin Server, Content Delivery Network(CDN), 그리고 Edge Server에 배포될 수 있다.
Origin Servers
이전에 언급했든 서버는 애플리케이션 코드를 저장하고 실행시키는 메인 컴퓨터를 의미한다. Origin Server의 'Origin'(기원, 원본)은 다른 서버(CDN, Edge 등)와 구분하기 위해 붙은 것이다.
오리진 서버가 요청을 받으면 응답하기 전에 약간의 계산 과정을 거친다. 계산의 결과는 CDN으로 이동할 수 있다.
CDN(Content Delivery Network)
CDN은 클라이언트와 오리진 서버 사이에 위치하여 이미지나 HTML 파일 같은 정적인 콘텐츠를 전 세계 여러 위치에서 저장한다. 새로운 요청이 들어오면 사용자의 위치에서 가장 가까운 CDN에서 캐싱된 결과로 응답을 보내줄 수 있다. 이를 이용하면 매 요청마다 계산을 할 필요가 없기 때문에 오리진 서버의 부하를 줄인다. 또한 실제 지리적으로 유저에게 가까운 위치에서 응답이 오기 때문에 응답 속도도 빠르다.
Next.js에서는 사전 렌더링을 수행할 수 있기 때문에 CDN에 정적 페이지(결과물)를 저장하여 콘텐츠 전달을 더 빠르게 할 수 있다.
The Edge
엣지는 유저에게 가장 가까운 네트워크의 가장자리(끝자락)이라는 뜻이다. 즉 보편적인 유저들이 사용하고 존재하는 네트워크 환경이 '엣지'다. CDN은 네트워크의 말단에 정적 콘텐츠를 저장하기 때문에 이 '엣지'의 일부로 간주될 수 있다.
CDN과 마찬가지로 엣지는 전 세계의 여러 위치에 배포된다. 하지만 정적 콘텐츠만 저장하는 CDN과 달리, 일부 엣지 서버는 작은 코드 snippet을 실행할 수 있다. 이는 캐싱과 코드 실행이 유저에게 더 가까운 엣지에서 모두 수행될 수 있다는 것을 의미한다.
기존에 클라이언트 측이나 서버 측에서 수행된 일부 작업을 엣지로 이동하면 애플리케이션의 성능을 향상시킬 수 있다. 클라이언트에 보내지는 코드의 양은 줄어들고, 사용자 요청의 일부는 오리진 서버로 갈 필요가 없어지기 때문이다.
Next.js에서는 미들웨어를 통해 Edge에서 코드를 실행할 수 있으며, 곧 React Server Components로도 가능해질 것이다.
'Dev > JS Family, HTML, CSS' 카테고리의 다른 글
Next.js 공식문서로 공부하기 - SSG와 SSR (0) | 2023.03.01 |
---|---|
NextJS 공식문서로 공부하기 - 블로그 만들기 (1) | 2023.02.19 |
JSDoc 페이지를 GitHub Page로 배포하기 (1) | 2023.02.03 |
[TypeScript] 타입스크립트 입문(6) - JSDoc, 유틸리티 타입, 모듈, 타입선언, 네임스페이스 (0) | 2022.09.22 |
[TypeScript] 타입스크립트 입문(5) - 타입 시스템 (0) | 2022.09.17 |