시청자 참여 이벤트를 생성했는데 문제가 발생했다. 페이지에서 <form>태그의 데이터를 ajax로 전송하는데 데이터는 컨트롤러에 정상적으로 들어왔는데 문제가 발생했다. 분명히 ajax로 데이터를 전송할 때 ContentType을 application/json으로 설정했다. 그런데 서버에서 application/x-www-form-urlencoded로 메세지를 받았다고 경고를 한다.
org.springframework.web.HttpMediaTypeNotSupportedException: Content type ‘application/x-www-form-urlencoded;charset=UTF-8’ not supported
데이터 파싱이 안되고 저장이 안됐다면 잘못된 부분을 좀 더 알 수 있었을 거 같은데 저장이 잘 되었고 성공 메세지를 보냈다. 이게 뭔 상황일까?
1. 현상 발생 원인 <form> 내부 <button> 태그
<form> 태그를 이용해서 데이터를 전달할 때 내부에 <button> 태그를 누르면 디폴트로 데이터를 지정된 URL로 전송한다.
attributes를 이용하여 다양한 설정을 할 수 있다. action을 설정하여 어느 페이지로 전송할지, target을 이용하여 어느 URL로 보낼지 등을 설정할 수 있다. 이런 모든 요소를 선택하지 않으면 디폴트로 현재 페이지, 현재 URL을 기준으로 데이터를 전송한다.
이때 디폴트로 데이터를 전달할 때 데이터를 전달하는 형식이 application/x-www-form-urlencoded;charset=UTF-8 이다. <form> 내부에 <button>에 별다른 설정을 하지 않게 되면 디폴트로 <button>의 type=submit으로 되어 있다.
이러면 별다른 스크립트 없이 버튼을 누르면 디폴트 설정의 데이터를 ‘application/x-www-form-urlencoded;charset=UTF-8’의 형식으로 전달한다.
2. 현상 발생 과정
<form id="form-poll-entry" method="post">
<input id="vote1" type="checkbox" name="key1" value="option1"/>
<input id="vote2" type="checkbox" name="key2" value="option2"/>
<input id="vote3" type="checkbox" name="key3" value="option3"/>
<button class="poll-submit" >제출</button>
</form>
대략 이런 방식으로 form을 만들어 전송한다. form 내부에 버튼이 있는 형태로 만들어져 있기 때문에 버튼을 누르게 되면 디폴트로 현재 페이지에 URL에 form 데이터를 전달한다. 여기서 form을 전달하면 ‘application/x-www-form-urlencoded;charset=UTF-8’로 데이터를 전달한다.
문제는 이 페이지에서는 데이터 전송을 form페이지에 의지한 것이 아니라 ajax로 호출 된 데이터를 이용해서 데이터를 전달했다는 것이다.
var postData = $('#form-poll-entry').serializeArray();
var postPayload = { userPollEntry: [] };
$.ajax(window.location.href , {
contentType: 'application/json',
data: JSON.stringify(postPayload),
method: 'POST',
dataType: 'json',
processData: true,
crossDomain: true,
xhrFields: {
withCredentials: true
}
});
실제 코드와는 다르지만 이 스크립트를 통하여 특정 태그를 가진 <button> 또는 <a> 태그를 누르면 ID가 form-poll-entry인 데이터를 불러와서 저장하고 Ajax 호출을 실행한다. 그리고 서버에서 @RequestBody 를 이용하여 데이터를 파싱한다.
이때 호출을 2번 하게 된것이다. 첫번째 호출에서는 ajax를 통하여 정상적으로 application/json형태의 데이터를 호출한다. 당연히 정상 처리된 데이터를 정상적으로 파싱하여 데이터를 정리한다.
두번째 <form> 내부의 <button>에서 호출된 데이터는 위에 설정되어 있는 POST 방식으로 디폴트(현재 페이지, 현재 URL)로 전달한다.
이때는 ‘application/x-www-form-urlencoded;charset=UTF-8’ 형태로 데이터가 전달되고 위에 ajax의 컨트롤러와 같은 컨트롤러로 데이터가 전달되면서 application/json의 데이터를 처리하도록 만들어진 컨트롤러에서 에러를 발생 시켰던 것이다.
어처구니 없는 실수 였지만 솔직히 당시에는 생각도 못했고… 결국 선배님께 요청을 하여 원인을 알게 되었다.
3. 해결 방법
<form> 태그 버튼에서 값을 전송하지 않게 만들면 된다. 디폴트값으로 <form> 내부의 <button>은 type =”submit” 으로 되어 있다. 이때 버튼을 누르면 <form>을 호출하게 된다.
이를 막기 위해서 <button type=”button”> 으로 설정을 하면 버튼의 역할만 수행하고 <form> 호출은 하지 않는다. 위에 html에서는 다음과 같이 변경하면 된다.
<form id="form-poll-entry" method="post">
<input id="vote1" type="checkbox" name="key1" value="option1"/>
<input id="vote2" type="checkbox" name="key2" value="option2"/>
<input id="vote3" type="checkbox" name="key3" value="option3"/>
<button class="poll-submit" type="button">제출</button>
</form>
