[FedEx 스터디] Part 1. Next.js의 세계로

상민·2023년 3월 12일
0

[FedEx] 스터디

목록 보기
1/10
post-thumbnail

About Next.js

Next.js는 자바스크립트 웹 프레임워크 ,리액트에는 없는 SSR, SSG, ISR 등 제공

React는 기본적으로 클라이언트 사이드에서만 동작

→ 따라서 SEO(검색 엔진 최적화)의 효과를 거의 볼 수 없음.

→ 브라우저가 전체 웹 애플리케이션 번들을 다운로드 하고 그 내용을 분석하고 코드를 실행하기 까지 오래걸림 (첫 화면을 표시하기까지 수 초가 소요)

⇒ 해결책) 서버에서 미리 렌더링을 해두는 방식

HTML페이지로 미리 렌더링을 해두고 브라우저가 이를 다운로드하여 즉각 화면에 표시

클라이언트에서 JS 번들을 다 받으면 그제서야 사용자가 웹 앱과 상호 작용

Next.js

Next.js가 제공하는 기능

  • 정적 사이트 생성 (SSG)
  • 증분 정적 콘텐츠 생성 (ISR)
  • 서버 사이드 렌더링 (SSR)
  • 코드 분할 (code spliting)
  • 파일 기반 라우팅
  • 경로 기반 프리페칭
  • 타입스크립트에 대한 기본 지원
  • 자동 폴리필 적용
  • 이미지 최적화
  • 웹 애플리케이션의 국제화 지원
  • 성능 분석

Next.js와 비슷한 프레임워크

Gastby → SSG만 제공. 성능이 놀랍도록 좋음. 데이터에 따라 동적으로 변하는 복잡한 웹 사이트는 만들 수 없음.

Razzle → SSR이 가능. 복잡한 설정을 추상화하고 단순하게 만들 수 있음. 프레임워크에 대한 지식이 없어도 됨.

Nuxt.js → Vue.js를 사용함. Next.js와 유사. 더 많은 설정을 필요로 함.

Why Next.js?

Next.js는 활동적인 커뮤니티를 가지고 있음.

React에서 제공하지 않는 여러 기능을 지원.

리액트 자바스크립트 라이브러리 ↔  Next.js 프레임워크

SSR, SSG는 모두 Node.js에서 실행되기 때문에 fetch, window, document같은 웹 브라우저에서 제공하는 전역 객체나 canvas같은 HTML 요소에는 접근할 수 없음!

fs, child_process같은 Node.js에서만 사용할 수 있는 라이브러리나 API를 사용하려는 경우에는 서버 사이드 코드를 실행하거나 페이지 생성 시점에 해당 코드를 처리하는 방식 사용.

Next.js 시작하기

  • Node.js와 npm이 설치되어 있어야 함

Vercel에서 제공하는 create-next-app을 사용.

$ npx create-next-app <app-name>
$ npx create-next-app <app-name> --typescript

yarn 패키지가 설치된 경우 yarn으로 프로젝트를 초기화함.

npm을 기본으로 사용하고 싶다면 --use-npm 옵션을 추가하면 됨.

Github에 원하는 보일러플레이트 코드를 다운로드 받아서 새 Next.js 프로젝트를 시작할 수 있음.

--example 옵션을 사용. 깃허브 레포 주소 입력.

$ yarn create next-app <app-name> --example https://github.com/ww8007/Next_boilerplate

Next.js에서 네비게이션은 pages 디렉토리를 사용하기 때문에 react-router 라이브러리를 설치할 필요 없음.

public 디렉토리는 모든 퍼블릭 페이지와 정적 콘텐츠가 있다.

ex) 이미지 파일, 폰트 …

pagespublic 디렉토리는 필수이고 최상위 디렉터리에 필요한 디렉토리나 파일을 추가해도 괜찮다.

ex) components, utils 등…

JS → TS 전환 방법

  1. tsconfig.json 파일 생성

  2. npm run dev (yarn dev) 명령어 실행

  3. 표시된 메시지와 같이 필요한 패키지를 설치

    yarn add -D typescript @types/react @types/node
  4. 최상위 디렉토리에 next-env.d.ts 파일 생성

Next.js에서 바벨, 웹팩 커스터마이징 하기

  • 바벨 커스터마이징 하기 최상위 디렉토리에 .babelrc 파일 생성
    {
    	"presets": ["next/babel"]
    }
    파일에 최소 위 내용은 있어야함 next/babel은 Vercel에서 Next.js 애플리케이션을 빌드하고 개발할 때 사용할 수 있는 설정을 미리 저장해둔 바벨 프리셋 해당 파일에 상황에 따라 프리셋, 플러그인 추가 가능
  • 웹팩 커스터마이징 하기 next.config.js 파일의 기본값을 변경
    module.exports = {
    	webpack: (config, options) => {
    		config.module.rules.push({
    			test: /\.js/,
    			use: [
    				options.defaultLoaders. babel,
    				// 이 부분의 내용은 예시이기 때문에
    				// 실제로 사용하면 애플리케이션이 작동하지 않습니다.
    				{
    					loader: “my-custom-loader", // 사용할 로더 지정
    					options: loaderOptions,     // 로더의 옵션 지정
    				},
    			],
    		});
    		return config;
    	},
    }
    이런식으로 웹팩 설정을 추가하면 Next.js의 기본 설정과 합쳐진다.

렌더링 전략

렌더링 전략이란? 웹 애플리케이션을 웹 브라우저에 제공하는 방법

Next.js에서는…

  • 빌드 시점에 정적으로 생성 → SSG
  • 실행 시점에 동적으로 제공 → SSR
  • 요청이 있을 때마다 페이지를 다시 생성 → ISR
  • 클라이언트에서 렌더링해야 할 컴포넌트도 지정 → CSR

이 모든 렌더링 전략을 제공한다.

서버 사이드 렌더링 (SSR)

각 요청에 따라 서버에서 HTML 페이지를 동적으로 렌더링하고 웹 브라우저로 전송

서버에서 렌더링한 페이지에 스크립트 코드를 집어넣어서 웹 페이지를 동적으로 처리할 수 있다 → 하이드레이션

하이드레이션이란, 서버에서 생성한 HTML 페이지에 클라이언트 측에서 실행하는 자바스크립트 코드를 추가해서 애플리케이션 상태를 관리하고 렌더링하는 기법

  1. 사용자가 페이지에 접근
  2. 서버는 페이지를 렌더링해서 결과로 생성한 HTML 페이지를 클라이언트로 전송
  3. 브라우저는 페이지에서 요청한 모든 스크립트를 다운로드한 후 DOM 위에 각 스크립트 코드를 하이드레이션

→ 하이드레이션 덕분에 SPA 처럼 작동 (CSR과 SSR의 장점을 모두 가진다.)

SSR의 이점

  • 더 안전한 웹 애플리케이션
  • 더 뛰어난 웹 사이트 호환성
  • 더 뛰어난 SEO

SSR의 단점

  1. 페이지를 다시 렌더링할 수 있는 서버가 필요
    CSR이나 SSG는 클라우드 서비스에 배포하고 클라이언트에 제공 가능
  2. 더 많은 자원을 소모, 더 많은 부하
  3. 유지 보수 비용 증가
  4. 페이지에 대한 요청을 처리하는 시간이 길어짐
    Next.js는 네비게이션 성능을 향상시킬 수 있는 좋은 기능을 제공

Next.js는 기본적으로 빌드 시점에 정적으로 페이지를 만든다.

SSR에서 REST API Data Fetching

import axios from 'axios';
import { NameReturn } from '@/types/api';
import { GetServerSideProps } from 'next';

type IndexPageProps = {
  name: string;
};

export default function IndexPage({ name }: IndexPageProps) {
  return <>{name}</>;
}
export const getServerSideProps: GetServerSideProps = async () => {
  const userRequest = await axios.get<NameReturn>(
    'http://localhost:3000/api/hello'
  );
  const data = userRequest.data;
  return {
    props: {
      name: data.name,
    },
  };
};

getServerSideProps 예약 함수를 사용한다.

  1. getServerSideProps 비동기 함수를 export한다.
    빌드 과정에서 Next.js는 이 함수를 export하는 모든 페이지를 찾아서 서버가 페이지 요청을 처리할 때
    getServerSideProps 함수를 호출하도록 만든다. 해당 함수 내의 코드는 항상 서버에서만 실행된다.
  2. getServerSideProps 함수는 props 라는 속성값을 갖는 객체를 반환한다.
    Next.js는 props를 컴포넌트로 전달하여 서버와 클라이언트 모두가 props에 접근할 수 있다.

브라우저 전용 객체나 API(window, document)를 사용해야 하는 컴포넌트가 있다면 해당 컴포넌트를 반드시 브라우저에서 렌더링하도록 명시적으로 지정해야 한다.

클라이언트 사이드 렌더링 (CSR)

서버에서 자바스크립트 번들을 클라이언트로 전송한 다음 렌더링을 시작

  1. 브라우저는 HTML 마크업을 받아서 화면에 렌더링
  2. 자바스크립트 번들과 CSS 파일을 다운로드하여 브라우저가 HTML 페이지에 불러오도록 만듬
  3. 전체 애플리케이션을 렌더링

CSR의 이점

  • 네이티브 애플리케이션처럼 느껴지는 웹 애플리케이션
  • 쉬운 페이지 전환
  • 지연된 로딩과 성능
  • 서버 부하 감소

CSR의 단점

  • 네트워크가 느린 환경에서 로딩 시간동안 사용자는 빈 페이지를 바라보고 있어야 한다.
  • 낮은 SEO 점수

Next.js에서 CSR 사용하기

import React from 'react';

export default function CSRPage() {
  const pEl = document.querySelector('#p');
  pEl?.classList.add('class');
  return (
    <div>
      <p id="p"></p>
    </div>
  )
}

해당 페이지에 접근하려고 하면 다음과 같은 에러가 나온다.

Next.js는 기본적으로 빌드 시점에 정적으로 페이지를 만들기 때문에 브라우저에서 제공하는 객체인 document에 접근할 수 없다.

따라서 위 코드를 사용하기 위해 useEffect훅을 리액트 하이드레이션 이후 브라우저에서 실행하도록 만들어야 한다.

import React, { useEffect } from 'react';

export default function CSRPage() {
  useEffect(() => {
    const pEl = document.querySelector('#p');
    pEl?.classList.add('class');
  }, []);
  return (
    <div>
      <p id="p"></p>
    </div>
  );
}

에러 없이 해당 코드를 잘 실행시킬 수 있다.

useEffect와 useState를 함께 써서 특정 컴포넌트를 정확히 클라이언트에서만 렌더링하도록 지정할 수 있다.

next/dynamic

import dynamic from 'next/dynamic';

const Highlight = dynamic(
	() >= import(' ../components/Highlight'),
	{ ssr: false } 
);

이 코드를 실행하면 Highlight 컴포넌트를 동적 임포트로 불러온다.

동적 임포트를 사용하면 Next.js는 해당 컴포넌트를 서버에서 렌더링하지 않는다.

정적 사이트 생성 (SSG)

일부 또는 전체 페이지를 빌드 시점에 미리 렌더링한다.

내용이 거의 변하지 않는 페이지는 정적 페이지 형태로 만들어서 제공하는 것이 좋음

SSR처럼 빌드 과정에 미리 렌더링해서 HTML 마크업 형태로 제공.

→ 리액트 하이드레이션 덕분에 이런 정적 페이지에서도 여전히 사용자와 웹 페이지 간의 상호 작용이 가능

SSG 이점

  • 쉬운 확장
  • 뛰어난 성능
  • 더 안전한 API 요청

SSG 단점

  • 웹 페이지를 만들고 나면 다음 배포 전까지 내용이 변하지 않음

Next.js에서는 이런 문제를 해결하기 위해 증분 정적 재생성 (ISR)을 제공

→ 어느 정도의 주기로 정적 페이지를 다시 렌더링하고 해당 내용을 업데이트할지 정할 수 있음

import { NameReturn } from '@/types/api';
import axios from 'axios';
import { GetStaticProps } from 'next';

type IndexPageProps = {
  name: string;
};

export default function IndexPage({ name }: IndexPageProps) {
  return <>{name}</>;
}

export const getStaticProps: GetStaticProps = async () => {
  const response = await axios.get<NameReturn>(
    'http://localhost:3000/api/hello'
  );
  const data = response.data;
  return {
    props: {
      name: data.name,
    },
    revalidate: 600, // 10분
  };
};

getStaticProps 함수는 빌드 과정에서 페이지를 렌더링할 때 이 함수를 호출, 다음 번 빌드 시점까지 더 이상 호출하지 않음

→ 콘텐츠를 바꾸려면 전체 웹 사이트를 새로 빌드해야 한다는 단점

이런 단점을 보완하기 위해 revalidate 옵션 사용

→ 요청이 발생할 때 어느 정도의 주기로 새로 빌드해야 하는지를 나타냄

위 코드는 revalidate를 10분으로 주었기 때문에 아래와 같은 프로세스로 동작함.

  1. 해당 페이지는 빌드 과정을 거쳐 정적 페이지로 만들어짐
  2. 처음 10분간 해당 페이지를 요청하는 사용자는 동일한 정적 페이지를 제공받음
  3. 10분이 지나고 페이지 요청이 들어오면 해당 페이지를 재렌더링하고 getStaticProps함수를 다시 호출
  4. 새로운 정적 페이지를 만들고 새로 만든페이지로 덮어씀
  5. 이후 10분간 동일한 페이지에 대한 모든 요청에 대해 새로 만든 정적 페이지를 제공

ISR을 최대한 지연시켜서 처리하려고 하므로 10분이 지난 후라도 페이지 요청이 없으면 페이지를 새로 빌드하지 않음.

현재까지는 ISR의 페이지를 재생성을 강제로 하는 방법은 없음.

revalidate값에 지정된 시간만큼 기다려야 함.

Next.js 기초와 내장 컴포넌트

라우팅 시스템

파일시스템 기반 페이지라우팅

pages 디렉토리 안의 모든 파일은 곧 애플리케이션의 페이지와 라우팅 규칙을 의미

동적 라우팅 규칙

웹 사이트에 새로운 글을 쓸때마다 수동으로 새 페이지를 만들지 않도록 posts 디렉토리 안에 [slug].tsx 파일을 만듬

[slug]는 경로 매개변수로 사용자가 브라우저 주소창에 입력하는 값은 모두 가질 수 있음

http://localhost:3000/posts/2, http://localhost:3000/posts/sangmin 등 주소로 접근 가능

동적 라우팅 규칙을 중첩도 가능

위와 같은 주소로 접근 가능

페이지에서 경로 매개변수 사용

pages/greet/[name].tsx 파일 생성

  • pages/greet/[name].tsx
import { GetServerSideProps, GetServerSidePropsContext } from 'next';
import React from 'react';

type GreetProps = {
  name: string;
};

export default function Greet({ name }: GreetProps) {
  return <div>{name}</div>;
}

export const getServerSideProps: GetServerSideProps = async ({
  params,
}: GetServerSidePropsContext) => {
  const name = params?.name;
  return {
    props: {
      name,
    },
  };
};

컴포넌트에서 경로 매개변수 사용하기

useRouter훅을 사용

  • pages/greet/[name].tsx
import { useRouter } from 'next/router';
import React from 'react';

export default function Greet() {
  const { query } = useRouter();
  return <div>{query.name}</div>;
}

http://localhost:3000/greet/sangmin 으로 접근 시 query.name은 sangmin

클라이언트에서의 네비게이션

Link 컴포넌트 이용

import Link from 'next/link';
import React from 'react';

export default function Navbar() {
  return (
    <div>
      <Link href="/">Home</Link>
      <Link href="/about">About</Link>
      <Link href="/contact" preload={false}>Contact</Link>
    </div>
  );
}

Next.js는 모든 Link에 대해 연결된 부분 또는 페이지를 미리 읽어온다.

페이지의 링크를 클릭했을 때 브라우저는 해당 페이지를 화면에 표시하기 위해 필요한 모든 데이터를 이미 불러온 상태.

→ 해당 기능은 Link 컴포넌트에 preload={false} 속성을 전달하면 비활성화 가능

정적 자원 제공

정적 자원은 /public 디렉토리 안에 저장하는 방식으로 클라이언트에 제공

자동 이미지 최적화

Image 컴포넌트를 사용해서 이미지를 자동으로 최적화할 수 있음

→ 이미지를 WebP와 같은 최신 이미지 포맷으로 제공 가능

next.config.ts 파일의 images 속성에 서비스 호스트명을 추가하면 해당 도메인의 이미지를 불러올 때마다 Next.js가 자동으로 해당 이미지를 최적화함

_app.tsx와 _document.tsx 페이지 커스터마이징

_app.tsx_document.tsx → 렌더링한 HTML을 클라이언트에 보내기 전 특정 작업을 처리

_app.tsx

export default function App({ Component, pageProps }: AppProps) {
  return <Component {...pageProps} />
}

App컴포넌트는 모든 페이지에서 같은 컴포넌트를 보여줄 때 사용 가능

모든 페이지에 Navbar 컴포넌트를 보여준다고 할 때 아래와 같이 작성 가능

import Navbar from '@/components/Navbar';

export default function App({ Component, pageProps }: AppProps) {
  return (
		<>
			<Navbar />
			<Component {...pageProps} />
		</>
	);
}

이외에도 다크모드를 구현하기위한 themeContext 추가 등 다양하게 커스텀 가능.

_app.tsx파일을 커스터마이징할 때는 다른 페이지처럼 getServerSidePropsgetStaticProps 같은 함수를 사용해서 데이터를 불러오는 용도로 사용할 수 없다.

_app.tsx 파일의 주된 목적

  • 서로 다른 페이지 간 상태 유지
  • 전역 스타일 추가
  • 페이지 레이아웃 관리
  • 페이지 속성에 데이터를 추가

_document.tsx

_document.tsx 파일에서는 기본적인 HTML 태그인 , , 태그를 수정할 수 있음

import { Html, Head, Main, NextScript } from 'next/document'

export default function Document() {
  return (
    <Html lang="en">
      <Head />
      <body>
        <Main />
        <NextScript />
      </body>
    </Html>
  )
}
  • Html태그에 해당. lang과 같은 표준 HTML 속성을 전달할 수 있음
  • Head
    모든 페이지에 대한 공통 태그를 정의할 때 이 컴포넌트를 사용.
  • Main
    페이지 컴포넌트를 렌더링하는 곳. 외부의 컴포넌트는 브라우저에서 초기화되지 않기 때문에 페이지 간에 공통으로 사용되는 컴포넌트가 있다면 반드시 _app.tsx 파일에서 해당 컴포넌트를 사용해야 함.
  • NextScript
    Next.js는 클라이언트에 전송할 페이지를 렌더링하고, 클라이언트에서 실행할 코드나 리액트 하이드레이션같은 작업을 처리할 수 있는 커스텀 스크립트를 끼워넣을 수 있다.
    NextScript는 이런 커스텀 자바스크립트가 위치하는 곳.

_document.tsx도 _app.tsx와 마찬가지로 서버에서 데이터를 불러오는 함수를 사용할 수 없음.

profile
FE Developer

0개의 댓글