Django와 HTMX로 동적 웹 개발을 단순화하기 (4편) : payload는 어떻게?
기존 자바스크립트의 fetch를 이용한 Ajax 요청에서는 POST 요청을 보낼 때, 보통 JSON.stringify()를 이용해 페이로드(payload)를 직접 조립해서 보냅니다.
대략 이런 감각이죠.
fetch("/api/todos/", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-CSRFToken": csrftoken
},
body: JSON.stringify({
title: "장보기",
done: false,
priority: 3
})
})
그렇다면 HTMX에서는 어떨까요? 도대체 hx-post는 데이터를 어떤 식으로 서버에 보내는 걸까요? 많은 예제가 아주 단순한 기능만 보여주다 보니, 복잡한 페이로드를 만들어 보내는 실전 예제를 찾기가 의외로 힘듭니다.
이번 포스트에서는 HTMX에서의 데이터 전송 방식과 서버에서 이를 처리하는 법을 완전히 정리해 보겠습니다.

HTMX의 데이터 전송 방식
fetch의 페이로드 감각과 hx-post의 데이터 전송 방식은 꽤 다릅니다.
fetch에서는 개발자가 직접 객체를 만들고 JSON으로 변환해 바디(body)에 넣습니다. 반면 HTMX는 기본적으로 DOM에 있는 값들을 수집해서 전송합니다.
HTMX의 사고방식은 "JS 객체를 직접 조립하는 것"이 아니라, "HTML 요소에서 값들을 모아 요청 파라미터를 만드는 것"에 가깝습니다. 이를 위해 보통 다음 3가지 방식을 사용합니다.
1) Form 기반으로 값 모아서 보내기
가장 HTML답고 Django에도 잘 맞는 방식입니다.
<form hx-post="/todos/create/" hx-target="#todo-list">
<input type="text" name="title" placeholder="제목">
<input type="number" name="priority" value="3">
<input type="hidden" name="done" value="false">
<button type="submit">등록</button>
</form>
이 경우 HTMX는 폼 안의 입력값들을 수집해서 요청에 담아 보냅니다. 기본 인코딩은 일반 폼 제출과 같은 URL-encoded form data 방식입니다.
Django 뷰에서는 평소처럼 이렇게 받으면 됩니다.
def create_todo(request):
title = request.POST.get("title")
priority = request.POST.get("priority")
done = request.POST.get("done")
2) Form 없이 다른 요소의 값 포함하기: hx-include
버튼 하나에 hx-post를 달면서, 멀리 떨어져 있는 입력값만 골라 같이 보내고 싶을 때 사용합니다.
<input type="text" id="title" name="title" placeholder="제목">
<input type="number" id="priority" name="priority" value="3">
<button hx-post="/todos/create/"
hx-include="#title, #priority"
hx-target="#todo-list">
등록
</button>
hx-include는 지정한 요소들의 값을 요청에 포함시킵니다. 굳이 전체를 <form>으로 감싸지 않아도 페이로드와 비슷한 결과물을 만들 수 있어, 입력 항목이 적을 때 매우 유용합니다.
3) 숨겨진 값이나 계산된 값 추가하기: hx-vals
이게 바로 fetch에서 페이로드 객체를 일부 직접 만드는 개념과 가장 가까운 기능입니다.
<button hx-post="/todos/create/"
hx-vals='{"title": "장보기", "done": false, "priority": 3}'
hx-target="#todo-list">
빠른 등록
</button>
hx-vals는 요청에 추가 파라미터를 넣습니다. 기본은 위처럼 JSON 문법을 쓰지만, js: 접두사를 붙이면 동적인 자바스크립트 계산값도 보낼 수 있습니다.
<input type="text" id="title" placeholder="제목">
<button hx-post="/todos/create/"
hx-vals='js:{title: document.querySelector("#title").value, done: false, priority: 3}'
hx-target="#todo-list">
등록
</button>
이 경우도 Django에서는 request.POST로 읽습니다. hx-vals는 "JSON처럼 생긴 문법으로 값을 정의"하는 것이지, 요청 본문 자체를 JSON으로 만드는 것은 아니기 때문입니다.
주의할 점: hx-vals는 "JSON Payload"와 다릅니다
HTMX를 처음 접할 때 가장 헷갈리는 부분입니다.
<button hx-post="/my-url/" hx-vals='{"a":1, "b":2}'>
이 코드는 "JSON 객체를 body로 보낸다"는 뜻이 아니라, 요청 파라미터에 a=1&b=2를 추가한다는 의미에 가깝습니다. 즉, 서버에서는 여전히 폼 데이터를 받듯 처리해야 합니다.
정말로 JSON 바디를 보내고 싶다면?
만약 꼭 fetch(... JSON.stringify(payload))와 같은 application/json 형식을 써야 한다면, HTMX의 json-enc 확장이 필요합니다.
공식 문서의 안내에 따라 다음과 같이 설정합니다.
<script src="https://unpkg.com/htmx.org@1.9.12/dist/ext/json-enc.js"></script>
<button hx-post="/api/todos/"
hx-ext="json-enc"
hx-vals='{"title": "장보기", "done": false, "priority": 3}'
hx-target="#result">
JSON으로 전송
</button>
이제야 비로소 우리가 익숙한 JSON 페이로드 방식이 됩니다. 이때 Django 뷰에서는 request.POST가 비어있으므로, request.body를 직접 읽어야 합니다.
import json
def create_todo_api(request):
data = json.loads(request.body)
title = data.get("title")
# ... 로직 처리 ...
return JsonResponse({"ok": True})
HTMX와 DRF는 철학의 갭이 있지만 합쳐질 필요도 없다.
확장팩을 써서 JSON을 보낼 수는 있지만, 과연 이것이 HTMX다운 방식인가 하는 의문은 남습니다. 이는 "데이터 중심(DRF)"과 "하이퍼미디어 중심(HTMX)"이라는 두 철학이 충돌하는 지점이기 때문입니다.
HTMX를 제대로 쓰기로 했다면, 어쩌면 데이터 중심의 사고방식에서 잠시 벗어날 필요가 있다고 생각합니다. <form>, hx-include, hx-vals로 값을 보내고, 서버는 request.POST로 이를 받아 JSON이 아닌 HTML 스니펫을 반환하는 것. 그 자체가 HTMX가 가장 빛나는 지점입니다.
하지만 DRF 시리얼라이저는 포기하기 아깝다면?
DRF의 시리얼라이저는 정말 강력합니다. HTMX를 쓴다고 해서 이 편리한 유효성 검사 도구를 버리고 request.POST.get() 노가다를 해야 할까요?
다행히 DRF 시리얼라이저는 JSON뿐만 아니라 폼 데이터도 기가 막히게 검증해 줍니다. 이 부분은 내용이 길어질 것 같아, 다음 편에서 "HTMX와 DRF 시리얼라이저의 공존"에 대해 깊이 다뤄보도록 하겠습니다.
마무리
오늘 내용을 요약하자면 다음과 같습니다.
- HTMX는 기본적으로 DOM에서 값을 수집합니다. 직접 페이로드 객체를 짜는
fetch와는 접근 방식이 다릅니다. - 전송 방식 3가지: 전체를 보내는
<form>, 골라서 보내는hx-include, 값을 추가하는hx-vals. - 기본은 폼 데이터입니다.
hx-vals가 JSON 문법을 쓴다고 해서 실제 JSON body로 가는 것은 아니니 주의하세요. - JSON이 꼭 필요하다면
json-enc확장을 쓰세요. 하지만 HTMX 본연의 맛은 HTML 조각을 주고받는 데 있음을 기억하면 좋습니다.
다음 시간에는 DRF의 편리함을 HTMX에 녹여내는 방법으로 돌아오겠습니다!
관련글읽기
댓글이 없습니다.