FormData 객체

hangkemiii·2023년 1월 31일
0

Javascript

목록 보기
10/11
post-thumbnail
post-custom-banner

업무 중, 이미지 파일을 서버에 보내주는 작업이 필요했는데 이전 해커톤과 기업협업 때 사용했던 formdata 객체가 명확히 기억이 나지 않아서 다시 한번 정리 후에 작업을 해보려고 한다.

FormData

FormData는 ajax로 폼을 쉽게 보내주도록 도와주는 객체이다. 여기서 FormData 객체는 HTML 폼 데이터를 나타낸다.

HTML form
HTML <form> 요소는 정보를 제출하기 위한 대화형 컨트롤을 포함하는 문서 구획을 나타낸다.

<form action="" method="get" class="form-example">
  <div class="form-example">
    <label for="name">Enter your name: </label>
    <input type="text" name="name" id="name" required>
  </div>
  <div class="form-example">
    <label for="email">Enter your email: </label>
    <input type="email" name="email" id="email" required>
  </div>
  <div class="form-example">
    <input type="submit" value="Subscribe!">
  </div>
</form>
Enter your name:
Enter your email:

위 코드에서는 name과 email에 있는 정보가 form 안에 담겨 제출되게 된다.
let formData = new FormData([form]);

HTML에 <form> 요소가 있는 경우, 위와 같은 코드를 작성하면 해당 폼 요소의 필드 전체가 자동으로 반영되게 된다.

fetch 등의 네트워크 메서드가 FormData 객체를 바디로 받는다는 것이 FormData의 특징이다. 이때 브라우저가 전송하는 HTTP 메시지는 인코딩되고 Content-Type 속성은 multipart/form-data로 지정된 후 전송되게 된다.

이때 서버 관점에서는 FormData를 사용한 방식과 일반 폼 전송 방식에 차이가 없다.

<form id="formElem">
  <input type="text" name="name" value="Bora">
  <input type="text" name="surname" value="Lee">
  <input type="submit">
</form>

<script>
  formElem.onsubmit = async (e) => {
    e.preventDefault();

    let response = await fetch('/article/formdata/post/user', {
      method: 'POST',
      body: new FormData(formElem)
    });

    let result = await response.json();

    alert(result.message);
  };
</script>

위와 같이 간단한 폼을 전송한다고 가정해봤을 때, formElem이라는 폼 안에 있는 name과 surname의 value는 submit 되었을 때, FormData를 통해 body에 담겨서 서버측으로 전송되게 된다. 서버는 POST 요청을 받아 FormData를 받아 '저장 성공'이라는 응답을 다시 보내준다.

FormData 메서드

FormData에 속하는 필드는 아래와 같은 메서드로 수정할 수 있다.

  • formData.append(name, value) -
    namevalue를 가진 폼 필드를 추가
  • formData.append(name, blob, fileName)<input type="file">형태의 필드를 추가. 세 번째 인수 fileName은 사용자가 해당 이름을 가진 파일을 폼에 추가한 것처럼 설정해줌
  • formData.delete(name)name에 해당하는 필드를 삭제
  • formData.get(name)name에 해당하는 필드의 값을 가져옴
  • formData.has(name)name에 해당하는 필드가 있으면 true를, 그렇지 않으면 false를 반환

폼은 name이 같은 필드 여러 개를 허용하기 때문에 append 메서드를 여러 번 호출해 이름이 같은 필드를 계속 추가해도 문제가 없다.

append 메서드 이외에도 set으로도 필드 추가를 할 수 있지만, set을 사용할 경우에는 동일한 이름을 가진 필드를 모두 제거하고 새로운 필드 하나로만 덮어씌워지기 때문에 주의해야 한다.

Blob 데이터가 있는 폼 전송하기

Blob이란?
Blob은 Binary Large Object의 줄임말로 텍스트 혹은 이진 데이터를 담고 있는 큰 객체이다. 주로 이미지, 오디오, 비디오 등의 다른 타입에 비해 크기가 큰 타입들을 관리할 때 많이 사용하며, 멀티미디어를 저장하는 목적이 있다.

위와 같은 Blob 객체 역시 fetch 메서드의 body 매개변수에 바로 넘겨줄 수 있지만, 폼에 필드를 추가하고 이미지 이름등의 메타데이터를 같이 실어 넘겨주는 것이 좀 더 편리하다.

서버의 입장에서도 원시 바이너리 데이터를 받는 것 보다 multipart-encoded 폼을 받는 것이 조금 더 적합하다.

<body style="margin:0">
  <canvas id="canvasElem" width="100" height="80" style="border:1px solid"></canvas>

  <input type="button" value="이미지 전송" onclick="submit()">

  <script>
    canvasElem.onmousemove = function(e) {
      let ctx = canvasElem.getContext('2d');
      ctx.lineTo(e.clientX, e.clientY);
      ctx.stroke();
    };

    async function submit() {
      let imageBlob = await new Promise(resolve => canvasElem.toBlob(resolve, 'image/png'));

      let formData = new FormData();
      formData.append("firstName", "Bora");
      formData.append("image", imageBlob, "image.png");

      let response = await fetch('/article/formdata/post/image-form', {
        method: 'POST',
        body: formData
      });
      let result = await response.json();
      alert(result.message);
    }

  </script>
</body>

위 코드는 <canvas>를 사용해 만든 이미지를 FormData를 사용해 폼 형태로 다른 추가 필드와 함께 전송하는 예시이다. 위 코드에서 formData.append("image", imageBlob, "image.png"); 코드를 통해 <input type="file" name="image">에서 image.png라는 파일을 전송하는 것과 같이 canvas의 이미지 파일을 서버로 전송할 수 있다. 즉, 두 번째 인수의 Blob 객체를 첫번째 인수의 key값으로 세 번째 인수의 value의 이름을 가지게 하여 서버에 전송하는 것이다.


현재 고민하고 있던 작업은, 모든 input 값의 value와 이미지를 서버에 어떻게 전송하냐였는데, FormData를 활용하여 각각의 input value를 append하여 필드로 추가해 주고, 이미지 역시 Blob 객체로 전환하여 서버로 전송해주면 해결될 것 같다. 역시 한번 정리하는게 머리에 잘들어오는군..

참고 문서

https://ko.javascript.info/formdata
https://developer.mozilla.org/ko/docs/Web/HTML/Element/form
https://medium.com/hcleedev/web-javascript-blob-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0-9140146e87a8

profile
Front-End Developer
post-custom-banner

0개의 댓글