파일 객체를 ObjectURL로 만들면 파일에 대한 주소를 만들 수 있다
인터넷에 올린 파일 링크 같이 사용자 컴퓨터에 있는 파일을 주소로 사용할 수 있다
mport { useEffect, useRef, useState } from "react";
// 이미지 파일 선택 창
function FileInput({ name, value, onChange }) {
// 파일 미리보기 State
const [preview, setPreview] = useState();
// Ref 객체 생성
const inputRef = useRef();
const handleChange = (e) => {
const nextValue = e.target.files[0];
onChange(name, nextValue);
};
// 선택 파일 초기화
const handleClearClick = () => {
const inputNode = inputRef.current;
if (!inputNode) return;
inputNode.value = '';
onChange(name, null);
}
// 파일을 선택할 떄마다 미리 보기 주소 변경
useEffect(() => {
if (!value) return;
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>
);
}
export default FileInput;

사이드 이펙트(Side Effect)
ObjectURL을 만들면 웹 브라우저는 메모리를 할당하고 파일에 해당하는 주소를 만들어 준다
랜더링하는 과정에서 리액트 외부의 상태를 바꾸게 되는데 이럭식으로 컴포넌트 함수에서 외부의 상태를 바꾸는 걸 사이드 이펙트라고 한다
사이드 이펙트와 useEffect
useEffect 는 리액트 컴포넌트 함수 안에서 사이드 이펙트를 실행하고 싶을 때 사용하는 함수이다
예를들어
페이지 정보 변경
useEffect(() => {
document.title = title; // 페이지 데이터를 변경
}, [title]);
네트워크 요청
useEffect(() => {
fetch('https://example.com/data') // 외부로 네트워크 리퀘스트
.then((response) => response.json())
.then((body) => setData(body));
}, [])
데이터 저장
useEffect(() => {
localStorage.setItem('theme', theme); // 로컬 스토리지에 테마 정보를 저장
}, [theme]);
revokeObjectURL를 통해서 사이드 이펙트를 해제할 수 있다
useEffect(() => {
if (!value) return;
const nextPreview = URL.createObjectURL(value);
setPreview(nextPreview);
// 다른 파일 선택하거나 해제했을 때 사이드 이펙트 해제
return () => {
setPreview();
URL.revokeObjectURL(nextPreview);
}
}, [value]);
이 함수는 나중에 디펜던시 리스트 값이 바뀌어서 새로 콜백을 실행하게 되는데 새로 콜백을 실행하기 전에 리액트는 앞에서 리턴한 정리 함수를 실행해서 사이드 이펙트를 정리한다
정리 함수 (Cleanup Function)
useEffect(() => {
// 사이드 이펙트
return () => {
// 사이드 이펙트에 대한 정리
}
}, [dep1, dep2, dep3, ...]);
useEffect 의 콜백 함수에서 사이드 이펙트를 만들면 정리가 필요한 경우가 있다.
이럴 때 콜백 함수에서 리턴 값으로 정리하는 함수를 리턴할 수 있었는데 리턴한 정리 함수에서는 사이드 이펙트에 대한 뒷정리를 한다.
예를 들면 이미지 파일 미리보기를 구현할 때 Object URL을 만들어서 브라우저의 메모리를 할당(createObjectURL) 했는데 정리 함수에서는 이때 할당한 메모리를 다시 해제(revokeObjectURL)했다
정리 함수가 실행되는 시점
쉽게 말해서 콜백을 한 번 실행했으면 정리 함수도 반드시 한 번 실행된다고 생각하면 된다
정확히는 새로운 콜백 함수가 호출되기 전에 실행되거나 (앞에서 실행한 콜백의 사이드 이펙트를 정리), 컴포넌트가 화면에서 사라지기 전에 실행된다 (맨 마지막으로 실행한 콜백의 사이드 이펙트를 정리)
예시: 타이머
import { useEffect, useState } from 'react';
function Timer() {
const [second, setSecond] = useState(0);
useEffect(() => {
const timerId = setInterval(() => {
console.log('타이머 실행중 ... ');
setSecond((prevSecond) => prevSecond + 1);
}, 1000);
console.log('타이머 시작 🏁');
return () => {
clearInterval(timerId);
console.log('타이머 멈춤 ✋');
};
}, []);
return <div>{second}</div>;
}
function App() {
const [show, setShow] = useState(false);
const handleShowClick = () => setShow(true);
const handleHideClick = () => setShow(false);
return (
<div>
{show && <Timer />}
<button onClick={handleShowClick}>보이기</button>
<button onClick={handleHideClick}>감추기</button>
</div>
);
}
export default App;
일정한 시간 간격마다 콜백 함수를 실행하는 setInterval 이라는 함수도 정리가 필요한 사이드 이펙트다
참고
코드잇