이번 글은 Next.js 공식 튜토리얼을 번역한 글입니다.
공식 튜토리얼을 따라하면 Next.js 의 동작원리와 개발 방식에 대해 학습하기 쉽고 잘 쓰여진 튜토리얼이라고 생각되어 번역하게 되었습니다.
오타 또는 오역된 부분이 있다면 댓글 부탁드립니다.
Learn Next.js: Optimizing Fonts and Images | Next.js
Optimize fonts and images with the Next.js built-in components.
nextjs.org
지난 장에서는 Next.js 애플리케이션의 스타일링에 대해 알아보았습니다. 이번에는 홈페이지에 커스텀 폰트와 히어로 이미지를 추가하는 방법을 배워보겠습니다.
이번 장에서 다룰 주제는 다음과 같습니다.
- next/font를 사용하여 커스텀 폰트 추가하기
- next/image를 사용하여 이미지 추가하기
- Next.js에서 폰트와 이미지가 최적화되는 방식
폰트를 최적화하는 이유
폰트는 웹사이트의 디자인에 중요한 역할을 하지만, 커스텀 폰트를 사용하면 폰트 파일을 불러오고 로드하는 데 시간이 걸려 성능에 영향을 줄 수 있습니다.
구글이 웹사이트의 성능과 사용자 경험을 평가할 때 사용하는 지표 중 하나인 누적 레이아웃 이동(CLS)는 페이지가 로드될 때 콘텐츠가 이동하는 현상을 측정합니다. 폰트의 경우, 브라우저가 처음에는 대체 폰트나 시스템 폰트로 텍스트를 표시하고 이후 커스텀 폰트가 로드되면 이를 교체하는 과정에서 레이아웃이 변경될 수 있습니다.
이렇게 폰트가 로드되면서 발생하는 레이아웃 이동은 요소 간의 위치가 변경되거나 텍스트 크기와 간격이 달라질 수 있는 원인이 됩니다. Next.js는 next/font 모듈을 사용하면 애플리케이션 내 폰트를 자동으로 최적화합니다. 빌드 시 폰트 파일을 다운로드하여 다른 정적 파일과 함께 제공하므로, 사용자가 애플리케이션에 접근할 때 추가적인 폰트 관련 네트워크 요청 없이 빠르게 로드됩니다.
퀴즈: 방금 배운 내용을 테스트해봅시다.
Next.js는 어떻게 폰트를 최적화할까요?
- A: 추가적인 네트워크 요청을 발생시켜 성능을 높입니다.
- B: 모든 커스텀 폰트를 비활성화합니다.
- C: 런타임에 모든 폰트를 미리 로드합니다.
- D: 폰트 파일을 다른 정적 파일과 함께 호스팅하여 추가적인 네트워크 요청 없이 로드합니다.
정답: D
주요 폰트 추가하기
애플리케이션에 커스텀 Google 폰트를 추가해 보면서 이 작동 방식을 알아보겠습니다!
/app/ui 폴더에 fonts.ts라는 새 파일을 생성합니다. 이 파일은 애플리케이션 전반에서 사용될 폰트를 보관하는 데 사용됩니다.
next/font/google 모듈에서 Inter 폰트를 가져옵니다. 이 폰트는 주 폰트로 사용할 것입니다. 그런 다음, 로드할 서브셋을 지정합니다. 여기서는 'latin'을 사용합니다.
// /app/ui/fonts.ts
import { Inter } from 'next/font/google';
export const inter = Inter({ subsets: ['latin'] });
마지막으로 /app/layout.tsx의 <body> 요소에 폰트를 추가합니다.
// /app/layout.tsx
import '@/app/ui/global.css';
import { inter } from '@/app/ui/fonts';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body className={`${inter.className} antialiased`}>{children}</body>
</html>
);
}
Inter 폰트를 <body> 요소에 추가하면 애플리케이션 전반에 폰트가 적용됩니다. 여기서는 Tailwind의 antialiased 클래스를 추가하여 폰트를 부드럽게 표시하고 있습니다. 꼭 필요한 것은 아니지만, 미관상 도움이 됩니다.
브라우저로 이동하여 개발자 도구를 열고 body 요소를 선택합니다. 이제 Inter와 Inter_Fallback이 스타일에 적용된 것을 확인할 수 있습니다.
연습: 보조 폰트 추가하기
애플리케이션의 특정 요소에 폰트를 추가할 수도 있습니다.
이제 여러분 차례입니다! fonts.ts 파일에서 Lusitana라는 보조 폰트를 가져오고, /app/page.tsx 파일의 <p> 요소에 적용해 보세요. 이전과 같이 서브셋을 지정하는 것 외에도 폰트 두께를 지정해야 합니다.
준비가 되면 아래 코드 조각을 확장하여 솔루션을 확인하세요.
힌트
- 폰트에 어떤 두께 옵션을 전달해야 할지 모르겠다면 코드 편집기에서 TypeScript 오류를 확인하세요.
- Google Fonts 웹사이트에서 Lusitana를 검색하여 사용할 수 있는 옵션을 확인하세요.
- 여러 폰트를 추가하는 방법과 사용 가능한 옵션 목록을 보려면 문서를 참조하세요
import { Inter, Lusitana } from 'next/font/google';
export const inter = Inter({ subsets: ['latin'] });
export const lusitana = Lusitana({
weight: ['400', '700'],
subsets: ['latin'],
});
import AcmeLogo from '@/app/ui/acme-logo';
import { ArrowRightIcon } from '@heroicons/react/24/outline';
import Link from 'next/link';
import { lusitana } from '@/app/ui/fonts';
export default function Page() {
return (
// ...
<p
className={`${lusitana.className} text-xl text-gray-800 md:text-3xl md:leading-normal`}
>
<strong>Welcome to Acme.</strong> This is the example for the{' '}
<a href="https://nextjs.org/learn/" className="text-blue-500">
Next.js Learn Course
</a>
, brought to you by Vercel.
</p>
// ...
);
}
마지막으로, Lusitana 폰트를 사용하는 <AcmeLogo /> 컴포넌트도 주석 처리가 되어 있습니다. 이제 주석을 해제할 수 있습니다.
// /app/page.tsx
// ...
export default function Page() {
return (
<main className="flex min-h-screen flex-col p-6">
<div className="flex h-20 shrink-0 items-end rounded-lg bg-blue-500 p-4 md:h-52">
<AcmeLogo />
{/* ... */}
</div>
</main>
);
}
훌륭합니다!
이제 애플리케이션에 두 개의 커스텀 폰트를 추가했습니다. 다음으로 홈 페이지에 히어로 이미지를 추가해 보겠습니다.
왜 이미지를 최적화해야 할까요?
Next.js는 /public 폴더 아래에서 이미지와 같은 정적 자산을 제공할 수 있습니다. /public 안의 파일은 애플리케이션에서 참조할 수 있습니다.
일반적인 HTML에서는 다음과 같이 이미지를 추가할 수 있습니다.
<img
src="/hero.png"
alt="데스크톱 버전의 대시보드 프로젝트 스크린샷"
/>
하지만, 이 경우 다음을 수동으로 수행해야 합니다.
- 다양한 화면 크기에 대해 이미지가 반응형으로 표시되도록 합니다.
- 기기별로 이미지 크기를 지정합니다.
- 이미지를 로드할 때 레이아웃 이동을 방지합니다.
- 사용자의 뷰포트에 들어오지 않은 이미지를 지연 로드합니다.
이미지 최적화는 웹 개발에서 큰 주제이며 전문 분야로 여겨질 정도입니다. 이러한 최적화를 수동으로 구현하는 대신, next/image 컴포넌트를 사용하여 이미지를 자동으로 최적화할 수 있습니다.
<Image> 컴포넌트
<Image> 컴포넌트는 HTML <img> 태그의 확장이며, 다음과 같은 자동 이미지 최적화 기능을 제공합니다.
- 이미지 로드 시 레이아웃 이동을 자동으로 방지합니다.
- 작은 뷰포트를 가진 기기에 큰 이미지를 전송하지 않도록 이미지를 크기 조정합니다.
- 뷰포트에 들어오는 이미지를 기본적으로 지연 로드합니다.
- 브라우저가 지원할 경우, WebP 및 AVIF와 같은 최신 형식으로 이미지를 제공합니다.
데스크톱 히어로 이미지 추가하기
<Image> 컴포넌트를 사용해 보겠습니다. /public 폴더 안에 hero-desktop.png와 hero-mobile.png라는 두 개의 이미지가 있습니다. 이 두 이미지는 완전히 다르며, 사용자의 기기가 데스크톱인지 모바일인지에 따라 표시됩니다.
// /app/page.tsx
import AcmeLogo from '@/app/ui/acme-logo';
import { ArrowRightIcon } from '@heroicons/react/24/outline';
import Link from 'next/link';
import { lusitana } from '@/app/ui/fonts';
import Image from 'next/image';
export default function Page() {
return (
// ...
<div className="flex items-center justify-center p-6 md:w-3/5 md:px-28 md:py-12">
{/* Add Hero Images Here */}
<Image
src="/hero-desktop.png"
width={1000}
height={760}
className="hidden md:block"
alt="데스크톱 버전의 대시보드 프로젝트 스크린샷"
/>
</div>
//...
);
}
여기서 너비를 1000픽셀, 높이를 760픽셀로 설정하고 있습니다. 레이아웃 이동을 방지하기 위해 이미지의 너비와 높이를 설정하는 것이 좋습니다. 이 비율은 원본 이미지와 동일해야 합니다.
또한, hidden 클래스를 사용해 모바일 화면에서는 이미지를 DOM에서 제거하고, md:block 클래스를 사용해 데스크톱 화면에서는 이미지를 표시합니다.
이제 홈페이지는 다음과 같아야 합니다.
연습: 모바일 히어로 이미지 추가하기
이제 여러분 차례입니다! 방금 추가한 이미지 아래에 hero-mobile.png를 위한 <Image> 컴포넌트를 추가해 보세요.
- 너비는 343픽셀, 높이는 388픽셀로 설정하세요.
- md:hidden을 사용하여 데스크톱 화면에서는 이미지를 숨기세요.
준비가 되면 아래 코드 조각을 확장하여 솔루션을 확인하세요.
import AcmeLogo from '@/app/ui/acme-logo';
import { ArrowRightIcon } from '@heroicons/react/24/outline';
import Link from 'next/link';
import { lusitana } from '@/app/ui/fonts';
import Image from 'next/image';
export default function Page() {
return (
// ...
<div className="flex items-center justify-center p-6 md:w-3/5 md:px-28 md:py-12">
{/* Add Hero Images Here */}
<Image
src="/hero-desktop.png"
width={1000}
height={760}
className="hidden md:block"
alt="Screenshots of the dashboard project showing desktop version"
/>
<Image
src="/hero-mobile.png"
width={560}
height={620}
className="block md:hidden"
alt="Screenshot of the dashboard project showing mobile version"
/>
</div>
//...
);
}
좋아요! 이제 홈페이지에 사용자 정의 글꼴과 히어로 이미지가 있습니다.
퀴즈: 방금 배운 내용을 테스트해봅시다.
True 또는 False: 너비와 높이가 지정되지 않은 이미지와 웹 폰트는 레이아웃 이동의 흔한 원인이다.
- A. True
- B. False
정답: A
추천 문서
이 주제들에 대해 더 배울 것이 많습니다.
여기에는 원격 이미지 최적화와 로컬 폰트 파일 사용이 포함됩니다. 폰트와 이미지에 대해 더 깊이 알아보고 싶다면, 아래 자료를 참고하세요.
'Frontend > Next.js' 카테고리의 다른 글
[Next.js] Next 공식 튜토리얼 진행하기 - Chapter 4 (1) | 2024.11.17 |
---|---|
[Next.js] Next.js Middleware로 로그인 검증하기 (0) | 2024.11.15 |
[Next.js] Next 공식 튜토리얼 진행하기 - Chapter 2 (0) | 2024.11.10 |
[Next.js] Next 공식 튜토리얼 진행하기 - Chapter 1 (1) | 2024.11.10 |
[Next.js] Next 공식 튜토리얼 진행하기 - Introduction (2) | 2024.11.09 |