Next.js 소개와 14 버전 변경사항 – React 인기 라이브러리
React(리액트) 프레임워크 중 하나인 Next.js는 무엇일까요? 새로 출시된 14버전에는 어떤 변화가 있었을까요?
10월 26일에 Next.js 14 버전이 공개되었습니다. 이전 버전인 13버전이 나온 지 1년 밖에 지나지 않았습니다.
Next.js는 React.js 기반의 프레임워크로, 한국뿐만 아니라 전 세계에서도 많은 인기를 끌고 있는 라이브러리입니다.
간단히 Next.js에 대해 알아보고, 14버전의 주요 변화점을 정리해보겠습니다.
Next.js 소개
1. 웹 애플리케이션 제작에 필요한 빌딩 블록
Next.js 공식 웹사이트에서는 Next.js를 “빠른 웹 애플리케이션을 제작할 수 있는 빌딩 블록을 제공하는 유연한 React 프레임워크”라고 정의하고 있습니다. 이와 관련하여 필요한 빌딩 블록들도 명시하고 있습니다.
- 사용자 인터페이스(User Interface) – 사용자가 애플리케이션을 소비하고 상호 작용하는 방식.
- 라우팅(Routing) – 사용자가 애플리케이션의 여러 영역을 탐색하는 방법.
- 데이터 가져오기(Data Fetching) – 데이터의 저장 위치 및 데이터 가져오기 방법.
- 렌더링(Rendering) – 정적 또는 동적 콘텐츠를 렌더링하는 시기와 위치.
- 연동(Integrations) – 타사 서비스(CMS, 인증, 결제 등) 이용 및 연결 방법.
- 인프라(Infrastructure) – 애플리케이션 코드를 배포, 저장 및 운영하는 곳(Serverless, CDN, Edge 등).
- 성능(Performance) – 고객(사용자)를 위해 애플리케이션을 최적화하는 방법.
- 확장성(Scalability) – 팀, 데이터, 트래픽이 증가함에 따라 애플리케이션을 유연하게 조정하는 방법.
- 개발자 경험(Developer Experience) – 팀의 애플리케이션 구축 및 유지 관리 경험.
최신 애플리케이션 구축할때 고려해야할 몇가지 사항(Building Blocks)을 위와 같이 정의하였고, 각 부분에 대해 직접 구축할지 아니면 다른 라이브러리를 사용할지 결정해야 합니다.
2. React는 무엇인가요?
React는 대화형 사용자 인터페이스를 구축하기 위한 Javascript 라이브러리입니다. 사용자 인터페이스는 사용자가 화면에서 보고 상호작용하는 요소를 의미합니다.
React는 라이브러리(library)라는 점을 특별히 강조하는 이유가 있습니다. React는 UI를 구축하는 데 유용한 기능을 제공하지만, 애플리케이션에서 해당 기능을 어디에 사용할지는 개발자에게 맡긴다는 의미입니다.
React의 성공 요인 중 하나는 애플리케이션 구축의 다양한 측면에 대해 개별적인 방법을 선택하여 해결한 것입니다. 이로 인해 다양한 서드파티 도구와 솔루션을 포함한 풍부한 생태계가 형성되었습니다.
그러나 React 애플리케이션을 처음부터 구축하려면 상당한 노력이 필요하다는 의미이기도 합니다. 개발자는 애플리케이션 요구 사항에 맞게 도구를 구성하고 솔루션을 구성하는 데 시간을 투자해야 합니다.
3. Next.js란 무엇인가요?
Next.js는 웹 애플리케이션을 만들기 위한 빌딩 블록을 제공하는 React 프레임워크입니다.
프레임워크는 Next.js가 React에 필요한 도구와 구성을 지원하고 애플리케이션을 위한 별도의 구조, 기능 그리고 최적화를 제공한다는 의미입니다.
React를 사용하여 UI를 구축한 다음 Next.js 기능을 점진적으로 채택하여 라우팅, 데이터 가져오기, 연동과 같은 보편적 애플리케이션 요구 사항을 해결하는 동시에 개발자와 사용자 경험을 개선할 수 있습니다.
개인 개발자이든 대규모 팀의 구성원이든 React와 Next.js를 활용하여 인터랙티브하고 동적 성능이 뛰어난 완전한 웹 애플리케이션을 구축할 수 있습니다.
Next.js 14 변경사항
1. Next.js 컴파일러: 성능 향상
Next.js 13부터 페이지와 앱 라우터 모두에서 로컬 개발 성능을 개선하기 위해 노력해 왔습니다. 이전 버전에서는 성능 향상을 위하여 next dev 와 Next.js 의 다른 파트를 재정비 하였습니다. 이번 버전에서는 Next.js의 모든 기능 성능 향상에 대해 초점을 맞추었고, 그것은 Rust 기반 컴파일러 안정화로 이어질겁니다.
Rust 엔진 Turbo pack 의 next dev 검증을 위한 5000개의 통합 테스트는 현재 통과 중에 있으며, 이 테스트에는 7년간의 버그 수정과 재현이 포함되어 있습니다.
대규모 Next.js 애플케이션인 vercel.com 웹사이트에서 테스트 하는 동안 다음 결과를 확인하였습니다.
- 최대 53% 속도 향상 된 로컬 서버 구동 속도
- 최대 94% 속도 향상 된 코드 갱신 by Fast Refresh
이 벤치마크는 대규모 애플리케이션에서 기대할 수 있는 성능 개선에 대한 실질적인 결과입니다. next dev 테스트가 90%가 통과되었으므로, next dev –turbo를 사용하면 더 빠르고 안정적인 성능을 경험할 수 있습니다.
테스트 통과율이 100%에 도달하면 마이너 릴리스에서 터보팩을 stable 버전으로 전환할 예정입니다. 사용자 설정 및 에코시스템 플러그인을 위한 웹팩 사용도 계속 지원할 예정입니다.
2. Forms and Mutations
Next.js 9에서는 프론트엔드 코드와 함께 백엔드 엔드포인트를 빠르게 빌드하는 방법인 API Router가 도입되었습니다.
예를 들어 api/ 디렉터리에 새 파일을 생성할 수 있습니다.
아래와 같은 submit 파일명으로 API Router를 만들어 줍니다.
pages/api/submit.ts
import type { NextApiRequest, NextApiResponse } from 'next';
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
const data = req.body;
const id = await createItem(data);
res.status(200).json({ id });
}
그런 다음 클라이언트 측에서 React와 onSubmit 과 같은 이벤트 핸들러를 사용하여 API Route로 fetch 를 만들 수 있습니다.
pages/index.tsx
import { FormEvent } from 'react';
export default function Page() {
async function onSubmit(event: FormEvent<HTMLFormElement>) {
event.preventDefault();
const formData = new FormData(event.currentTarget);
const response = await fetch('/api/submit', {
method: 'POST',
body: formData,
});
// Handle response if necessary
const data = await response.json();
// ...
}
return (
<form onSubmit={onSubmit}>
<input type="text" name="name" />
<button type="submit">Submit</button>
</form>
);
}
이제 Next.js 14버전를 통해 data mutations(서버 데이터 수정) 작성하는 개발자 경험을 간소화할것입니다. 또한 저성능 디바이스와 느린 네트워크에서 발생되는 사용자 경험을 개선할것입니다.
Server Action 안정화
API Route를 수동으로 생성할 필요가 없다면 어떨까요? 대신 서버에서 보안적으로 실행되는 함수를 정의하고 React 컴포넌트에서 직접 호출할 수 있습니다.
App Router는 새로운 기능을 채택하기에 안정적인 React Canary 채널에서 구축되어있습니다. v14 버전의 Next.js는 안정적인 서버 액션을 포함하는 최신 React Canary 채널로 업그레이드되었습니다.
이전 라우터 예제를 하나의 Page 파일로 단순화할 수 있습니다. 단순화된 코드입니다.
app/page.tsx
export default function Page() {
async function create(formData: FormData) {
'use server';
const id = await createItem(formData);
}
return (
<form action={create}>
<input type="text" name="name" />
<button type="submit">Submit</button>
</form>
);
}
서버 액션은 이전에 서버 중심 프레임워크를 사용해 본 적이 있는 개발자에게는 친숙하게 느껴질 것입니다. Form과 FormData Web API와 같은 웹 기본 사항을 기반으로 구축되었습니다.
Form을 통해 서버 액션을 사용하는 것은 성능 향상에 도움이 되지만 필수 사항은 아닙니다. Form 없이 함수로 직접 호출할 수도 있습니다. TypeScript를 사용하면 클라이언트와 서버 간에 완전한 엔드-투-엔드 형태의 안전성을 확보할 수 있습니다.
Mutating data(서버 데이터 가공), 페이지 재렌더링 또는 리디렉션을 단 한 번의 네트워크 왕복 작업으로 수행할 수 있으므로 네트워크 업스트림 공급자의 속도가 느려도 클라이언트에 올바른 데이터가 표시되도록 보장할 수 있습니다. 또한 동일한 경로에서 여러 가지 작업을 포함하여 다양한 작업을 구성하고 재사용할 수 있습니다.
Caching, Revalidating, Redirecting, 그 외
서버 작업은 전체 App Router 모델에 통합되어 있습니다. 다음을 수행할 수 있습니다:
- revalidatePath() 또는 revalidateTag()를 사용하여 캐시된 데이터 재검증
- redirect()를 통해 다른 경로로 리디렉션
- useOptimistic()으로 최적화된 UI 업데이트 처리
- useFormState()로 서버의 오류를 포착하고 표시합니다.
- useFormStatus()로 브라우저에 로딩 상태 표시하기
서버 컴포넌트와 서버 작업에 대한 모범 사례는 Forms and Mutations with Server Actions 와 security model 페이지에서 확인 가능합니다.
3. Partial Prerendering (Preview)
빠른 초기 정적 응답 가진 동적 콘텐츠을 위한 컴파일러 최적화를 v14에 적용하는 작업을 진행중입니다.
“빠른 초기 정적 응답 가진 동적 콘텐츠는 공식홈페이지에서 dynamic content with a fast initial static response로 표현하며, next.js가 처음 페이지를 불러올때 정적인 컨텐츠를 미리 제공하는 방식입니다.”
Partial Prerendering은 서버 사이드 렌더링(SSR), 정적 사이트 생성(SSG), 그리고 증분적 정적 재검증(ISR)에 대한 수십 년의 연구와 개발을 기반으로 합니다.
Built on React Suspense
Partial Prerendering은 서스펜스 경계에 의해 정의됩니다. 작동 방식은 다음과 같습니다. 다음 전자상거래 페이지를 생각해 봅시다:
app/page.tsx
export default function Page() {
return (
<main>
<header>
<h1>My Store</h1>
<Suspense fallback={<CartSkeleton />}>
<ShoppingCart />
</Suspense>
</header>
<Banner />
<Suspense fallback={<ProductListSkeleton />}>
<Recommendations />
</Suspense>
<NewProducts />
</main>
);
}
Partial Prerendering이 활성화되면, 이 페이지는 <Suspense /> 경계를 기반으로 정적인 셸(shell)을 생성합니다. React Suspense의 fallback은 사전 렌더링(prerendered)됩니다.
그런 다음 셸의 Suspense fallback은 쿠키를 읽어 장바구니를 확인하거나 사용자를 기반으로 배너를 표시하는 등의 동적 컴포넌트로 대체됩니다.
요청이 발생하면 다음과 같은 정적 HTML 셸이 즉시 제공됩니다:
<main>
<header>
<h1>My Store</h1>
<div class="cart-skeleton">
<!-- Hole -->
</div>
</header>
<div class="banner" />
<div class="product-list-skeleton">
<!-- Hole -->
</div>
<section class="new-products" />
</main>
<ShoppingCart />가 사용자 세션을 확인하기 위해 Cookie를 읽기 때문에, 이 컴포넌트는 정적 셸과 동일한 HTTP 요청의 일부로 스트리밍됩니다. 추가적인 네트워크 왕복이 필요하지 않습니다.
app/cart.tsx
import { cookies } from 'next/headers'
export default function ShoppingCart() {
const cookieStore = cookies()
const session = cookieStore.get('session')
return ...
}
가장 세분화된 정적 셸을 사용하려면 Suspense 경계를 추가해야 할 수 있습니다. 그러나 현재 이미 loading.js를 사용하고 있는 경우 이는 암묵적인 Suspense 경계이므로 정적 셸을 생성하는 데 추가 변경이 필요하지 않습니다.
부분 사전 렌더링은 현재 활발히 개발 중입니다. 향후 마이너 릴리스에서 더 많은 업데이트를 공유할 예정입니다.
Metadata Improvements
서버에서 페이지 콘텐츠를 스트리밍하기 전에 뷰포트, 색 구성표 및 테마에 대한 중요한 메타데이터를 먼저 브라우저로 전송해야 합니다.
이러한 meta 태그들이 초기 페이지 콘텐츠와 함께 전송되도록 하면 부드러운 사용자 경험을 제공합니다. 이는 테마 색상을 변경으로 인해 페이지가 깜박이는 것을 방지해주며, 뷰포트 변경으로 인한 레이아웃 이동을 막습니다.
Next.js 14에서는 블로킹 메타데이터와 논 블로킹 메타데이터를 분리했습니다. 일부 메타데이터 옵션만 블로킹하며, 논 블로킹 메타데이터가 사전 렌더링된 페이지가 정적 셸을 제공하는 데 방해가 되지 않도록 하고자 합니다.
다음 메타데이터 옵션은 현재 더 이상 사용되지 않으며 향후 메이저 버전의 메타데이터에서 제거될 예정입니다:
- viewport : 뷰포트의 초기 확대/축소 및 기타 속성을 설정합니다.
- colorScheme : 뷰포트의 지원 모드(밝음/어두움)를 설정합니다.
- themeColor : 뷰포트 주변의 크롬이 렌더링할 색상을 설정합니다.
Next.js 14부터는 이러한 옵션을 대체하는 새로운 옵션인 viewport 및 generateViewport가 추가되었습니다. 다른 모든 metadata 옵션은 동일하게 유지됩니다.
4. Next.js Learn Course
Next.js Learn에서 새로운 무료 강좌를 출시합니다. 이 강좌에서는 다음 내용을 학습합니다:
- The Next.js App Router
- Styling and Tailwind CSS
- Optimizing Fonts and Images
- Creating Layouts and Pages
- Navigating Between Pages
- Setting Up Your Postgres Database
- Fetching Data with Server Components
- Static and Dynamic Rendering
- Streaming
- Partial Prerendering (Optional)
- Adding Search and Pagination
- Mutating Data
- Handling Errors
- Improving Accessibility
- Adding Authentication
- Adding Metadata
참고 자료
2023/10/26
nextjs.org – Next.js 14 |