안드로이드 HEIC 이미지 업로드 이슈 해결기

·
6 min read

최근 숙소 등록 기능에서 플랫폼에 따라 이미지 업로드가 다르게 작동하는 문제를 겪었다. 웹과 iOS에서는 문제가 없었는데, 안드로이드에서만 이미지 업로드 후 서버에서 썸네일을 생성하는 과정에서 에러가 발생했다.

처음에는 서버에 문제가 있다고 생각했다. 사용자가 이미지를 업로드하면, 서버에서는 sharp라는 이미지 처리 라이브러리를 이용해서 썸네일을 생성하는데, 이때 아래와 같은 에러가 발생했다.

VipsJpeg: Premature end of input file
VipsJpeg: Premature end of input file

처음엔 단순히 업로드 도중 파일이 깨졌거나, sharp 설정 문제일 것이라고 생각했다. 그런데 이상한 점이 있었다. 사용자가 업로드한 건 HEIC 이미지였는데, 서버에 저장된 파일은 JPEG 포맷이었다. 변환이 어디서 일어났는지 추적이 필요했다.

원인 분석

프론트엔드 코드를 다시 확인해보니, 파일 입력 필드에 accept="image/*" 속성이 지정되어 있었다.

<input type="file" accept="image/*" />
<input type="file" accept="image/*" />

혹시 이게 브라우저에서 어떤 자동 처리를 유도하는 게 아닐까? 관련 키워드로 검색도 해보고, ChatGPT를 활용해 자료를 찾아보던 중, 일부 안드로이드 브라우저에서는 accept="image/*"가 설정된 상태되어 있을 경우, 사용자가 HEIC 이미지를 선택하면 브라우저나 카메라 앱이 이를 자동으로 JPEG로 변환하여 업로드하는 경우가 있다는 걸 알게 되었다.

문제는 이렇게 변환된 JPEG 파일이 정상적인 이미지 포맷이 아닐 수 있다는 것이다. 변환 과정에서 메타데이터나 파일 구조가 깨지면서 sharp가 이 파일을 읽지 못하는 경우가 생긴다. 즉, 파일은 존재하지만 sharp가 읽을 수 없는 상태가 되어버리는 것이다.

해결 방법

가장 간단한 해결책은 accept 속성을 제거하는 것이었다.

<input type="file" />
<input type="file" />

제거한 뒤 테스트해보니, 안드로이드에서도 HEIC 원본 파일이 그대로 서버에 업로드되었고, 정상적으로 썸네일을 생성할 수 있었다.

새로운 고민

문제는 accept 속성을 없애면 이미지 외의 다른 파일도 사용자가 선택할 수 있다는 점이다. 이를 보완하기 위해 클라이언트에 업로드 하려는 파일의 MIME 타입을 검사해서, 이미지가 아닌 경우 업로드를 막는 처리를 추가했다.

if (!file.type.startsWith('image/')) {
throw new Error('이미지 파일만 업로드할 수 있습니다.');
}
if (!file.type.startsWith('image/')) {
throw new Error('이미지 파일만 업로드할 수 있습니다.');
}

이렇게 하면 적어도 이미지가 아닌 파일이 서버로 저장되는 걸 막을 수 있다. 사용자 경험 면에서는 조금 아쉬운 부분도 있지만, 안정성이 더 중요하다고 판단했다.

플랫폼별로 accept 속성을 다르게 설정하는 방법도 고민했었다. 하지만 플랫폼 감지 로직은 완벽하지 않기도 하고, 브라우저나 Webview 환경에 따라 다르게 작동할 수 있어 유지보수가 어려울 것 같았다. 또한 사용자 입장에서도 기기마다 파일 선택 UI나 허용되는 확장자가 달라지면 혼란을 줄 수 있다고 판단했다. 결국 모든 플랫폼에서 일관되게 동작하도록 하는 방향을 선택했다.

마무리

이번 이슈를 통해 느낀 점은 브라우저에 명시적으로 설정한 속성이 플랫폼에 따라 다르게 해석되거나, 의도치 않은 자동 처리를 유발할 수 있다는 점이다. 특히 이미지처럼 운영체제나 브라우저가 개입할 여지가 있는 기능에서는 예상하지 못한 문제가 발생할 수 있다는 걸 경험했다.

아직 완벽한 해결책이라고 생각하진 않는다. 클라이언트에서 이미지 포맷을 명확하게 확인하거나, 서버에서 HEIC 지원을 확대하는 방향도 향후 보완 과제로 남아 있다. 그래도 이번에는 문제의 원인을 정확히 파악하고, 가능한 선택지 중 가장 현실적인 대응을 했다는 점에서 좋은 경험이 되었다고 생각한다.