많은 서비스에 이미지 업로드 기능이 들어간다. 내가 이전 프로젝트들에서 이미지를 업로드했었다. 하지만 업로드할 이미지 미리보기 기능은 필수는 아니였다. 이미지를 제대로 서버에 올리고 나중에 제대로 받아오는 것이 더 중요하다. 하지만 사용자가 내가 올릴 이미지가 어떤 것인지 확인하고 잘못올렸을 경우 다른 이미지로 대체할 수 있도록 한다면 더 나은 사용자 경험을 줄 수 있다. 그래서 많은 서비스들이 이미지 미리보기 기능을 거의 필수로 구현한다.
이번 프로젝트에서는 이미지 미리보기 기능을 필수로 구현하게 되었다. 해당 기능을 구현하기 위해 여러가지 이미지 미리보기 기능을 구현할 수 있는 방법을 찾아보게 되었고 찾아보며 생각한 것들을 정리하게 되었다.
이미지를 미리보기를 구현하는 여러가지 방법이 있다.
첫번째는 이미지 서버에 올리고 반환된 이미지 주소를 활용하여 보여주는 것이다. 이 경우 몇가지 문제가 있다. 우선 이미지 서버에 이미지를 올리는데 꽤나 시간이 걸린다. 그렇기 때문에 이미지를 선택한 후 이미지를 화면에 띄우기까지 시간 틈이 있어 사용자가 느끼기에 즉각적이지 않다. 만약 여러번 이미지를 교체하게 된다면 대기시간의 불편함은 더 커지게 될 것이다. 두번째는 이미지를 올릴 때마다 서버에 저장되는 이미지가 생기기 때문에 이미지 서버의 비용이 당연히 늘어날 수 밖에 없다. 개인 프로젝트라면 큰 차이가 없겠지만 많은 사람들이 사용하는 서비스라면 엄청난 금액이 지불될 수 있다.
이미지 서버와 통신하지 않고 이미지 미리보기를 구현하는 방법으로는 크게 두가지가 있다. 바로 FileReader와 createObjectURL이다. 그중 FileReader를 활용한 구현 방법 먼저 확인해보자.
// JS
const [imgSrc, setImgSrc] = useState('');
const encodeFileToBase64 = (fileBlob) => {
const reader = new FileReader();
reader.readAsDataURL(fileBlob);
return new Promise((resolve) => {
reader.onload = () => {
setImgSrc(reader.result);
resolve();
};
});
};
// HTML(input)
<input
type="file"
onChange={(e) => {
encodeFileToBase64(e.target.files[0]);
}}
/>
위의 encodeFileToBase64
함수는 다음과 같이 진행된다.
마지막으로 내가 선택한 createObjectURL이다.
// JS
const [imgSrc, setImgSrc] = useState('');
const saveFileImage = (fileBlob) => {
const fileUrl = URL.createObjectURL(fileBlob);
setImgSrc(fileUrl);
};
// HTML(input)
<input
type="file"
onChange={(e) => {
saveFileImage(e.target.files[0]);
}}
/>
코드를 보면 훨씬 간결해졌다. saveFileImage
함수는 다음과 같이 진행된다.
createObjectURL을 활용한 방식은 같은 객체를 사용하더라도, createObjectURL()을 매번 호출할 때마다 새로운 객체 URL을 생성한다. 그렇기 때문에 생성된 객체 URL을 직접 하나씩 해제해주어야한다.
URL.revokeObjectURL(objURL);
objURL을 해제해주는 방법은 간단한다. 위처럼 해제하려는 objURL URL.revokeObjectURL()
함수 안에 넣어서 호출하면 된다.
위에서 이미지 미리보기를 구현 할 수 있는 여러가지 방법을 소개했다. 여러가지 방법을 소개했지만 앞선 두가지 방법은 장점보다 단점이 많았기 때문에 createObjectURL 방식을 추천한다.