❓Next는 어떤 성능을 고려하여 만든 프레임워크인가?
일단 Next의 구현방식은 React와 거의 동일하다. 페이지를 구성하는 컴포넌트 단위의 개발을 하고,
Hook메서드나컴포넌트를 사용할때는 React 라이브러리를 사용한다. 하지만 Next는 리액트와 달리 동작방식에서 클라이언트의 부하를 낮추기 위해 동작을 수행하는 메서드(getServerSideProps, getStaticProps 등)를 지원한다.
❓Next와 웹속도 성능
next로 개발된 웹사이트는 속도 면에서 성능이 아주 뛰어나다.
pages하위 폴더에 생성한 컴포넌트를 next가 빌드 타임에 미리 만들어 놓은static page를 클라이언트에게 전달하기 때문이다!(사전 렌더링(Pre-rendering)과 정적 사이트 생성(Static Site Generation)을 지원하여 빌드 타임에 페이지를 사전에 생성하고, 이를 클라이언트에게 전송) 브라우저는 전송받은page를paint히고 이는 page렌더링 속도를 상대적으로 절감할 수 있다.
<Image>
next를 사용할때 이미지가 많은 페이지에 이 이미지 컴포넌트를 사용해 초기 뷰포트 성능을 개선할 수 있다. 쉽게 풀어서 설명하자면 우리가 브라우저에서 이미지를 로드할때 유저가 보는 화면 밖에 있는 이미지들은 유저가 스크롤을 내리지 않으면 보지 않아도 되는 부분이다. 이미지 컴포넌트를 첫 뷰포트의 밖에 있는 이미지는 로드하지 않아 첫 로딩 속도를 감소시킨다.Code Spltting
이미지 컴포넌트와 비슷하게 필요한 시점에 렌더링을 시키는 것이라고 생각하면 된다. 이러한 코드 분할(Code Spltting)을 next는 자체적으로 자동으로 지원한다. pages폴더 안에 각 파일이 빌드하는 동안 자체 자바스크립트 번들로 자동 코드 분할 하는데 이렇게 하면 그 페이지를 실행하는데 꼭 필요한 코드만 가져오도록 하는 것이기에 초기 로딩시간을 향상시키는 것이다.
또한 페이지 간에 공유되는 코드는 추가 탐색 시 같은 코드를 재다운로드하지 않도록 다른 번들로 분할된다.next/dynamic
Next.js는 ES2020의 dynamic import 문법을 지원한다. dynamic import를 사용하면 모듈을 빌드 타임이 아닌 런타임에 불러오도록 한다. 이를 통해 번들 파일을 분리하고 퍼포먼스 향상을 기대할 수 있다.
원래dynamic import는 이렇게 import 호출이 promise를 리턴하는 것처럼 코드를 작성한다.// add.js const add = (a, b) => a + b; export default add // index.js import('./add.js') .then(module => module.default(7, 4)) // returns 11 .catch(error => // log error);하지만 next.js에서는
dynamic이라는 모듈을 제공해promise resolve과정 없이도 변수에 할당할 수 있도록 해준다!
따라서 우리는 Next를 이용해 아주 간단하게 first Load의 사이즈를 줄이고 페이지별 번들 파일의 사이즈를 줄일 수 있다!
1. 구글에서 자바스크립트를 처리하는 방식
(1) 크롤링 => (2) 렌더링 => (3) 색인생성!
구글봇이 페이지를 크롤링 및 렌더링 대기열에 추가한다! => 구글이 HTTP 요청을 전송하여 크롤링 대기열에서 URL을 가져올 때는 크롤링이 허용되는지 먼저 확인한다! (이때 구글봇이 robots.txt 파일을 읽고 URL이 허용되어 있지 않으면 이 URL에 관한 HTTP요청과 URL자체를 건너 뛴다.) => 그런 다음 Googlebot은 HTML 링크의 href 속성에 있는 다른 URL에 관한 응답을 파싱하고 크롤링 대기열에 이러한 URL을 추가한다.단, 구글이 지원하는 API와 자바스크립트 기능은 일부 제한되어 있다
을 참고하면 좋을 것 같다!
마이그레이션을 더 작은 단계로 나누어 업데이트의 결합된 복잡성을 줄여야한다! pages 디렉토리가 점진적인 페이지 단위의 마이그레이션을 허용하기 위해서 app 디렉토리는 동시에 작동하도록 의도적으로 설계되어있다. 즉, app 디렉도리는 기존 pages 디렉토리로 사용할 수도 있다. nextJs 13을 pages디렉토리 기반으로도 사용할 수 있는 것이다. 이에 점진적으로 채택할 수 있다. 하지만 app 디렉토리의 새로운 앱 빌드 방식은 아키텍처에 많은 개선점을 제공하기에 고민해보고 시간과 비용에 맞게 채택하는 것이 좋을 것 같다.
pages에서app으로 마이그레이션
$ npm install next@latest
먼저 최신 넥제로 업데이트를 한 후 , app의 루트에 새 디렉토리를 만든다.
app/layout.tsx디렉터리 안에 새 파일을 만든다.
기존 _document.js및_app.js 파일이 있는 경우 이것을 루트 레이아웃에 복사할 수 있다 (app/layout.tsx)
주의할 점은 마이그레이션 하는 동안은 경로가 중단되지 않도록 _app을 유지한 뒤, 완전히 마이그레이션 된 후 안전하게 삭제하여야한다.
pages 디렉토리에서 사용하던 <head>는 title과 meta 요소와 같은 HTML요소를 next/head React component에서 관리 했는데 이제 이것은 app 페이지에서 한번에 관리된다! 기본제공 SEO지원으로 대체되는 것이다.
//pages > index.tsx
import Head from 'next/head';
export default function Page() {
return (
<>
<Head>
<title>My page title</title>
</Head>
</>
);
}
//app> page.tsx
import { Metadata } from 'next';
export const metadata: Metadata = {
title: 'My Page Title',
};
export default function Page() {
return '...';
}
app디렉초리의 페이지는 기본적으로 서버 컴포넌트여서 클라이언트 컴포넌트와는 다르다.
데이터 패치도 getServerSideProps, getStaticProps, getInitialProps 에서 단순한 API로 대체되었다.
공식문서에서 추천하는 마이그레이션은 두가지로 나눠서 진행된다.
1단계로 내보낸 기본 페이지 컴포넌트를 새 클라이언트 컴포넌트로 이동한다. 그리고 2단계, 새 클라이언트 app 컴포넌트를 page.js 내의 새 파일로 가져온다.
그리고 클라이언트 컴포넌트를 메ㅔ 디렉토리에 별도로 새 파일로 만들고 'use client';맨 위에 이것을 추가한다.
app디렉토리에는 새로운 hook을 사용해야한다.next/navigation : useRouter() usePathname() useSearchParams()
이때 주의해야할 점은 useRouter훅은 app디렉토리에서 지원되지 않지만, pages디렉토리에서 계속 해서 사용할 수 있다는 것이다. 그리고 basePath와 isReady는 더이상 필요하지 않아 제거되었다. 추가적으로 정적 렌더링 중 훅을 사용하는 모든 구성요소는 useSearchParams()에서 사전 렌더링 단계를 건너뛰고 대신 런타임에 클라에서 렌더링된다. 자세한 점은 useRouter를 참고하면 된다!
pages디렉토리는 페이지의 데이터를 가져오기 위해 getServerSideProps와 getStaticProps를 사용한다. 이것은 app 디렉토리에서 fetch()와 async 바탕의 리액트 서버 컴포넌트에 구축된 simpler API 로 대체되었다.
정리
정리하자면 Next.js13을 마이그레이션 할때 고려해야할 사항들이 위와 같이 있는데 trade-off 분석을 통해 마이그레이션을 실행하기 위해서는 우선적으로 나의 프로젝트의 규모와 현재 상태를 정확하게 파악하는 것부터 이루어져야한다고 생각한다! Next.js 13으로 마이그레이션을 할때 버전이 높다고 무조건적으로 좋다고 할 수 없다! 내가 편하게 사용하던 부분이 업데이트 되면서 불편하게 변했을 수도 있기에 이것을 잘 고려해야한다.
Next.js13으로 마이그레이션할 때 고민해봐야할 부분은 1) fetch API 말고 다른 서드파티 라이브러리는 아직 지원하지 않는다는 것이다. fetch API를 Next.js에서 커스텀해서 사용하여 SSR, SSG 등을 통합했지만
다른 데이터 페칭 라이브러리는 아직 사용할 수 없다는 아쉬운 점이 있다.그리고 2) 상위 경로로 데이터를 전달할 수 없다. 기존 12버전에서는 하위 경로의 getStaticProps를 통해 props를 주면 최상위 경로인_app.tsx에서 하위 경로까지 props가 전달되는 구조였디. 그래서 react-query 같은 데이터 페칭 라이브러리를 이용해 SSG에서 prefetch를 하고 브라우저로 dehydrate를 할 수가 있었는데 next.js 13에서는_app.tsx의 appProps를 대체할 방법이 없다! 그래서 next/font 커스텀된 것을 사용할 때도 어려움을 겪었었다. next를 13으로 마이그레이션하는게 생각보다 신경쓸 부분이 많고 오래걸릴 수 있는데 그에 따른 장점도 단점도 존재하니 한번쯤 생각해보고 필요성에 따라 내가 맡은 프로젝트를 마이그레이션 시키는 것이 좋을 것 같다!
nextJs는 12버전 이전에는 transpile을 하려고
babel을 사용하였다. 하지만 바벨은 배포 후 개발자들이 바벨이 변경한 코드를 쉽게 알아볼 수 없다는 단점과, 코드의 길이가 길고,polyfill을 사용해서 babel이 지원해주지 않는 범위는 따로 변환해주어야하고, 다른 컴파일러보다 시간이 오래걸린다는 단점이 있다. 따라서 NEXT JS 12버전부터는SWC컴파일러로 바꿨다. 이것은 Rust로 작성된 컴파일러로 매우 빠르게 동작한다. 이것은 개발자들이 라이브러리를 fork할 필요없이 미리 설치된 SWC를 사용할 수 있다는 장점이 있다. 또 Rust의 WASM(Web Assembly) 지원으로 어떤 종류의 플렛폼에서도 next JS를 개발할 수 있게 되었다. 이것의 성능은 다른 컴파일러보다 뛰어나다.
SWC 적용하기
next js를 12버전 이상으로 업그레이드 한후 혹시
.babelrc,babel.config.js이런 파일이 있다면 지워준다. 그리고 최적화 옵션을next.config.js에 적용할 수 있다.module.exports = { swcMinify: true, }
넥스트...어렵기만 했는데 쉽게 설명해주셔서 감사해요~~ 잘 읽고 갑니당!