이미지 파일을 업로드받기 위해 input type="file"을, 업로드된 파일을 미리보기로 표시하기 위해 FileReader API를 사용했다. 그리고 이미지 파일과 일반 string 데이터를 함께 서버로 전송하기 위해 FormData API를 사용함!
input type은 text, password, number, range... 이런 것만 써왔는데, file
이라는 타입도 있다는 걸 이번에 배우게 되었다. type="file"로 설정하면 자동으로 파일 선택 버튼이 생기고, 클라이언트로부터 파일을 업로드 받을 수 있다! multiple 속성을 주면 여러 파일 업로드도 가능~~
<input type="file" accept="받을 파일 종류" />
여기에 onClick={e => {console.log(e)}로 이벤트를 출력해 보면 필요한 속성들을 얻을 수 있음!
- e.target.value: 파일의 경로(string) - 보안상
C:\\fakepath\\파일명
으로 표시됨
- e.target.files: 파일 정보를 담은 File 객체를 담은 FileList 객체!
0: File, 1: File, 이런 식으로 떠서 처음에는 배열인 줄 알았는데, 0, 1, 2, ...(업로드된 파일 수만큼), 그리고 length를 key로 가지는 object였음! 아무것도 업로드 하지 않을 경우 {length: 0}.
- e.target.accept: 업로드할 수 있는 파일 종류
다른 타입의 input들은 e.target.value가 중요하지만 file의 경우 value는 경로(string)에 불과하고, 실제 파일 정보를 담은 오브젝트인 e.target.files가(특히 e.targtet.files[0]!) 아주 많이 쓰인다.
위 input으로 업로드 받은 이미지를 미리보기로 화면상 띄우기 위해 사용할 수 있는 것이 FileReader API! 업로드 받은 건 파일 형태이고, 화면에 표현하기 위해서는 url 주소가 필요한데, FileReaderAPI의 readAsDataURL() 메서드로 서버 업로드 전에 미리 url 주소를 사용할 수 있다~~!
url을 얻기 위해 readURL이라는 함수를 만들었다. input의 onChange시에 실행할 것!
const readURL = e => { if (e.target.files.length) { const reader = new FileReader(); reader.readAsDataURL(e.target.files[0]); reader.onload = function (e) { setPreviewImg(e.target.result); }; } };
line by line으로 분석해 보면
if (e.target.files.length)
const reader = new FileReader()
reader.readAsDataURL(e.target.files[0])
reader.onload = function (e)
setPreviewImg(e.target.result);
그리고 이 previewImg를 원하는 img 요소의 src 속성값으로 넣어주면 사용자가 이미지를 올렸을 때 미리보기로 출력된다~~!
프리뷰와 별개로, 업로드 받은 이미지를 서버에 전송하려면 FormData API를 사용할 수 있음! 특히 이번에는 썸네일용 이미지 파일과 기타 정보(텍스트 형태)가 담긴 오브젝트를 함께 전송해야 했는데, 그러려면 얘네를 formdata에 묶어서 보내야 했다. (GOD유진님의 도움으로 알게됨...✨)
- input으로부터 이미지 파일 가져오기
<input type="file" accept="image/*" onChange={e => {setThumbnail(e.target.files[0])}} required />
thumbnail이라는 state 변수에 담아주었다!
- 함께 보낼 다른 타입의 정보(string, object 등) JSON.stringify() 해 주기
const classInfo = JSON.stringify({ title: title, sub_category: values.subCategory.id, ... });
파일은 stringify를 해 주면 안 돼서 따로 처리해 주는 것!
- 새로운 FormData 생성하고, 거기에 append 메서드로 데이터 추가해 주기
const classData = new FormData(); classData.append('course', classInfo); classData.append('image', thumbnail);
append의 첫 번째 인자는 백엔드와 맞춰야하는 key값이고, 두 번째 인자가 각각의 데이터에 해당한다.
- body에 담아 서버에 전송~~~!
fetch(`${API}/courses/register`, { method: 'POST', headers: { Authorization: localStorage.getItem('access_token'), }, body: classData, })
신기하고 유용한 API가 정말 정말 많은 것 같다.🍀🍀