diff --git a/system3-nonconformance/api/Dockerfile b/system3-nonconformance/api/Dockerfile
index fdb7697..f326cde 100644
--- a/system3-nonconformance/api/Dockerfile
+++ b/system3-nonconformance/api/Dockerfile
@@ -2,11 +2,16 @@ FROM python:3.11-slim
WORKDIR /app
-# 시스템 패키지 설치 (gosu: entrypoint 에서 root→appuser 강등용)
+# 시스템 패키지 설치
+# - gosu: entrypoint 에서 root→appuser 강등용
+# - imagemagick + libheif: HEIC(iPhone) 등 pillow_heif 가 처리 못하는 이미지 fallback (2026-04-09)
RUN apt-get update && apt-get install -y \
gcc \
gosu \
- && rm -rf /var/lib/apt/lists/*
+ imagemagick \
+ libheif1 \
+ && rm -rf /var/lib/apt/lists/* \
+ && sed -i 's|||' /etc/ImageMagick-6/policy.xml 2>/dev/null || true
# Python 의존성 설치
COPY requirements.txt .
diff --git a/system3-nonconformance/api/services/file_service.py b/system3-nonconformance/api/services/file_service.py
index 5308029..fbb39d3 100644
--- a/system3-nonconformance/api/services/file_service.py
+++ b/system3-nonconformance/api/services/file_service.py
@@ -84,11 +84,48 @@ def save_base64_image(base64_string: str, prefix: str = "image") -> Optional[str
print(f"✅ HEIF 재시도 성공: {image.format}, 모드: {image.mode}, 크기: {image.size}")
except Exception as heif_e:
print(f"❌ HEIF 처리도 실패: {heif_e}")
- print("❌ 지원되지 않는 이미지 형식")
- raise e
+ image = None # ImageMagick fallback 시도
else:
print("❌ HEIF 지원 라이브러리가 설치되지 않거나 처리 불가")
- raise e
+
+ # ImageMagick fallback (pillow_heif 가 특정 iPhone HEIC 메타데이터를 처리 못하는 경우)
+ if image is None:
+ print("🔄 ImageMagick 으로 fallback 시도...")
+ try:
+ import subprocess
+ import tempfile
+ with tempfile.NamedTemporaryFile(suffix='.bin', delete=False, dir='/tmp') as tmp_in:
+ tmp_in.write(image_data)
+ tmp_in_path = tmp_in.name
+ tmp_out_path = tmp_in_path + '.jpg'
+ # convert 는 ImageMagick 6, magick 은 ImageMagick 7
+ cmd = None
+ for binary in ('magick', 'convert'):
+ try:
+ subprocess.run([binary, '-version'], capture_output=True, timeout=5, check=True)
+ cmd = binary
+ break
+ except (FileNotFoundError, subprocess.CalledProcessError, subprocess.TimeoutExpired):
+ continue
+ if not cmd:
+ raise Exception("ImageMagick not installed")
+ result = subprocess.run(
+ [cmd, tmp_in_path, tmp_out_path],
+ capture_output=True, timeout=30, text=True
+ )
+ if result.returncode != 0:
+ raise Exception(f"ImageMagick 변환 실패: {result.stderr[:200]}")
+ image = Image.open(tmp_out_path)
+ image.load() # 파일 삭제 전 강제 로드
+ print(f"✅ ImageMagick fallback 성공: {image.format}, 모드: {image.mode}, 크기: {image.size}")
+ try:
+ os.unlink(tmp_in_path)
+ os.unlink(tmp_out_path)
+ except OSError:
+ pass
+ except Exception as magick_e:
+ print(f"❌ ImageMagick fallback 실패: {magick_e}")
+ raise e # 원래 PIL 에러를 올림
# 이미지가 성공적으로 로드되지 않은 경우
if image is None: