Next.js 공식문서로 시작하기

sxxng_ju·2022년 9월 20일
0

Web

목록 보기
3/4

CSR 이란?

CSR은 Client Side Rendering의 약자로, 클라이언트에서 렌더링을 하는 방식이다. 브라우저가 담당하여 렌더링하기 때문에 서버 트래픽을 감소시키고, 사용자에게 빠른 인터렉션을 제공해준다. 새로고침이 없기 때문에 사용자가 네이티브 앱을 사용하는 듯한 느낌을 받는다.

하지만 첫 페이지 로딩 속도가 서버 사이드 렌더링에 비해 느리다. 서버 첫 요청 시 모든 페이지를 받아오기 때문이다. 또한 검색엔진최적화에 취약하다는 단점이 있다.

SSR의 필요성

SPA는 서버로부터 처음에만 페이지를 받아오고 이후에는 동적으로 페이지를 구성하기 때문에 검색엔진 최적화가 취약하다고 언급했다.

그렇다면 SPA에서 검색엔진최적화를 하기 위해서는 어떻게 해야할까 ?

Next.js 는 React Framework로 React의 선행학습이 필요하다. React에서 제공하지 않는 기능들이 Next.js안에 기본적으로 제공하기 때문에 편하게 개발할 수 있다는 장점이 있다.

Next.js의 장점

  • SEO(검색 엔진 최적화)
  • API Routes 제공
  • 코드 분할 / 파일명 기반 Routing
  • 이미지 최적화

Next.js 시작하기

npx create-next-app@latest --typescript

Next.js 폴더구조

create-next-app을 통해 Next.js를 시작하게 되면 page, styles, public등 다양한 폴더 들이 생성된다.

이 중 page가 Next에서 제공하는 핵심 기능 중 하나이다. page안에 home.tsx와 같이 파일을 작성하고 실행하게 되면 우리가 접속한 /home 과 매칭이 된다.

export default function Home() {
  return (
    <>
      <div>이곳은 Home 페이지입니다.</div>
    </>
  );

위에서 언급한 파일명이 곧 url과 매칭되는 것이다.

이 외에 styles는 프로젝트에서 사용할 css 파일이나 style과 관련된 파일을 관리하고 public에서는 아이콘이나 svg파일과 같은 어플리케이션에 사용되는 정적 파일들을 관리한다.

기본적으로 폴더구조가 잡혀있는 상태지만 프로젝트를 진행하면서 많은 고민을 하게 되었다. 고민 끝에 선택한 폴더구조는 다음과 같다.

public, styles 폴더는 그대로두고 pages폴더를 src 폴더 안으로 집어 넣어 관리하였다. src 폴더 안으로 pages를 넣어도 별도의 설정없이 기존의 pages와 동일하게 동작한다.

api - axios 관련 파일
components - Container, Layout, Form ...
hooks - React custom hooks
types - 타입 정의
utils - 공통적으로 사용하는 함수, 기타 도구 들

폴더구조는 정답이 없기 때문에 프로젝트를 진행하면서 각자 자신에게 맞는 폴더구조를 고민해보는 것을 추천한다.

Pre-rendering & Data Fetching

Next.js 는 모든 페이지를 pre-rendering 한다. JavaScript가 모든 작업을 랜더링 하는 것이 아니라 각 페이지에 대해 미리 HTML파일을 만들어 빌드하는 것을 의미한다.

Next에서 제공하는 Pre-rendering은 두가지이다. SSG와 SSR이다.
SSG(Static-Site-Generation)은 HTML을 빌드 타임에 생성하여 각 요청마다 다시 불러오지 않고 재사용된다.
SSR(Server-Side-Rendering)은 HTML을 매 요청마다 생성해서 반환한다.

마케팅 페이지, 블로그, 제품 리스트와 같은 정적인 요소를 반환하는 경우에 SSG를 사용하고 분석 차트, 게시판과 같이 사용자의 요청마다 매번 다른 내용을 보여줘야할 경우 SSR을 사용한다.

Next 공식문서에서도 가능한 SSG 방식을 추천한다. 페이지가 빌드되고 각 요청마다 페이지를 렌더하는 속도가 다른 것보다 빠르기 때문이다.

이제 SSG, SSR를 어떻게 하는지 예제를 통해 알아보자.

getStaticProps

export default function Main({data}) { ... }

export async function getStaticProps(context) {
  const data = await ...
  
  return {
    props: {data}, // will be passed to the page component as props
  }
}

개발환경에서는 SSG가 SSR과 같이 동작하기 때문에 배포환경에서 테스트 확인할 수 있다.

만약 getStaticProps를 사용하는 페이지가 동적 라우팅을 사용하고 있다면 getStaticPaths를 통해 빌드 타임 때 정적으로 렌더링할 경로를 설정해주어야 한다.

getServersideProps

export default function Main({data}) { ... }

export async function getServerSideProps(context) {
  return {
    props: {data}, // will be passed to the page component as props
  }
}

Layouts, Image Optimization

Layouts

프로젝트를 진행하다보니 같은 레이아웃을 공유하는 페이지들이 생겼다. 재사용이 가능한 컴포넌트를 묶어 사용할 수도 있겠지만 Next.js에서는 Layouts 기능을 제공한다.

흔히 우리가 많이 사용하는 공통된 레이아웃에는 Header, Navbar, Footer 등이 있다.

// components/layout.tsx

import Navbar from './navbar'
import Footer from './footer'

export default function Layout({ children }) {
  return (
    <>
      <Navbar />
      <main>{children}</main>
      <Footer />
    </>
  )
}

// pages/_app.tsx

import Layout from '../components/layout'
import type { ReactElement, ReactNode } from "react";
import type { NextPage } from "next";
import type { AppProps } from "next/app";

export type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & {
  getLayout?: (page: ReactElement) => ReactNode;
};

type AppPropsWithLayout = AppProps & {
  Component: NextPageWithLayout;
};

export default function MyApp({ Component, pageProps }: AppPropsWithLayout) {
  return (
    <Layout>
      <Component {...pageProps} />
    </Layout>
  )
}

layout.js에 사용하고자 하는 Navbar, Footer 등을 반환하고 children을 prop으로 받아 컴포넌트를 감싼다.

그 뒤 공통으로 사용하기 때문에 _app.js에 가서 Component를 Layout으로 감싸면 끝이다.

하지만 모든 페이지가 항상 같은 레이아웃을 공유하는 것은 아니기 때문에 각 페이지마다 다른 레이아웃을 적용하는 것도 알아보자.

// pages/index.tsx
export default function Home() { ... }

Home.getLayout = function getLayout(page: ReactElement) {
  return <Layout>{page}</Layout>;
};

// pages/_app.tsx
import type { ReactElement, ReactNode } from "react";
import type { NextPage } from "next";
import type { AppProps } from "next/app";

export type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & {
  getLayout?: (page: ReactElement) => ReactNode;
};

type AppPropsWithLayout = AppProps & {
  Component: NextPageWithLayout;
};

export default function MyApp({ Component, pageProps }: AppPropsWithLayout) {
  const getLayout = Component.getLayout ?? ((page) => page);

  return getLayout(<Component {...pageProps} />)
}

index 페이지에만 레이아웃이 적용되게 하는 코드이다.

Image Optimization

Next.js는 이미지 최적화 기능도 제공한다. <img> 대신 next/image 에서 <Image>를 불러와서 사용하면 된다.

Image는 기본적으로 리사이징, 최적화, 브라우저 호환 등을 지원한다. img와 다른 점은 반드시 width와 height를 명시해야한다는 점이다.

import Image from 'next/image'

function Home() {
  return (
    <>
      <Image
        src="/test.png"
        alt="test"
        width={300}
        height={300}
      />
    </>
  )
}

export default Home

이때 이미지가 외부 사이트의 이미지인 경우v next.config.js 파일 안에 이미지 src 의 도메인을 옵션에 명시해야 이미지를 불러올 수 있다.

module.exports = {
  images: {
    domains: ['example.com'],
  },
}

이밖에 Optional Props는 Next.js의 공식문서를 보는 것이 더 좋을 것 같다.
Next.js 이미지최적화 공식문서

API Routes

다음은 Next.js가 제공하는 API Routes이다. 별다른 서버 구축없이도 클라이언트 코드와 함께 작성할 수 있다. 그렇다고 클라이언트에서 서버가 돌아가는 것은 아니다.

import type { NextApiRequest, NextApiResponse } from "next";

export default function handler(req: NextApiRequest, res: NextApiResponse) {
 if (req.method === 'POST') {
    // Process a POST request
  } else {
    // Handle any other HTTP method
  }
}

0개의 댓글