Next.js의 내장 컴포넌트, 이미지 최적화, 기본 파일 커스텀🧶

Sheryl Yun·2023년 5월 21일
0

목차

  • 라우팅 시스템
  • 페이지 간 이동 최적화 (Link, useRouter)
  • 정적 자원 제공
  • Image 컴포넌트를 사용한 자동 이미지 최적화
  • 메타 데이터 처리
  • _app.js와 _document.js 파일 커스터마이징

라우팅 시스템

리액트에서의 react-router

  • 클라이언트에서의 라우팅만 구현 가능
  • 모든 페이지가 클라이언트 사이드에서 만들어지고 렌더링됨

페이지 기반 라우팅

  • Next.js가 기본적으로 갖고 있는 pages/ 폴더
  • pages/ 폴더 안의 모든 파일이 곧 Next.js의 페이지와 라우팅 규칙을 의미
  • Next.js 페이지 = pages/ 폴더의 파일에서 export한 JSX를 반환하는 리액트 컴포넌트

페이지 컴포넌트 함수는 반드시 JSX를 반환해야 함
=> 그래야 서버에서 함수를 실행하고 렌더링한 HTML을 브라우저로 전송 가능

예시

  • pages/ 폴더 안에 index.js를 만들고 JSX를 반환하는 Homepage 컴포넌트 함수를 만들어 export
    => http://localhost:3000 에 접근했을 때 반환된 JSX 확인 가능
  • pages/ 폴더의 contacts.js 파일에 ContactPage 함수를 만들고 export
    => http://localhost:3000/contacts 로 접근하면 Contack 페이지 확인 가능
  • 파일명(경로명)에 하이픈('-') 붙일 수 있음
    • contact-us.js로 만들면 http://localhost:3000/contact-us로 이동 시 확인 가능

동적 라우팅

  • 라우팅 규칙은 계층적
    • 예: pages/ 폴더 안에 posts/ 폴더 생성
      => http://localhost:3000/posts 주소로 posts/ 폴더의 index.js 페이지 함수에 접근 가능
pages/  -> pages 폴더
  - index.js
  - contact-us.js
  - posts/  -> pages/ 안에 posts 폴더
    - index.js
    - [slug].js

목적

  • 웹 사이트에 새로운 글을 등록할 때마다 수동으로 새 페이지를 만들지 않도록 함

형태

  • 예: pages/posts/ 폴더 안의 [slug].js 파일

경로 매개변수에 따라 서로 다른 동적 페이지 렌더링하기

경로 매개변수(route variable)란?

  • [slug]든 [id]든 이름은 상관 없다.
    • params로 경로 변수를 가져올 때 변수명을 설정을 식별하기 위한 이름
    • 대괄호 안에 매칭되는 경로에는 어떤 경로든 들어갈 수 있음

동적 렌더링 과정

  • params를 getServerSideProps 등을 통해 props로 받아서 컴포넌트에 전달

  • JSX(뷰)에 props 내용을 끼워넣으면 동일한 뷰로 내용만 바뀐 화면 구현 가능

    export async function getServerSideProps({ params }) {
    	const { name } = params;
       
       return {
       	props: {
         		name,  
           },
       };
    }
    
    function Greet(props) {
    	return (
       	<h1> Hello, {props.name}! </h1>
       );
    }
    
    export default Greet;

=> http://localhost:3000/greet/Sheryl 주소로 가면 화면에서 'Hello, Sheryl!' 이라는 문구 확인

페이지가 아닌 컴포넌트 안에서 경로 매개변수 가져오기: useRouter 훅 사용

  • 페이지 컴포넌트에서는 getServerSideProps와 getStaticProps 함수로 경로 매개변수를 가져올 수 있음
    • 하지만 이 두 함수는 페이지 컴포넌트에서만 사용 가능
  • 페이지가 아닌 컴포넌트에서 경로 매개변수를 가져오려면 useRouter 훅을 사용
    • 리액트 훅으로 어떤 컴포넌트 안에서도 사용 가능
import { useRouter } from 'next/router';

function Greet() {
	const { query } = useRouter();
    
    return <h1>Hello {query.name}!</h1>;
}

export default Greet;
  • useRouter의 query로 경로 매개변수를 가져올 수 있음

  • query를 console.log로 출력

    • 경로 매개변수(name)와 쿼리 매개변수('?' 뒤의 key=value)를 객체 형태로 출력
      • 예: http://localhost:3000/greet/Sheryl?learning_next.js=true에 접근하면
        • 콘솔에 출력되는 내용: { learning_next.js: true, name: Sheryl }
  • 경로 매개변수와 같은 key 이름의 쿼리 매개변수도 지정 가능

Next.js의 페이지 이동 방법 2가지

  • Next.js에서 HTML의 표준 <a> 태그 대신 사용되는 내장 컴포넌트
  • 서로 다른 라우트 간 이동 최적화 가능

Link 안에 <a> 태그를 꼭 넣어야 하나요?
Next.js 13 버전부터 Link 컴포넌트 안의 string을 <a> 태그로 감싸지 않아도
자동으로 <a> 태그로 감싸짐

import Link from 'next/link';

function Navbar() {
    return (
    	<div>
        	<Link href='/'>Home</Link>
            <Link href='/about'>About</Link>
            <Link href='/contacts'>Contacts</Link>
        </div>
    );
}

export default Navbar;

기본적으로 현재 화면에 표시되는 페이지의 모든 Link로 연결된 페이지를 미리 읽어옴 (preload 기능)

  • 페이지 링크를 클릭했을 때 이미 브라우저는 해당 페이지 화면을 표시하기 위해 필요한 데이터를 모두 불러온 상태
  • 이 preload 기능을 비활성화하려면 Link 컴포넌트에 preload={false}를 전달
import Link from 'next/link';

function Navbar() {
    return (
    	<div>
        	<Link href='/' preload={false}>Home</Link>
            ...
        </div>
    );
}

동적 경로 매개변수로 페이지 연결하기

  • 예: /posts/[date]/[slug].js 페이지를 Link에 연결하기
    • Next.js 10 이전에는 as가 필요했으나 이제는 href에 바로 연결 가능
import Link from 'next/link';

function Posts() {
    return (
    	<div>
        	<Link href='/posts/2023-05-21/queen-charlotte-is-attractive' preload={false}>
            	Read Post
            </Link>
            ...
        </div>
    );
}

export default Posts;

Link의 href 속성에 복잡한 값을 객체로 전달 가능

  • href의 객체가 가질 수 있는 속성
    • pathname, query 등
<Link
	href={{
    	pathname: 'posts/[date]/[slug].js'
        query: {
        	date: '2023-04-24',
            slug: 'start-course',
            foo: 'bar'
        }
    }}
>
	Read Post
</Link>
  • 주의: query 객체 프로퍼티 중에서 pathname에 명시되지 않은 속성(예: foo)은 경로 매개변수가 아닌 쿼리 매개변수로 들어감
    • 예: 위 링크 클릭 시 http://localhost:3000/posts/2023-04-24/start-course?foo=bar로 이동

2. useRouter의 router.push()

예: 로그인한 사용자만 접근 가능한 페이지를 위해 useAuth 훅 생성

  • 사용자가 로그인하지 않았을 경우 router.push()로 로그인 페이지로 이동시킴
import { useEffect } from 'react';
import { useRouter } from 'next/router';
import PrivateComponent from '../components/Private';
import useAuth from '../hooks/auth';

function MyPage() {
	const router = useRouter(); // useRouter 훅으로 router 생성
    const { isLogin } = useAuth();
    
    // 로그인이 되지 않았을 때 로그인 페이지로 이동
    // useEffect 훅을 사용하여 useEffect 내부 코드가 클라이언트에서만 실행되도록 함
    useEffect(() => {
    	if (!isLogin) {
        	router.push('/login');
        }
    }, [isLogin]);
    
    return isLogin ? <PrivateComponent /> : null;
}

router.push()에 복잡한 값을 객체로 전달

	router.push({
    	pathname: 'posts/[date]/[slug].js'
        query: {
        	date: '2023-05-19',
            slug: 'end-course',
            foo: 'bar'
        }
	});
  • router.push가 호출되면 http://localhost:3000/posts/2023-05-19/end-course?foo=bar로 이동

Link와 router.push의 활용 예시

  • Link에는 연결된 페이지를 미리 불러오는 preload 기능이 있지만 router.push에는 preload 기능이 없음
    • 클라이언트 내비게이션 구현에는 router.push가 아닌 Link를 사용해야!
      • 페이지 이동 최적화를 위해
  • router.push는 특정 상황 또는 이벤트 발생 시 클라이언트에서 사용자를 특정 페이지를 보낼 때 사용
    • 예: 페이지에 들어온 사용자가 로그인이 안 되어 있는 경우 로그인 페이지로 이동시킴

정적 자원 제공

정적 자원이란?

  • 동적으로 변하지 않는 모든 종류의 파일
    • 예: 이미지, 폰트, 아이콘, 컴파일한 CSS 파일, 컴파일한 JS 파일 등
  • public/ 폴더 안에 저장
    • public/ 폴더 안에 있는 모든 파일은 Next.js가 정적 자원으로 간주

이미지 파일

  • 정적 자원 중 웹 사이트의 성능과 SEO 점수에 영향을 미치는 자원
  • 최적화되지 않은 이미지를 제공하면
    • 이미지를 불러오는 데 시간이 오래 걸림
    • 불러온 후에 이미지 주변의 레이아웃이 변경되는 CLS 현상 발생

누적 레이아웃 이동 (Cumulative Layout Shift, CLS)
이미지를 불러온 뒤 페이지의 레이아웃이 변경되는 현상
예: 이미지 로딩 후 이미지의 높이만큼 문자 영역이 아래로 밀림

Image 컴포넌트

  • Next.js가 제공하는 내장 컴포넌트
  • 이미지 자동 최적화, CLS 문제 해결
  • 이미지를 WebP 등의 최신 이미지 포맷으로 제공
    • 해당 브라우저가 WebP를 지원할 경우에만
    • 지원하지 않는 브라우저를 대비해 png, jpeg 등 예전 이미지 포맷도 가능
  • 이미지 크기 조절
    • 이미지 다운로드 속도 상승
  • 클라이언트가 이미지를 요구(요청)하는 시점에 최적화 진행

외부 이미지 서비스(Unsplash, Pexel)로 이미지 제공

  • 표준 HTML img 태그를 사용한 경우
<img 
	src='https://images.unsplash.com/photo-115jlljffaseh3232k'
    alt='강아지'
/>
  • 화면 크기별로 이미지 조절(반응형 이미지)하려면 srcset 속성값 사용 및 이후 몇 가지 작업을 추가해야 함

Next.js에서 다음 2가지 방법으로 쉽게 외부 서비스로 이미지 제공 가능

  1. next.config.js 파일에 설정 추가
  • 외부 이미지 서비스에서 바로 이미지를 제공할 수 있도록 다음과 같이 설정
module.exports = {
	images: {
    	domains: ['images.unsplash.com']  // 'http://~' 뒷 부분 작성
    }
};
  • Image 컴포넌트에서 해당 도메인의 이미지를 불러올 때마다 Next.js가 자동으로 이미지를 최적화하게 됨
  1. 컴포넌트 JSX에서 Image 컴포넌트를 사용하여 이미지 추가
import Image from 'next/image';

function IndexPage() {
	return (
    	<div>
        	<Image
            	src='https://images.unsplash.com/photo-115jlljff32k'
    			width={500}
                height={200}
                alt='강아지'
            />
        </div>
    );
}

export default IndexPage;
  • 외부 이미지를 사용할 때는 무조건 width, height 속성을 입력해야 함
    • 정적 이미지일 경우 Next.js가 빌드할 때 width, height 속성을 계산할 수 있지만, 외부 이미지는 Next.js가 width, height 속성을 계산하지 못하기 때문
  • Image 컴포넌트를 위와 같이 width와 height를 고정하면 width, height 속성 값에 따라 사진이 양옆으로 늘어난 형태로 렌더링

Image 컴포넌트의 layout 속성 종류

이미지 크기를 원하는 대로 조절하기

1. fixed

  • 화면 크기와 상관 없이 이미지의 크기 고정

2. responsive

  • 반응형 이미지 크기 (fixed와 반대)
  • 화면이 작거나 큼에 따라 이미지의 크기를 조절

3. intrinsic

  • 이미지의 크기보다 작은 화면에서는 이미지를 화면에 맞게 줄이고 (responsive)
  • 이미지의 크기보다 큰 화면에서는 이미지 크기 고정 (fixed)

4. fill

  • 부모 요소 크기에 따라 이미지 크기 조절
  • 상위 엘리먼트에 반드시 position: relative 옵션을 적용
  • width, height 속성과 함께 사용할 수 없음 (둘 중에 하나만 가능)
  • 이미지 사이즈를 모를 때 사용하면 좋다

이미지를 화면 크기에 따라 조절하는 예시 (layout: fill + objectFit: cover)

import Image from 'next/image';

function IndexPage() {
	return (
    	<div>
        	<div
            	style={{
                	width: 500,
                    height: 200,
                    position: 'relative',
                }}
            >
            	<Image
            		src='https://images.unsplash.com/photo-115jlljff32k'
    				layout='fill'
                    objectFit='cover'
                	alt='강아지'
            	/>
            </div>
        </div>
    );
}

export default IndexPage;
  • Image 컴포넌트를 width와 height가 고정된 div로 감싸고
  • 감싼 div의 position 속성을 relative로 지정
  • Image 컴포넌트의 layout 속성을 fill로 지정
    • 이미지의 크기가 부모 요소인 div의 크기에 따라 조절됨
  • Image 컴포넌트에 objectFit: cover 지정
    • 부모 요소 div의 크기에 따라 이미지를 '잘라냄'

objectFit: cover에 대해 찾아보다가 알게 된 사실:
next/image 13 버전에서 objectFit: cover => deprecated
참고 링크

  • 기존에 이미지 비율을 맞추던 방법

    • layout: fill + objectFit: cover
  • 이제는 next/Image의 새로운 props 중 하나인 fill 속성을 사용

    • layout 대체
    • 이미지가 부모 요소를 채우도록 함
    • boolean 타입의 props
    • 부모 요소는 반드시 position: "relative, fixed, absolute"를 가지고 있어야 함
    <div style={{position:"relative"}}>
    	<Image
        src={source}
        alt=""
        fill
        style={{ objectFit:"cover" }} // objectFit은 style에 선언
      />
    </div>

=> next/Image가 더 이상 css역할을 담당하지 않게 됨

  • 공식 문서: 'Remove layout prop in favor of style'
    • 'style을 지지하며 layout prop을 없앴다'는 의미
    • css 역할은 style에게 넘기고 next/image는 최적화에 집중하겠다는 의도 (추측)

Image 컴포넌트 추가 내용

  • 화면 별로 여러 가지 크기의 이미지를 생성하기 위해 기본적으로 img 태그의 srcset 속성 사용

  • 구글 크롬이나 파이어폭스 프라우저에서 이미지 정보를 확인하거나 다른 이름으로 저장하면 원래 이미지가 jpeg 포맷이어도 WebP 포맷으로 제공

    • iOS의 사파리 브라우저에서는 WebP 포맷을 아직 지원하지 않기 때문에 jpeg 포맷으로 제공

Next.js가 아닌 외부 서비스로 이미지 최적화하기

  • Next.js는 이미지에 대한 요청이 있을 때만 이미지 최적화 진행
  • 이미지 최적화 과정은 Next.js 서버에서 일어남
    • 웹 애플리케이션이 매우 많은 이미지를 사용할 경우 서버 성능에 영향

=> 이미지 최적화를 Next.js가 아닌 외부 서비스에서 처리하는 방법 필요

방법: next.config.js 파일의 loader 속성 활용

  1. next.config.js에 loader 속성을 지정

    • 외부 서비스를 통해 자동 이미지 최적화 작업 처리

    • 예: 외부 서비스 Akamai 사용하기

module.exports = {
	images: {
    	loader: 'akamai',
        domains: ['images.unsplash.com']
    }
};
  • Vercel로 배포하는 경우
    • Vercel이 알아서 이미지 파일을 최적화 및 제공
      • loader 속성 따로 설정 안 해도 됨
  • Vercel 외 이미지 최적화 외부 서비스: Akamai, Imgix, Cloudinary
    • 여기는 서비스 별로 이미지 최적화를 위한 API 형태가 다름
    • 반드시 관련 문서 숙지 후 사용
  1. 컴포넌트에 loader 적용
import Image from 'next/image';

const loader = ({ src, width, quality }) => {
	return `https://example.com/${src}?w=${width}&q=${quality || 75}`;
}

function CustomImage () {
	return (
    	<Image
        	loader={loader}
            src="/myimage.png"
            alt="내 이미지"
            width={350}
            height={540}
        />
    );
}

메타데이터 관리

  • 웹 스크래퍼, 봇, 웹 스파이더 등의 웹 문서 수집기들은
    • 웹 페이지의 메타데이터를 통해 다음을 수행
      • 색인(index) 만들기
      • 페이지 연결
      • 웹 애플리케이션이나 사이트를 평가하여 점수 부여 (SEO)

활용

  • 오픈 그래프 (Open Graph)
    • 소셜 네트워크나 웹 사이트에서 해당 페이지를 공유하면 보여지는 카드에 나타날 정보
  • 메타데이터를 제공하지 않으면 검색 엔진이 웹 사이트가 꼭 필요한 정보를 제공하지 않아서 해당 페이지의 점수를 낮게 매김
  • 메타데이터를 통해 브라우저가 사용자 경험을 최적화

Head 컴포넌트 (next/head)

  • 메타데이터를 쉽게 다룰 수 있도록 하는 내장 컴포넌트
    • 어떤 컴포넌트에서든 HTML 페이지의 <head> 내부 데이터를 변경할 수 있게 해줌
    • 동적으로 메타데이터, link, script 등의 정보를 변경, 추가, 삭제 가능

공통 메타 태그를 다루는 컴포넌트 만들기

  1. 각 페이지에 공통으로 들어갈 Head 컴포넌트 만들기 (PostHead 컴포넌트)
import Head from 'next/head';

function PostHead(props) {
	return (
    	<Head>
        	<title>{props.title}</title>
            <meta name="description" content={props.subtitle} />
            {/* og 메타데이터 */}
            <meta property="og:title" content={props.title} />
            <meta property="og:description" content={props.subtitle} />
            <meta property="og:image" content={props.image} />
            
            {/* twitter 카드 메타데이터 */}
            <meta name="twitter:card" content="summary" />
            <meta name="twitter:card" content={props.title} />
            <meta name="twitter:card" content={props.description} />
            <meta name="twitter:card" content={props.image} />
        </Head>
    );
}

export default PostHead;
  1. 블로그 메타데이터를 위한 dummy 데이터 (예: posts.js)
export default [
	{
    	id: 'cho-co-late',
        slug: 'i-love-chocolate',
        title: '생초콜릿 맛있어',
        subtitle: '김보람초콜릿 짱짱',
        image: 'http://images.unsplash.com/아마-초콜릿-사진
    },
    {
    	id: 'cat-cat',
        slug: 'i-love-cat',
        title: '고양이 키우고 싶어',
        subtitle: '간택 당해버려',
        image: 'http://images.unsplash.com/검은-옷에-고양이-안은-사진'
    }
];
  1. 페이지 컴포넌트에서 PostHead로 페이지별 head(메타데이터) 추가
import PostHead from '../../components/PostHead';
import posts from '../.../data/posts';

export function getServerSideProps({ parmas }) {
	const { slug } = params;
    const selectedPost = posts.find((post) => post.slug === slug);
    
    return {
    	props: {
        	post: selectedPost,
        },
    };
}

function Post({ post }) {
	return (
    	<div>
        	<PostHead {...post } /> // 경로 params로 선택된 post 데이터는 전부 props로 전달
            <h1>{post.title}</h1>
            <p>{post.subtitle}</p>
        </div>
    );
}

_app.js와 _document.js 파일 소개

_app.js 파일

  • Next.js로 프로젝트 생성 시 다음과 같은 기본 파일 생성
import '../styles/globals.css';

function MyApp({ Component, pageProps }) {
	return <Component {...pageProps} />;
}

export default MyApp;
  • 구성
    • Component라는 Next.js 페이지 컴포넌트
    • 해당 컴포넌트의 속성(pageProps)

주요 목적

  • 전역 스타일 추가 (styles/global.css)
  • 페이지별 동일한 레이아웃 추가 (Layout 또는 Navbar 컴포넌트)
  • 페이지 간 동일한 상태나 테마 유지 (다크모드, 장바구니 등)
  • 페이지 속성(pageProps)에 데이터 추가

활용 예: Navbar

  • 모든 페이지에서 같은 Navbar 컴포넌트를 사용
  1. Navbar 컴포넌트 선언
import Link from 'next/link';

function Navbar() {
	return (
    	<div>
        	<div>나의 웹 사이트</div>
            <div>
            	<Link href='/'>Home</Link>
                <Link href='/about'>About</Link>
                <Link href='/contacts'>Contacts</Link>
            </div>
        </div>
    );
}

export default Navbar;
  1. Navbar 컴포넌트를 _app.js 파일에 추가
import Navbar from '../components/Navbar';
import '../styles/globals.css';

function MyApp({ Component, pageProps }) {
	return (
    	<>
        	<Navbar />
        	<Component {...pageProps} />
        </>
    ;
}

export default MyApp;

=> Navbar 컴포넌트가 모든 페이지에 공통으로 들어감

활용 예: 다크 모드

  • 리액트의 context API를 사용한 이유:
    • _app.js는 페이지 컴포넌트가 아니어서 getServerSideProps나 getStaticProps 함수로 데이터를 pre-rendering할 수 없기 때문에
  1. ThemeContext 선언
import { createContext } from 'react';

const ThemeContext = createContext({
	theme: 'light',
    toggleTheme: () => null
});

export default ThemeContext;
  1. _app.js에 theme 스타일 추가, ThemeProvider로 하위 컴포넌트 감싸기
import { useState } from 'react';
import ThemeContext from '../components/themeContext';
import Navbar from '../components/Navbar';
import '../styles/globals.css';

const themes = {
	dark: {
    	background: 'black',
        color: 'white'
    },
    light: {
    	background: 'white',
        color: 'black'
    }
};

function MyApp({ Component, pageProps }) {
	const [theme, setTheme] = useState('light');
    const toggleTheme = () => {
    	setTheme(theme === 'dark' ? 'light' : 'dark');
    }
    
	return (
    	<ThemeContext.Provider value={{ theme, toggleTheme }}>
        	<div
        	style={{
            	width: '100%',
                minHeight: '100vh',
                ...themes[theme] // themes 스타일 객체에서 theme 번째 스타일을 가져옴
            }}
        >
        	<Navbar />
        	<Component {...pageProps} />
        </div>
        </ThemeContext.Provider>
    ;
}

export default MyApp;
  1. 다크모드를 변경하기 위한 버튼을 Navbar 컴포넌트에 추가
import { useContext } from 'react';
import Link from 'next/link';
import ThemeContext from '../components/themeContext';

function Navbar() {
	const { theme, toggleTheme } = useContext(ThemeContext);
    const newThemeName = theme === 'dark' ? 'light' : 'dark';
    
	return (
    	<div>
        	<div>나의 웹 사이트</div>
            <div>
            	<Link href='/'>Home</Link>
                <Link href='/about'>About</Link>
                <Link href='/contacts'>Contacts</Link>
                <button onClick={toggleTheme}>
                	{newThemeName} 모드로 변경
                </button>
            </div>
        </div>
    );
}

export default Navbar;

번외: getInitialProps

  • _app.js 파일에 추가 가능한 예약 함수
  • 모든 페이지 렌더링 때 동일한 데이터를 서버에서 불러와야 할 경우
    • 2가지 부작용
      • Next.js가 모든 페이지를 무조건 서버에서 렌더링하게 됨
      • 동적 페이지에 대한 정적 최적화가 사라짐
  • 그래도 사용하고 싶다면 아래처럼
// _app.js

import App from 'next/app'; // next/app에서 App 컴포넌트 임포트

function MyApp({ Component, pageProps }) {
	return <Component {...pageProps} />;
}

// 컴포넌트 위쪽이 아닌 밑에서 선언
MyApp.getInitialProps = async (appContext) => {
	const appProps = await App.getInitialProps(appContext);
    const additionalProps = await fetch(...); // fetch API로 불러온 값
    
    return {
    	...appProps, // 기본 App 컴포넌트의 props
        ...additionalProps // fetch로 추가된 props
    };
}

_document.js 파일

  • Next.js의 기본 페이지 컴포넌트들에서 <head>, <html>, <body>와 같은 태그를 다루지 않음
  • <head> 태그는 공통 컴포넌트로 만들어서 페이지 별로 메타데이터를 추가했었음
  • <html><body> 태그는 _document.js 파일에서 처리
  • _app.js와의 공통점
    • getServerSideProps와 getStaticProps 함수 사용 불가
    • getInitialProps 함수를 사용하면 2가지 부작용 발생
      • 반드시 서버에서 모든 페이지를 렌더링해야 함
      • 사이트 최적화 기능을 사용할 수 없음
import Document, { Html, Head, Main, NextScript } from 'next/document';

class MyDocument extends Document { // 불러온 Document를 상속
	static async getInitialProps(ctx) {
    	const initialProps = await Document.getInitialProps(ctx);
        return { ...initialProps };
    }
    
    render() {
    	return (
        	<Html> // * 추가
            	<Head /> // * 추가
                <body>
                	<Main /> // * 추가
                    <NextScript /> // * 추가
                </body>
            </Html>
        );
    }
}

export default MyDocument;

커스텀 순서

  1. 커스텀 스크립트를 추가할 Document 클래스를 import
  2. Next.js 앱이 작동하기 위해 꼭 필요한 4개의 컴포넌트(Html, Head, Main, NextScript)도 import
    • 이 중 하나라도 없으면 Next.js 앱이 동작하지 않음
  • Html
    • Next.js 앱의 <html> 태그에 해당
    • 기존 index.html과 동일하게 lang과 같은 속성 설정 가능
  • Head
    • 여기서의 Head는 위에서 본 next/head의 Head와 다름
    • 반드시 웹 사이트의 모든 페이지에서 공통으로 사용되는 코드가 있을 때 사용
  • Main
    • 하위 (클라이언트) 페이지 컴포넌트를 렌더링하는 곳
    • Main 바깥의 컴포넌트는 브라우저에서 초기화되지 않음
      => 페이지 간에 공통으로 사용되는 컴포넌트는 여기가 아닌 _app.js에 추가해야
  • NextScript
    • NextScript는 클라이언트에서 실행할 코드나 리액트 하이드레이션과 같은 작업을 처리

정리

  1. 라우팅 시스템
    • 페이지 기반 라우팅
    • 동적 라우팅
  2. 페이지 이동 2가지 방법
    • Link
    • useRouter (router.push)
  3. 정적 자원
    • 이미지 최적화
  4. 메타데이터 관리
  5. _app.js와 _document 파일
profile
데이터 분석가 준비 중입니다 (티스토리에 기록: https://cherylog.tistory.com/)

0개의 댓글