ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 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 컴포넌트를 사용하는 이유

    1. Client-side navigation
      <Link> 컴포넌트는 Next.js 어플리케이션에서 서로 다른 두 개의 페이지를 JavaScript를 이용해서 이동할 수 있도록 하는 client-side navigation을 할 수 있게 하는데, 이 방식을 이용하면 브라우저에서 기본적으로 제공하는 navigation 보다 더 빠르게 페이지를 이동할 수 있습니다.

      만약 <Link href="...">을 사용하는 것 대신 <a href="...">를 사용한다면, 브라우저는 페이지를 완전히 새로고침 합니다.

    2. 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 GenerationServer-side Rendering입니다. 이 두 형태의 차이점은 페이지의 HTML을 언제 만들어내는지 입니다.

    • Static Generation: 빌드할 때 HTML을 생성하는 pre-rendering 방법입니다. pre-render된 HTML은 각 요청마다 재사용 됩니다.
    • Server-side Rendering: 각 요청마다 HTML을 생성하는 pre-rendering 방법입니다.


    💡 Note

    • 개발 모드(npm run dev or yarn 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 Generation

    Next.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 목록은 getStaticPropsparams로 사용할 수 있습니다.

    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>;
    }


    댓글

Designed by Tistory.