대부분의 서비스에서는 사용자의 프로필 사진을 변경 할 수 있는 기능이 있습니다.
이 기능을 리액트에서는 어떻게 구현할까요? 다양한 방법이 있겠지만 저는 FileReader Api 를 이용하였습니다.
input 태그를 아시나요? 아마 자주 사용하시고 계실겁니다. input 태그에는 type 이라는 속성이 있는데요. type 이라는 속성 값에는 text, radio, submit, password, checkbox, file 등이 있습니다. 여기서 우리는 file 이라는 속성을 사용 할 것 입니다.
그럼 바로 코드를 보겠습니다.
const ImageUpload = ()=> {
return <>
<input type = "file" accept = "image/*"/>
</>
}
그럼 이렇게 화면에 보여질거에요. 파일 선택 버튼을 클릭하면 사용자의 컴퓨터의 파일을 선택 할 수 있습니다.
이렇게 선택한 파일을 서버에 보내기 전에 먼저 확인 할 수는 없을까요? 자바스크립트를 이용해서 가능 합니다.
const ImageUpload = ()=> {
const [uploadImgUrl, setUploadImgUrl] = useState("");
const onchangeImageUpload = (e)=> {
const {files} = e.target;
const uploadFile = files[0];
const reader = new FileReader();
reader.readAsDataURL(uploadFile);
reader.onloadend = ()=> {
setUploadImgUrl(reader.result);
}
}
return <>
<img src = {uploadImgUrl} img = "img"/>
<input type = "file" accept = "image/*" onChange = {onchangeImageUpload}/>
</>
}
위의 코드를 보면 uploadImgUrl 이라는 상태가 있는데 해당 상태는 업로드한 이미지의 url 주소를 가지게 될 것 입니다.
그리고 input 태그의 onChange 함수로 onchangeImageUpload 함수를 넣어 줍니다.
이 함수는 선택한 사진의 파일을 FileReader Api 를 이용해서 url 주소를 생성하고 그 주소를 uploadImgUrl 상태에 업데이트 하는 코드 입니다.
선택한 파일을 가지고 오는 방법은 다양하게 있을 수 있습니다.
onchange 이벤트가 발생했을때 넘겨 받는 event 객체의 files 프로퍼티를 이용 할 수 있고 ref 객체를 이용할수도 있겠습니다.
아니면 직접 document.querySelector 를 이용해서 돔의 정보를 가지고 올수도 있겠죠.
저는 onchange 이벤트가 발생했을때 넘겨 받는 event 객체를 이용하였습니다.
FileReader 는 데이터를 비동기적으로 읽는데 도움을 주는 웹 api 입니다. FileReader 는 객체 형태이기 때문에 api 를 사용하기 위해서 new FileReader() 로 객체를 생성해줘야 합니다.
readAsDataUrl 은 선택한 파일을 url 로 변환해주는 함수 입니다.
함수의 인수에 선택한 파일을 넘겨줍니다.
쉽게 말해서 선택한 파일을 쉽게 사용 할 수 있도록 주소로 변경하는 것 입니다.
onloadend 는 읽기 동작이 발생하면 성공여부와 상관없이 호출되는 이벤트 핸들러 입니다.
위의 코드에서는 읽기 동작이 발생하면 콜백 함수가 호출되어 FileReader 의 객체인 reader 의 result 를 uploadImgUrl 상태로 업데이트 하는 코드 입니다.
onload 도 있는데 onload 는 읽기 동작이 발생하고 성공하였을때만 호출되는 이벤트 핸들러 입니다.
그럼 reader.result 에서 result 는 뭘까요?
파일의 컨텐츠 입니다. 이 값은 읽기 동작이 완료된 후에만 유효 합니다.
직접 확인 해보니 읽기 동작 전에는 result 값은 null 이었습니다.
이제 선택한 이미지를 서버에 보낼때는 uploadImgUrl 값을 보내면 되겠죠?
여기서 기본 input 태그로 이미지 업로드 하는 ui 는 이뻐보이지 않습니다.
이때는 label 을 이용해서 input 태그와 연결하고 input 태그는 display : none; 속성으로 화면에 안보이게 해주고 label 태그을 이미지 업로드 버튼으로 스타일 해서 사용하면 좀 더 좋은 ui 를 만들 수 있을것 입니다.
리액트에서 이미지 파일을 어떻게 업로드하는지 알아 보았습니다. 처음에는 어렵겠지만 코드를 잘 따라가면서 이해하면 그렇게 어렵지 않을 것 입니다.
정리가 너무 완벽해서 간단하게 받아들여졌네요 :)