이전 장에서는 Next.js 애플리케이션의 스타일을 지정하는 방법을 배웠다. 사용자 정의 폰트와 히어로 이미지를 추가하여 웹페이지 작업을 계속해 보자.
이번 장에서 다룰 주제는 아래와 같다.
- next/font를 사용하여 사용자 정의 폰트를 추가하는 방법.
- next/image로 이미지를 추가하는 방법.
- Next.js에서 폰트와 이미지가 최적화되는 방법.
폰트를 최적화하는 이유
폰트는 웹 사이트 디자인에서 중요한 역할을 하지만 프로젝트에서 사용자 정의 폰트를 사용하면 폰트 파일을 가져와서 로드해야 하는 경우 성능에 영향을 미칠 수 있다.
Cumulative Layout Shift (CLS 누적 레이아웃 변경) 은 Google이 웹사이트의 성능과 사용자 경험을 평가하는 데 사용하는 측정항목이다. 폰트를 사용하면 브라우저가 처음에 대체 폰트나 시스템 폰트로 텍스트를 렌더링된 후 사용자 지정 폰트로 교체할 때 레이아웃 변경이 발생하게된다. 이 교체로 인해 텍스트 크기, 간격 또는 레이아웃이 변경되고 그 주위의 엘리먼트가 이동될 수 있다.
Next.js는 next/font 모듈을 사용할 때 애플리케이션의 폰트를 자동으로 최적화한다. 빌드 시 폰트 파일을 다운로드하고 다른 정적 자산과 함께 다운로드한 폰트를 호스팅한다. 따라서 사용자가 애플리케이션을 방문할 때 성능에 영향을 미칠 수 있는 폰트에 대한 추가 네트워크 요청이 없음을 의미한다.
기본 폰트 추가
애플리케이션에 맞춤 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>
);
}
<body> 엘리먼트에 Inter를 추가하면 해당 폰트가 애플리케이션 전체에 적용된다. 여기서는 폰트를 부드럽게 만드는 Tailwind 안티앨리어싱 클래스도 추가한다. 이 클래스를 사용할 필요는 없지만 멋진 느낌을 더해줄 것이다.
브라우저로 이동하여 개발 도구를 열고 body 엘리먼트를 선택해보자. 이제 Inter 및 Inter_Fallback이 스타일 아래에 적용되는 것을 볼 수 있다.
연습문제: 보조 폰트 추가하기
애플리케이션의 특정 엘리먼트에 폰트를 추가할 수도 있다.
Fonts.ts 파일에서 Lusitana라는 보조 폰트를 가져와 /app/page.tsx 파일의 <p> 엘리먼트에 전달한다. 이전과 같이 서브셋을 지정하는 것 외에도 폰트 두께도 지정해야한다. 준비가 되면 아래 코드 조각을 확장하여 솔루션을 확인하자.
힌트:
- 폰트에 전달할 가중치 옵션이 확실하지 않은 경우 코드 편집기에서 TypeScript 오류를 확인하자.
- Google Fonts 웹사이트를 방문하여 Lusitana를 검색하여 사용 가능한 옵션을 확인하면된다.
- 여러 폰트 추가 및 전체 옵션 목록에 대한 설명서를 참고하자.
app/ui/fonts.ts
import { Inter, Lusitana } from 'next/font/google';
export const inter = Inter({ subsets: ['latin'] });
export const lusitana = Lusitana({
weight: ['400', '700'],
subsets: ['latin'],
});
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';
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>
// ...
);
}
마지막으로 <AcmeLogo /> 컴포넌트도 Lusitana를 사용하고, 오류를 방지하기 위해 주석 처리되었다. 이제 주석 처리를 해제할 수 있다.
/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="Screenshots of the dashboard project showing desktop version"
/>
그러나 이는 수동으로 다음을 수행해야 함을 의미한다.
이미지가 다양한 화면 크기에 반응하는지 확인하자.
- 다양한 장치에 대한 이미지 크기를 지정한다.
- 이미지가 로드될 때 레이아웃이 바뀌는 것을 방지한다.
- 사용자 뷰포트 외부에 있는 지연 로드 이미지이다.
이미지 최적화는 그 자체로 전문 분야로 간주될 수 있는 웹 개발의 큰 주제이다. 이러한 최적화를 수동으로 구현하는 대신 next/image 컴포넌트를 사용하여 이미지를 자동으로 최적화할 수 있다.
<Image> 컴포넌트
<Image> 컴포넌트는 HTML <img> 태그의 확장이며 다음과 같은 자동 이미지 최적화 기능이 제공된다.
- 이미지가 로드될 때 자동으로 레이아웃 이동을 방지한다.
- 뷰포트가 더 작은 장치에 큰 이미지가 전달되는 것을 방지하기 위해 이미지 크기를 조정한다.
- 기본적으로 이미지 지연 로딩(이미지가 뷰포트에 들어갈 때 로드됨)을 지원한다.
- 브라우저가 지원하는 경우 WebP 및 AVIF와 같은 최신 형식으로 이미지를 제공한다.
데스크탑 히어로 이미지 추가
<Image> 컴포넌트를 사용해 보겠다. /public 폴더를 살펴보면 Hero-desktop.png와 Hero-mobile.png라는 두 개의 이미지가 있는 것을 볼 수 있다. 이 두 이미지는 완전히 다르며 사용자의 기기가 데스크톱인지 모바일인지에 따라 표시된다.
/app/page.tsx 파일의 next/image에서 컴포넌트를 가져온다. 그런 다음 주석 아래에 이미지를 추가한다.
/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="Screenshots of the dashboard project showing desktop version"
/>
</div>
//...
);
}
여기서는 너비를 1000으로, 높이를 760픽셀로 설정한다. 레이아웃 변경을 방지하기 위해 이미지의 너비와 높이를 설정하는 것이 좋고, 따라서 소스 이미지와 가로세로 비율이 동일해야한다.
또한 모바일 화면의 DOM에서 이미지를 제거하기 위해 숨겨진 클래스와 데스크톱 화면에 이미지를 표시하기 위해 md:block이 있음을 알 수 있다.
이제 웹페이지는 다음과 같을 것이다.
연습문제: 모바일 히어로 이미지 추가하기
방금 추가한 이미지 아래에 Hero-mobile.png에 대한 또 다른 <Image> 컴포넌트를 추가합니다.
이미지의 너비는 560픽셀, 높이는 620픽셀이어야 한다.
모바일 화면에는 표시되고 데스크탑에서는 숨겨져야 하고, 개발 도구를 사용하여 데스크탑과 모바일 이미지가 올바르게 교체되었는지 확인할 수 있다.
준비가 되면 아래 코드 조각을 확장하여 솔루션을 확인해보자.
/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="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>
//...
);
}
이제 웹페이지에서 사용자 정의 폰트와 히어로 이미지를 볼 수 있다.
관련 자료
원격 이미지 최적화 및 로컬 글꼴 파일 사용을 포함하여 이러한 주제에 대해 더 많은 내용을 배울 수 있다. 폰트와 이미지에 대해 더 자세히 알아보려면 다음을 참조하자.
- Image Optimization Docs
- Font Optimization Docs
- Improving Web Performance with Images (MDN)
- Web Fonts (MDN)
'Next.js 개발 가이드 > 06. Learn Next.js 공식 가이드' 카테고리의 다른 글
06. 데이터베이스 설정 (0) | 2023.12.23 |
---|---|
05. 페이지간 이동 (0) | 2023.12.22 |
04. 레이아웃 및 페이지 만들기 (0) | 2023.12.20 |
02. CSS 스타일링 (0) | 2023.12.20 |
01. 시작 (0) | 2023.12.20 |