[Javascript] 비동기 통신(AJAX)으로 폼 데이터를 전송하는 최신 방법들 - 비동기 통신을 위한 URLSearchParams와 FormData 객체 사용법
<form name="optionform" id="optionform" method="post" action="./response">
<input type="hidden" name="currpage" id="currpage" value="1" />
<label for="startdate">시작일</label><input type="text" name="startdate" id="startdate" class="ymd10" value=""/>
<label for="enddate">종료일</label><input type="text" name="enddate" id="enddate" class="ymd10"/>
<span>페이지 개수</span><select name="pagesize" id="pagesize">
<option value="100">100개</option>
<option value="200">200개</option>
<option value="500">500개</option>
<option value="1000">1000개</option>
</select>
<input type="checkbox" name="chkinclude" id="chkinclude" class="bigcheckbox" value="Y"/>
<input type="button" name="btnsubmit" id="btnsubmit" value="전송" onclick="dosubmit()"/>
</form>
폼 데이터를 서버로 전송해서 원하는 결과를 얻으려면 폼 데이터 전체를 서버로 전송하고, 웹 페이지는 결과를 표시하기 위해 화면을 갱신하게 됩니다.
웹이 진화하면서 리액트(React) 같은 SPA기반 프런트엔드 프레임워크나 하이브리드 앱 같은 모바일 UI를 지원하는 웹이 일반화되면서 자바스크립트의 비동기 통신이 기본으로 자리 잡게 되었습니다.
일반 웹에서 조차도 서버와의 통신은 비동기로 처리하고, 사용자는 내부에서 일어나는 처리 프로세스를 최대한 감추는 추세로 바뀌어 가고 있습니다.
폼 데이터를 다룰 때 기존에는 폼 전체를 서버로 전송만 하면 되기 때문에 폼 데이터를 별도로 핸들링할 필요가 없었습니다.
비동기 통신을 하게 되면 폼 데이터를 전송할 수 있는 데이터 포맷으로 생성한 후 전송하는 URL에 함께 전달해야 합니다.
과거 우리가 사용했던 원시적인 폼 데이터 전달 방법은 다음과 같았습니다.
아마도 이런 방식을 아직도 사용하고 있다면, 구석기 시대에서 전혀 진화가 안된 개발자이거나, 비동기 전송을 위한 폼 데이터 처리하는 방법을 처음부터 잘못 배운 것입니다.
var qs = "startdate="+document.querySelector('#startdate')+"&enddate="+document.querySelector('#startdate')+"&pagesize="+encodeURIComponent(document.querySelector('#pagesize'));
이 방법이 잘못된 사용방법은 아닙니다.
다만, 폼 데이터를 다루는 더 효율적이고 좋은 방법이 있는데도 이런 방법을 계속 사용하고 있으면 낮은 코딩 효율에 머물러서 시간을 버리고 있는 것 밖에 안됩니다.
먼저 비동기 전송을 할 때 폼 데이터가 전송되는 기본 방식부터 알아보겠습니다.
1. 비동기 폼 전송 기본
위의 폼 예에서는 버튼(button)을 클릭하면 폼 데이터가 서버로 전송되는 게 아니라 "dosubmit()" 함수를 호출합니다.
폼 안에 다음과 같은 폼 전송 버튼을 대신 넣고 클릭하면 폼 데이터가 폼 태그(<form>)의 "action" 속성에 정의된 URL로 전달됩니다.
<input type="submit" name="btnsubmit" id="btnsubmit" value="전송"/>
"method" 속성이 "get" 이면 쿼리 스트링으로 "post"면 폼 데이터를 요청 헤더에 담아서 보내게 됩니다.
이런 방식을 비동기와 구분하기 위해 동기식 전송이라고 합니다.
비동기로 폼 데이터를 전송할 때 전송할 데이터를 생성해서 보내는 기본인 방법은 다음과 같습니다.
비동기 전송 처리를 담당하는 객체인 XMLHttpRequest 객체의 send() 메서드 인자로 쿼리스트링 형태의 폼 데이터를 만들어서 보내게 됩니다.
const req = new XMLHttpRequest();
req.open("POST", '/response', true);
req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
req.onreadystatechange = () => {
if (req.readyState === XMLHttpRequest.DONE && req.status === 200) {
// 비동기 실행 결과 처리
}
}
req.send("data1=val1¶m1=val2");
비동기 폼 데이터 전송은 XMLHttpRequest 객체보다는 프로미스(Promise)나 패치(Fetch)와 같은 비동기 지원 객체를 더 많이 사용합니다.
2. URLSearchParams 객체로 전송할 폼 데이터 생성
URLSearchParams 객체는 키/밸류로 쌍으로 구성된 데이터들을 관리하기 위해 제공되는 객체입니다.
JSON 데이터를 표현하는 것과 같은 방법으로 서술한 키/밸류 데이터를 URLSearchParams 객체 생성자 인수로 넘겨서 객체를 생성합니다.
그리고 이 URLSearchParams 객체는 비동기 통신을 할 때 객체 그 자체를 폼 데이터로 넘겨서 전송할 수 있습니다.
let params = new URLSearchParams({userid: 'ryon', username: '라이언', address: '카카오', zipcode: '12345'});
fetch('./make_request', {
method: "POST", body: params //URLSearchParams 객체 자체를 전송할 폼데이터로 사용
}).then(response => {
response.text().then(ret => {
//성공 결과 반환값 처리
})
})
앞서의 쿼리스트링 형태로 전송할 문자열을 생성하는 것과 별로 다를 게 없어 보이지만, URLSearchParams 객체는 객체 데이터를 관리할 수 있는 다양한 메서드를 기본으로 제공합니다.
append() 메서드로 키/밸류 쌍을 추가할 수도 있고, 객체 데이터를 쿼리스트링 포맷으로 된 문자열로 출력할 수 있는 toString() 메서드도 제공합니다.
인코딩이 필요한 데이터는 인코딩까지 자동으로 해주기 때문에 별도로 전송 데이터를 인코딩하는 수고로움을 하지 않아도 됩니다.
폼 필드의 값을 URLSearchParams 객체 데이터로 추가하려면 다음처럼 작성하면 됩니다.
function dosubmit2(pagesize){
let params = new URLSearchParams({startdate: document.querySelector('#startdate').value, enddate: document.querySelector('#enddate').value, pagesize: pagesize});
params.append('chkinclude',document.querySelector('#chkinclude').value);
console.log(params.toString());
//AJAX...
}
3. FormData 객체로 전송할 폼 데이터 생성
폼 데이터 전체를 전송할 때는 URLSearchParams 는 이래저래 불편합니다.
데이터를 관리하는 편리한 메서드들이 있지만, 처음 객체를 생성할 때는 폼 필드 값을 하나씩 가져와야 하는 번거로움이 있습니다.
이런 불편함을 한번에 해결해 주는 객체가 FormData 객체입니다.
FormData 객체는 생성자에 쿼리선택자로 폼 태그를 선택해서 넘기면 모든 폼 필드의 키/밸류를 가진 객체를 생성합니다.
URLSearchParams 객체와 유사한 메서드들을 제공하기 때문에 폼 필드 키/밸류 추가/삭제 등을 할 수 있습니다.
let formData = new FormData(document.querySelector('#optionform'));
fetch('./response', {
method: "POST", body: formData
}).then(response => {
response.text().then(ret=> {
})
}).catch(error => {
console.error('에러.')
});
FormData 객체는 URLSearchParams 객체와 달리 문자열 쿼링스트링 형태로 출력하는 메서드는 제공하지 않으며, entries() 메서드로 키/밸류 이터레이터 데이터를 가져와 루프문으로 데이터를 출력할 수 있습니다.
const fd = new FormData(document.querySelector('#optionform'));
for (const field of formData.entries()) {
console.log(`${field[0]}, ${field[1]}`);
}
펼침 연산자를 이용해 배열로 FormData 객체의 데이터를 확인할 수도 있습니다.
const fd = new FormData(document.querySelector('#optionform'));
console.table([...fd]);
4. jQuery로 폼 데이터 가져오기
참고로 jQuery에는 serialize() 함수가 있어서 폼 데이터를 직렬화(serialize()) 해서 쿼리스트링으로 생성해 주는 함수가 있습니다.
let formData = $("#optionform").serialize();//currpage=1&startdate=&enddate=&pagesize=100
fetch('./response', {
method: "POST", body: formData
}).then(response => {
response.text().then(ret=> {
})
}).catch(error => {
console.error('에러.')
});
앞서의 HTML 폼을 직렬화한 데이터를 콘솔에 출력하면 다음과 같이 생성된 쿼리스트링을 확인할 수 있습니다.