이미지 프리뷰 나타내기
<input type="file"> 로 입력한 파일의 URL에 직접 접근할 수 없는 이유
브라우저는 “동일 출처 정책”(Same-Origin Policy)을 구현하여 한 출처에서 로드된 문서나 스크립트가 다른 출처의 리소스와 상호 작용하지 못하도록 합니다. 이는 브라우저에서 실행 중인 스크립트가 다른 출처로 간주되어 사용자 시스템의 파일에 직접 액세스할 수 없음을 의미합니다. 이 정책은 악성 웹사이트가 잠재적으로 민감한 로컬 파일을 읽을 수 있는 위험을 완화하는 데 도움이 됩니다.
따라서, 브라우저에서 실행되는 스크립트는 로컬 파일에 직접 접근할 수 없습니다. 로컬 파일은 다른 출처로 간주되기 때문입니다. 만약 브라우저가 로컬 파일의 URL을 직접 읽을 수 있다면 보안상의 문제가 발생할 수 있으므로 URL.createObjectURL()을 통해 객체의 URL을 생성해야 합니다.
<input type="file"> 의 files를 console.log로 출력해보면 다음과 같이 나옵니다.

URL.createObjectURL을 사용하면 다음과 같이 URL을 생성할 수 있습니다.

MDN 문서의 예제에서도 preview 를 나타낼 때 URL.createObjectURL을 사용하는 것을 볼 수 있습니다.
https://developer.mozilla.org/ko/docs/Web/HTML/Element/input/file#%EC%98%88%EC%A0%9C
React에서 적용한 코드
<input id="imageUploadInput" type="file" accept="image/*" ref={inpRef} onChange={handleImgPreview} className="ir"/>// 이미지 미리보기function handleImgPreview(e) { setPreview(URL.createObjectURL(e.target.files[0]));}이미지 서버에 전송하기
file을 FormData로 전송해야하는 이유
FormData는 이미지 파일과 같은 바이너리 파일 데이터 양식을 서버로 보내야 할 경우에 사용됩니다.
스택오버플로우에서는 FormData가 파일을 전송하는 유일하게 합리적인 방법이라고 설명하고 있습니다.
Why should FormData be used when uploading a file via AJAX?
“FormData 객체를 사용하면 키/값 쌍 집합을 컴파일하여 Fetch 또는 XMLHttpRequest API를 사용하여 전송할 수 있습니다. 주로 양식 데이터를 전송하는 데 사용되지만 키 데이터를 전송하기 위해 양식과 독립적으로 사용할 수도 있습니다. “ 라고 합니다.
처음에는 이 방법을 몰라 여러 가지로 시도해보았는데,
- URL.createObjectURL()로 생성된 값 그대로 보내기
- File을 FormData를 거치지 않고 그대로 보내기
이 두 가지 방법이었습니다…
이러한 방법들은 서버에서 바이너리 형식의 데이터를 요구했기 때문에 제대로 작동하지 않습니다.
async await 사용 이유
이미지를 먼저 서버에 보내 URL 값을 받아온 후, 그 URL 값과 함께 다른 form에 입력된 값들을 전달해야 했습니다. 그래서 비동기적으로 작동하도록 async/await을 사용하기로 했습니다.
그렇기 때문에 비동기적으로 작동해야 하기 때문에 async await 을 사용하기로 했다.
비동기 작업의 필요성
비동기 작업을 통해 이미지를 업로드한 후 반환된 URL을 받아 다른 데이터와 함께 서버에 전송할 수 있습니다. 이를 통해 전체 프로세스가 원활하게 진행되도록 합니다.
React 적용 코드
// 상품등록 데이터 전송const createProduct = async (e) => { e.preventDefault(); if (state) { const url = "<https://주소.co.kr>"; const getToken = localStorage.getItem("token"); const res = uploadImage(); try { await axios.post( `${url}/product`, { product: { itemName: name, price: parseInt( price.split(",").reduce((curr, acc) => curr + acc, "") ), link: link, itemImage: await res, // 이미지 파일 데이터 받아오기 }, }, { headers: { Authorization: `Bearer ${getToken}`, "Content-type": "application/json", }, } ); navigate("/myprofile"); } catch (error) { console.log(error); } }};📍틀린 부분이 있다면 말해주세요!
참고
Why local links are disabled by default in modern browsers?