formData 이미지 전송 웹(react)과 하이브리드 앱(react native) 차이

Rosevillage·2024년 10월 10일
0

이미지 업로드를 개발하던 도중 (백엔드 + 앱) 개발 팀원이자 선배 개발자의 조언대로 코드를 작성하려다가 웹에서는 동작하지 않아 알게된 정보에 대해서 정리해 본다.

FormData

formData는 html의 form 필드의 데이터를 일련의 key/value 쌍으로 생성해 쉽게 전송할 수 있도록 돕는 인터페이스로 form element를 전달해 사용하는 방법부터 특정 값들을 직접 formData에 append하는 방법 등으로 사용가능하다.

form 필드의 쉬운 전송을 돕는 formData 객체가 fetch 등 http 요청에서 request body에 지정되었을때 브라우저는 내부적으로 multipart/form-data 형식으로 인코딩하고 Content-Type을 multipart/form-data; boundary=... 형태로 정의한 후 요청을 전송한다.

formData는 간단하게 사용하는 경우 form element를 그대로 formData 객체에 전달해 네트워크 요청에서 사용할 수 있다.

function App() {
 function handleSubmit(e) {
  e.preventDefault();
  fetch('https://example.com/api/endpoint', {
   method: 'POST',
   body: new FormData(formElem)
  });
 }
 
 return (
  <form>
   <input type="text" name="name" value="James">
   <input type="number" name="age" value="2">
   <button type="submit">
  </form>
 )
}

form element를 직접 전달하는 것 뿐만 아니라 특정 값을 직접 formData에 포함시키는 것도 가능하다.

...
const formData = new FormData();
formData.append('name', 'rose')

이러한 formData는 웹뿐만 아니라 react native에서도 사용이 가능하다 다만 이미지 등의 파일을 처리하는 방식에 있어서 차이점을 보인다.

웹(react)에서의 formData

formDataFile 객체와 Blob 객체 또한 포함시킬 수 있기에 input type file과 같이 사용되기도 하는 등 전송하려는 데이터가 file 혹은 blob 객체일 경우에 주로 사용된다.

...
//selectedImage: imageFile
function handleChangeImage(e) {
 const file = e.target.files[0]
 setSelectedImage(file)
}

function handleSendImage() {
 const formData = new FormData();
 formData.append('image', selectedImage)
 fetch('https://example.com/api/endpoint', {
   method: 'POST',
   body: formData
  });
}

return (
 <>
  <input 
   type="file"
   multiple
   accept="image/*"
   onChange={handleChangeImage}
  />
  <button onClick={handleSendImage}>이미지 전송</button>
 </>
)

웹에서는 formData에 이미지를 포함시키기 위해서는 이미지를 File 객체의 형태로 포함시켜야 한다.

react native에서의 formData

react native의 formData는 서버와의 통신에서 주로 파일 업로드를 처리하기 위해 사용되며, 일반적으로 이미지 파일이나 동영상 파일을 서버로 보내는 상황에서 많이 사용된다.

react native에서 formData는 웹과 달리 모바일 파일 시스템에 접근해야 하므로, 파일 전송 방식이 약간 다른데, react native는 파일을 uri, type, name 필드를 가진 객체로 저장하면, 객체를 적절히 변환하여 multipart/form-data로 처리한다.

const image = {
  uri: 'file://path/to/image.jpg',  // 파일 URI
  type: 'image/jpeg',  // 파일 MIME 타입
  name: 'image.jpg',  // 파일 이름
};

const formData = new FormData();
formData.append('image', image);

여기서 uri 필드는 반드시 절대 경로로 제공되어야 하며, 모바일 장치에서 파일 시스템에 접근하여 이미지를 읽어오기 때문에 파일의 경로가 정확해야 한다.

차이점

웹과 react native에서formData를 다루는 방식의 차이는 파일을 처리하는 방식에서 온다. 간단하게 정리하자면

  • 웹 : 파일을 전송하는 경우 formDataFile 객체를 전달해야한다.
  • react native(하이브리드 앱) : 파일을 전송하는 경우 formData에 특정한 형태의 객체(딕셔너리)를 전달해야한다.

이 차이는 다음과 같은 예시에서 명확하게 보여진다.

url로 가지고 있는 이미지

api를 통해 받은 이미 서버에 존재하는 이미지의 url을 요청에 포함시켜야 하는 경우가 있을때 우리는 formData에 그 이미지를 넣어 요청에 전달해야 하는데 이때 RN(react native)의 경우 다음과 같이 처리할 수 있다.

//imgUrl : api로 받은 이미지의 url
const image = {
  uri: url,
  type: 'image/png', 
  name: 'image.png', 
};

const formData = new FormData();
formData.append('image', image);

RN의 경우 uri 필드에 'file://' 형식이 아닌 'https://' 형식의 url을 넣어도 file로 인식해 처리한다. 때문에 url를 uri 필드에 그대로 적용하는 방식으로 처리가 가능하다.

그러나 웹에서는 file 객체의 형태로 url를 재가공해 전달해야 한다.

async function urlToFile(imageUrl, fileName, mimeType) {
  const response = await fetch(imageUrl);
  const blob = await response.blob();

  return new File([blob], fileName, { type: mimeType });
};

//imgUrl: api로 받은 이미지의 url
const imageFile = urlToFile(imgUrl, 'image.png', 'image/png');
const formData = new FormData();
formData.append('image', imageFile)

Reference

모던 자바스크립트 튜토리얼: FormData 객체

0개의 댓글

관련 채용 정보