무료 Unsplash API는 1시간당 50회 호출 제한이 있어, 다음과 같은 UX 흐름에서 빠르게 한도에 도달하는 문제가 발생했다:
🔍 검색어 입력 시 → 실시간 API 호출
📜 스크롤 이동 시 → 무한스크롤 기반 추가 호출
⛔ 디바운싱을 적용해도 TanStack Query의 fetchNextPage와 중첩되어 호출량이 급증, 기능이 차단되는 상황이 빈번히 발생했다.
요청 수를 줄이기 위해, 서버에서 4페이지(50장 × 4)의 이미지를 한 번에 받아와 클라이언트에서 필터링하는 구조로 변경했다.
export async function GET() {
const allImages: UnsplashImage[] = [];
const pageRequests = Array.from({ length: 4 }, (_, i) =>
fetch(`${BASE_URL}?page=${i + 1}&per_page=50&client_id=${ACCESS_KEY}`).then((res) => res.json())
);
const results = await Promise.all(pageRequests);
for (const pageData of results) {
if (Array.isArray(pageData)) allImages.push(...pageData);
}
return NextResponse.json(allImages);
}
⚠️ 하지만 두 가지 문제가 발생했다
API 쿼터를 확장(프로덕션 승인)받은 뒤, 다시 검색어 기반 호출과 무한스크롤을 활성화하는 구조로 복구했다.
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const query = searchParams.get('query') || '';
const page = Number(searchParams.get('page') || '1');
const perPage = Number(searchParams.get('per_page') || 20);
if (page > MAX_UNSPALSH_API_PAGES) {
return NextResponse.json(
{ error: '요청 페이지 수 제한 초과' },
{ status: 429 }
);
}
const baseURL = query
? `https://api.unsplash.com/search/photos`
: `https://api.unsplash.com/photos`;
const url =
`${baseURL}?client_id=${ENV.UNSPLASH_ACCESS_KEY}` +
`&page=${page}&per_page=${perPage}` +
(query ? `&query=${encodeURIComponent(query)}` : '');
const res = await fetch(url, { next: { revalidate: 3600 } });
if (!res.ok) {
const errorData = await res.json().catch(() => null);
return NextResponse.json(
{ error: '이미지 불러오기 실패', details: errorData || res.statusText },
{ status: res.status }
);
}
const json = await res.json();
return query
? NextResponse.json({ results: json.results, total_pages: json.total_pages })
: NextResponse.json(json);
}
💡 고려 사항 및 결과
✅ 검색어 유무에 따라 search/photos 또는 photos API로 자동 분기
✅ TanStack Query 기반 무한스크롤 구조에 적합
✅ revalidate: 3600을 적용하여 1시간 단위 캐싱으로 서버 부담 최소화