파일 업로드 오류 처리는 단순히 try...catch로 감싸는 문제가 아니다. 사용자가 선택한 파일이 이미지가 아닐 수도 있고, 확장자는 PNG인데 MIME 타입은 비어 있을 수도 있으며, 브라우저가 파일은 읽었지만 이미지 디코딩에는 실패할 수도 있다. 공개 도구에서는 이런 실패를 한 문장으로 뭉개면 사용자가 다음 행동을 결정하기 어렵다.
이 글은 브라우저 이미지 압축 구현 전체 가이드의 입력 검증 부분을 더 깊게 나눈 글이다. 압축, 미리보기, 다운로드 기능을 안정적으로 만들려면 업로드 단계에서 실패 이유를 잘라내는 기준이 먼저 필요하다.
오류는 단계별로 분리해야 한다
파일 업로드 흐름은 대략 선택, 검증, 읽기, 디코딩, 변환, 결과 생성으로 나뉜다. 각 단계의 실패 원인이 다르기 때문에 메시지도 달라야 한다. 선택 단계에서는 파일이 없는 상황을 처리하고, 검증 단계에서는 형식과 크기를 확인한다. 읽기 단계에서는 FileReader 또는 Object URL 생성 실패를 보고, 디코딩 단계에서는 브라우저가 실제 이미지를 열 수 있는지 확인한다.
| 단계 | 대표 실패 | 사용자 메시지 방향 |
|---|---|---|
| 선택 | 파일 없음 | 처리할 파일을 선택하게 안내 |
| 검증 | 지원하지 않는 형식 | 지원 형식을 구체적으로 안내 |
| 읽기 | 브라우저 파일 읽기 실패 | 다른 파일 선택 또는 재시도 안내 |
| 디코딩 | 손상 이미지 | 이미지 자체를 열 수 없다고 안내 |
| 변환 | Canvas 또는 Blob 생성 실패 | 브라우저 한계 또는 파일 조건 확인 안내 |
MIME 타입과 확장자를 함께 본다
file.type은 편리하지만 항상 완전하지 않다. 일부 환경에서는 빈 문자열일 수 있고, 사용자가 확장자를 바꾼 파일은 이름만 믿을 수 없다. 그래서 1차 검증에서는 MIME 타입과 확장자를 함께 보고, 최종 검증은 실제 이미지 로드 성공 여부로 확인하는 편이 안정적이다.
const imageTypes = ["image/jpeg", "image/png", "image/webp"];
const imageExtensions = [".jpg", ".jpeg", ".png", ".webp"];
function isSupportedImage(file) {
const lowerName = file.name.toLowerCase();
const validType = imageTypes.includes(file.type);
const validExtension = imageExtensions.some((ext) => lowerName.endsWith(ext));
return validType || validExtension;
}
이 코드는 완전한 보안 장치가 아니라 사용자 경험을 위한 1차 필터다. 실제 서비스에서 서버 업로드가 있다면 서버에서도 별도 검증이 필요하다. 하지만 브라우저 안에서 처리하는 도구라면 이 정도의 초기 안내만으로도 불필요한 실패를 크게 줄일 수 있다.
이미지 디코딩 실패는 따로 잡아야 한다
파일 읽기에 성공했다고 해서 이미지로 사용할 수 있다는 뜻은 아니다. 손상 파일이거나 브라우저가 지원하지 않는 인코딩이면 이미지 로드 단계에서 실패한다. 이때는 파일 읽기 오류와 다른 메시지를 보여줘야 한다.
function loadImageFromUrl(url) {
return new Promise((resolve, reject) => {
const image = new Image();
image.onload = () => resolve(image);
image.onerror = () => reject(new Error("이미지를 열 수 없습니다."));
image.src = url;
});
}
이 흐름은 FileReader, Data URL, Blob URL, Object URL 선택 기준과도 연결된다. 큰 파일은 Object URL로 미리보고, 더 이상 필요하지 않을 때 URL을 해제하면 메모리 부담을 줄일 수 있다.
여러 파일에서는 오류도 파일별로 저장한다
다중 파일 업로드에서 가장 나쁜 UX는 하나가 실패했다고 전체 작업을 멈추는 것이다. 사용자는 성공한 파일을 먼저 받을 수 있어야 하고, 실패한 파일만 이유를 확인하고 다시 선택할 수 있어야 한다. 따라서 결과 배열과 오류 배열을 분리하거나, 파일별 상태 객체를 유지하는 구조가 좋다.
const fileStates = files.map((file) => ({
file,
status: "pending",
error: null,
result: null
}));
이 구조는 다중 파일 업로드 진행률 UI 상태 설계에서 더 자세히 다룬다. 업로드 오류 처리는 UI 상태 설계와 분리될 수 없다. 오류를 어떻게 저장하느냐가 곧 화면에 무엇을 보여줄지 결정하기 때문이다.
정리
브라우저 파일 업로드 오류 처리는 형식 검증, 파일 읽기, 이미지 디코딩, 변환 실패를 분리하는 일이다. 각 단계의 메시지가 명확하면 사용자는 실패를 이해하고 다시 시도할 수 있다. 애드센스 승인 관점에서도 이런 글은 단순 코드 조각이 아니라 실제 문제를 끝까지 해결하는 콘텐츠로 보이는 데 도움이 된다.