Next 13
Next 13
버전업이 되면서 기존 프로젝트를 마이그레이션하는 경우 Breaking Changes로 인해 전반적으로 변경사항에 맞춰 업그레이드를 해줘야 한다.
Next는 이것을 방지하고자 새로 추가된 기능과 규칙들을 app/ 에 적용하여 기존 page/ 구조의 앱이 망가지지 않고 점진적으로 마이그레이션할 수 있도록 제공한다.
Routing
app
├── api
│ └── hello
│ └── route.ts
├── layout.tsx
├── page.tsx
└── product
├── [slug]
│ └── page.tsx
├── layout.tsx
└── page.tsx매칭할 경로를 폴더 이름으로 두고 하위에 page 컴포넌트 파일을 두면 자동으로 라우팅 된다.
폴더(경로)별로 범위를 좁혀 재사용되는 레이아웃, 에러 상태, 로딩 UI 등을 공유하여 해당 라우트 안에서 처리해주기 위함
page/→app/Folder & File based routing
Dynamic Routing
generateStaticParams()
generateStaticParams()이전 버전의 getStaticPaths() 를 대체하는 함수
미리 생성할 페이지의 동적 경로(slug)를 지정하고 { slug: value } 구조의 배열을 반환하기만 하면 된다.
Layout
레이아웃은 기본적으로 서버 컴포넌트(변경 가능)
같은 경로 및 하위 경로의 페이지에 공유되며 래핑된다. → 레이아웃(컴포넌트)
경로 별로 레이아웃을 지정 가능하며 상위에 있는 레이아웃에 중첩되어 적용된다.
데이터 패칭이 가능하며 레이아웃간 전달이 불가능하지만 경로가 같은 경우 각 레이아웃이 중복으로 데이터 패칭시 React가 알아서 중복된 요청을 제거한다.
Root Layout (필수)
app/ 내에서 가장 상위에 있는 layout.tsx 를 루트 레이아웃이라 칭하며 모든 페이지에 래핑된다.
이전 버전의
_app.tsx,_document.ts 를 대체한다.서버에서 반환된 초기 HTML을 수정할 수 있다
Next.js가 자동으로
<html>,<body>를 생성하지 않으므로 루트 레이아웃에 해당 태그를 정의해야 한다.
Not Found Page
커스텀 404 page를 만드려면 파일이름을 not-found로 지정해야 한다.
폴더(경로)내에 가장 가까운 not-found 파일이 사용되어 페이지 별로 에러 페이지를 설정 가능
현재 시점에는 notFound() 호출에 의해 트리거될 때만 렌더링된다.
Global Not Found Page
현 시점 13.2.4 버전에서는 Global Not Found Page 기능이 app/ 라우팅에서는 아직 제공되지 않고 있다.
Global Not Found Page를 구현하려면?
기존 방식대로
page/하위의404.tsx컴포넌트를 추가해 사용app/ 내에서동적 라우팅을 활용하여 구현
우회해서 app/ 라우팅으로 Global Not Found Page 구현하기
우회해서 app/ 라우팅으로 Global Not Found Page 구현하기Metadata
어떤 페이지(or 레이아웃)에서든 객체 형태로 된 metadata 변수를 export 해주기만 하면 된다.
Dynamic Metadata
generateMetadata() 함수를 통해 비동기 데이터를 패칭 후 메타데이터 객체를 반환한다.
generateMetadata() 함수를 통해 비동기 데이터를 패칭 후 메타데이터 객체를 반환한다.사용자에게 페이지를 주기 전 generateMetadata() 호출되고 메타데이터 정보가 완성 되어야지만 페이지를 전송한다.
Default Metadata
따로 추가하지 않아도 기본적으로 설정되는 메타태그
Rendering
렌더링 방식이 (page/) 페이지 단위 -> (app/) 컴포넌트 단위로 변경되었다.
기존 page 단위 렌더링 방식의 문제점
페이지 단위의 렌더링 방식은 앱의 규모가 커질수록 클라이언트가 받는 번들 자바스크립트 코드가 커지는 문제가 있었다.
이를 해결하기 위해 React 18 버전에 맞춰 서버 컴포넌트, 클라이언트 컴포넌트로 구분하여 렌더링 하는 방식이 도입되었다.
컴포넌트 단위 렌더링 방식의 장점
하나의 페이지 안에서 여러 컴포넌트를 각기 다른 렌더링 방식을 규정해 조합하여 사용이 가능하다.
서버 컴포넌트는 서버에서만 작업이 이루어지고 서버에서 렌더링되어 클라이언트에게 보낼 코드가 없기 때문에 클라이언트가 받을 번들 자바스크립트 코드가 현저히 줄어들게 된다.
즉, 사용자의 인터렉션이 필요한 코드만 클라이언트에게 전달되어 초기 로딩 속도가 더욱 빨라지게 된다.
React v18에서 새롭게 추가된 기능
서버상에서 리액트 코드를 작성할 수 있게 되었다.
서버상에서만 동작하는 컴포넌트
SSR !== RSC, 서로 대체제가 아니며 사용자 경험을 향상시키기 위해 서로 보완하여 사용되는 것
프리 렌더링(SSR) + 번들사이즈 최소화(RSC)
app/내의 모든 컴포넌트는 기본적으로 서버 컴포넌트이다.서버에서 빌드 될 때 실행되는 컴포넌트, 기본적으로 SSG
정적인 HTML 형태로 클라이언트에게 전달되며 클라이언트에게 전달되는 자바스크립트 코드는 Zero
브라우저에서 제공하는 API, Hooks를 사용할 수 없지만 node.js(서버)에서 제공하는 API는 사용이 가능하다.
서버 컴포넌트에서 데이터를 공유할 떄 전역 싱글톤과 같은 네이티브 자바스크립트 패턴을 활용
서버 컴포넌트 간에 서버(DB)에서 패칭한 데이터 자원을 공유하고 싶을 때는 데이터를 불필요하게 전달할 필요 없이 그 데이터를 필요로 하는 곳에서 패칭하면 된다. 가져온 데이터는 캐싱 되어 넥스트에서 중복 요청을 제거하는 식으로 최적화가 자동으로 이루어진다.
Client Component
이전 버전에서 사용했던 프리렌더링 + 하이드레이션 방식
디폴트가 서버컴포넌트이기 때문에 클라이언트 컴포넌트로 사용하기 위해서는 상단에
use client를 명시해주어야 한다.클라이언트 컴포넌트라고 해서 무조건
csr로 동작하는 것이 아니다.서버에서 컴포넌트를 읽고 정적인 부분을 분리하여 프리렌더링에 포함시키고 하이드레이션 과정이 일어난다. (기존 방식과 동일)
사용자의 이벤트가 있거나 브라우저에서 실행되어야 하는 코드들이 있다면 클라이언트 컴포넌트이다.
즉, 사용자(브라우저)에게 보내져야 하는 코드들이 담긴 컴포넌트
상태를 쓰거나 hooks를 사용한다면 클라이언트 컴포넌트로 사용해야 한다.
서버 컴포넌트에서 클라이언트 컴포넌트로 전달하는 props는 serialization(직렬화)가 가능해야 한다.
클라이언트 컴포넌트 내부에서 사용되는 모듈, 하위 컴포넌트들은 모두 클라이언트 컴포넌트가 된다.
클라이언트 컴포넌트 내부에서 서버 컴포넌트를 임포트해서 사용할 수 없다.
따라서 가능하다면 클라이언트 컴포넌트는 컴포넌트 Tree에서 가장 끝단에 위치하는 leaf로 이동시키는 것이 좋다.
불가피할 경우 넥스트에서 컴포지션 패턴(자식 컴포넌트를 props로 전달하여 자식 컴포넌트를 조합하는 패턴)을 활용할것을 권고한다.
서버 컴포넌트 클라이언트 컴포넌트 구분하기
사용자에게 전달 되어야 하는 코드가 포함된 경우만 클라이언트 컴포넌트로 만들어 성능을 높이자.
데이터 패칭
✅
데이터 패칭이 가능하지만 특별한 이유가 없다면 서버에서 데이터 패칭을 하는 것이 좋다. (성능, 사용성)
백엔드 리소스 직접 접근
✅
❌
노출되면 안되는 민감한 데이터(엑세스 토큰, API Keys)
✅
❌
사용자와 상호작용하는 코드 이벤트 핸들러 사용
❌
✅
React Hooks 사용 (useState, useEffect 등)
❌
✅
브라우저 API 사용
❌
✅
Data Fetching
getStaticProps(),gstServerSideProps()를 대체하는 여러가지 방식이 추가 되었다.
어느 시점에 페이지(or 레이아웃)를 재생성 할지 지정
revalidate
브라우저에서 페이지(or 레이아웃)의 재검증하는 기본 시간을 지정
fetch() 의 설정된 캐시 옵션을 override 하지 않는다.
-> revalidate < fetch()
fetch(url, options)
async/await을 사용하여 데이터 패칭
Next.js는 Web fetch API를 확장하여 사용
캐시 유무에 따라 SSG, ISR, SSR 로 나뉘게 된다.
Dynamic Rendering
동적 함수 를 사용하거나 fetch() 요청의 캐시 옵션이 no-store 일 때 동적 렌더링(SSR)으로 전환된다.
동적 함수(Dynamic fuction)
사용자의 요청 시점에 알 수 있는 정보를 사용한 함수를 동적 함수라 한다.
ex) 요청 시점에 알 수 있는 정보 -> 쿠키, HTTP 요청 헤더, URL params
API Route
Next가 Full Stack으로 개발할 수 있는 이유
요청 별(Get, Post)로 나누어 구조적으로 구현 가능하도록 바뀌었다.
라우트당 폴더 하나
라우트가 되는 폴더 즉, 페이지 별로 다양하고 유용한 아이템들을 설정할 수 있다.
폴더 별로 설정할 수 있는 항목들
page.tsx: 해당 폴더를 경로로 하는 페이지 컴포넌트
layout.tsx: 페이지를 감싸는 골격을 설정 (상위 레이아웃에 중첩됨)
template.tsx: 레이아웃과 비슷하나. 페이지 전환시 컴포넌트의 새 인스턴스가 마운트되어 실행되는 차이가 있다.
loading.tsx: 해당 라우트에서 페이지 UI가 준비되기 전 사용자에게 보여주는 UI 컴포넌트 (React.Suspense)
error.tsx: 런타임 에러시 대체 UI 컴포넌트 (React.ErrorBoundary)
not-found.tsx: 해당 라우트의 404 UI 컴포넌트
loading.tsx
해당 라우트의 페이지가 준비되는 동안 loading.tsx에 선언한 컴포넌트가 보여지게 된다. * layout을 제외한 page 부분만 적용됨
이 컴포넌트를 사용하면 내부적으로 React.Suspense 를 사용한다.
로딩은 동적인 SSR을 할 때 빠르게 로딩 UI를 먼저 보여주고 실제 컴포넌트를 병렬적으로 백그라운드에서 실행후 완료되면 전송하는 식으로 사용자에게 페이지가 서빙중이라는 것을 알린다.
한계점
해당 컴포넌트는 페이지 전체를 묶기 때문에 병렬 스트리밍을 위해서는 내부 요소들에 서스펜스를 따로 설정 해줘야 한다.
Suspense 사용 이점
UI를 렌더링하기 전에 모든 데이터가 로드될 떄까지 기다릴 필요 없이 페이지의 일부를 더 빨리 표시할 수 있다.
Streaming Server Rendering: 스트리밍을 사용하면 페이지의 HTML을 더 작은 청크로 나누고 해당 청크를 서버에서 클라이언트로 점진적으로 전송할 수 있습니다
Selective Hydration: 사용자 인터렉션을 통해 어떤 컴포넌트를 먼저 수화할지 선택적으로 결정
error.tsx
에러 컴포넌트는 어떤 에러가 발생했는지와 페이지를 리셋할 수 있는 콜백함수를 props로 제공한다.
내부적으로 ErrorBoundary 를 사용한다.
에러 컴포넌트는 런타임에 필요한 사용자 코드와 useEffect 같은 훅들을 사용해야 하니 무조건 Client 컴포넌트가 된다.
loading.tsx 와 마찬가지로 페이지 내부 작은 단위로 에러 UI를 보여주고 싶다면 해당 컴포넌트를 ErrorBoundary로 감싸면 된다.
가장 근접한 부모의 error.tsx 를 보여준다. (버블링 됨)
Last updated