코드 리뷰 및 최적화
북스토어 코드를 책임·가독성·안전성 기준으로 리뷰하고 이미지·폰트·데이터·번들 성능을 측정해 개선합니다.
실전 프로젝트를 마무리할 때 코드 리뷰와 최적화는 꼭 거쳐야 하는 단계입니다. 기능이 동작하는 수준을 넘어 코드를 더 효율적이고 유지보수하기 쉽게 만들고, 성능까지 끌어올리는 데 직접 도움이 되기 때문입니다. 이 글에서는 코드 리뷰의 핵심 관점과 일반적인 최적화 기법, 그리고 성능을 측정하고 개선하는 도구의 활용법을 다룹니다.
코드 리뷰의 중요성 및 방법
코드 리뷰는 바뀐 코드의 정확성, 유지보수성, 성능에 주는 영향, 테스트 범위를 검토하는 과정입니다. 리뷰에서는 단순한 스타일보다 요구사항을 충족하는지와 회귀가 생길 위험을 먼저 확인합니다.
북스토어의 변경을 리뷰할 때는 무엇을 개선하려는지 한 가지로 좁혀 보는 것이 좋습니다. 예를 들어 도서 목록 이미지의 다운로드가 큰 문제라면 이미지의 표시 폭과 요청 크기부터 확인하고, 검색 버튼의 반응이 느리다면 같은 검색 동작의 실행 기록과 요청 시간을 봅니다. 코드 줄 수가 줄거나 린트가 통과했다는 사실만으로 두 문제가 해결된 것은 아닙니다. 기능 조건도 함께 지켜야 하므로, 장바구니 버튼을 숨겨 렌더링을 줄이더라도 서버의 주문 권한 검사를 생략한다면 올바른 최적화가 아닙니다.
코드 리뷰의 이점
코드 리뷰를 하면 개발 초기에 논리적 오류나 잠재적인 버그를 찾아 고치는 비용을 줄일 수 있고, 가독성, 유지보수성, 재사용성 면에서 더 나은 코드를 쓰도록 이끕니다. 팀원끼리 기술과 모범 사례를 나누며 서로의 강점과 약점을 알게 되어 배울 기회가 생기고, 코딩 스타일과 아키텍처 패턴 같은 프로젝트 전반의 일관성을 유지하는 데도 도움이 됩니다. 잠재적인 보안 취약점을 찾아 개선할 수도 있습니다.
효과적인 코드 리뷰 방법
먼저 기능의 정확성, 성능, 보안, 가독성 가운데 무엇을 중점적으로 볼지 미리 정합니다. 한 번에 너무 많은 코드를 리뷰하지 않도록 PR(Pull Request)을 작게 유지하는 것이 좋고, 비판보다 건설적인 피드백에 집중하며 서로 존중하는 태도로 의견을 나눕니다. 일반적인 코딩 표준, 보안 고려사항, 성능 지침을 담은 체크리스트를 쓰면 빠뜨리지 않고 검토할 수 있습니다. ESLint나 Prettier 같은 린터(linter)와 포매터(formatter)로 기본적인 스타일과 잠재적 오류를 자동으로 검사하면 사람이 하는 리뷰의 부담이 줄어들며, 관련 테스트 코드가 충분한지, 테스트가 통과하는지도 확인합니다.
Next.js 성능 최적화 기법
Next.js는 기본적으로 빠른 성능을 제공하지만, 복잡한 애플리케이션에서는 추가로 최적화가 필요합니다. 이 절에서는 북스토어에 바로 적용할 수 있는 다섯 가지 기법을 차례로 봅니다. 앞의 세 가지인 이미지, 폰트, 서드파티 스크립트는 브라우저가 내려받는 자원을 줄이거나 늦추는 방법이고, 뒤의 두 가지인 데이터 캐싱과 코드 분할은 서버와 번들이 하는 일을 줄이는 방법입니다. 어느 쪽이든 적용 전후를 같은 조건에서 측정해야 실제로 나아졌는지 말할 수 있으며, 측정 도구는 다음 절에서 다룹니다.
이미지 최적화 (next/image)
next/image는 이미지 최적화, 크기별 제공, 기본 지연 로딩을 지원하며, 그 효과는 이미지, 설정, 캐시, 네트워크에 따라 측정합니다. 일반 <img> 태그 대신 next/image를 쓰고 src와 의미에 맞는 alt를 지정하며, 이미지의 공간은 정적 import가 알아낸 크기, width와 height, 또는 fill 가운데 하나로 확보합니다. 다음 예시의 preload는 첫 화면의 주요 이미지에만 쓰는 선택이며, 모든 도서 카드에 붙이지 않습니다.
import Image from 'next/image';
function BookImage({ src, alt }: { src: string; alt: string }) {
return (
<Image
src={src}
alt={alt}
width={500}
height={600}
sizes="(max-width: 768px) 100vw, 500px"
style={{ width: '100%', height: 'auto' }}
placeholder="blur"
blurDataURL="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw=="
preload
/>
);
}위의 width와 height는 500:600이라는 비율을 알려 공간을 확보하고, CSS는 부모 폭에 맞춰 이미지를 보여 줍니다. sizes는 표시 폭을 브라우저에 알려 srcset 후보를 고르게 하는 힌트일 뿐, 이미지의 CSS 폭을 직접 정하지 않습니다. 작은 화면에서 실제 카드가 viewport 전체보다 좁다면 무조건 100vw를 쓰기보다 실제 레이아웃에 맞는 힌트를 씁니다. preload도 모든 이미지에 적용하면 같은 네트워크 자원을 두고 경쟁하므로, 첫 화면의 주요 이미지에 필요한지 측정합니다. 예제의 1픽셀 blurDataURL은 자리표시자의 동작을 보여 주려는 값이며, 실제 책 표지의 축소 미리보기가 아닙니다.
폰트 최적화 (next/font)
next/font는 글꼴 파일을 애플리케이션에서 직접 제공하고, 폰트 메트릭을 조정해 글꼴이 바뀔 때의 레이아웃 이동을 줄일 수 있습니다. next/font/google은 빌드할 때 파일을 가져오므로 브라우저가 Google에 직접 요청하지 않지만, 자체 호스팅된 폰트의 다운로드는 여전히 일어나며, 모든 원인의 CLS가 없어지지는 않습니다. next/font/google이나 next/font/local에서 폰트를 import해 씁니다.
import { Inter } from 'next/font/google';
import type { ReactNode } from 'react';
const inter = Inter({ subsets: ['latin'], display: 'swap' });
export default function RootLayout({ children }: { children: ReactNode }) {
return (
<html lang="ko" className={inter.className}>
<body>{children}</body>
</html>
);
}inter.className은 Inter 글꼴을 지정하는 클래스 이름이며, <html>에 붙였으므로 문서 전체에 적용됩니다. 12장에서 본 variable 방식과 달리 CSS 변수를 거치지 않고 바로 글꼴을 정하므로, 글꼴 하나만 쓰는 프로젝트에서는 이쪽이 간단합니다. display: 'swap'은 기본값과 같은 값을 명시한 것입니다.
스크립트 최적화 (next/script)
Google Analytics나 광고 스크립트 같은 서드파티 스크립트는 페이지 로딩 성능에 큰 영향을 줄 수 있으며, next/script 컴포넌트를 쓰면 스크립트를 불러오는 전략을 제어할 수 있습니다. beforeInteractive는 하이드레이션 전에 꼭 필요한 스크립트로 루트 레이아웃에 두고, 기본값인 afterInteractive는 페이지의 일부가 하이드레이션된 뒤에 불러옵니다. lazyOnload는 브라우저가 한가할 때 낮은 우선순위로 불러오며, worker는 웹 워커에서 불러오는 실험적 전략으로 App Router에서는 지원하지 않습니다.
import Script from 'next/script';
function MyAnalytics() {
return (
<>
<Script
src="https://www.googletagmanager.com/gtag/js?id=GA_MEASUREMENT_ID"
strategy="afterInteractive"
/>
<Script id="google-analytics" strategy="afterInteractive">
{`
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'GA_MEASUREMENT_ID');
`}
</Script>
</>
);
}첫 번째 Script는 외부 파일을 불러오고, 두 번째는 id를 붙인 인라인 스크립트입니다. 인라인 스크립트의 id는 Next.js가 그 스크립트를 추적하고 한 번만 넣는 데 쓰므로 생략하지 않습니다. 두 스크립트 모두 afterInteractive라서 첫 화면의 HTML 렌더링을 막지 않으며, GA_MEASUREMENT_ID는 실제 측정 ID로 바꿔야 하는 자리표시자입니다.
데이터 페칭 최적화 (캐싱 및 재검증)
Next.js의 캐싱 방식을 이해하고 효율적으로 씁니다. Data Cache에서는 Next.js 15 이후 fetch 응답이 기본적으로 캐시되지 않으므로, 반복해서 다시 쓸 데이터는 cache: 'force-cache'로 명시해 캐싱합니다. Next.js 16에서 cacheComponents: true를 켠 프로젝트라면 use cache 지시어도 쓸 수 있습니다. Full Route Cache는 정적으로 렌더링한 라우트의 HTML과 RSC Payload를 서버에 저장하며, 개발 모드에서는 페이지를 요청할 때 렌더링하므로 운영 환경의 캐시 동작과 구분해야 합니다.
ISR(Incremental Static Regeneration)을 쓰면 정적으로 만든 페이지를 유효 기간이 지난 뒤의 다음 요청을 계기로 재검증하거나, 명시적인 온디맨드 재검증으로 갱신할 수 있어 빌드 시간을 줄이면서도 최신 콘텐츠를 줄 수 있습니다. App Router에서는 fetch의 revalidate 옵션을 쓰고, 필요할 때 경로는 revalidatePath, 태그의 stale-while-revalidate 갱신은 revalidateTag(tag, 'max'), 서버 액션 안의 즉시 만료는 updateTag로 처리합니다.
다음 세그먼트 설정은 cacheComponents를 쓰지 않는 이전 캐시 모델용입니다. [id] 경로를 ISR로 처리하려면 generateStaticParams로 정적 경로를 준비하거나 빈 배열을 돌려주는 등 정적 생성 조건도 함께 갖춰야 합니다. 17장 2절의 주석 처리된 generateStaticParams를 그대로 둔 채 이 한 줄만 추가한다고 모든 요청이 캐시되지는 않습니다.
export const revalidate = 60; // 60초가 지난 뒤 다음 요청을 계기로 재검증/_next/static 아래의 해시 기반 빌드 자산은 오래 캐시하기에 알맞습니다. 반면 public 파일은 같은 이름으로 바뀔 수 있으므로, 필요한 캐시 헤더를 배포 환경에서 따로 정합니다.
ISR을 요청 시각으로 따라가 보겠습니다. cacheComponents를 쓰지 않고 정적 생성 조건을 갖춘 운영 라우트에서, 시각 0초에 만든 페이지가 있고 DB 값은 20초에 바뀌었다고 합시다. revalidate = 60만으로 60초 정각에 새 페이지를 만드는 타이머가 생기지는 않습니다. 만료 뒤의 첫 요청이 75초에 오면 기존 페이지를 먼저 주면서 재생성을 시작할 수 있고, 재생성이 성공한 뒤의 요청은 새 페이지를 받습니다. 실패하면 마지막으로 성공한 페이지를 유지하고 이후의 요청에서 다시 시도합니다. 재검증 전에도 재고가 반드시 최신이어야 하는 주문 검사는, 이 캐시된 화면의 값만 믿지 않고 서버에서 실제 업무 조건을 다시 확인합니다.
페이지 단위 분할과 지연 로딩 설계
Next.js는 페이지 단위의 코드 분할을 자동으로 처리합니다. 다만 공유 레이아웃과 공통 모듈, 미리 가져오는 경로도 함께 고려해야 하므로, 네트워크 요청이 현재 페이지의 고유한 코드에만 한정된다는 뜻은 아닙니다. 여기에 더해 특정 컴포넌트나 라이브러리를 필요할 때만 불러오도록 동적 import를 쓸 수 있으며, next/dynamic으로 클라이언트 컴포넌트나 무거운 라이브러리를 지연 로드합니다.
"use client";
// 무거운 차트 라이브러리를 동적으로 로드
import dynamic from 'next/dynamic';
import Loading from './Loading';
const DynamicChart = dynamic(() => import('../components/Chart'), {
loading: () => <Loading />, // 로딩 중 표시할 컴포넌트
ssr: false, // 이 컴포넌트는 클라이언트에서만 렌더링 (SSR 제외)
});
export default function MyDashboard() {
return (
<div>
<DynamicChart />
</div>
);
}SSR에서 빼는 것과 쓰는 시점까지 미루는 것은 다릅니다. 위의 MyDashboard는 처음부터 DynamicChart를 렌더링하며, ssr: false는 서버 사전 렌더링을 빼는 선택일 뿐 클릭 조건을 만들지는 않습니다. 차트를 열 때만 렌더링하려면 다음처럼 별도의 표시 조건을 둡니다. Chart와 Loading은 앞 예제처럼 프로젝트에 준비되어 있다고 전제합니다.
'use client';
import { useState } from 'react';
import dynamic from 'next/dynamic';
import Loading from './Loading';
const DynamicChart = dynamic(() => import('./Chart'), {
loading: () => <Loading />,
ssr: false,
});
export default function OnDemandDashboard() {
const [open, setOpen] = useState(false);
return (
<section>
<button onClick={() => setOpen(true)}>차트 열기</button>
{open && <DynamicChart />}
</section>
);
}여기서 첫 렌더링에는 버튼만 들어가고, open이 true인 렌더링부터 차트가 들어갑니다. 코드 분할, 다운로드, 차트 실행 시간이 실제로 어떻게 바뀌었는지는 빌드와 Network, Performance 기록으로 확인합니다. 한 조건이 false라는 것만으로 다른 공통 모듈, 프리페치, 네트워크 비용까지 모두 사라졌다고 단정하지 않습니다.
성능 측정 및 디버깅 도구
Next · Performance evidence scopes
성능 질문에 맞는 측정 근거
첫 화면 진단, 실행 원인, 실제 사용자 분포, 번들 크기는 서로 다른 근거입니다. 한 도구의 점수나 크기만으로 북스토어의 전체 성능과 원인을 확정하지 않습니다.
| 대상 | 먼저 볼 근거 | 이것만으로 알 수 없는 것 |
|---|---|---|
| 첫 화면 지연 | Lighthouse의 실험실 LCP·CLS·TBT | 실제 사용자의 모든 상호작용 지연 |
| 실행 원인 | DevTools Performance의 작업·렌더링 기록 | 전체 사용자 집단의 분포 |
| 실사용 환경 | Speed Insights 등의 실제 사용자 지표 | 느린 함수의 구체적인 실행 원인 |
| 번들 크기 | 사용한 번들러의 모듈별 분석 | 모듈 크기만으로 실제 실행 시간 |
- 대상: 첫 화면 지연
- 먼저 볼 근거
- Lighthouse의 실험실 LCP·CLS·TBT
- 이것만으로 알 수 없는 것
- 실제 사용자의 모든 상호작용 지연
- 대상: 실행 원인
- 먼저 볼 근거
- DevTools Performance의 작업·렌더링 기록
- 이것만으로 알 수 없는 것
- 전체 사용자 집단의 분포
- 대상: 실사용 환경
- 먼저 볼 근거
- Speed Insights 등의 실제 사용자 지표
- 이것만으로 알 수 없는 것
- 느린 함수의 구체적인 실행 원인
- 대상: 번들 크기
- 먼저 볼 근거
- 사용한 번들러의 모듈별 분석
- 이것만으로 알 수 없는 것
- 모듈 크기만으로 실제 실행 시간
같은 페이지·기기·네트워크 조건에서 변경 전후를 비교합니다. 이 표는 도구의 범위를 설명하며 북스토어의 측정 결과를 담지 않습니다.
위 표는 성능에 관한 질문마다 먼저 봐야 할 측정 자료와, 그 자료만으로는 알 수 없는 것을 정리합니다. 첫 화면의 지연은 Lighthouse의 실험실 LCP, CLS, TBT를 먼저 보지만, 이것만으로 실제 사용자의 모든 상호작용 지연을 알 수는 없습니다. 실행 원인은 DevTools Performance의 작업과 렌더링 기록으로 찾지만, 전체 사용자 집단의 분포는 알 수 없습니다. 실사용 환경은 Speed Insights 같은 실제 사용자 지표로 보지만, 느린 함수의 구체적인 원인까지는 알려 주지 않습니다. 번들 크기는 쓰는 번들러의 모듈별 분석으로 보지만, 모듈 크기만으로 실제 실행 시간을 알 수는 없습니다.
표의 아래 설명처럼 같은 페이지, 기기, 네트워크 조건에서 변경 전후를 비교해야 하며, 이 표는 도구의 범위를 설명할 뿐 북스토어를 측정한 결과를 담지 않습니다. 변경 전후에는 라우트, 데이터 수, 로그인 상태, 캐시를 처음 쓰는 상태와 다시 쓰는 상태, 기기와 네트워크를 함께 기록하고, 원인의 위치를 좁힌 뒤 같은 조건에서 반복 측정해 개선 여부를 판단합니다.
Lighthouse 기준선 진단
Lighthouse는 성능, 접근성, 권장 사항, SEO를 진단합니다. PWA 카테고리는 Lighthouse 12에서 빠졌으므로, 설치와 서비스 워커의 동작은 따로 확인합니다. Chrome 개발자 도구의 Lighthouse 탭에서 Analyze page load를 실행하면 되며, 로딩 검사에서 LCP, CLS, TBT 등을 확인합니다. 사용자 입력이 없는 한 번의 로딩 검사로는 INP를 측정하지 못하므로, 상호작용 기록이나 실제 사용자 지표를 함께 씁니다.
Chrome DevTools (성능 탭, 네트워크 탭)
Performance 탭은 런타임 성능을 기록하고 분석해, JavaScript 실행, 렌더링, 레이아웃 계산에서 생기는 병목을 눈으로 파악하게 합니다. Network 탭은 페이지를 불러올 때 생기는 JS, CSS, 이미지, API 호출 같은 모든 네트워크 요청의 크기, 로딩 시간, 캐싱 여부를 보여 주어 불필요한 요청이나 큰 자원을 찾게 돕습니다.
Vercel Analytics / Speed Insights
Vercel은 배포한 Next.js 애플리케이션에 내장된 분석과 성능 지표를 제공합니다. Vercel Analytics는 방문자 수, 페이지 뷰, 트래픽 출처 같은 일반적인 웹사이트 분석 데이터를 제공합니다. Speed Insights는 실제 사용자 데이터(RUM, Real User Monitoring)를 바탕으로 Core Web Vitals를 포함한 페이지 성능 지표를 모니터링하며, Vercel 대시보드에서 기능을 켜고 @vercel/speed-insights를 설치한 뒤 루트 레이아웃에 <SpeedInsights />를 렌더링해야 데이터를 모읍니다.
번들 분석기 (Bundle Analyzer)
@next/bundle-analyzer는 Webpack 빌드용 도구이므로, 다음 예제는 Next.js 16에서도 --webpack으로 빌드할 때 적용합니다. Turbopack을 분석할 때는 설치한 Next.js가 제공하는 별도의 분석 기능을 쓰며, 이 플러그인의 보고서와 섞지 않습니다. 번들 분석기로 어떤 모듈이 가장 많은 공간을 차지하는지 파악해 불필요한 코드나 큰 라이브러리를 찾아 없앨 수 있습니다.
먼저 npm install --save-dev @next/bundle-analyzer로 설치하고, next.config.js에 설정합니다. 다음은 CommonJS 형식의 예시이며, 기존의 next.config.ts 등과 두 설정 파일을 함께 두지 말고 고른 형식의 한 파일에 기존 설정을 보존해 합칩니다.
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true',
});
module.exports = withBundleAnalyzer({
// Next.js config options
});macOS와 Linux에서는 ANALYZE=true npm run build -- --webpack으로, PowerShell에서는 $env:ANALYZE='true'; npm run build -- --webpack으로 실행합니다. 빌드가 끝나면 번들 분석 보고서가 웹 브라우저에 열립니다. 바꾼 범위와 해결하려는 병목을 연결하고, 수정한 뒤에는 그 기능을 같은 조건에서 측정한 값을 함께 확인합니다.
정리
- 코드 리뷰는 요구사항 충족과 회귀 위험을 먼저 보고, 개선하려는 문제를 한 가지로 좁혀 그 문제에 맞는 자료로 확인합니다.
next/image,next/font,next/script는 각각 이미지, 폰트, 서드파티 스크립트의 로딩을 최적화하지만, 효과는 실제 조건에서 측정합니다.- Next.js 15 이후
fetch는 기본으로 캐시되지 않으며, ISR은 만료 뒤의 요청을 계기로 재생성하고 실패하면 이전 페이지를 유지합니다. ssr: false는 서버 사전 렌더링을 뺄 뿐이고, 쓰는 시점까지 미루려면 별도의 표시 조건을 둡니다.- Lighthouse, DevTools Performance, Speed Insights, 번들 분석기는 서로 다른 질문에 답하므로, 한 도구의 결과로 전체 성능을 확정하지 않습니다.
연습 문제
1. 측정으로 말할 수 있는 범위
다음 값은 연습을 위한 가상의 측정입니다. 같은 기기와 네트워크에서 클라이언트 번들 크기는 200KB에서 160KB로 줄었지만, 같은 검색 동작의 상호작용 지연은 210ms에서 260ms로 늘었습니다. 각 원래 값을 기준으로 번들 감소율과 지연 증가율을 계산해 보세요. 작은 번들을 이유로 검색이 빨라졌다는 리뷰를 승인할 수 있을까요?
해설 보기
번들은 (200 - 160) ÷ 200 × 100 = 20% 줄었고, 지연은 50 ÷ 210 × 100 ≈ 23.81% 늘었습니다. 주어진 지연 측정은 나빠졌으므로, 검색이 빨라졌다는 결론을 뒷받침하지 않습니다. 다운로드 크기의 감소와 상호작용의 개선은 같은 결론이 아닙니다.
원인을 찾으려면 같은 검색 동작의 Performance 기록과 서버, DB 요청 시간을 이어서 보고, 같은 조건에서 반복 측정해 작업, 렌더링, 서버 요청 가운데 어느 비용이 늘었는지 확인합니다. 한 번의 측정으로 전체 사용자 분포나 장기적인 효과까지 확정하지 않습니다.
2. sizes와 실제 표시 폭
viewport 폭은 600px이고, 실제 책 카드의 이미지 표시 폭은 300px입니다. sizes="100vw"가 브라우저에 알려 주는 폭은 얼마일까요? 이 값이 표시 폭 300px을 CSS로 바꿀까요? 어떤 측정과 수정이 필요할까요?
해설 보기
100vw는 600px을 힌트로 알려 줍니다. CSS로 그 폭을 정하지는 않으므로, 실제 표시 폭은 여전히 300px입니다. 실제 레이아웃에 맞는 sizes를 쓰고, 브라우저가 고른 srcset 후보와 다운로드 크기를 비교합니다. 힌트가 두 배라고 실제 파일 크기도 정확히 두 배가 되는 것은 아니며, 기기의 픽셀 비율(DPR)과 후보 목록도 영향을 줍니다.
3. 두 대시보드의 차트 렌더링
MyDashboard와 OnDemandDashboard의 open=false 상태를 비교해 보세요. 둘 다 ssr: false인 차트를 쓴다는 이유로, 클릭 전에 차트를 렌더링하는 조건이 같을까요? 버튼을 눌러 open=true가 되면 무엇이 달라지고, 무엇은 아직 측정해야 할까요?
해설 보기
다릅니다. MyDashboard는 DynamicChart를 언제나 포함하지만, OnDemandDashboard는 open=false일 때 포함하지 않습니다. ssr: false는 서버 사전 렌더링을 빼는 조건일 뿐입니다. open=true가 되면 OnDemandDashboard도 차트를 포함하며, 실제 청크를 요청하는 시점과 크기, 첫 표시 시간, 상호작용 비용은 따로 측정해야 합니다.
4. ISR의 요청 시각 따라가기
본문의 ISR 조건에서 0초에 저장한 화면이 A이고, 20초에 DB 값이 B로 바뀌었습니다. 만료 뒤의 첫 요청은 75초이며 재생성은 76초에 성공합니다. 60초에 요청이 없을 때, 75초의 첫 응답, 77초의 응답을 판단해 보세요. 재생성이 실패했다면 마지막 답은 어떻게 달라질까요?
해설 보기
60초에는 요청이 없으므로 재검증이 시작되지 않습니다. 75초의 응답은 A이며, 이 요청이 백그라운드 재생성을 시작합니다. 재생성이 성공한 뒤인 77초의 응답은 B입니다. 재생성이 실패했다면 마지막으로 성공한 A를 유지하고, 이후의 요청을 계기로 다시 시도합니다. 화면의 캐시 정책과 주문할 때의 최신 재고 확인은 서로 다른 역할입니다.
Next.js의 최적화 기능과 측정 방법은 공식 문서의 Optimizing 안내와 Package Bundling 안내에서 확인할 수 있습니다.
다음 글 추가 기능 확장 제안에서는 북스토어에 더할 수 있는 기능과, 그 기능을 넣을 때 고려할 점을 살펴봅니다.