input type=file onChange 미작동 문제 해결
2025-08-10모바일/웹 환경에서 파일 업로드 UI를 커스터마이징하다 보면 다음과 같은 패턴을 자주 사용합니다.
<input type="file">는 숨김 처리- 클릭 가능한
label또는div로 파일 선택 제어 - 선택된 이미지를 미리보기로 표시
- "삭제" 버튼으로 미리보기 제거
이 방식은 UI적으로 깔끔하지만, 특정 상황에서 onChange 이벤트가 동작하지 않는 문제가 발생할 수 있습니다.
1. 문제 상황
아래와 같은 코드에선 이미지를 업로드한 뒤 UI상에서 삭제하고 동일한 파일을 다시 업로드하면 onChange 이벤트가 아예 발생하지 않습니다.
<input
type="file"
id="fileInput"
style={{ display: 'none' }}
onChange={handleFileChange}
/>
<label htmlFor="fileInput">이미지 선택</label>
{preview && (
<div>
<img src={preview} alt="미리보기" />
<button onClick={handleRemove}>삭제</button>
</div>
)}
2. 원인
브라우저는 같은 파일을 재선택하면 불필요한 이벤트 호출을 막기 위해 onChange를 발생시키지 않도록 설계되어 있습니다.
즉, UI에서 이미지를 삭제하더라도 input의 files 속성을 초기화하지 않으면 같은 파일은 다시 선택 불가 상태가 됩니다.
이 현상은 플랫폼/브라우저마다 기준은 다르지만 기본적으로 동일한 현상이 발생할 수 있습니다.
| 플랫폼/브라우저 | 동일 파일 재선택 시 동작 | 비고 |
|---|---|---|
| 안드로이드 Chrome/WebView | 거의 항상 onChange 미발생 | 문제 가장 자주 발생 |
| iOS Safari | 메타데이터 변동 시 발생 가능 | 카메라 촬영/갤러리 선택 시 파일이 달라질 수 있음 |
| 데스크톱 Chrome/Edge | 미발생 | 표준 동작 |
| 데스크톱 Firefox | 미발생 | 표준 동작 |
4. 해결 방법
UI에서 이미지를 삭제할 때 input.value를 초기화해야 합니다.
import { useRef, useState } from "react";
function ImageUploader() {
const fileInputRef = useRef(null);
const [preview, setPreview] = useState(null);
function handleFileChange(e) {
const file = e.target.files?.[0];
if (file) {
const url = URL.createObjectURL(file);
setPreview(url);
}
}
function handleRemove() {
setPreview(null);
if (fileInputRef.current) {
fileInputRef.current.value = ""; // 핵심: 파일 값 초기화
}
}
return (
<>
<input
type="file"
ref={fileInputRef}
style={{ display: "none" }}
onChange={handleFileChange}
/>
<label htmlFor="fileInput">이미지 선택</label>
{preview && (
<div>
<img src={preview} alt="미리보기" />
<button onClick={handleRemove}>삭제</button>
</div>
)}
</>
);
}
위 코드처럼 새로운 파일을 업로드하기 전, 기존 input을 초기화하면 문제가 해결됩니다.