일전에 패캠 JS과제로 이미지 업로드 및 미리보기 기능을 경험해봤지만
당시에는 JS 지식이 부족했고 (물론 지금도 많이 부족함...)
그냥 구글링으로 얻어낸 코드를 복붙하고 제대로 이해하지 못한 것 같아서
이번 기회를 통해 코드 한줄 한줄에 대한 이해를 기반으로 파편화된 지식을 정리해보려 한다.
//EditPhoto.tsx
const EditPhoto = () => {
const [imgFile, setImgFile] = useState<File>();
const [imgPath, setImgPath] = useState("");
const imgRef = useRef<HTMLInputElement>(null);
const MAX_IMAGE_SIZE_BYTES = 1024 * 1024 * 2;
return (
<label htmlFor="photo">
<img
//사용자가 이미지 파일을 업로드하면 해당 이미지를 보여주고, 없으면 기본 이미지를 보여준다.
src={imgPath ? imgPath : `/images/upload.png`}
alt="이미지 업로드"
/>
</label>
<input
type="file"
id="photo"
name="photo"
accept=".png, .jpeg, .jpg"
onChange={previewImage}
ref={imgRef}
/>
);
};
export default EditPhoto;
이미지 업로드 기능은 매우 간단하다.
<input type="file>
태그를 활용하여 만들 수 있다.
좀 더 구체적으로는 이미지만 받고 싶을 경우 accept
속성을 사용해서 파일 형식을 특정해줄 수 있다.
accept=".png, .jpeg, .jpg"
accept="image/jpg, image/png, video/mp4, video/avi"
두 가지 방식 모두 가능하다.
만약 이미지 파일만 받을 수 있는 경우라면 전자처럼 작성하는 방식이 편할 것이고,
동영상 파일 등 여러 가지 형식을 복합적으로 받을 수 있는 경우라면 후자처럼 작성하는 방식이 좋을 것 같다.
사진의 사이즈가 너무 크면 DB 관리도 힘들고, 업로드하고 불러오는 데에도 좋지 않기 때문에 제한을 두어 받는 것이 좋다.
if (img.size > MAX_IMAGE_SIZE_BYTES) {
alert("최대 2MB까지 업로드 가능합니다.");
return;
}
<input type="file>
태그로 작성하게되면 기본으로 제공하는 파일 선택 버튼이 생성된다.
이때, 더 예쁜 UI를 위해 이 버튼을 눌러서 파일을 업로드하지 않고, 위의 이미지를 눌러 파일을 업로드하게 할 수 있다.
<label HtmlFor="">
속성 사용기본 html에서는
<label for="">
이지만, 리액트에서는<label HtmlFor="">
라고 사용해야 한다. 그냥for
이라고 작성하면 리액트에서는 반복문for
로 인식한다고 한다.
<label>
태그의 htmlFor
속성을 사용하여 input 태그와 연결시킨다.
<label>
태그 내부에 보여줄 이미지 또는 텍스트 등을 입력하고,
<input type="file>
의 스타일링을 display:none
으로 설정하면 이미지를 눌러서 파일을 업로드할 수 있게 된다.
FileReader API를 사용하기 위해 생성자로 선언해 준다.
바이너리 파일을 Base64 Encode 문자열로 반환한다.
Ex.) data:image/jpeg; base64, GDYG…
이렇게 취득한 문자열로 이미지를 브라우저로 바로 출력시키면 사용자는 해당 이미지를 동적으로 확인할 수 있다.
파일 읽기를 성공적으로 완료했을 때 실행할 동작을 작성해준다.
reader.onload = () => {
setImgPath(reader.result as string);
};
사용자가 업로드한 이미지가 있을 경우 해당 이미지를 미리 보여주는 기능을 하는 함수이다.
const previewImage = () => {
if (imgRef.current && imgRef.current.files) {
const img = imgRef.current.files[0];
setImgFile(img);
//이미지 미리보기 기능
const reader = new FileReader();
reader.readAsDataURL(img);
reader.onload = () => {
setImgPath(reader.result as string);
};
}
};