[Next.js] Asset 최적화

Next의 이미지 최적화

Next는 자동으로 이미지 크기를 최적화해주는 이미지 컴포넌트(Image)를 제공한다.

 

Image 태그의 기능

1) 크기 최적화

- 각 기기에 맞는 크기의 이미지를 자동으로 제공하고, WebP 및 AVIF와 같은 최신 이미지 형식을 사용

// next.config.js 설정 추가
module.exports = {
  images: {
	  // ...
    formats: ['image/avif', 'image/webp'],
  },
}

 

2) 시각적 안정성

- 이미지가 로딩도리 때 레이아웃 이동을 자동으로 방지

 

3) 빠른 페이지 로드

- 네이티브 브라우저 지연 로딩을 사용하여 이미지가 뷰포트에 들어올 때만 로드됨

- 블러업 플레이스 홀더를 옵션으로 사용 가능

 

4) 자산 유연성

- 원격 서버에 저장된 이미지도 포함하여 필요에 따라 이미지 조정

 

사용법

1) 로컬이미지

import Image from 'next/image'
import profilePic from './me.png'

export default function Page() {
  return (
    <Image
      src={profilePic}
      alt="Picture of the author"
      // width={500} 자동 제공
      // height={500} 자동 제공
      // blurDataURL="data:..." 자동 제공
      // placeholder="blur" // 로딩 중 블러업 옵션
    />
  )
}

 

2) 원격, 네트워크 이미지

import Image from 'next/image'

export default function Page() {
  return (
    <Image
      src="<network-image>"
      alt="Picture of the author"
      width={500}
      height={500}
    />
  )
}
// next.config.js 설정
/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: false,
  images: {
    remotePatterns: [
      {
        protocol: "https",
        hostname: "fakestoreapi.com",
        pathname: "/**",
      },
    ],
    formats: ["image/avif", "image/webp"],
  },
};

 

최적화 원리

1) lazy loading

- 뷰포트에 노출되는 이미지만 로드하여 페이지 로드 속도를 개선

- js의 경우 따로 구현하지 않는 이상 페이지의 전체 데이터를 모두 로드

 

2) 이미지 사이즈 최적화

- 최초 요청시 Next.js 서버에서 이미지를 용량이 작은 WebP, AVIF 포맷으로 변환

 

 

Metadata를 이용한 SEO 최적화

정적 메타데이터

// layout.tsx | page.tsx
import type { Metadata } from 'next'

export const metadata: Metadata = {
  title: '...',
  description: '...',
}

export default function Page() {}

 

동적 메타데이터

generateMetadata 함수를 활용한다!

// app/products/[id]/page.tsx
import type { Metadata, ResolvingMetadata } from 'next'

type Props = {
  params: { id: string }
  searchParams: { [key: string]: string | string[] | undefined }
}

export async function generateMetadata(
  { params, searchParams }: Props,
): Promise<Metadata> {
  // 경로 매개변수 읽기
  const id = params.id

  // 데이터 가져오기
  const product = await fetch(`https://.../${id}`).then((res) => res.json())

  return {
    title: product.title,
  }
}

export default function Page({ params, searchParams }: Props) {}

 

필드 상속

// app/layout.js
export const metadata = {
  title: 'Acme',
  openGraph: {
    title: 'Acme',
    description: 'Acme is a...',
  },
}

// app/about/page.js
export const metadata = {
  title: 'About',
}

// 출력:
// <title>About</title>
// <meta property="og:title" content="Acme" />
// <meta property="og:description" content="Acme is a..." />

app/about/page.js가 openGraph 메타데이터를 설정하지 않기 때문에 app/layout.js의 모든 openGraph 필드는 app/about/page.js에 상속된다.

 

 

Static Asset

넥스트에서는 정적 데이터를 public 폴더에서 쉽게 관리할 수 있다.

import Image from 'next/image'
import Logo from "/public/assets/logo.png";

<Image height={40} src={Logo} alt="logo"></Image>

 

 

Font 최적화

next/font는 모든 폰트 파일에 대해 자동으로 셀프 호스팅을 제공한다.

레이아웃 시프팅 없이 최적의 방식으로 웹 폰트를 로드할 수 있도록 도와준다.

 

구글 폰트 사용하기

next/font/google에서 사용할 폰트를 가져와 함수로 사용한다.

import { Inter } from 'next/font/google'

const inter = Inter({
  weight: '400',  // font weight 지정 가능
  subsets: ['latin'],
  display: 'swap',
})

export default function RootLayout({ children }) {
  return (
    <html lang="en" className={inter.className}>
      <body>{children}</body>
    </html>
  )
}

 

Multiple Fonts 사용하기

여러 폰트를 사용할 때는 폰트를 내보내고 필요한 곳에서 가져와 사용한다.

// fonts 파일에서 폰트를 설정해 내보낸다.
import { Inter, Roboto_Mono } from 'next/font/google'

export const inter = Inter({
  subsets: ['latin'],
  display: 'swap',
})

export const roboto_mono = Roboto_Mono({
  subsets: ['latin'],
  display: 'swap',
})

// 사용할 곳에서 폰트를 가져와 사용한다.
import { inter } from './fonts'

export default function Layout({ children }) {
  return (
    <html lang="en" className={inter.className}>
      <body>
        <div>{children}</div>
      </body>
    </html>
  )
}

 

Local Fonts 사용하기

next/font/local로 로컬 폰트 변수를 생성하여 사용한다.

import localFont from 'next/font/local'
 
// Font files can be colocated inside of `app`
const myFont = localFont({
  src: './my-font.woff2',
  display: 'swap',
})
 
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en" className={myFont.className}>
      <body>{children}</body>
    </html>
  )
}

 

 

Script

next/script를 사용해 스크립틀을 최적화할 수 있다.

import Script from 'next/script'

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>{children}</body>
      <Script src="https://example.com/script.js" />
    </html>
  )
}

 

스크립트 로딩 전략

세부 로딩 전략을 조정할 수 있다.

1) beforeInteractive: Next.js 코드 및 페이지 하이드레이션 전에 스크립트를 먼저 로드

2) afterInteractive: (기본값) 페이지 하이드레이션 후 스크립트를 로드

3) lazyOnload: 브라우저 유휴 시간에 스크립트 로드

4) worker: 웹 워커에서 스크립트 로드 (실험적)

 

import Script from 'next/script'

export default function Home() {
  return (
    <>
      <Script src="https://example.com/script.js" strategy="worker" />
    </>
  )
}