-
Next.js 정리Next.js 2022. 7. 3. 23:58
Next.js App 설치 및 실행
Create Next.js App
npx create-next-app <project name>
Start Next.js App
npm run dev
페이지간 이동하기
Page란?
Next.js에서의 Page는
pages
폴더의 한 파일에서export default
된 React 컴포넌트를 말합니다.export default function HomePage() { return <h1>Home Page</h1>; };
라우트 구성하기
pages
폴더 안에 있는 파일들의 이름이 페이지의 라우트가 됩니다.예를 들어,
pages/index.js
는/
라우트가 됩니다. 만약/customer
라는 라우트를 만들고 싶으면 다음과 같이 두 가지 방법 중 하나를 선택해서 만들면 됩니다.pages/customer.js
pages/customer/index.js
Link 컴포넌트란?
Next.js에서는 어플리케이션에서 서로 다른 페이지를 이동할 수 있도록
next/link
모듈에서<Link>
라는 컴포넌트를 제공합니다.Link 컴포넌트 사용법
<a>
태그를 단독으로 사용하는 것 대신,<Link>
컴포넌트로<a>
태그를 감싸줍니다.<a>
태그에서 사용하는 href 속성을Link
컴포넌트의 prop으로 사용합니다.
import Link from 'next/link'; export default function FirstPost() { return ( <> <h1>First Post</h1> <h2> <Link href="/"> <a>Back to home</a> </Link> </h2> </> ); };
Link 컴포넌트를 사용하는 이유
Client-side navigation
<Link>
컴포넌트는 Next.js 어플리케이션에서 서로 다른 두 개의 페이지를 JavaScript를 이용해서 이동할 수 있도록 하는 client-side navigation을 할 수 있게 하는데, 이 방식을 이용하면 브라우저에서 기본적으로 제공하는 navigation 보다 더 빠르게 페이지를 이동할 수 있습니다.만약
<Link href="...">
을 사용하는 것 대신<a href="...">
를 사용한다면, 브라우저는 페이지를 완전히 새로고침 합니다.Code splitting과 Prefetching
Next.js는 각 페이지에 필요한 것만 로드할 수 있도록 자동으로 code splitting을 합니다. 예를 들어, home 페이지가 로드될 때, 다른 페이지에 필요한 코드를 초기에 제공하지 않습니다. 이것은 수 백개의 페이지가 있더라도 home 페이지를 빠르게 로드할 수 있도록 합니다.또한 페이지에 필요한 코드만 로드하는 것은 페이지들이 독립되어 있다는 것을 의미하는데, 만약 특정 페이지가 error를 발생시킨다 하더라도 어플리케이션의 나머지 부분은 여전히 동작할 것입니다.
게다가 Next.js의 어플리케이션이 빌드되고 나서는
<Link>
컴포넌트가 브라우저의 뷰 포트에 나타날 때마다 링크된 페이지의 코드를 자동으로 prefetch 합니다. 이것은 사용자가 링크를 클릭할 때까지 해당 페이지에 대한 코드가 미리 로드되어 있을 것이고 페이지 이동은 거의 즉시 이동할 것입니다.
💡 Note
- 만약 외부 외부 페이지를 링크해야한다면,
<Link>
컴포넌트 없이<a>
태그를 사용하면 됩니다. className
과 같이 속성을 추가해야 한다면,<Link>
컴포넌트가 아니라<a>
태그에 추가하세요.
Assets, Metadata, and CSS
Assets
Next.js는 최상위 디렉토리 레벨에 있는 public 디렉토리에서 이미지와 같은 정적 에셋을 제공할 수 있습니다.
public에 있는 파일들은 페이지들과 비슷하게 어플케이션의 루트로부터 참조될 수 있습니다.(http://domain.com/images/01.png)최적화 되지 않은 이미지
HTML에서는 다음과 같이 프로필 이미지를 추가할 수 있습니다.
<img src="/images/profile.jpg" alt="Your Name" />
그러나 이것은 다음과 같은 것을 직접 다루어야 하는 것을 의미합니다.
- 다른 스크린 크기에대해 이미지가 반응적임을 보장하기
- third-party tool 또는 라이브러리로 이미지를 최적화하기
- 이미지들이 뷰포트에 나타났을 때, 이미지만 로딩하기
이미지 컴포넌트와 이미지 최적화
next/image
는 모던 웹에 맞게 발전한 HTML<img>
요소의 확장입니다.이미지 컴포넌트 사용하기
빌드 타임에 이미지를 최적화하는 것 대신에, Next.js는 유저들이 요청할 때마다 이미지를 최적화합니다.
이미지들은 기본적으로 lazy load 됩니다. 뷰포트 외부에 있는 이미지가 페이지의 속도에 영향을 미치지 않는다는 것을 의미합니다. 이미지는 뷰포트에 스크롤될 때 로드됩니다.import Image from 'next/image' const YourComponent = () => ( <Image src="/images/profile.jpg" height={144} width={144} alt="Your Name" /> );
Metadata
<title>
HTML 테그와 같이 페이지의 메타 데이터를 수정하고 싶다면,next/head
모듈로부터Head
컴포넌트를 가져올 수 있습니다.import Head from 'next/head'; export default function () { return ( <Head> <title>Create Next App</title> <link rel="icon" href="/favicon.ico" /> </Head> ); }
💡 만약
<lang>
속성을 추가하는 예처럼<html>
태그를 커스텀하고 싶다면pages/_document.js
파일을 생성함으로써 해결할 수 있습니다.Third-Party JavaScript
Third-Party JavaScript 추가하기
메타 데이터 외에도 가능한 빨리 로드되고 실행되는 스크립트들은 보통 페이지의
<head>
내부에 추가 되어집니다. 일반 HTML<script>
요소를 사용하여 다음과 같이 외부 스크립트를 추가할 수 있습니다.<Head> <title>First Post</title> <script src="https://connect.facebook.net/en_US/sdk.js" /> </Head>
비록 이러한 방식은 작동하지만, 이러한 방식으로 스크립트를 포함하는 것은 같은 페이지에서 다른 JavaScript 코드를 받아오는 관점에서 언제 로드되어야 할지가 명확하지 않습니다. 만약 특정 스크립트가 blocking 중이고 다른 컨텐츠들을 로딩하는데 지연시킬 수 있다면, 이것은 상당한 성능 문제를 일으킬 수 있습니다.
스크립트 컴포넌트 사용하기
next/script
는 HTML<script>
요소의 확장이며 추가적인 스크립트들이 언제 받아와지고 실행되어져야하는 것을 최적화합니다.import Head from 'next/head'; import Link from 'next/link'; import Script from 'next/script'; export default function FirstPost() { return ( <> <Head> <title>First Post</title> </Head> <Script src="https://connect.facebook.net/en_US/sdk.js" strategy="lazyOnload" onLoad={() => console.log(`script loaded correctly, window.FB has been populated`)} /> <h1>First Post</h1> <h2> <Link href="/"> <a>Back to home</a> </Link> </h2> </> ); }
strategy
는 언제 third-party 스크립트가 로드 되어야하는지 컨트롤합니다.onLoad
는 스크립트 로딩이 끝난 후에 즉시 실행되어져야하는 자바스크립트 코드를 실행하는데 사용됩니다.
CSS Styling
styled-jsx
styled-jsx는 "CSS-in-JS"입니다. 이것은 리엑트 컴포넌트 내에서 CSS를 쓸 수 있도록 해주고 scoped 되도록 해줍니다.
<style jsx>{` ... `}</style>
CSS 쓰고 import하기
Next.js는
.css
와.scss
파일을 불러올 수 있도록 지원합니다.Layout 컴포넌트
components/layout.module.css
.container { min-width: 36rem; padding: 0 1rem; margin: 3rem auto 6rem; }
components/layout.js
import styles from './layout.module.css'; export default function Layout({ children }) { return <div className={styles.container}>{children}</div>; }
💡 CSS 모듈을 사용하기 위해서는 CSS 파일 이름이
.module.css
로 끝나야 합니다.Global Styles
CSS 모듈은 컴포넌트 레벨의 스타일링에 유용합니다. 하지만 만약 전체 페이지에 CSS를 적용하기를 원한다면
pages/_app.js
에 다음과 같이 작성하면 됩니다.export default function App({ Component, pageProps }) { return <Component {...pageProps} />; }
여기서
App
컴포넌트는 모든 페이지의 최상위 컴포넌트입니다. 예를들면 이 App 컴포넌트를 다른 페이지로 이동할 때 state를 유지시키는데 사용할 수 있습니다.💡 중요:
pages/_app.js
에 무엇인가를 추가했다면 개발 서버를 재실행해야 합니다.Global CSS 추가하기
Next.js에서 global CSS 파일을
pages/_app.js
에서 불러와서 사용할 수 있습니다.styles/global.css
html, body { padding: 0; margin: 0; font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; line-height: 1.6; font-size: 18px; } * { box-sizing: border-box; } a { color: #0070f3; text-decoration: none; } a:hover { text-decoration: underline; } img { max-width: 100%; display: block; }
pages/_app.js
import '../styles/global.css'; export default function App({ Component, pageProps }) { return <Component {...pageProps} />; }
Pre-rendering과 Data fetching
Pre-rendering
기본적으로 Next.js는 모든 페이지를 pre-render합니다. 이것은 클라이언트 측에서 JavaScript에 의해서 HTML을 생성하는것 대신, Next.js가 각 페이지에 대한 HTML을 미리 생성한다는 것을 말합니다. Pre-rendering은 성능과 SEO를 더 좋게 합니다.
각 생성된 HTML은 해당 페이지에 필요한 최소한의 코드와 연결됩니다. 페이지가 브라우저에 의해서 로드될 때, JavaScript 코드는 페이지를 완전히 동적으로 만들어 줍니다. (이 과정을 hydration이라고 말합니다.)
Pre-rendering의 두 가지 형태
Next.js에는 두 가지 형태의 pre-rendering이 있습니다. 바로 Static Generation과 Server-side Rendering입니다. 이 두 형태의 차이점은 페이지의 HTML을 언제 만들어내는지 입니다.
- Static Generation: 빌드할 때 HTML을 생성하는 pre-rendering 방법입니다. pre-render된 HTML은 각 요청마다 재사용 됩니다.
- Server-side Rendering: 각 요청마다 HTML을 생성하는 pre-rendering 방법입니다.
💡 Note
- 개발 모드(
npm run dev
oryarn dev
)에서는 Static Generation을 사용한 페이지라 할지라도 페이지는 요청할 때 pre-render 됩니다.
Static Generation vs Server-side Rendering 언제 사용해야 할까?
페이지가 한 번만 제공되고 CDN에 의해서 제공될 수 있기 때문에, 가능하면 Static Generation을 사용하는 것을 추천합니다.
Static Generation은 다음과 같이 여러 가지의 형태에 사용될 수 있습니다.
- 마케팅 페이지
- 블로그 포스트
- E-commerce 제품 리스트
- Help and Documentation
만약 페이지를 유저 요청전에 pre-render 할 수 있다면 Static Generation을 선택해야 합니다. 만약 페이지가 자주 업데이트된 데이터를 보여줘야 하고 페이지의 내용이 매 요청마다 바뀐다면 Server-side Rendering을 사용할 수 있습니다. Server-side Rendering은 조금 더 느리지만 pre-render 된 페이지는 항상 최신의 상태를 유지합니다. 혹은 pre-rendering을 스킵하고 자주 업데이트 되는 데이터를 위해 클라이언트 측에서 JavaScript를 이용할 수 있습니다.
데이터가 있는 혹은 없는 Static Generation
Static Generation은 데이터가 있거나 혹은 없을 때도 할 수 있습니다. 외부 데이터를 필요로 하지 않는 페이지들은 어플리케이션이 프로덕션으로 빌드될 때 자동으로 정적으로 생성됩니다.
그러나 몇몇 페이지들은 파일 시스텀에 접근해야 한다던지 외부 API를 호출해야 한다던지 빌드시에 데이터 베이스에 쿼리를 보내야 하는 것처럼 처음에 외부 데이터 없이 HTML 페이지를 생성할 수 없는 경우도 있습니다. Next.js는 이와 같이 데이터가 필요한 Static Generation을 지원합니다.
getStaticProps
를 이용한 데이터가 있는 Static GenerationNext.js에서 페이지 컴포넌트를 export 할 때,
getStaticProps
라는 비동기 함수도 export 할 수 있습니다.getStaticProps
는 프로덕션에서 빌드시에 실행됩니다.- 함수안에서 외부 데이터를 받아오고 이것은 페이지의 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: ... } }
요청시에 데이터 받아오기
만약 빌드할 때가 아니라 요청시에 데이터를 받아와야 한다면,
getServerSideProps
를 이용하여 Server-side Rendering을 시도할 수 있습니다.export async function getServerSideProps(context) { return { props: { // props for your component }, }; }
getServerSideProps
를 사용하면 매번 요청마다 서버는 연산을 해야하고 추가적인 설정 없이는 CDN에 의해 캐시되지 않을 수 있기 때문에getStaticProps
보다 더 느릴 것입니다. 따라서 반드시 요청시에 받아와야 하는 데이터를 가진 페이지를 pre-render 할때만getServerSideProps
를 사용해야 합니다.클라이언트 측에서 렌더링하기
만약 pre-render를 할 필요가 없다면 다음과 같이 Client-side Rendering이라고 불리는 전략을 사용할 수 있습니다.
- 한 페이지에서 외부 데이터를 필요로 하지 않는 부분은 정적으로 생성합니다.
- 페이지가 로드될 때, JavaScript를 이용하여 클라이언트에서 외부 데이터를 받아와서 나머지 부분들을 렌더링합니다.
이러한 접근법은 유저 대시보드 페이지에 적합합니다. 왜냐하면 대시보드 페이지는 private하고 유저에 특화된 페이지이고 SEO에 관련이 없으며 페이지가 pre-render 되야할 필요가 없기 때문입니다.
Dynamic Routes
Next.js는 dynamic URLs를 이용해서 외부 데이터에 의존적인 path를 가진 페이지를 정적으로 생성할 수 있도록 해줍니다.
Next.js에서 동적 라우트는
pages
폴더에서 파일 이름을[]
로 감싸주면 됩니다.pages/posts/[id].js
pages/posts/[id]/index.js
외부 데이터에 의존적인 path를 가진 페이지를 정적으로 생성하기 위해서는
getStaticPaths
라는 비동기 함수를 export하면 됩니다. 이 함수에서는id
로 가능한 값 목록을 반환해야 합니다.getStaticPaths
에서 반환된id
목록은getStaticProps
의params
로 사용할 수 있습니다.import Layout from '../../components/layout'; export default function Post() { return <Layout>...</Layout>; } export async function getStaticPaths() { // Return a list of possible value for id } export async function getStaticProps({ params }) { // Fetch necessary data for the blog post using params.id }
Catch-all Routes
동적 라우트는 대괄호 안에 세개의 점을 추가함으로써 모든 path를 포착할 수 있습니다.
pages/posts/[...id].js
는/posts/a
뿐만 아니라/posts/a/b
,/posts/a/b/c
등과 일치합니다.
Router
만약 Next.js 라우터에 접근해야 한다면,
next/router
로부터useRouter
훅을 이용할 수 있습니다.404 페이지
커스텀 404 페이지를 만들기 위해서는
pages/404.js
를 생성하면 됩니다. 이 파일은 빌드시에 정적으로 생성됩니다.export default function Custom404() { return <h1>404 - Page Not Found</h1>; }