파일 객체를 ObjectURL로 만들면 파일에 대한 주소를 만들 수 있다. 이렇게 만들어진 파일 주소는, 사용자 컴퓨터에 있는 파일을 (인터넷에 올린 파일 링크와 같이) 사용할 수 있도록 한다.
function FileInput({ onChange, value, name }) {
const [preview, setPreview] = useState();
const inputRef = useRef();
// value = e.target.files[0]; 로, 파일 input 객체이다.
// handleChange, handleClearClick 생략
useEffect(() => {
if (!value) return; // 값이 없는 경우 바로 리턴
// ObjectURL 생성하고, preview 를 이미지 주소값으로 사용
const nextPreview = URL.createObjectURL(value);
setPreview(nextPreview);
}, [value]);
return (
<div>
<img src={preview} alt="이미지 미리보기" />
<input type="file" onChange={handleChange} ref={inputRef} />
{value && <button onClick={handleClearClick}>X</button>}
</div>
);
}
ObjectURL을 만들면 웹 브라우저는 메모리를 할당하고 파일에 해당하는 주소를 만들어준다.
사이드 이펙트 : 컴포넌트 함수에서 외부의 상태를 변경하는 것
- 네트워크 리퀘스트, 메모리 할당 등
function FileInput({ onChange, value, name }) {
const [preview, setPreview] = useState();
const inputRef = useRef();
// value = e.target.files[0]; 로, 파일 input 객체이다.
// handleChange, handleClearClick 함수 생략
useEffect(() => {
if (!value) return;
const nextPreview = URL.createObjectURL(value);
setPreview(nextPreview);
return () => { // 사이드 이펙트 정리 함수
setPreview();
URL.revokeObjectURL(nextPreview); // 할당되었던 메모리 정리
};
}, [value]);
return (
<div>
<img src={preview} alt="이미지 미리보기" />
<input type="file" accept="image/png, image/jpeg" onChange={handleChange} ref={inputRef} />
{value && <button onClick={handleClearClick}>X</button>}
</div>
);
파일이 바뀔 때마다 메모리를 할당하기만 하면 메모리가 낭비된다.
따라서 다른 파일을 선택하거나 선택을 해제했을 때 revokeObjectURL
을 이용해 메모리도 같이 해제해주어야 한다.
useEffect에서 사이드 이펙트를 만든 후 정리하는 방법 : 리턴 값으로 함수를 리턴하면 된다.
① 사용자가 파일 선택
② value 값이 배뀌고 재랜더링 발생
③ 랜더링이 끝나고 useEffect 안의 콜백함수가 실행되면서 objectURL을 만들고 preview의 상태를 바꾼다.
(사이드 이펙트 : 메모리 할당)
④ return에 있는 사이드 이펙트 정리 함수를 등록해둔다.
⑤ 다음 value 값이 바뀌었을 때, 등록해둔 함수가 먼저 실행되며 사이드 이펙트를 정리하고,
⑥ 다시 useEffect 안의 함수들이 실행된다.
정리 함수가 실행되는 시점
쉽게 말해서 콜백을 한 번 실행했으면, 정리 함수도 반드시 한 번 실행된다.
① 새로운 콜백 함수가 호출되기 전에 실행되거나 (앞에서 실행한 콜백의 사이드 이펙트를 정리),
② 컴포넌트가 화면에서 사라지기 전에 실행된다 (맨 마지막으로 실행한 콜백의 사이드 이펙트를 정리).
+덧 :
<input type="file" />
에accept="image/png, image/jpeg"
속성을 추가하면 이미지 파일만 받을 수 있다.