React Native 에서 이미지 최적화 (2. URL 리사이징)

eeennsu·2026년 8월 25일

React Native

목록 보기
86/91

개요

모던 웹·앱에서 이미지를 다룰 때, 원본 이미지를 그대로 내려받는 경우는 거의 없다. 대신 이미지 CDN 또는 이미지 변환 서버(image transformation server)에 쿼리 파라미터를 붙여서 "내가 필요한 크기·품질·포맷으로 변환해서 내려달라"고 요청한다. 이 방식을 on-the-fly image resizing 또는 dynamic image optimization이라 부른다.

대표적인 서비스로 Cloudinary, imgix, Thumbor, Cloudflare Images, Next.js Image Optimization, ImageKit 등이 있다. 파라미터 이름은 서비스마다 조금씩 다르지만 개념은 동일하다.

https://cdn.example.com/photo.jpg?w=300&q=85&f=webp
                                  └──────┬──────┘
                                   변환 요청 파라미터

서버는 이 요청을 받으면 원본을 변환한 결과를 생성하고, 그 결과를 캐시에 저장한 뒤 응답한다. 같은 파라미터로 다시 요청이 오면 캐시된 결과를 즉시 반환하므로 변환 비용은 최초 1회만 발생한다.



1.주요 파라미터

1) w / h — 너비·높이 (width / height)

출력 이미지의 크기를 픽셀 단위로 지정한다. 가장 핵심적인 파라미터다.

?w=300        // 너비 300px로 리사이즈 (높이는 비율 유지)
?w=300&h=200  // 300×200으로 리사이즈

원본이 4000×3000인 이미지를 화면에서는 300px로만 보여준다면, 원본을 그대로 받는 것은 거대한 낭비다. w=300으로 요청하면 서버가 300px짜리로 줄여서 내려주므로 전송량이 수십 배 줄어든다.


2) q — 품질 (quality)

JPEG·WebP 같은 손실 압축 포맷의 압축 강도를 지정한다. 보통 1~100 범위이며, 값이 낮을수록 용량은 작아지지만 화질이 떨어진다.

?q=85   // 품질 85 (용량/화질 균형점으로 널리 쓰임)

대체로 75~85 구간이 사람 눈에 화질 저하가 거의 느껴지지 않으면서 용량을 크게 줄이는 sweet spot이다. 100은 거의 무손실에 가깝지만 용량 대비 이득이 작아 실무에서는 잘 쓰지 않는다.


3) f / fm / format — 출력 포맷 (format)

이미지를 어떤 포맷으로 변환할지 지정한다.

?f=webp   // WebP로 변환
?f=avif   // AVIF로 변환
?f=auto   // 클라이언트가 지원하는 최적 포맷 자동 선택

포맷별 특징은 아래와 같다.

포맷압축률투명도호환성비고
JPEG보통최고사진에 적합, 오래된 표준
PNG낮음(무손실)최고로고·아이콘·투명 이미지
WebP높음높음JPEG 대비 25~35% 용량 절감
AVIF매우 높음중간WebP보다 더 작지만 디코딩 비용 큼

동일 화질 기준으로 WebP는 JPEG보다 보통 25~35% 작고, AVIF는 그보다 더 작다. 다만 AVIF는 디코딩에 CPU를 더 쓰므로 저사양 기기에서는 렌더링이 느려질 수 있어 트레이드오프를 고려해야 한다.


4) dpr — 디바이스 픽셀 비율 (device pixel ratio)

고해상도 디스플레이를 위한 배율이다. 일부 CDN은 w와 별개로 dpr를 받아 내부적으로 곱해준다.

?w=100&dpr=3   // 실제로는 300px짜리를 생성

이 파라미터를 지원하지 않는 서버라면, 클라이언트에서 직접 w에 픽셀 비율을 곱해 보내야 한다(아래 React Native 섹션 참고).


5) fit / c — 크롭·맞춤 모드 (fit / crop)

지정한 박스에 이미지를 어떻게 맞출지 결정한다.

?w=300&h=300&fit=cover     // 박스를 꽉 채우고 넘치는 부분은 잘라냄
?w=300&h=300&fit=contain   // 비율 유지하며 박스 안에 모두 담음(여백 생길 수 있음)

cover는 썸네일·프로필 이미지처럼 정사각형 박스를 빈틈없이 채워야 할 때, contain은 이미지 전체가 잘리지 않고 보여야 할 때 쓴다.




React Native에서의 활용

React Native에서는 PixelRatio를 통해 기기의 픽셀 밀도를 얻어 w 파라미터에 반영하는 패턴이 핵심이다. 레이아웃 단위(dp)와 실제 물리 픽셀(px)이 다르기 때문이다.

import { PixelRatio } from 'react-native';

export const resizeImageUrl = (url: string, width: string): string => {
  if (!url) return '';

  const newWidth = width * PixelRatio.get();
  const quality = 85;
  const separator = url.includes('?') ? '&' : '?';

  return url + `${separator}w=${Math.floor(newWidth)}&q=${quality}&f=webp`;
};

핵심은 width * PixelRatio.get()이다. PixelRatio.get()은 기기의 DPR을 반환한다(@2x 기기면 2, @3x 기기면 3). 화면에 100dp로 그릴 이미지라도 @3x 기기에서는 300px짜리 이미지를 받아와야 선명하게 보인다. 이 보정을 하지 않으면 고해상도 화면에서 이미지가 뿌옇게 보이고, 반대로 항상 최대 배율로 받으면 저해상도 기기에서 불필요하게 큰 이미지를 받게 된다.

separator 처리는 URL에 이미 쿼리스트링이 있는지에 따라 ?&를 구분해 붙여 URL이 깨지지 않게 하는 안전장치다.

보완 포인트

PixelRatio.get()을 그대로 곱하면 @3x 기기에서 넓은 이미지를 요청할 때 요청 너비가 과하게 커질 수 있다. 다음과 같은 방식으로 다듬을 수 있다.

// 방법 1: RN이 제공하는 헬퍼 사용 (반올림 처리까지 해줌)
const newWidth = PixelRatio.getPixelSizeForLayoutSize(width);

// 방법 2: DPR에 상한선을 둠 (예: 최대 2배까지만)
const dpr = Math.min(PixelRatio.get(), 2);
const newWidth = width * dpr;

DPR에 상한을 두는 이유는, @3x와 @2x의 실질적 화질 차이가 사람 눈에 잘 구분되지 않는 경우가 많은 반면 용량 차이(3²:2² = 2.25배)는 크기 때문이다. 화질과 용량의 균형을 위해 실무에서 자주 쓰는 기법이다.



효과

1) 네트워크 트래픽 절감

가장 직접적인 효과다. 4000×3000(약 5MB) 원본을 300px WebP(수십 KB)로 변환하면 전송량이 수십 배에서 100배 이상 줄어든다. 모바일 데이터 환경에서 체감 로딩 속도가 크게 개선되고, CDN 전송 비용도 절감된다.

2) 메모리 사용량 감소

이 효과는 특히 React Native에서 중요하다. 이미지는 디코딩되면 압축이 풀린 비트맵 상태로 메모리에 올라간다. 비트맵 메모리는 대략 너비 × 높이 × 4바이트(RGBA)로 계산된다.

  • 4000×3000 원본 : 약 48MB (파일 용량과 무관하게 디코딩 시 메모리)
  • 300×225로 리사이즈 : 약 270KB

긴 리스트에서 원본 이미지를 그대로 쓰면 메모리가 폭증해 OOM(Out Of Memory) 크래시로 이어지기 쉽다. 표시 크기에 맞춰 리사이즈된 이미지를 받으면 이 문제를 근본적으로 줄일 수 있다.

3) 렌더링·스크롤 성능 향상

디코딩해야 할 픽셀 수가 줄어들면 디코딩 시간이 짧아지고, GPU에 올리는 텍스처 크기도 작아진다. 그 결과 이미지가 화면에 표시되기까지의 지연이 줄고, 리스트 스크롤 시 프레임 드랍이 완화된다.

4) 포맷 최적화에 따른 추가 절감

f=webp 또는 f=avif로 변환하면 같은 화질에서 용량을 추가로 줄인다. WebP는 JPEG 대비 평균 25~35% 작으므로, 리사이즈 효과와 곱해져 전체 절감 폭이 더 커진다.

5) CDN 캐싱과의 시너지

변환 결과는 파라미터 조합을 키로 캐싱된다. 즉 ?w=300&q=85&f=webp로 들어온 요청은 최초 1회만 변환되고 이후에는 엣지 캐시에서 즉시 응답된다. 사용하는 파라미터 조합의 가짓수를 일정하게 유지(예: DPR 상한, 정해진 너비 단계 사용)하면 캐시 적중률이 높아져 변환 부하와 응답 지연이 함께 줄어든다.

profile
이력서 https://resume.eunsu.pro

0개의 댓글