배경
백엔드는 Google Places 사진을 요청할 때 고정된 크기로 가져오고 있다.
하지만 계획 장소 카드에서는 해당 사진을 다시 next/image로 렌더링하면서 Vercel Image Optimization을 한 번 더 거친다.
- 운영 환경의
/_next/image 요청에서 402 Payment Required 발생
- 응답 헤더:
X-Vercel-Error: OPTIMIZED_IMAGE_REQUEST_PAYMENT_REQUIRED
- Vercel Image Transformation 월 한도 소진 확인
현재 동작
백엔드
- Google 사진을 고정 크기로 요청
- 프론트엔드에 Google 사진 URL을 전달
프론트엔드
계획 장소 사진:
<Image
src={resolvedPhotoUrl}
alt={place.title}
fill
sizes="96px"
className="object-cover"
/>
관련 위치:
src/app/(main)/plan/_components/itinerary/PlanPlaceCard.tsx
src/hooks/usePlanPlaceCardPhoto.ts
src/lib/places/place-photo-refresh.ts
next.config.ts
현재 next.config.ts에서 deviceSizes, imageSizes, qualities를 별도로 제한하지 않아 Next.js 기본 이미지 크기 목록을 사용한다.
설치된 Next.js 16.2.1 기준으로 fill + sizes="96px"는 다음 15개 폭 후보를 srcset에 생성한다.
32, 48, 64, 96, 128, 256, 384,
640, 750, 828, 1080, 1200, 1920, 2048, 3840
브라우저가 모든 후보를 요청하는 것은 아니지만, 화면 크기와 DPR에 따라 서로 다른 w 값이 요청된다. Vercel의 원격 이미지 캐시 키에는 url, w, q, Accept가 포함되므로 각 조합의 cache MISS/STALE가 별도 transformation으로 집계된다.
문제점
1. 불필요한 이중 리사이징
백엔드가 이미 고정 크기로 제한한 사진을 Vercel이 다시 여러 폭으로 변환한다. 고정된 96px 장소 썸네일에서 생성되는 후보 수에 비해 실질적인 최적화 이득이 작을 수 있다.
2. Google 사진 URL 갱신으로 인한 캐시 분산
handlePlacePhotoImageError는 이미지 로딩 실패 시 사진 URL을 갱신한다.
Vercel 402도 이미지 오류로 처리되므로 다음 흐름이 발생할 수 있다.
- 기존 Google URL로
/_next/image 요청
- Vercel 402 반환
- 프론트엔드에서 Google 사진 URL 갱신
- 변경된 URL로 새로운
/_next/image 요청
- 새로운 원본 캐시 키가 생성되지만 한도 초과로 다시 실패
백엔드에서 요청하는 픽셀 크기가 동일하더라도 절대 URL이 바뀌면 Vercel에서는 별도 원본으로 취급한다.
3. 이중 압축 가능성
Google에서 이미 리사이징·압축된 사진을 Vercel이 WebP 품질 75로 다시 변환한다. 원본이 작은 경우 전송량 절감보다 추가 압축에 따른 화질 저하가 커질 수 있다.
4. 사용자 영향
- 계획 장소 사진이 표시되지 않고 alt 텍스트 또는 fallback이 노출됨
- 이미지 실패 시 불필요한 사진 URL 갱신 요청 발생
- 새로운 사진 조합이 계속 생성되어 Image Transformation 사용량 증가
개선 방향
고정 크기 장소 썸네일에는 아래 방향을 우선 검토한다.
권장안: Google 장소 사진의 Vercel 최적화 우회
- 백엔드 Google 사진 크기를 실제 표시 크기 × DPR 2 수준으로 고정
- 96px 표시 기준 약 192px 또는 256px
- 계획 장소 사진에
unoptimized 적용 또는 네이티브 img 사용
- CSS에서는 기존과 같이 96×96px로 표시
- Google 사진 URL을 가능한 한 안정적으로 유지
- Vercel 402와 원본 Google 사진 오류를 구분하여, 최적화 서비스 오류에서는 사진 URL을 갱신하지 않도록 처리
대안: Vercel 최적화 유지
- 고정 썸네일에는
fill + sizes 대신 명시적인 width={96}, height={96} 사용
imageSizes를 실제 필요한 1x/2x 폭으로 제한
- 예: 96px, 192px
- 사진 URL 갱신으로 캐시 키가 계속 변경되지 않도록 원본 URL 안정화
완료 조건
참고
배경
백엔드는 Google Places 사진을 요청할 때 고정된 크기로 가져오고 있다.
하지만 계획 장소 카드에서는 해당 사진을 다시
next/image로 렌더링하면서 Vercel Image Optimization을 한 번 더 거친다./_next/image요청에서402 Payment Required발생X-Vercel-Error: OPTIMIZED_IMAGE_REQUEST_PAYMENT_REQUIRED현재 동작
백엔드
프론트엔드
계획 장소 사진:
관련 위치:
src/app/(main)/plan/_components/itinerary/PlanPlaceCard.tsxsrc/hooks/usePlanPlaceCardPhoto.tssrc/lib/places/place-photo-refresh.tsnext.config.ts현재
next.config.ts에서deviceSizes,imageSizes,qualities를 별도로 제한하지 않아 Next.js 기본 이미지 크기 목록을 사용한다.설치된 Next.js 16.2.1 기준으로
fill + sizes="96px"는 다음 15개 폭 후보를srcset에 생성한다.브라우저가 모든 후보를 요청하는 것은 아니지만, 화면 크기와 DPR에 따라 서로 다른
w값이 요청된다. Vercel의 원격 이미지 캐시 키에는url,w,q,Accept가 포함되므로 각 조합의 cache MISS/STALE가 별도 transformation으로 집계된다.문제점
1. 불필요한 이중 리사이징
백엔드가 이미 고정 크기로 제한한 사진을 Vercel이 다시 여러 폭으로 변환한다. 고정된 96px 장소 썸네일에서 생성되는 후보 수에 비해 실질적인 최적화 이득이 작을 수 있다.
2. Google 사진 URL 갱신으로 인한 캐시 분산
handlePlacePhotoImageError는 이미지 로딩 실패 시 사진 URL을 갱신한다.Vercel 402도 이미지 오류로 처리되므로 다음 흐름이 발생할 수 있다.
/_next/image요청/_next/image요청백엔드에서 요청하는 픽셀 크기가 동일하더라도 절대 URL이 바뀌면 Vercel에서는 별도 원본으로 취급한다.
3. 이중 압축 가능성
Google에서 이미 리사이징·압축된 사진을 Vercel이 WebP 품질 75로 다시 변환한다. 원본이 작은 경우 전송량 절감보다 추가 압축에 따른 화질 저하가 커질 수 있다.
4. 사용자 영향
개선 방향
고정 크기 장소 썸네일에는 아래 방향을 우선 검토한다.
권장안: Google 장소 사진의 Vercel 최적화 우회
unoptimized적용 또는 네이티브img사용대안: Vercel 최적화 유지
fill + sizes대신 명시적인width={96},height={96}사용imageSizes를 실제 필요한 1x/2x 폭으로 제한완료 조건
참고