npx create-next-app@latest
npm run dev
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
next dev
: Next.js 를 개발모드에서 실행
next build
: 프로덕션에서 사용할 애플리케이션을 빌드
next start
: 프로덕션 서버 Next.js 실행
next lint
: 만들어진 Next.js 용 ESLint 설정파일을 설정한다.
Next.js 는 파일 시스템 라우팅
이라는 것을 사용한다. 이는 파일을 어떻게 구성하는가에 의해 애플리케이션에서 라우팅을 결정한다는 것을 의미한다.
src/app/layout.js 는 기본적인 웹 페이지의 골격을 구성한다.
app
디렉토리새 애플리케이션에서 App Router
를 사용하는 것을 권장한다. 이는 React 의 최신 기능들을 사용할 수 있게 하고 커뮤니티의 피드백에 의해 Pages Router
에서 진화된 라우터이다.
이미지 출처 : https://nextjs.org/docs/getting-started/installation
app/
디렉토리에 layout.tsx
, page.tsx
를 추가하자. 이들이 유저가 애플리케이션에 /
로 접속할때 렌더링된다.
app/layout.tsx
에 필요한 html 과 body 태그
안에 루트 레이아웃을 만들자.
app/page.tsx
를 만들어 초기 내용을 생성할 수 있다.
// app/page.tsx
export default function Page() {
return <h1>Hello, Next.js!</h1>
}
알아두면 좋은 것
만약layout.tsx
생성을 잊었다고 해도next dev
로 개발 서버를 실행하면 Next.js 가 이 파일을 자동으로 생성해준다.
pages
디렉토리 (optional)만약 App router 대신 Pages Rouer 를 사용하고자 한다면 pages/
디렉토리를 프로젝트 루트에 생성하여 사용할 수 있다.
그러면 index.tsx 를 pages 폴더에 추가해야 한다. 이게 홈페이지 /
가 될 것이다.
// pages/index.tsx
export default function Page() {
return <h1>Hello, Next.js!</h1>
}
다음으로 _app.tsx
파일을 pages/
안에 추가하여 global layout 을 추가한다.
// pages/_app.tsx
import type { AppProps } from 'next/app'
export default function App({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />
}
마지막으로 _document.tsx
파일을 pages/
내에 추가하여 서버로부터 초기 response 를 제어할 수 있다.
// pages/_document.tsx
import { Html, Head, Main, NextScript } from 'next/document'
export default function Document() {
return (
<Html>
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
알아두면 좋은 것
App Router
나Pages Router
나 두가지 라우터를 같은 프로젝트에서 동시에 사용해도 되지만 app 경로가 pages 보다 우선순위를 갖는다. Next.js 는 혼란을 피하기 위해 프로젝트에서 하나의 라우터만 사용할 것을 권장한다.
이미지, 폰트같은 정적 assets 를 저장하기 위해 public
folder 를 생성할 수 있다. public
디렉토리 내의 파일들은 base URL /
로 시작하여 코드에서 참조할 수 있다.
// app/layout.tsx
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
layout.tsx 의 children 은 같은 디렉토리의 page.tsx 가 리턴한 것을 가져온다.
root layout 은 app 디렉토리의 최상단에 정의되고 모든 라우트에 적용된다. 이 레이아웃은 서버에 의해 반환되는 초기 HTML 을 변경할 수 있도록 한다.
알아두면 좋은 것
app
디렉토리는 반드시 루트 레이아웃을 포함해야 한다.- 루트 레이아웃은 반드시 html 과 body 태그를 정의해야 하는데 이는 Next.js 가 자동으로 그들을 생성하지 않기 때문이다.
- title 요소와 같이 head HTML 요소를 조절하기 위해 SEO 를 돕는 기능을 사용할 수 있다.
- 다수의 루트 레이아웃들을 생성하기 위해 route groups 를 사용할 수 있다.
- 루트 레이아웃은 기본인 서버 컴포넌트이며 클라이언트 컴포넌트에 의해서는 설정할 수 없다.
루트 레이아웃은
_app.js
,_document.js
파일을 대체한다.
다중 루트 레이아웃들을 생성하기 위해 가장 최상단의 layout.js 를 제거하고 각 route groups 안에 layout.js 를 추가하자. 이는 완전히 다른 UI 나 경험을 갖는 섹션으로 애플리케이션을 분할하는데 유용하다. html 과 body 태그가 각 루트 레이아웃에 추가되어야 한다.
위의 예시는 marketing 과 shop 이 각각의 루트 레이아웃을 갖는다.
알아두면 좋은 것
- route groups 의 이름은 조직을 위한 것 외에는 특별한 중요도를 갖지 않는다. URL 경로에 영향을 끼치지 않는다.
- route group 을 포함하는 Routes 는 다른 routes 와 같은 URL 경로를 가져서는 안된다. 예를 들어 route groups 가 URL 구조에 영향을 끼치지 않기에
(marketing)/about/page.js
와(shop)/about/page.js
는 둘다/about
라고 보기에 에러를 유발할 수 있다.- 최상단의 layout.js 없이 다중 루트레이아웃을 사용한다면 home 인
page.js
파일은route groups
중 하나에 정의되어야 한다. ex)app/(marketing)/page.js
- 다중 루트 레이아웃을 탐색하면 (navigating) 이는 전체 페이지 로드를 야기할 수 있다. (클라이언트 측 탐색과 대조적) 예를 들어
app/(shop)/layout.js
를 사용하는/cart
로부터app/(marketing)/layout.js
를 사용하는/blog
로 탐색한다고 할때 이는 전체 페이지 로드를 야기한다. 이는 오로지 다중 루트 레이아웃에 적용된다.
리액트의 서버 컴포넌트는 서버측에서 렌더링되고 선택적으로 캐싱될 수 있는 UI 를 작성할 수 있도록 한다.
Next.js 에서 렌더링 작업은 스트리밍과 분할된 렌더링을 가능하게 하기 위해 route segments 에 의해 분할된다. Next.js 는 세가지 렌더링 전략을 갖는다.
Static Rendering
Dynamic Rendering
Streaming
정적 렌더링과 함께 routes (경로) 는 빌드 타임 혹은 데이터 재검증 이후 백그라운드에서 렌더링된다. 결과는 캐시되어 CDN (Content Delivery Network) 로 푸시될 수 있다. 이 최적화를 통해 사용자와 서버 요청 간 렌더링 작업 결과를 공유할 수 있다.
정적 렌더링은 경로에 상용자에게 개인화되지 않은 데이터가 있고 정적 블로그 게시물이나 제품 페이지와 같이 빌드 시 알 수 있는 데이터가 있을때 유용하다.
데이터 재검증 Data Revalidation 은 데이터 캐시를 제거하고 최신 데이터를 다시 가져오는 프로세스이다. 캐시된 데이터는 다음 두가지 방법으로 유효성을 검사할 수 있다.
1. 시간 기반 재검증
- 일정 시간이 지나면 데이터를 자동으로 재검증한다. 이는 자주 변경되지 않고 최신성이 중요하지 않은 데이터에 유용하다.
fetch('https://...', { next: { revalidate: 3600 } })
- 혹은 fetch 경로 세그먼트의 모든 요청을 재검증하기 위해 Segment Config Options 를 사용할 수 있다.
// layout.js | page.js export const revalidate = 3600 // revalidate at most every hour
2. 주문형 재검증
- 이벤트를 기반으로 데이터를 수동으로 재검증한다. 주문형 재검증에서는 태그 기반 또는 경로 기반 접근 방식을 사용해서 데이터 그룹을 한 번에 재검증할 수 있다.
export default async function Page() { const res = await fetch('https://...', { next: { tags: ['collection'] } }) const data = await res.json() // ... }
동적 렌더링을 사용하면 요청시 각 사용자에 대한 경로가 렌더링된다.
동적 렌더링은 경로에 사용자에게 맞춤화된 데이터가 있거나 쿠키나 URL 의 검색 매개변수와 같이 요청 시에만 알 수 있는 정보가 있을 때 유용하다.
스트리밍을 사용시 서버에서 UI 를 점진적으로 렌더링할 수 있다. 작업은 여러 다누이로 분할되어 준비가 되면 클라이언트로 스트리밍된다. 이를 통해 사용자는 전체 콘텐츠의 렌더링이 완료되기 전에 페이지의 일부를 즉시 볼 수 있다.
스트리밍은 기본적으로 Next.js 앱 라우터에 내장되어 있다. 이는 초기 페이지 로딩 성능 뿐 아니라 전체 경로 렌더링을 차단하는 느린 데이터 가져오기에 의존하는 UI 를 모두 개선하는데 도움이 된다. ex) 제품 페이지 리뷰
loading.js 나 React Suspense 와 함께 UI 컴포넌트를 사용하는 스트리밍 route segment 를 시작할 수 있다.
React Suspense 는 children 의 로딩이 완료될 때까지 fallback 유틸을 보여줄 수 있다.
<Suspense fallback={<Loading />}> <SomeComponent /> </Suspense>
기볼적으로 Next.js 는 서버 컴포넌트를 사용하여 추가 구성없이 서버 렌더링을 자동으로 구현하며 필요시 클라이언트 컴포넌트 사용을 선택할 수 있다.
클라이언트 컴포넌트를 사용하면 request time 에 클라잉너트에 렌더링할 수 있는 대화형 (reactive) UI 를 작성할 수 있다. Next.js 에서 클라이언트 렌더링은 옵션이다. 즉, React 가 클라이언트에서 렌더링해야 하는 컴포넌트를 명시적으로 결정해야 한다.
상단에 use client
지시어를 추가하면 된다. 그러면 하위 구성요소를 포함하여 해당 파일로 가져온 다른 모든 모듈이 클라이언트 번들의 일부로 간주된다.
// app.couter.tsx
'use client'
import { useState } from 'react'
export default function Counter() {
const [count, setCount] = useState(0)
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
)
}
여러
use client
진입점 정의하기
- 리액트 컴포넌트 트리에 다중
use client
진입점을 정의할 수 있다. 이를 통해 앱을 다중 클라잉너트 번들로 쪼갤 수 있다.- 그러나
use client
는 클라잉너트에 렌더링될 모든 컴포넌트에 정의될 필요는 없다. 한번 경계에 정의하면 모든 자식 컴포넌트와 해당 컴포넌트로 import 된 모듈들은 클라이언트 번들의 일부로 간주된다.
app/dashboard/layout.js
와 같이 폴더 내에 정의된 레이아웃은 특정한 탐색 조각에 적용될 수 있고 (acme.com/dashboard
) 이 segment 들이 활성화되어있을 때 렌더링된다. 기본적으로 ㄹ파일 계층 구조 내의 레이아웃들은 중첩되며 이는 그들의 자식 레이아웃들을 children prop 을 통해 감싼다는 것을 의미한다.
export default function DashboardLayout({
children,
}: {
children: React.ReactNode
}) {
return <section>{children}</section>
}
알아두면 좋은 것
- 오직 루트 레이아웃만 html 과 body 태그를 갖는다.
만약 위의 예시에서 두 레이아웃을 결합한다면 app/layout.js
는 app.dashboard/layout.js
를 감싸 app/dashboard/*
처럼 사용한다.
템플릿은 각 자식 레이아웃이나 페이지를 감싼다는 점에서 레이아웃과 유사하다. 그러나 경로 전반에 걸쳐 지속되고 상태를 유지하는 레이아웃과 달리 템플릿은 탐색에서 각 자식마다 새로운 인스턴스를 생성한다.
이는 유저가 경로 사이에서 템플릿을 공유할 때 그 컴포넌트의 새로운 인스턴스가 마운트되고 DOM 요소가 다시 생성되고 상태는 보존되지 않으며 effect 는 다시 동기화됨을 의미한다.
다음과 같은 경우 템플릿은 layout 보다 더 나은 선택이 될 수 있다.
템플릿은 template.js 파일에서 기본적인 리액트 컴포넌트를 export 하여 정의될 수 있다. 컴포넌트는 반드시 children prop 을 받아야 한다.
// app/template.tsx
export default function Template({ children }: { children: React.ReactNode }) {
return <div>{children}</div>
}
중첩 측면에서 template.js
는 layout 과 layout 의 children 사이에서 렌더링된다.
<Layout>
{/* Note that the template is given a unique key. */}
<Template key={routeParam}>{children}</Template>
</Layout>
<head>
태그 수정하기app
디렉토리에서 title 과 SEO 를 돕는 meta 같은 head HTML 요소를 수정할 수 있다.
Metadata 는 metadata 객체나 layout.js
혹은 page.js
파일 내의 generatedMetadata 함수를 통해 exporting 하여 정의될 수 있다.
// app.page.tsx
import { Metadata } from 'next'
export const metadata: Metadata = {
title: 'Next.js',
}
export default function Page() {
return '...'
}
알아두면 좋은 것
title
,meta
같은 head 태그를 루트 레이아웃에 수동으로 추가하면 안된다. 대신 자동으로 스트리밍이나 중복 제거같은 것을 다루는 Metadata API 를 사용해야 한다.
app
: App Router
pages
: Pages Router
public
: Static assets to be served
src
: 선택적인 애플리케이션 소스 폴더
next.config.js
: Next.js 설정 파일
package.json
: 프로젝트 의존성과 스크립트
instrumentation.ts
: OpenTelementry 와 Instrumentation 파일
middleware.ts
: Next.js 요청 미들웨어
.env
: 환경 변수
.env.local
: 로컬 환경 변수
.env.production
: 프로덕션 환경 변수
.env.development
: 개발 환경 변수
.eslintrc.json
: ESLint 설정 파일
.gitignore
: 무시할 Git 파일과 폴더
next-env.d.ts
: Next.js 를 위한 타입스크립트 선언 파일
tsconfig.json
: 타입스크립트 설정 파일
jsconfig.json
: 자바스클비트 설정 파일