React/Next.js에서 고급 이미지 최적화: 디바이스 기반 반응형 이미지로 최고 성능 (파트 1)

발행: (2025년 12월 4일 오후 03:47 GMT+9)
5 min read
원문: Dev.to

Source: Dev.to

Advanced Image Optimization in React/Next.js: 디바이스 기반 반응형 이미지로 최고의 성능을 구현 (Part 1) 커버 이미지

이미지는 일반적인 웹 페이지 전체 크기의 50‑70%를 차지합니다. 디바이스 기능과 화면 밀도에 따라 최적화된 이미지를 제공하는 것은 성능, 사용자 경험, SEO에 필수적입니다. 이 포괄적인 가이드에서는 React와 Next.js를 사용한 디바이스 픽셀 비율(DPR) 최적화와 반응형 이미지 기법을 살펴봅니다.

디바이스 픽셀 비율(DPR) 이해하기

디바이스 픽셀 비율은 물리적 픽셀과 CSS 픽셀 간의 관계를 나타냅니다. 최신 디바이스는 다양한 DPR을 가지고 있습니다:

  • 표준 디스플레이: DPR 1 (1920×1080 모니터)
  • 레티나/HD 디스플레이: DPR 2 (MacBook Pro, iPhone)
  • 울트라 HD 디스플레이: DPR 3+ (iPhone 15 Pro, Samsung Galaxy)

1× 이미지를 3× 디바이스에 제공하면 흐릿한 화면이 되고, 3× 이미지를 1× 디바이스에 제공하면 대역폭이 낭비됩니다.

JavaScript에서 디바이스 픽셀 비율 감지하기

// Get current device pixel ratio
const dpr = window.devicePixelRatio || 1;

// Listen for DPR changes (when moving between monitors)
const dprQuery = window.matchMedia(`(resolution: ${dpr}dppx)`);
dprQuery.addEventListener('change', (e) => {
  if (!e.matches) {
    console.log('DPR changed, reload optimized images');
  }
});

디바이스 감지를 위한 React Hook

디바이스 기능을 감지하는 커스텀 훅을 만들어 보세요:

// hooks/useDeviceInfo.js
import { useState, useEffect } from 'react';

export const useDeviceInfo = () => {
  const [deviceInfo, setDeviceInfo] = useState({
    dpr: 1,
    width: 0,
    isMobile: false,
    isTablet: false,
    isDesktop: false,
    connection: 'unknown',
  });

  useEffect(() => {
    const updateDeviceInfo = () => {
      const width = window.innerWidth;
      const dpr = window.devicePixelRatio || 1;
      const connection = navigator.connection?.effectiveType || 'unknown';

      setDeviceInfo({
        dpr: Math.min(dpr, 3), // Cap at 3x for practicality
        width,
        isMobile: width <= 768,
        isTablet: width > 768 && width <= 1024,
        isDesktop: width > 1024,
        connection,
      });
    };

    updateDeviceInfo();
    window.addEventListener('resize', updateDeviceInfo);
    return () => window.removeEventListener('resize', updateDeviceInfo);
  }, []);

  return deviceInfo;
};

반응형 이미지 컴포넌트 만들기 (React)

// components/OptimizedImage.jsx
import React, { useState, useEffect } from 'react';
import { useDeviceInfo } from '../hooks/useDeviceInfo';

const OptimizedImage = ({
  src,
  alt,
  sizes = '100vw',
  quality = 75,
  className = '',
}) => {
  const { dpr, width, connection } = useDeviceInfo();
  const [imageSrc, setImageSrc] = useState('');
  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {
    const adjustedQuality = connection === '2g' ? 50 : quality;
    const imageWidth = calculateImageWidth(width, dpr);
    const optimizedSrc = buildOptimizedUrl(src, {
      width: imageWidth,
      quality: adjustedQuality,
      dpr,
      format: 'auto', // WebP when supported
    });
    setImageSrc(optimizedSrc);
  }, [src, dpr, width, connection, quality]);

  const calculateImageWidth = (viewportWidth, deviceDpr) => {
    const targetWidth = viewportWidth * deviceDpr;
    const standardSizes = [
      320, 640, 750, 828, 1080, 1200, 1920, 2048, 3840,
    ];
    return standardSizes.find((size) => size >= targetWidth) || 3840;
  };

  const buildOptimizedUrl = (originalSrc, options) => {
    const { width, quality, dpr, format } = options;
    return `https://res.cloudinary.com/your-cloud/image/upload/w_${width},q_${quality},dpr_${dpr},f_${format}/${originalSrc}`;
  };

  return (
    <img
      src={imageSrc}
      alt={alt}
      className={className}
      sizes={sizes}
      onLoad={() => setIsLoading(false)}
      style={{ display: isLoading ? 'none' : 'block' }}
    />
  );
};

export default OptimizedImage;

아트 디렉션을 위한 <picture> 요소 사용하기

디바이스마다 다른 이미지 크롭이나 구성이 필요할 수 있습니다:

const ResponsiveArtDirectedImage = ({ images, alt }) => {
  const { dpr } = useDeviceInfo();

  const buildOptimizedUrl = (src, opts) => {
    const { dpr } = opts;
    return `https://res.cloudinary.com/your-cloud/image/upload/dpr_${dpr}/${src}`;
  };

  return (
    <picture>
      {/* Mobile – portrait crop */}
      <source
        media="(max-width: 767px)"
        srcSet={buildOptimizedUrl(images.mobile, { dpr })}
      />
      {/* Tablet – landscape crop */}
      <source
        media="(min-width: 768px) and (max-width: 1023px)"
        srcSet={buildOptimizedUrl(images.tablet, { dpr })}
      />
      {/* Desktop – full width */}
      <source
        media="(min-width: 1024px)"
        srcSet={buildOptimizedUrl(images.desktop, { dpr })}
      />
      <img src={buildOptimizedUrl(images.desktop, { dpr })} alt={alt} />
    </picture>
  );
};

파트 2에서는 Next.js 이미지 최적화, 레이지 로딩 전략, 성능 모니터링 기법을 계속해서 다룹니다.

키워드: React 이미지 최적화, 디바이스 픽셀 비율, 반응형 이미지, Next.js 성능, DPR 최적화, 레이지 로딩, 웹 바이탈

Back to Blog

관련 글

더 보기 »

React 및 Next.js의 RCE 취약점

기사 URL: https://github.com/vercel/next.js/security/advisories/GHSA-9qr9-h5gf-34mp 댓글 URL: https://news.ycombinator.com/item?id=46136026 포인트: 26 Co...