정의
프레임워크의 내장 이미지 최적화를 못 쓰는 상황에서, 큰 원본 이미지를 화면에 필요한 작은 크기·가벼운 포맷으로 서버가 대신 바꿔 내려보내고 싶을 때 만드는 중간 서버(엔드포인트)다. 브라우저는 원본 대신 이 서버가 리사이즈·변환해 준 이미지를 받는다.
정확히 말하면, 서버 이미지 프록시는 클라이언트가 요청한 표시 크기에 맞게 upstream(원본 이미지가 있는 대상 서버) 이미지를 fetch → 리사이즈·포맷 변환(예: WebP, 용량이 작은 웹 이미지 포맷) → 응답하는 HTTP 엔드포인트다. 이렇게 포맷·크기를 바꾸는 것을 트랜스코딩이라 한다. CDN edge 캐시(사용자와 가까운 서버 노드에 결과를 저장해 재사용)와 함수 인메모리 LRU(Least Recently Used, 오래 안 쓴 항목부터 버리는 메모리 캐시)를 겹쳐 쓰고, miss(캐시에 없어 원본을 새로 처리하는 경우) 시 스트리밍 tee(하나의 데이터 스트림을 둘로 갈라 한쪽은 바로 응답, 한쪽은 캐시에 저장)로 TTFB(Time To First Byte, 요청 후 첫 바이트가 도착하기까지의 시간)를 줄인다.
왜 필요한가
프레임워크 images.unoptimized: true 설정이면 <Image>가 원본 PNG/JPEG를 그대로 내려보낼 수 있다. 캐러셀 썸네일처럼 화면상 작은 이미지는 서버에서 336672px WebP로 바꾸기만 해도 **전송 바이트가 수십수백 배** 줄고, LCP(Largest Contentful Paint, 화면에서 가장 큰 콘텐츠가 그려지는 시점 지표) 병목이 "이미지 다운로드"에서 "렌더"로 바뀌는 경우가 많다. 성능 A/B에서 전송 바이트가 localhost LCP보다 재현 가능한 headline(대표 지표)이 된다.
동작 원리
SSRF는 Server-Side Request Forgery의 약자로, 공격자가 url 파라미터에 내부망 주소를 넣어 서버가 대신 요청하게 만드는 취약점이다. allowlist(허용된 호스트 목록)로 이를 막는다.
| 층 | 역할 |
|---|---|
CDN (s-maxage) | cold start 이후 durable, 엣지 서빙 |
| 모듈 LRU | warm lambda 반복 hit, ephemeral |
클라이언트는 loader 대신 src를 프록시 URL로 직접 교체하는 편이 unoptimized 환경에서 결정적이다. <link rel="preload"> href도 동일 URL이어야 이중 다운로드를 막는다.
실무 적용
w는 고정 집합만 허용(400 otherwise) — DoS·캐시 키 폭발 방지.- Node runtime + sharp(또는 동급).
- MSW/E2E: mock URL이 라우트에 도달 못 하면 테스트 환경에서 프록시 우회 분기.
트레이드오프
| 선택 | 이득 | 비용 |
|---|---|---|
| 자체 프록시 | unoptimized 우회, 포맷·품질 통제 | 서버 CPU·SSRF 표면 |
toBuffer() 후 응답 | 구현 단순 | miss TTFB 증가 |
| 스트리밍 tee | 첫 바이트 빠름 | 스트림 중간 실패 시 캐시 스킵 로직 필요 |
| WebP q60 | 작은 바이트 | 품질·아티팩트 — 용도별 조정 |
사용하면 안 되는 경우
- 이미 CDN·이미지 CDN이 최적화·캐시를 제공하는데 중복 프록시만 추가.
- allowlist 없이 임의 URL fetch.
흔한 실수
- preload는 원본,
img src는 프록시 — preload 무효·이중 요청. - 캐시 키에 전체 presigned URL 포함 — 서명 로테이션마다 miss.
- localhost trace LCP만 headline — 실배포 CDN 이득은 더 큼.
관련 개념
- ssrf-url-fetch-proxy-guard — fetch 전 보안
- lqip-blur-placeholder-ssr — blur placeholder (LCP 지표와 별개)
- resource-priority — preload·fetchpriority
- largest-contentful-paint — LCP 병목 전환