Axios로 POST 요청을 보내는 방식은 크게 HTML 기본 form 전송과 JS로 직접 Axios를 사용하는 방식(JSON, x-www-form-urlencoded, FormData)로 나눌 수 있다.
브라우저는 자바스크립트 없이도 <form> 태그만으로 POST 요청을 보낼 수 있다.
이때 핵심은 action, method, enctype 세 속성이다.
<form action="http://localhost:8081/post/register2"
method="post"
enctype="application/x-www-form-urlencoded">
<div class="mb-2">
<label class="form-label" for="ftitle">제목</label>
<input class="form-control" id="ftitle" name="title" />
</div>
<div class="mb-3">
<label class="form-label" for="fcontents">내용</label>
<textarea class="form-control" id="fcontents" name="contents" rows="4"></textarea>
</div>
<button class="btn btn-dark" type="submit">폼 제출(POST)</button>
</form>
method="post": POST 요청으로 전송.enctype="application/x-www-form-urlencoded" name=value&name2=value2 형태로 body를 인코딩한다.@RequestParam, @ModelAttribute) 형태로 받기 좋다.<form action="http://localhost:8081/post/registerWithFile"
method="post"
enctype="multipart/form-data">
<div class="mb-2">
<label class="form-label" for="ftitle2">제목</label>
<input class="form-control" id="ftitle2" name="title" />
</div>
<div class="mb-2">
<label class="form-label" for="fcontents2">내용</label>
<textarea class="form-control" id="fcontents2" name="contents" rows="4"></textarea>
</div>
<div class="mb-3">
<label class="form-label" for="ffile">첨부파일</label>
<input class="form-control" id="ffile" name="file" type="file" />
</div>
<button class="btn btn-dark" type="submit">파일 포함 제출</button>
</form>
enctype="multipart/form-data" @RequestParam MultipartFile file 같은 형태로 처리.요약:
JS 객체를 만들어서 axios.post(url, data)에 넘기면, Axios가 기본적으로 JSON으로 직렬화해서 전송하는 패턴이다.
const onClickPostRegisterJson = async () => {
try {
const titleValue = document.getElementById("title").value;
const contentsValue = document.getElementById("contents").value;
const data = { title: titleValue, contents: contentsValue };
await axios.post("http://localhost:8081/post/register1", data);
window.location.href = "./post_list.html";
} catch (error) {
console.log(error);
alert("예상치 못한 에러가 발생하였습니다.");
}
};
그리고 HTML 입력 영역:
<div class="mb-2">
<label class="form-label">제목</label>
<input id="title" class="form-control" placeholder="테스트 제목" />
</div>
<div class="mb-3">
<label class="form-label">내용</label>
<textarea id="contents" class="form-control" rows="4" placeholder="테스트 내용"></textarea>
</div>
<button class="btn btn-dark" type="button" onclick="onClickPostRegisterJson()">
JSON 방식으로 전송
</button>
Content-Type: application/json으로 보내는 패턴으로 많이 사용된다.@RequestBody DTO로 받는 케이스와 잘 맞는다.form 기본 인코딩과 같은 형식으로 보내고 싶다면, URLSearchParams를 사용해 key/value를 구성한 뒤 전송하면 된다.
const onClickPostRegisterUrlencoded = async () => {
try {
const titleValue = document.getElementById("title2").value;
const contentsValue = document.getElementById("contents2").value;
const params = new URLSearchParams();
params.append("title", titleValue);
params.append("contents", contentsValue);
await axios.post("http://localhost:8081/post/register2", params, {
headers: { "Content-Type": "application/x-www-form-urlencoded" },
});
window.location.href = "./post_list.html";
} catch (error) {
console.log(error);
alert("예상치 못한 에러가 발생하였습니다.");
}
};
입력 영역:
<div class="mb-2">
<label class="form-label">제목</label>
<input id="title2" class="form-control" placeholder="테스트 제목" />
</div>
<div class="mb-3">
<label class="form-label">내용</label>
<textarea id="contents2" class="form-control" rows="4" placeholder="테스트 내용"></textarea>
</div>
<button class="btn btn-dark" type="button" onclick="onClickPostRegisterUrlencoded()">
x-www-form-urlencoded 방식으로 전송
</button>
URLSearchParams는 내부적으로 title=값&contents=값 형태의 쿼리 스트링을 만들어준다.application/x-www-form-urlencoded로 맞춰주는 것이 관례상 깔끔하다.파일 업로드, 이미지, 복합 데이터 전송이 필요할 때는 FormData를 사용한다.
const onClickPostRegisterFormData = async () => {
try {
const titleValue = document.getElementById("title3").value;
const contentsValue = document.getElementById("contents3").value;
const formData = new FormData();
formData.append("title", titleValue);
formData.append("contents", contentsValue);
await axios.post("http://localhost:8081/post/register3", formData);
window.location.href = "./post_list.html";
} catch (error) {
console.log(error);
alert("예상치 못한 에러가 발생하였습니다.");
}
};
입력 영역:
<div class="mb-2">
<label class="form-label">제목</label>
<input id="title3" class="form-control" placeholder="테스트 제목" />
</div>
<div class="mb-3">
<label class="form-label">내용</label>
<textarea id="contents3" class="form-control" rows="4" placeholder="테스트 내용"></textarea>
</div>
<button class="btn btn-dark" type="button" onclick="onClickPostRegisterFormData()">
FormData 방식으로 전송
</button>
<div class="mt-3 text-muted small">
참고: 변수 이름을 <code>titleValue</code> / <code>contentsValue</code>로 일관되게 써주지 않으면,
<code>data.append("title", title)</code>처럼 잘못된 변수를 넣어버려 값이 전송되지 않을 수 있다.
</div>
파일까지 포함하려면:
<input id="file3" class="form-control" type="file" />
const fileInput = document.getElementById("file3");
if (fileInput.files[0]) {
formData.append("file", fileInput.files[0]);
}
multipart/form-data로 전송된다.Content-Type을 직접 쓰지 않고 Axios/브라우저에게 맡기는 경우가 많다(경계(boundary) 자동 생성).@RequestPart + DTO, MultipartFile 조합으로 받는 패턴이 흔하다.간단히 비교하면:
| 상황 | 권장 방식 |
|---|---|
| SPA/REST API, JSON DTO | Axios + JSON |
| 기존 폼 파라미터 스타일 API | Axios + x-www-form-urlencoded (또는 form) |
| 파일/이미지 포함 | Axios + FormData |
| 간단한 페이지 전환형 폼 | 순수 HTML form POST |
@RequestBody와 잘 맞는다URLSearchParams + Content-Type 헤더를 명시해 “진짜 폼 인코딩”을 의도대로 맞춘다input[type=file].files[0]로 꺼내서 append 해야 한다 Content-Type은 직접 건드리지 않고 브라우저/axios에 맡기는 것이 일반적이다titleValue, contentsValue)을 FormData/params 구성 시에 동일하게 사용하지 않으면 값이 서버로 안 가는 버그가 생기기 쉬우므로, 이름 일관성이 중요