FormData 객체

파일 여부나 추가 필드 여부 등과 상관없이 통용되는
HTML 폼 전송 방법

설명

FormData 는 폼을 쉽게 보내도록 도와주는 객체입니다.

이름을 보고 유추하셨듯이 FormData 객체는 HTML 폼 데이터를 나타냅니다.

 

생성자는 다음과 같습니다.

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>

 

FormData 메서드

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

 

1. formData.append(name, value)

name 과 value 를 가진 폼 필드를 추가

 

2. formData.append(name, blob, fileName)

<input type="file"> 형태의 필드를 추가, 세 번째 인수 fileName 은 사용자가 해당 이름을 가진 파일을 폼에 추가한 것처럼 설정해 줍니다.

 

3. formData.delete(name)

name 에 해당하는 필드를 삭제합니다.

 

4. formData.get(name)

name 에 해당하는 필드의 값을 가져옵니다.

 

5. formData.has(name) 

name 에 해당하는 필드가 있다면 true , 그렇지 않으면 false 를 반환합니다.

 

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

append 메서드 이외에 필드 추가 시 사용할 수 있는 메서드로 set 도 있습니다. set 이 append 메서드와 다른 점은 set 은 name 과 동일한 이름을 가진 필드를 모두 제거하고 새로운 필드 하나를 추가한다는 데 있습니다.

따라서 set 메서드를 쓰면 name 을 가진 필드가 단 한개만 있게끔 보장할 수 있습니다.

이 외에 다른 기능은 append 와 동일합니다.

  • formData.set(name, value)
  • formData.set(name, blob, fileName)

또한 폼 데이터 필드에 반복 작업이 필요할 땐 for..of 루프를 사용할 수 있습니다.

let formData = new FormData();
formData.append('key1', 'value1');
formData.append('key2', 'value2');

// key/value 쌍이 담긴 리스트
for(let [name, value] of formData) {
  alert(`${name} = ${value}`); // key1 = value1, then key2 = value2
}

파일이 있는 폼 전송하기

폼을 전송할 때 HTTP 메시지의 Content-Type 속성은 항상 multipart/form-data 이고 메시지는 인코딩되어 전송됩니다.

파일이 있는 폼도 당연히 이 규칙을 따르기 때문에 <input type="file"> 로 지정한 필드 역시 일반폼을 전송할 대와 유사하게 전송됩니다.

<form id="formElem">
  <input type="text" name="firstName" value="Bora">
  Picture: <input type="file" name="picture" accept="image/*">
  <input type="submit">
</form>

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

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

    let result = await response.json();

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

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

이미지 같은 동적으로 생성된 바이너리 파일은 Blob 객체를 사용해 쉽게 전송할 수 있습니다.

이때 Blob 객체는 fetch 메서드의 body 매개변수에 바로 넘겨줄 수 있습니다.

Blob (Binary Large Object)
이진 데이터를 다루기 위한 객체입니다.
파일 데이터, 이미지, 오디오, 비디오, 또는 텍스트 데이터와 같은 큰 데이터 덩어리를 브라우저에서 처리하거나 서버와 주고받을 때 사용합니다.
즉, 대량의 이진 데이터를 다룰 수 있게 해주는 객체입니다.

 

그런데 실제 코딩을 하다 보면 이미지를 별도로 넘겨주는 것 보다 폼에 필드를 추가하고 여기에 이미지 이름 등의 메타데이터를 같이 실어 넘겨주는게 좀 더 편리합니다.

 

서버 입장에서도 원시 바이너리 데이터를 받는 것보다 multipart-encoded 폼을 받는게 좀 더 편하고 적합합니다.

formData.append(name, blob, fileName);

 

1. name

폼 필드의 이름입니다. 서버에서 이 이름을 통해 데이터를 참조합니다.

 

2. blob

파일 데이터(내용) 입니다.

File 객체, Blob 객체, 또는 다른 데이터 형식(텍스트 등) 을 넣을 수 있습니다.

 

3. fileName

서버에서 이 파일을 받을 때 사용자가 업로드한 파일 이름처럼 설정됩니다.

즉, 사용자가 파일을 업로드하지 않았더라도, 특정 이름을 가진 파일이 서버로 전송되는 것처럼 보이게 합니다.

 

fileName 은 필드 이름이 아닙니다.
필드 이름(name) : 서버에서 폼 필드 데이터를 참조할 때 사용하는 이름입니다.
파일 이름(fileName) : 전송된 파일의 이름입니다. 서버는 이 이름을 사용해 파일을 저장하거나 처리합니다. 

 

const formData = new FormData();

// HTML Input으로부터 파일 가져오기
const fileInput = document.querySelector('input[type="file"]');
const file = fileInput.files[0];

// FormData에 파일 추가
formData.append('profileImage', file, 'renamedImage.png');

// 전송
fetch('https://example.com/upload', {
  method: 'POST',
  body: formData,
});

 

1. profileImage

서버에서 폼 필드로 참조할 이름

 

2. file

사용자가 선택한 파일 데이터

 

3. renamedImage.png

서버에서 파일 이름을 참조

 

이렇게 서버는 파일 이름을 renamedImage.png 로 인식하며, 사용자가 이 이름의 파일을 업로드한 것처럼 처리합니다.

// node express 예시

app.post('/upload', upload.single('profileImage'), (req, res) => {
  // 파일 정보
  console.log('파일 이름:', req.file.originalname);
  console.log('파일 경로:', req.file.path);
  console.log('필드 이름:', req.file.fieldname);

  // 추가적으로 FormData의 다른 데이터도 받을 수 있음
  console.log('폼 데이터:', req.body);

  res.json({ message: '파일 업로드 성공!', file: req.file });
});

 

'JavaScript' 카테고리의 다른 글

jQuery  (0) 2024.11.27
Chrome 으로 기초 디버깅  (1) 2024.10.29
JavaScript 최신 문법 정리  (0) 2024.10.28