-
Notifications
You must be signed in to change notification settings - Fork 1
프로필 이미지 최적화 개선 (Next Image 업로드 전처리)
FE_juncci edited this page Mar 5, 2026
·
1 revision
문제
-
/me페이지 LCP 후보 이미지가 1025×1024 PNG(약 944KB) 로 전달됨 - 표시 크기(약 96~112px)에 비해 원본 크기가 과도
- Lighthouse
Improve image delivery경고 발생 - S3 캐시 정책이 없으면 반복 방문 시 매번 다운로드
해결
-
next/image원격 이미지 최적화 경로 활성화 - 프로필 업로드 시 256×256 WebP(quality 0.82) 로 전처리
- LCP 이미지에 priority + fetchPriority="high" + loading="eager" 적용
-
/_next/image경유 구조에서 불필요한 S3 preconnect 제거
결과
- 이후 업로드되는 프로필 이미지 용량 대폭 감소
- LCP 이미지 디스커버리 및 로드 우선순위 개선
- 기존 대용량 이미지는 재업로드 또는 백필 전까지 유지
| Metric | 개선 전 (추정) | 개선 후 | 개선율 |
|---|---|---|---|
| FCP | ~0.8s | 0.3s | ⬆ 62% 개선 |
| LCP | ~2.5s | 1.8s | ⬆ 28% 개선 |
| TBT | ~50ms | 0ms | ⬆ 100% 개선 |
| CLS | ~0.08 | 0.017 | ⬆ 79% 개선 |
| Speed Index | ~2.0s | 1.0s | ⬆ 50% 개선 |
| 항목 | 개선 전 | 개선 후 | 절감 |
|---|---|---|---|
| 프로필 이미지 | 944 KB | 13 KB | ⬇ 98.6% 감소 |
| 추가 이미지 | 900KB+ | 8 KB | ⬇ 99% 감소 |
| 전체 이미지 요청 | ~950KB | ~30KB | ⬇ 약 97% 감소 |
기존 /me 페이지에서는 next/image를 사용하고 있었지만, 실제로는 대용량 원본 이미지가 그대로 전달되는 상황이 발생하고 있었습니다.
대표적인 Lighthouse 경고
Improve image delivery
LCP request discovery
Use efficient cache lifetimes
문제의 핵심
작게 표시되는 프로필 이미지를
대용량 원본으로 전달하고 있음
예시
표시 크기 : 96px
원본 크기 : 1025px PNG
용량 : 약 944KB
1️⃣ LCP 후보 이미지의 다운로드 크기 감소
2️⃣ 새로 업로드되는 프로필 이미지를 일관된 규격으로 관리
3️⃣ Next.js 이미지 optimizer 파이프라인 정상 사용
4️⃣ 운영 환경에서 캐시 정책 적용 가능하도록 구조 정리
| 전략 | 특징 |
|---|---|
| 표시 시점 최적화만 | Next optimizer 의존 |
| 업로드 시점 전처리 | 원본 자체를 줄임 |
표시 시점만 최적화하면
- 런타임 환경
- 배포 설정
- optimizer 동작 여부
에 따라 효과 편차가 발생할 수 있음
표시 시점 + 업로드 시점
이중 최적화
- 표시 시점 →
next/image - 업로드 시점 → 256px WebP 전처리
- 원본 자체를 작게 만들면 어떤 경로로 전달돼도 기본 비용이 낮음
- 런타임 최적화 실패 시 최악 상황 방지
| 방식 | 특징 |
|---|---|
| lazy | 초기 로딩 지연 가능 |
| eager | 초기 네트워크 우선 로드 |
LCP 후보는 lazy일 경우
이미지 발견 → 로드 시작
순서가 늦어질 수 있음
priority
fetchPriority="high"
loading="eager"
- 초기 HTML 단계에서 이미지 디스커버리 강화
- 브라우저 스케줄러에 우선순위 신호 전달
- LCP 지연 가능성 감소
이미지 요청 흐름
브라우저
↓
/_next/image
↓
Next 서버
↓
S3
즉 브라우저는 먼저 1st-party origin에 연결
S3 preconnect → 실제 사용되지 않는 연결
Lighthouse
unused preconnect
S3 preconnect 제거
- 실제 요청 흐름과 맞지 않는 힌트 제거
- 불필요 네트워크 연결 방지
파일
next.config.mjs
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'refit-storage-prod.s3.ap-northeast-2.amazonaws.com',
pathname: '/**',
},
],
formats: ['image/avif', 'image/webp'],
}의도
- S3 이미지를 Next optimizer가 처리하도록 허용
- WebP / AVIF 변환 가능성 확보
파일
src/widgets/me/ui/MyPage.tsx
<Image
src={user.profile_image_url ?? defaultUserImage}
alt="프로필"
width={112}
height={112}
priority
fetchPriority="high"
loading="eager"
sizes="96px"
/>효과
- LCP 이미지 늦은 로딩 방지
- 초기 렌더 단계에서 네트워크 우선순위 확보
파일
useMyPageEditProfileImage.client.ts
const processed = await processImageFile(file, {
maxWidth: 256,
maxHeight: 256,
mimeType: 'image/webp',
quality: 0.82,
});
normalizedFile = processed.size <= file.size ? processed : file;의도
- 업로드 시 이미지 크기 표준화
- 변환 실패 시 fallback
- 변환 후 오히려 커지면 원본 유지
파일
MyPageEdit.tsx
<Image
src={profileImagePreview ?? profileImageUrl ?? defaultUserImage}
alt="프로필 이미지"
width={80}
height={80}
unoptimized={!!profileImagePreview}
sizes="80px"
/>의도
- blob preview는 optimizer 대상이 아님
- 원격 URL은 optimizer 경로 유지
파일
layout.tsx
// removed
<link rel="preconnect" href="https://refit-storage-prod.s3.ap-northeast-2.amazonaws.com" />
<link rel="dns-prefetch" href="https://refit-storage-prod.s3.ap-northeast-2.amazonaws.com" />효과
- 실제 요청 흐름과 맞지 않는 힌트 제거
- 새로 업로드되는 프로필 이미지
-
/me,/me/edit프로필 이미지 렌더링
- 기존 S3에 저장된 대용량 이미지
- S3 캐시 정책 (인프라 영역)
중요 포인트
기존 이미지는 그대로 남아있기 때문에
Lighthouse에서 큰 이미지가 계속 보일 수 있음
권장 캐시 정책
Cache-Control: public, max-age=31536000, immutable
운영 전략
- 파일명 해시 기반 버저닝 유지
- 캐시 무효화 대신 새 키 배포 전략
장점
- 이미지 전송 용량 감소
- LCP 안정성 개선
- 업로드 자산 품질 표준화
고려사항
- 클라이언트 전처리로 업로드 직전 CPU 사용 증가
- 기존 이미지 백필 없으면 개선 효과가 점진적
-
/_next/image최적화 경로 정상 동작 필요
이번 개선은 렌더링 시점 최적화와 자산 생성 시점 최적화를 동시에 적용한 구조 개선입니다.
핵심은 다음 한 문장으로 요약됩니다.
프로필 이미지를 업로드 시점에서 작게 만들고,
화면에서는 LCP 우선순위를 명확히 부여한다.