Technology/JavaScript

서버에 이미지 파일 전송하기(feat. FormData, Ajax)

ikjo 2022. 2. 5. 18:55

이미지 파일 업로드하기

웹 브라우저단에서 서버로 이미지 파일을 전송하고 싶은 경우에는 어떻게 할까? 우선 당연히 브라우저 단에 이미지 파일이 업로드가 되있어야 한다. 이때 HTML form 태그를 이용하면 되는데, form 태그 내 input 태그의 type을 file로 설정하여 이미지 파일을 업로드 받을 수 있다. 여기서 중요한 것은 form 태그의 속성값 중 enctype(인코딩 타입)을 "multipart/form-data"로 설정해주어야 한다는 것이다.

 

여기서 multipart란 웹 브라우저가 서버에 요청을 보낼 때 HTTP 프로토콜 바디 부분에 데이터를 여러 부분으로 나눠서 보내겠다는 것이다. 그러면 왜 굳이 데이터를 여러 부분으로 나누는 것일까? 그 이유는 HTTP Request Body에 들어가는 데이터의 타입이 여러개일 수도 있기 때문이다. 예를 들어, 이미지 파일과 함께 해당 이미지 파일에 대한 설명이 들어간 텍스트 데이터를 서버에 보내야할 경우이다. 이때 HTTP Request Body에 들어가는 데이터 타입을 명시하는 HTTP Request Header는 Content-type로 하나의 타입만 명시할 수 있기 때문이다.

 
<form id = "uploadForm" method="post" action="upload" enctype="multipart/form-data">
  <input type="file" name="file">
  <input type="text" name="text">
  <input type="submit">
</form>

※ enctype을 별도 지정하지 않을 경우 form submit에 의해 전송되는 데이터들의 Content-type은 application/x-www-form-urlencode으로 설정된다.

 

 

비동기적으로 이미지 파일 전송하기

만일 동기적으로 파일을 전송할 것이라면 위 상태에서 submit 버튼을 클릭하여 서버에 이미지 파일을 전송하면 된다. 이 경우 action 속성에 지정한 페이지로의 페이지 전환이 발생하면서 사용자가 잠시 대기하는 시간이 발생한다. 하지만 사용자 입장에서 만일 이미지 파일 전송 이후 또 다른 추가 작업이 바로 이어져야 한다면 이는 안 좋은 UX를 제공할 것이다. (서버 입장에서도 웹 페이지 리소스를 다시 호출해야하므로 트래픽 부담이 발생한다.)

※ 단, submit 사용 시 event.preventDefault()를 활용하면 페이지 전환 없이 원하는 작업만 수행시킬 수 있다.

 

그렇다면 비동기적으로 이미지 파일을 전송하려면 어떻게 할까? 바로 Ajax를 이용하면 된다. Ajax의 기본 사용법은 다음 글을 참고할 수 있다.

 

 

Ajax의 기본 사용법(Vanilla JavaScript)

웹 개발 트렌드 최근(정확하게는 몇년 전부터) 웹 개발 트렌드는 jsp를 사용하지 않고 프론트엔드 코드와 API 코드를 따로 구성하여 운영하는 추세이다. 서버 코드가 클라이언트 페이지에 존재하

ikjo.tistory.com

 

하지만 Ajax로 서버에 데이터를 전송할 때 이미지 파일을 어떻게 전송할지 의문이다. 이때 사용되는 것이 자바스크립트의 FormData 객체이다. FormData 객체는 key/value 쌍으로 데이터를 저장하는데, form 태그에 업로드 된 이미지 파일과 함께 전송할 데이터들을 FormData 객체에 추가시켜주면 된다.

 

// form 태그 DOM 지정
let form = document.querySelector('#uploadForm');

// FormData 객체 생성
let formData = new FormData();

// 생성한 FormData 객체에 데이터 추가
formData.append('text', form.text.value);
formData.append('file', form.file.files[0]); // 업로드된 이미지 파일이 하나인 경우

※ FormData 객체에는 이외에도 여러가지 메서드들이 존재하는데 여기서는 다루지 않겠다.

 

전송할 데이터를 FormData 객체에 모두 추가시켰으니 이제 Ajax를 통해 서버에 전송시키기만 하면 된다.

 

let uploadBtn = document.querySelector('.btn');

uploadBtn.addEventListener('click', this.sendData.bind(this));

function sendData() {

	// (...생략...) FormData 객체 생성 및 데이터 저장

	let xhttp = new XMLHttpRequest();

	if(!xhttp) {
		alert("XMLHTTP 인스턴스를 만들 수 없습니다.");
		return false;
	}

	xhttp.addEventListener('load', (() => {
		alert("전송 완료!!");
	}).bind(this));

	xhttp.open("POST", "upload");
	xhttp.setRequestHeader("hello", "world"); // 비표준 헤더 지정
	xhttp.send(formData);
}

 

이로써 브라우저 단에서 처리해야 할 이미지를 업로드 및 전송 작업은 끝이 났다. 이제는 서버에 맡기면 된다. 하지만 위 코드에서 특이한 점은 RequestHeader를 비표준 헤더를 설정했다는 것이다. 일반적으로 저 자리는 Content-Type을 명시하는 자리였다. 그 이유는 FormData 객체를 전송할 때는 브라우저에 의해 헤더 값이 자동으로 multipart/form-data으로 설정되기 때문에 특정 표준 Content-Type을 명시할 경우 충돌이 일어나기 때문이다. 

 

 

참고자료

  • 부스트코스 "웹 프로그래밍(풀스택)"
  • https://velog.io/@shin6403/HTTP-multipartform-data-%EB%9E%80
  • https://stackoverflow.com/questions/23802133/send-xmlhttprequest-with-both-header-and-formdata