API
API 연동 중 빌드 시간이 갑자기 길어질 때
빌드 시간이 예상보다 오래 걸리면, 보통 API 호출이 병렬이 아닌 직렬로 처리되거나 타임아웃이 없어서다.
Next.js에서 정적 페이지 생성 중 데이터를 외부 API에서 가져올 때, 어느 순간부터 빌드가 엄청 오래 걸리는 경험을 했을 것이다. 특히 페이지 수가 많다면 더 심하다. 원인은 대부분 API 호출이 하나씩 순차적으로 실행되기 때문이다.
빌드 시간 측정하고 기록하기
먼저 빌드가 정확히 어디서 오래 걸리는지 확인한다.
time npm run build
# 또는 프로파일링
NODE_OPTIONS="--prof" npm run build
node --prof-process isolate-*.log > profile.txt
이전 빌드와 비교해서 어디서 시간이 증가했는지 찾는다.
API 호출이 병렬인지 직렬인지 확인
페이지 데이터를 가져올 때 가장 흔한 실수가 for 루프로 하나씩 호출하는 것이다.
// 나쁜 예: 직렬 호출 (10개의 API 호출 = N배 느림)
export async function generateStaticParams() {
const ids = Array.from({ length: 10 }, (_, i) => i + 1);
const pages = [];
for (const id of ids) {
const data = await fetch(`/api/items/${id}`);
pages.push(id);
}
return pages;
}
// 좋은 예: 병렬 호출 (모든 호출이 동시 실행)
export async function generateStaticParams() {
const ids = Array.from({ length: 10 }, (_, i) => i + 1);
const pages = await Promise.all(
ids.map(id => fetch(`/api/items/${id}`))
);
return pages;
}
동적 경로의 수 줄이기
모든 항목마다 페이지를 생성하지 않고, 필요한 것만 생성할 수도 있다.
export async function generateStaticParams() {
// 상위 100개만 정적 생성, 나머지는 온디맨드
const popular = await fetch('/api/items?popular=true&limit=100');
return popular.map(item => ({
id: item.id.toString(),
}));
}
// next.config.js에서 ISR 활성화
module.exports = {
experimental: {
isrMemoryCacheSize: 50 * 1024 * 1024, // 50MB
},
};
API 타임아웃 설정
느린 API 호출로 인해 전체 빌드가 멈추지 않도록 타임아웃을 설정한다.
export async function getStaticProps({ params }) {
try {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 5000); // 5초 타임아웃
const data = await fetch(`/api/items/${params.id}`, {
signal: controller.signal,
});
clearTimeout(timeout);
return {
props: { data },
revalidate: 3600, // 1시간마다 재검증
};
} catch (error) {
// 실패하면 폴백
return {
notFound: true,
revalidate: 60, // 1분 후 다시 시도
};
}
}
빌드 캐시 활용
Dependencies를 명시하지 않으면 매번 모든 페이지를 다시 빌드한다. Incremental Static Regeneration을 활용하면 변경된 것만 다시 빌드한다.
export async function getStaticProps() {
const data = await fetch('/api/data');
return {
props: { data },
revalidate: 1, // 매 요청마다 재검증 가능
};
}
데이터 소스 고려
API 호출 대신 로컬 데이터베이스나 파일을 읽으면 빌드 시간을 크게 줄일 수 있다.
import { db } from '@/lib/db';
export async function generateStaticParams() {
// 외부 API 대신 로컬 DB 쿼리
const items = await db.items.findMany({ select: { id: true } });
return items.map(item => ({
id: item.id.toString(),
}));
}
체크리스트
time npm run build로 빌드 시간을 측정한다.- 데이터 페칭이
Promise.all로 병렬 실행되는지 확인한다. - 동적 경로의 수를 줄일 수 있는지 검토한다.
- API 호출에 타임아웃을 설정한다.
- ISR로 모든 페이지를 다시 빌드하지 않도록 한다.
- 가능하면 외부 API 대신 로컬 데이터를 사용한다.
빌드 시간이 길어지는 문제는 보통 직렬 호출을 병렬로 바꾸는 것만으로도 크게 개선된다.