Next.js 13이 릴리즈되었지만, 공식 문서의 예제 코드는 12 기준으로 작성되어 있습니다.
13에서 변경된 점이나 12에서 13으로 업그레이드하는 내용은 아래 문서를 참고해주세요.
공식 문서
🙌 전체 번역본이 아닌 공부하면서 기록한 내용이라 의역, 오역이 있을 수 있습니다.
발견하시면 댓글이나 DM으로 알려주세요!
Plain Text
복사
아래 사용된 자료들의 출처는 모두 공식 문서입니다.
Create your first app
Create a Next.js App
•
React를 사용해서 웹 애플리케이션을 만들 때 불편한 점
◦
Webpack과 같은 번들러와 Babel 같은 컴파일러가 별도로 필요
◦
Code splitting과 같은 최적화를 해야 함
◦
성능이나 SEO를 위해 정적 사전 렌더링이 필요하거나, SSR, CSR을 사용하고 싶을 수 있음
◦
React App을 데이터 저장소에 연결하기 위해 서버 사이드 코드를 작성해야 될 수도 있음
•
Next.js가 위와 같은 문제들을 해결하기 위해 갖추고자 하는 내장 기능의 목표
◦
◦
◦
빠른 페이지 로딩을 위한 자동 code splitting
◦
최적화된 prefetching을 통한 Client-side routing
◦
◦
Fast Refresh가 지원되는 개발 환경
◦
서버 없이 구축할 수 있는 API endpont를 위한 API routes
◦
뛰어난 확장성
Navigate Between Pages
Pages in Next.js
•
pages 폴더에 JS 파일을 만드는 것으로 해당 페이지에 파일의 path인 URL로 접근 가능
Link Component
•
Link Component를 사용해서 페이지간 연결이 가능
import Link from 'next/link';
•
<Link>는 클라이언트 측에서 네비게이션이 가능하게 하고 props를 받아서 컨트롤 할 수 있음
•
외부 페이지 연결시에는 <a> tag를 사용해야함
•
className과 같은 attributes를 추가해야된다면 <Link><a> … </a></Link> 로 사용
Client-Side Navigation
•
브라우저에서 지원하는 default navigation 보다 빠른 JavaScript를 이용한 page transition
import Link from 'next/link';
export default function FirstPost() {
return (
<>
<h1>First Post</h1>
<h2>
<Link href="/">Back to home</Link>
</h2>
</>
);
}
JavaScript
복사
Code splitting and prefetching
•
해당 페이지에 필요한 부분만 로딩 되어서 많은 페이지가 있어도 빠르게 로딩 됨
•
각 페이지는 isolated되기 때문에 다른 페이지에서 error를 뱉어도 나머지 부분은 작동함
•
Next.js는 배포 빌드되었을 때, 브라우저의 viewport에서 Link를 찾으면 백그라운드에서 링크된 페이지들의 코드를 자동적으로 prefetch하기 때문에 페이지 전환이 매우 빠름
Assets, Metadata, and CSS
Assets
•
최상위 public 디렉토리에서 정적 assets을 가져와 쓸 수 있음
•
해당 디렉토리는 robots.txt 적용에도 유용함
Image Component and Image Optimization
•
Image Component
◦
HTML <img> element의 확장
•
Image Optimization
◦
resizing
◦
optimizing
◦
Webp 포맷 지원
◦
CMS와 같은 외부 소스에서 호스트 받는 이미지도 Optimization 가능
◦
빌드 때 이루어지지 않고 유저의 request가 있을 대 발생(빌드 타임 유지)
•
Lazy loaded by default
◦
이미지 로딩이 늦으면 다른 부분들을 먼저 보여줌
•
◦
CLS - 늦게 로딩된 이미지가 페이지 중간에 렌더링 되면서 다른 뷰의 위치가 변해버리는 현상
import Image from 'next/image';
const YourComponent = () => (
<Image
src="/images/profile.jpg" // Route of the image file
height={144} // Desired size with correct aspect ratio
width={144} // Desired size with correct aspect ratio
alt="Your Name"
/>
);
JavaScript
복사
•
Metadata
•
Head Component로 <head>를 수정 가능
import Head from 'next/head';
•
Third-Party JavaScript
•
Script Component를 이용해서 <script> 처럼 사용 가능
import Script from 'next/script';
<Script
src="https://connect.facebook.net/en_US/sdk.js"
strategy="lazyOnload"
onLoad={() =>
console.log(`script loaded correctly, window.FB has been populated`)
}
/>
JavaScript
복사
•
strategy : third-party script의 로드 타이밍을 컨트롤
◦
lazyOnload : 브라우저의 idle time에 로드
•
onLoad : script가 로딩이 끝났을 때 즉시 실행할 JS code
•
documentation - Script component
CSS Styling
•
CSS Modules을 사용하려면 반드시 파일명 뒤에 .module.css가 붙어야 함
import styles from './layout.module.css';
export default function Layout({ children }) {
return <div className={styles.container}>{children}</div>;
}
JavaScript
복사
•
이렇게 사용하면 자동적으로 유니크한 클래스 네임을 할당해줘서 충돌이 일어나지 않음
•
Code splitting 자동 지원
Global Styles
pages/_app.js를 생성
export default function App({ Component, pageProps }) {
return <Component {...pageProps} />;
}
JavaScript
복사
•
Global css를 해당 파일에 import해서 적용가능
•
Global css는 다른 곳에서는 import 불가능
Polishing Layout
•
<meta> tag를 통해 페이지의 콘텐츠를 설명
Styling Tips
Pre-rendering and Data Fetching
Pre-rendering
•
Better performance
•
SEO
•
최소화된 필수적인 JS code만 가진 HTML → fully interactive page ⇒ hydration
Two Forms of Pre-rendering
•
개발 모드에서는 SSG의 경우에도 매 request마다 페이지가 pre-render됨
Per-page Basis
•
Next.js에서는 어떤 pre-rendering 방식을 사용할 건지 각 페이지마다 정할 수 있음
When to use SSG vs SSR
•
SSG
◦
가능하면 추천
◦
페이지가 한 번 만들어져서 CDN에 위치하고나면 더 이상 render 할 필요가 없기 때문에
◦
유저의 request 전에 페이지를 만들 수 있나? → if true → SSG
◦
페이지의 정보가 자주 업데이트 되거나 request 마다 정보가 바뀐다면 사용하기 어려움
•
SSR
◦
SSG보다는 느림
◦
항상 최신 정보를 반영
•
CSR도 가능
Static Generation with and without Data
Static Generation with Data using
•
◦
async function
◦
production 빌드 때 외부 데이터를 받아와서 props로 넘겨줌
export default function Home(props) { ... }
export async function getStaticProps() {
// Get external data from the file system, API, DB, etc.
const data = ...
// The value of the `props` key will be
// passed to the `Home` component
return {
props: ...
}
}
JavaScript
복사
◦
개발 모드에서는 매 request 때 실행됨
Implement getStaticProps
// pages/index.js
import { getSortedPostsData } from '../lib/posts';
export async function getStaticProps() {
const allPostsData = getSortedPostsData();
return {
props: {
allPostsData,
},
};
}
export default function Home ({ allPostsData }) { ... }
JavaScript
복사
Fetch External API or Query Database
// API
export async function getSortedPostsData() {
// Instead of the file system,
// fetch post data from an external API endpoint
const res = await fetch('..');
return res.json();
}
JavaScript
복사
// Database
import someDatabaseSDK from 'someDatabaseSDK'
const databaseClient = someDatabaseSDK.createClient(...)
export async function getSortedPostsData() {
// Instead of the file system,
// fetch post data from a database
return databaseClient.query('SELECT posts...')
}
JavaScript
복사
◦
getStaticProps는 무조건 서버쪽에서 돌아가기 때문에 DB에 직접 접근해서 query문으로 데이터를 받아올 수 있음
◦
getStaticProps는 오직 페이지에서만 export 될 수 있음(non-page files에서는 불가)
▪
React가 페이지가 render 되기 전에 데이터를 모두 갖고 있어야 하기 때문에
Fetching Data at Request Time
Using getServerSideProps
export async function getServerSideProps(context) {
return {
props: {
// props for your component
},
};
}
JavaScript
복사
◦
추가 설정을 하지 않으면 결과가 CDN에 캐시되지 않음
CSR
◦
비밀, 개인적인 정보가 포함되어 SEO에 관련 없는 페이지일 때 사용
SWR
◦
Data fetch를 위한 React hook
▪
handles caching
▪
revalidation
▪
focus tracking
▪
refetching on interval
▪
and more
추가 문서
Dynamic Routes
Page Path Depends on External Data
•
각 페이지의 경로가 외부 데이터에 의존하는 경우
•
파일 이름을 [id].js로 만들어야 함
// lib/posts.js
export function getAllPostIds() {
const fileNames = fs.readdirSync(postsDirectory);
// Returns an array that looks like this:
// [
// {
// params: {
// id: 'ssg-ssr'
// }
// },
// {
// params: {
// id: 'pre-rendering'
// }
// }
// ]
return fileNames.map((fileName) => {
return {
params: {
id: fileName.replace(/\.md$/, ''),
},
};
});
}
JavaScript
복사
•
반환값은 반드리 객체 배열이어야하고, 각 객체는 id(dynamic route filename - […])를 키로하는 객체를 값으로 가지면서 해당 값의 키는 params여야 함
•
위 조건이 만족 되지 않으면 getStaticPaths가 실패
// [id].js
import { getAllPostIds } from '../../lib/posts';
export async function getStaticPaths() {
const paths = getAllPostIds();
return {
paths,
fallback: false,
};
}
JavaScript
복사
Implement getStaticProps
// lib/posts.js
export function getPostData(id) {
const fullPath = path.join(postsDirectory, `${id}.md`);
const fileContents = fs.readFileSync(fullPath, 'utf8');
// Use gray-matter to parse the post metadata section
const matterResult = matter(fileContents);
// Combine the data with the id
return {
id,
...matterResult.data,
};
}
JavaScript
복사
// pages/posts/[id].js
import { getAllPostIds, getPostData } from '../../lib/posts';
export async function getStaticProps({ params }) {
const postData = getPostData(params.id);
return {
props: {
postData,
},
};
}
JavaScript
복사
Details
•
API에서 가져오기
export async function getAllPostIds() {
// Instead of the file system,
// fetch post data from an external API endpoint
const res = await fetch('..');
const posts = await res.json();
return posts.map((post) => {
return {
params: {
id: post.id,
},
};
});
}
JavaScript
복사
Fallback
•
getStaticPaths → fallback: false
Catch-all Routes
•
pages/posts/[...id].js
◦
/posts/a
◦
/posts/a/b
◦
/posts/a/b/c
◦
getStaticPaths에서 id key의 배열을 반환해야함
// getStaticPaths
return [
{
params: {
// Statically Generates /posts/a/b/c
id: ['a', 'b', 'c'],
},
},
//...
];
JavaScript
복사
export async function getStaticProps({ params }) {
// params.id will be like ['a', 'b', 'c']
}
JavaScript
복사
Router
Error Pages
•
pages/404.js : custom 404 page
API Routes
Creating API Routes
// req = HTTP incoming message, res = HTTP server response
export default function handler(req, res) {
// ...
}
JavaScript
복사
Do Not Fetch an API Route from getStaticProps or getStaticPaths
•
두 함수는 서버 사이드에서만 돌아감
•
두 함수는 브라우저의 JS bundle에 포함되지 않음
•
즉, 서버 사이드에서 바로 DB query로 데이터를 가져와야 함
A Good Use Case: Handling Form Input
•
form input으로 POST요청을 보냈을 때 바로 DB에 저장
•
API Route 코드가 client bundle에 없기 때문에 안전하게 서버 사이드 코드를 작성 가능
export default function handler(req, res) {
const email = req.body.email;
// Then save email to your database, etc...
}
JavaScript
복사
Preview Mode
•
SSG는 headless CMS에서 데이터를 받아와 페이지를 보여줄 때 유용함
•
다만, headless CMS에 작성하고 있는 내용을 바로 미리보기할 수 없다는 단점
•