Next.js 공부를 위해 공식문서를 읽기 쉽게 제가 생각하는 핵심내용만 짚어서 정리합니다.
create-next-app
라는 명령어를 사용한다.
npx create-next-app@latest
명령어 입력하면 프로젝트의 여러 옵션이 표시되고 결정할 수 있는데 TailWind CSS, import alias 만 개인 취향으로 하고 나머지는 그냥 Yes 하면 될 것 같다.
아래 명령어를 입력해 직접 패키지를 설치한다.
npm install next@latest react@latest react-dom@latest
package.json 파일은 아래와 같이 수정한다.
{
"scripts": {
"dev": "next dev", // 개발서버를 실행
"build": "next build", // 앱을 프로덕션 버전으로 빌드
"start": "next start", // 프로덕션 서버 실행
"lint": "next lint" // ESLint 실행
}
}
Next.js 는 파일 시스템 기반의 라우팅을 제공하기 때문에 필요한 작업이다. 루트 경로에 app
디렉토리를 만들고 layout.tsx
파일을 만든다. 이 파일은 Root Layout 이다. (참고로 이 파일 생성을 잊어먹으면 프레임워크가 자동으로 생성한다)
Root Layout 은 반드시 html, body 를 포함해야 한다.
type Children = { children: React.ReactNode };
export default function RootLayout({ children }: Children) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
app
디렉토리에 page.tsx
를 만들어준다. 이건 레이아웃 내에 포함될 페이지 컴포넌트이다.
export default function Page() {
return <h1>Hello, Next.js!</h1>
}
이 컴포넌트는 URL 의 [schema]://[domain]/ 으로 접속 가능하다 (예시: https://localhost:3000/)
여기까지 진행했다면 아래와 같은 파일 구조일 것이다.
[app]
|-layout.tsx
|-page.tsx
src 디렉토리를 넣고 싶다면 app 폴더를 포함해서 넣도록 한다.
public
폴더를 루트 경로에 추가하여 정적 애셋을 추가한다. 이 애셋들은 base URL 으로 접근 가능하다.
import Image from 'next/image'
export default function Page() {
return <Image
src="/profile.png"
alt="Profile"
width={100}
height={100}
/>
}
npm run dev
최소 타입스크립트 버전은 4.5.2
Next.js 는 타입스크립트를 내장한다. 그러므로 js / jsx 확장자를 ts / tsx 로만 바꾸고 개발서버를 실행하면 된다. tsconfig.json 을 넣으면 원하는 설정을 추가할 수도 있다.
실행은 npm run lint
로 한다. (package.json 설정은 위에 참조)
콘솔창에 이 명령어를 실행하면 Lint 실행 옵션이 보인다.
Strict, Base 를 실행하면 ESLint 가 자동 설치. 이후엔 next lint
로 실행 가능.
tsconfig.json
혹은 jsconfig.json
에서 paths, baseUrl 설정이 가능하다. 절대경로를 @ 를 이용해서 가독성이 좋은 상대경로로 바꿀 수 있다. 이외에도 다른 경로도 alias 로 만들 수 있다.
{
"compilerOptions": {
"baseUrl": "src/",
"paths": {
"@/styles/*": ["styles/*"],
"@/components/*": ["components/*"]
}
}
}
Next.js 의 파일 컨벤션과 추천사항들을 다룬다.
public
src
|--app
의존성, 미들웨어, 모니터링 툴, 환경변수 설정
Next.js 는 파일 구성에 대해 이미지 정해진 룰이 있으나 아래 사항을 참고하면 좋다.
아래 파일에 정의된 컴포넌트는 위계구조를 갖는다.
<Layout>
<Template>
<ErrorBoundary fallback={<Error />}>
<Suspense fallback={<Loading />}>
<ErrorBoundary fallback={<NotFound />}>
<Page />
</ErrorBoundary>
</Suspense>
</ErrorBoundary>
</Template>
</Layout>
컴포넌트들은 연속적으로 렌더링된다. nested 세그먼트는 부모 세그먼트 안에 렌더링된다.
dashboard
|--layout.ts
|--error.js
|--loading.js
|---settings
|---layout.ts
|---error.js
|---loading.js
|---page.js
<Layout>
<ErrorBoundary fallback={<Error />}>
<Suspense fallback={<Loading />}>
// Start of settings
<Layout>
<ErrorBoundary fallback={<Error />}>
<Suspense fallback={<Loading />}>
<Page />
</Suspense>
</ErrorBoundary>
</Layout>
// End of settings
</Suspense>
</ErrorBoundary>
</Layout>
app
디렉토리에는 아래 폴더들이 Route 세그먼트를 정의한다(URL 의 세그먼트). 하지만 page.js
혹은 route.js
가 없으면 Route 세그먼트로 작동하지 않는다.
app
|---dashboard Not Routable
|---settings Not Routable
|---api Not Routable
반대로 page.js
혹은 route.js
만 있는 폴더라면 Route 세그먼트로 작동한다.
app
|---dashboard
|---page.js Routablable, /dashboard
|----settings
|----page.js Routable, /dashboard/settings
|---api
|---route.js Routable, /dashboard/api/
이는 라우팅할 파일이 정해져 있다는 뜻이기 때문에 실수를 줄일 수 있다.
폴더 앞에 _ 를 붙인다(%5F 도 가능). Route 세그먼트에 포함되지 않는다.
app
|---dashboard
|---page.js Routablable, /dashboard
|----_components
|----buttons.js
|----_lib
|----format-date.js
|----page.jsroute.js Not Routable, /dashboard/_lib/
Private folder 는 아래의 상황에서 사용한다.
(폴더이름) 으로 만들 수 있다. Route 세그먼트에 포함되지 않는다.
app
|---(admin)
|---dashboard
|---page.js Routablable, /dashboard
|---(marketing)
|---about
|---page.js Routablable, /about
|---blog
|---page.js Routablable, /blog
Route groups 는 아래의 상황에서 사용한다.
Optional. 루트의 루트. 앱의 코드와 프로젝트 설정파일 (예: package.json) 을 나누기 좋음.
page 는 렌더링 되는 컴포넌트이다.
app
|--page.js Routable, /
export default function Page() {
return <h1>Hello Next.js!</h1>
}
다수의 page 에서 공유하며, navigation 에도 그 상태와 상호작용이 유지된다. 렌더링 사이클은 page 와 같이 가져가지 않는다.
layout 파일을 만들면 된다. 아래 예시는 Root Layout 이다. Root Layout 은 특별히 html, body xormfmf 가져야 한다.
app
|--layout.js
|--page.js Routable, /
type Children = { children: React.ReactNode }
export default function DashboardLayout({ children }: Children) {
return (
<html lang="en">
<body>
<main>{children}</main>
</body>
</html>
)
}
앱 내 여러 URL 세그먼트를 정의할 수 있다.
/blog/[slug]
Layout 도 nested 된다. nested 된 layout 은 부모 layout 내에 렌더링 된다.
<Link> 컴포넌트를 이용하면 라우팅이 가능하다. Next.js 빌트인 컴포넌트이고 a 태그를 확장하였다.
아래 코드는 '/blog/[Dynamic_Routing_Segment]' 로 라우팅 하는 예제이다.
import Link from 'next/link';
export default async function Post({ post }) {
const posts = await getPosts();
return (
<ul>
{posts.map((post) => (
<li key={post.slug}>
<Link href={`/blog/${post.slug}`}>{post.title}</Link>
</li>
))}
</ul>
)
}
Link 컴포넌트는 대부분의 라우팅 상황에서 사용할 수 있지만 useRouter 훅을 이용해야 할 경우도 있다.
Next.js 는 이미지와 폰트에 대한 최적화가 이미 되어 있다.
Image 컴포넌트는 html <img> 태그를 확장한다. 이 컴포넌트는 다음의 기능을 제공한다.
import Image from 'next/image';
export default function Page() {
return <Image src="" alt="" />
}
src 속성은 로컬, 원격 주소 모두 가능.
public 폴더 내에 있다면 가능.
import Image from 'next/image';
import profilePic from './me.png';
export default function Page() {
return <Image src={profilePic} alt="Picture of the author" />
}
Next.js 는 import 한 이미지 크기를 이용해 내부 폭, 높이를 설정한다. blurDataURL, placeholder 속성도 있는데 설정 안하면 기본값이 제공된다.
URL string 을 제공한다.
import Image from 'next/image'
export default function Page() {
return (
<Image
src="https://s3.amazonaws.com/my-bucket/profile.png"
alt="Picture of the author"
width={500} height={500}
/>
)
}
이 경우는 빌드 타임에 이미지 크기를 Next.js 는 모르니 width, height 를 제공해주는 것이 좋다.
next.config.js 파일에 제공될 URL 패턴을 제공하면 자칫 잘못된 URL 을 사용하는 것을 막을 수 있다.
import { NextConfig } from 'next';
const config: NextConfig = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 's3.amazonaws.com',
port: '',
pathname: '/my-bucket/**',
search: '',
},
],
},
}
export default config;
next/font 모듈은 폰트를 최적화하고 네트워크 요청을 최소화 한다. 거기다 Next.js 는 내장된 폰트가 따로 있다.
만약 로컬 파일로 존재하는 폰트를 가져오기 위해서는 'next/font/local' 혹은 'next/font/google' 을 사용한다.
import { Geist } from 'next/font/google';
const geist = Geist({
weight: '400', // 속성 적용 가능
subsets: ['latin'],
});
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" className={geist.className}>
<body>{children}</body>
</html>
)
}
import localFont from 'next/font/local';
const myFont = localFont({
src: './my-font.woff2',
})
// 아래도 가능.
// const roboto = localFont({
// src: [
// {
// path: './Roboto-Regular.woff2',
// weight: '400',
// style: 'normal',
// },
// {
// path: './Roboto-Italic.woff2',
// weight: '400',
// style: 'italic',
// },
// {
// path: './Roboto-Bold.woff2',
// weight: '700',
// style: 'normal',
// },
// {
// path: './Roboto-BoldItalic.woff2',
// weight: '700',
// style: 'italic',
// },
// ],
// })
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" className={geist.className}>
<body>{children}</body>
</html>
)
}
CSS 에 관해서 Next.js 는 여러 개의 방식을 제공한다.
CSS classname 을 만들고 적용한다.
// app/blog/styles.module.css
.blog {
padding: 24px;
}
// app/blog/page.tsx
import styles from './styles.module.css'
export default function Page({ children }: { children: React.ReactNode }) {
return <main className={styles.blog}>{children}</main>
}
앱 전체에 적용하는 CSS 를 정의한다.
// app/global.css
body {
padding: 20px 20px 60px;
max-width: 680px;
margin: 0 auto;
}
// app/layout.tsx
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
Next.js 와 호환이 잘 된다.
npm install tailwindcss @tailwindcss/postcss postcss
root 프로젝트에 postcss.config.mjs 파일을 넣는다.
/** @type {import('tailwindcss').Config} */
export default {
plugins: {
'@tailwindcss/postcss': {},
},
}
Global Stylesheet (app/globals.css) 에 Tailwind directives 를 넣는다.
@import 'tailwindcss';
그리고 root layout 에 Global Stylesheet 를 추가한다.
import type { Metadata } from 'next'
// These styles apply to every route in the application
import './globals.css'
export const metadata: Metadata = {
title: 'Create Next App',
description: 'Generated by create next app',
}
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
이제 사용 가능
export default function Page() {
return <h1 className="text-3xl font-bold underline">Hello, Next.js!</h1>
}
npm install --save-dev sass
next.config.js 에서 아래와 같이 작성한다.
import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
sassOptions: {
additionalData: `$var: red;`,
},
}
export default nextConfig;
나머지는 나중에 필요하면 따로 공부.