지식 정리 📝

확장자는 JPG인데 Windows에서 열리지 않는 이미지 문제 해결한 방법

엄성준 2025. 9. 2. 20:08
728x90

"현재 지원되지 않는 형식이거나 파일이 손상되었기 때문에 사진을 열 수 없습니다." 에러 화면

 

Window 운영체제에서 .jpg 확장자 이미지가 다운로드는 되지만 열리지 않는 문제를 해결한 과정을 공유합니다.
이번 문제는 안드로이드 폰에서 캡처한 이미지가 .jpg 확장자로 저장되었지만, 실제 내부 포맷은 AVIF였기 때문에 발생했습니다.

 


❗️ 문제 상황

Mac이나 Chrome 브라우저에서는 정상적으로 열리지만, 특정 Windows 환경에서는 이미지를 열 수 없는 문제가 발생했습니다.

 

🔍 문제 분석

터미널에서 문제가 된 이미지를 확인해보았습니다.

터미널 이미지 파일 분석

autowini2@autowiniui-MacBookPro-5 Desktop % file test.jpg
test.jpg: ISO Media, AVIF Image

autowini2@autowiniui-MacBookPro-5 Desktop % file umsungjun.jpg
umsungjun.jpg: JPEG image data, JFIF standard 1.01, resolution (DPCM), density 37x37, segment length 16, baseline, precision 8, 500x500, components 1
  • test.jpg → 확장자는 .jpg이지만, 실제 내부 포맷은 AVIF
  • umsungjun.jpg → 정상적인 JPEG

원인

안드로이드에서 스크린샷을 찍으면, 기기 기본 설정에 따라 AVIF(또는 HEIF) 포맷으로 저장됩니다. 하지만 일부 환경에서는 파일 확장자를 .jpg로 강제로 붙이면서, 실제 포맷과 확장자가 불일치하는 문제가 발생합니다.

  • AVIF: 최신 이미지 포맷으로, 압축 효율이 높고 화질 대비 용량이 작음. 안드로이드 최신 버전에서 기본 지원.
  • JPEG: 오랜 기간 표준으로 쓰인 포맷. 대부분의 OS와 앱에서 호환 가능.
  • Windows 기본 사진 앱: AVIF를 지원하지 않음 → 확장자는 .jpg인데 실제로는 AVIF라서 파일 손상 에러가 발생.

포맷이란?

포맷(format)이란, 이미지 데이터를 어떻게 압축·저장할지 정한 규격(파일 형식)을 말합니다.

  • JPEG → 손실 압축, 높은 호환성
  • PNG → 무손실 압축, 투명 배경 지원
  • AVIF → 최신 규격, 높은 압축 효율

즉, 확장자 .jpg는 껍데기일 뿐이고, 내부에 저장된 실제 데이터가 어떤 포맷이냐에 따라 열 수 있느냐 없느냐가 결정됩니다.


✅  해결 방법

안드로이드 캡처 이미지처럼 확장자는 .jpg인데 내부 포맷이 AVIF인 경우, Windows 기본 뷰어에서는 열리지 않는 문제가 발생했습니다.
이를 해결하기 위해, 다운로드 시 blob의 MIME 타입이 image/avif라면 Canvas를 통해 JPG로 변환 후 저장하도록 처리했습니다.

/**
 * @description 파일 다운로드 함수
 * @param url - 파일 URL
 * @param filename - 다운로드할 파일 이름
 */
export const fileDownload = async (url: string, filename: string) => {
  const res = await fetch(url);
  if (!res.ok) throw new Error('파일 다운로드에 실패했습니다.');

  const blob = await res.blob();

  // avif → jpg 변환 (내부 MIME 타입이 avif일 때만 처리)
  const convertIfAvif = async (blob: Blob): Promise<Blob> => {
    if (blob.type === 'image/avif') {
      const img = await createImageBitmap(blob);
      const canvas = document.createElement('canvas');
      canvas.width = img.width;
      canvas.height = img.height;
      const ctx = canvas.getContext('2d');
      if (!ctx) throw new Error('Canvas context 생성 실패');

      ctx.drawImage(img, 0, 0);
      return new Promise<Blob>((resolve, reject) => {
        canvas.toBlob(
          (newBlob) => {
            if (newBlob) resolve(newBlob);
            else reject(new Error('JPG 변환 실패'));
          },
          'image/jpeg',
          0.9
        );
      });
    }
    return blob;
  };

  const finalBlob = await convertIfAvif(blob);

  const objectUrl = URL.createObjectURL(finalBlob);
  const link = document.createElement('a');

  // 내부 포맷이 avif였다면 확장자를 jpg로 강제 변경
  const finalFilename =
    blob.type === 'image/avif'
      ? filename.replace(/\.[^/.]+$/, '') + '.jpg'
      : filename;

  link.href = objectUrl;
  link.download = finalFilename || `file-${Date.now()}`;
  document.body.appendChild(link);
  link.click();
  document.body.removeChild(link);

  // 메모리 해제
  setTimeout(() => URL.revokeObjectURL(objectUrl), 100);
};

💡 느낀 점

처음에는 단순히 “파일이 손상된 것 같다”라고만 생각했지만, 이번 경험을 통해 확장자와 실제 파일 포맷이 다를 수 있다는 점을 알게 되었습니다.

 

앞으로 유사한 문제가 발생하더라도, 보다 빠르게 원인을 파악하고 해결할 수 있을 것 같습니다.