## [[Django]]와 HTMX로 동적 웹 개발을 단순화하기 : Forms와 Serializer 의 활용 {#sec-b233da7ff7c9} 지난 글에서는 **[[HTMX]]가 데이터를 서버로 전송하는 방식**을 살펴보았습니다. **지난글보기** : [Django와 HTMX로 동적 웹 개발 단순화하기 (4편): Payload 전송 방식](/ko/whitedec/2025/1/27/django-htmx-csrf-token-integration/) 기존 자바스크립트의 `fetch()`가 JSON payload를 직접 만들어 보내는 방식에 가깝다면, **HTMX는 DOM에서 값을 수집해 form-data처럼 보내는 방식**에 더 가깝다는 점을 확인했죠. 그렇다면 이제 자연스럽게 이런 질문이 떠오릅니다. **"HTMX로 들어온 이 데이터를, Django에서는 무엇으로 검증하는 것이 가장 자연스러울까?"** 처음에는 많은 분들이 **DRF Serializer**를 떠올릴지도 모릅니다. 실제로 Serializer는 강력한 유효성 검사 도구이고, JSON이 아니더라도 사용할 수 있습니다. 하지만 막상 HTMX와 함께 써보면 어딘가 조금 억지스럽다는 느낌이 들기도 합니다. 왜일까요? 그 이유는 단순합니다. **HTMX의 기본 흐름은 HTML form의 세계에 더 가깝고, Django에는 바로 그 세계를 위해 설계된 `Form`이 이미 존재하기 때문입니다.** 이번 글에서는 **HTMX 요청 처리에서 `Django Form`과 `DRF Serializer`를 각각 어떤 식으로 활용할 수 있는지**, 그리고 **어느 쪽이 더 자연스럽고 실용적인 선택인지**를 정리해 보겠습니다. ![HTMX 요청 처리에서 Form과 Serializer의 역할 비교](/media/whitedec/blog_img/06d08ad8294d4067af3a9001566ddf5a.webp) --- ## [[HTMX]]가 보내는 데이터는 결국 "폼 데이터"에 가깝다 {#sec-8c911eee119a} 지난 편에서 살펴본 것처럼, HTMX는 기본적으로 HTML 요소의 값을 수집해서 서버로 전송합니다. `
` 안의 입력값을 보내거나, `hx-include`로 특정 요소를 포함시키거나, `hx-vals`로 추가 값을 붙이는 식이죠. 즉, HTMX의 기본 철학은 다음에 가깝습니다. * 자바스크립트 객체를 직접 조립하지 않는다 * HTML 요소에서 값을 모은다 * 서버에 요청을 보낸다 * JSON보다 HTML 조각 응답과 더 잘 어울린다 이 흐름은 생각보다 아주 중요합니다. 왜냐하면 이 구조는 **전형적인 Django Form 처리 흐름과 거의 일치하기 때문**입니다. Django Form 역시 다음과 같은 전제를 가지고 설계되어 있습니다. * 사용자가 HTML form에 입력한다 * 서버가 `request.POST`를 받는다 * Form이 데이터를 검증한다 * 에러가 있으면 다시 렌더링한다 * 문제가 없으면 저장하고 결과를 응답한다 이쯤 되면 HMX과 Django Form은 신데렐라와 유리구두마냥 딱 맞아 떨어지는 것 같은 기분이 들기 시작합니다. 따라서 이런 결론을 내려도 되지 않을까 싶습니다. **HTMX와 가장 자연스럽게 맞물리는 Django의 검증 도구는 DRF Serializer보다 Django Form이다.** --- ## 왜 Django Form이 더 잘 어울릴까 {#sec-b1e7f36e5f83} DRF Serializer는 물론 훌륭한 도구입니다. 하지만 원래의 설계 맥락을 생각하면, Serializer는 **데이터 직렬화와 API 입력 검증**에 더 가까운 도구입니다. 반면 Django Form은 처음부터 다음을 위해 만들어졌습니다. * HTML 폼 입력 처리 * 서버 사이드 유효성 검사 * 에러 메시지 표시 * 입력값 유지 * 템플릿 재렌더링 즉, HTMX처럼 **"HTML 일부를 다시 받아서 바꿔치기하는 방식"** 과 붙여 놓았을 때 훨씬 더 자연스럽습니다. 예를 들어 생각해 봅시다. 사용자가 댓글 작성 폼을 제출합니다. * 유효성 검사에 실패하면? * 입력했던 내용은 유지되어야 합니다 * 어떤 필드가 문제인지 보여줘야 합니다 * 에러가 붙은 폼을 다시 부분 렌더링해야 합니다 이런 UX는 Django Form이 정말 잘합니다. 물론 Serializer도 `errors`를 제공하고, 검증도 가능합니다. 하지만 그다음 단계인 **"HTML 폼 UX 전체를 다시 구성하는 일"** 은 Form 쪽이 훨씬 매끈합니다. 그래서 HTMX와의 궁합만 놓고 보면, Form이 기본 선택이라고 보는 것이 맞습니다. --- ## 가장 자연스러운 조합: [[HTMX]] + Django Form {#sec-23fc963f6963} 먼저 가장 기본적인 예제를 보겠습니다. 간단한 할 일 등록 폼입니다. ### Form 정의 {#sec-fba81a38a40a} ```python from django import forms class TodoForm(forms.Form): title = forms.CharField(max_length=100, label="제목") priority = forms.IntegerField(min_value=1, max_value=5, label="우선순위") ``` 이제 뷰에서는 `request.POST`를 Form에 그대로 넣어 검증하면 됩니다. ### View 처리 {#sec-568150151637} ```python from django.shortcuts import render from django.http import HttpResponse def todo_create(request): if request.method == "POST": form = TodoForm(request.POST) if form.is_valid(): title = form.cleaned_data["title"] priority = form.cleaned_data["priority"] # 저장 로직 수행 # Todo.objects.create(title=title, priority=priority) return render(request, "todos/partials/todo_item.html", { "title": title, "priority": priority, }) return render(request, "todos/partials/todo_form.html", { "form": form, }, status=400) form = TodoForm() return render(request, "todos/partials/todo_form.html", { "form": form, }) ``` 여기서 포인트는 아주 분명합니다. 1. **HTMX가 보낸 값을 `request.POST`로 받는다** 2. **Form이 검증한다** 3. **성공하면 HTML partial을 반환한다** 4. **실패하면 에러가 담긴 Form을 다시 렌더링한다** 이 흐름은 Django스럽고, HTMX스럽고, 무엇보다 유지보수가 편합니다. --- ## 템플릿에서 에러를 자연스럽게 다시 보여주기 {#sec-18af3291951b} Django Form이 특히 빛나는 부분은 바로 여기입니다. 검증 실패 시, Form 객체에는 이미 다음 정보가 들어 있습니다. * 사용자가 입력했던 값 * 필드별 에러 메시지 * non-field 에러 * 어떤 필드가 유효하지 않은지에 대한 상태 따라서 partial 템플릿에서는 이를 그대로 렌더링하면 됩니다. ### `todo_form.html` {#sec-8a4853cf4018} ```html {% csrf_token %} {% if form.non_field_errors %}
{{ form.non_field_errors }}
{% endif %}
{{ form.title }} {% if form.title.errors %}
{{ form.title.errors }}
{% endif %}
{{ form.priority }} {% if form.priority.errors %}
{{ form.priority.errors }}
{% endif %}
``` 이 예제는 매우 단순하지만, [[HTMX]]와 Django Form의 궁합을 잘 보여줍니다. * 실패 시 폼 전체를 다시 렌더링할 수 있고 * 사용자가 입력한 값도 유지되며 * 에러 메시지도 필드 옆에 자연스럽게 붙습니다 기존의 `fetch()` + JSON + 수동 DOM 조작 방식이었다면, 이런 흐름을 구현하기 위해 자바스크립트를 꽤 많이 써야 했을 것입니다. 하지만 HTMX와 Django Form 조합에서는 서버 쪽 코드와 템플릿만으로 훨씬 단순하게 처리할 수 있습니다. --- ## 그러면 Serializer는 필요 없을까? {#sec-5f0d93fcd48a} **Serializer가 필요 없다.** 라는 말은 너무 나간 것 같고, 이정도로 표현하면 좋지 않을까 싶습니다. **굳이 Serializer를 기본으로 선택할 것은 아니다** 정도가 어떨까요? 실제로 [[DRF]] Serializer는 다음과 같은 상황에서 여전히 충분히 매력적입니다. * 이미 프로젝트 전반에서 DRF를 적극 사용 중인 경우 * 같은 검증 로직을 API와 서버 렌더링 화면에서 함께 재사용하고 싶은 경우 * 입력 검증 로직이 복잡하고, 이를 Serializer에 이미 잘 정리해 둔 경우 * 나중에 동일한 기능을 모바일 앱이나 외부 API에도 노출할 계획이 있는 경우 즉, Serializer는 **"HTMX에서 쓸 수 있느냐?"** 의 문제가 아니라, **"굳이 여기서까지 Serializer를 가져오는 게 전체 아키텍처상 이득이 있느냐?"** 의 문제에 가까울 것입니다. --- ## Serializer도 사용할 수는 있다 {#sec-bfbe9c61b12c} 예를 들어, 이미 아래와 같은 Serializer가 있다고 해봅시다. ```python from rest_framework import serializers class TodoSerializer(serializers.Serializer): title = serializers.CharField(max_length=100) priority = serializers.IntegerField(min_value=1, max_value=5) ``` 이 경우 HTMX 요청에서도 충분히 사용할 수 있습니다. ```python from django.shortcuts import render def todo_create_with_serializer(request): if request.method == "POST": serializer = TodoSerializer(data=request.POST) if serializer.is_valid(): title = serializer.validated_data["title"] priority = serializer.validated_data["priority"] return render(request, "todos/partials/todo_item.html", { "title": title, "priority": priority, }) return render(request, "todos/partials/todo_form_serializer.html", { "errors": serializer.errors, "data": request.POST, }, status=400) ``` 보시는 것처럼 **기술적으로는 전혀 문제 없습니다.** Serializer는 JSON만 받는 도구가 아니기 때문입니다. 하지만 여기서 미묘한 차이가 드러납니다. Form을 사용할 때는: * 입력값 유지 * 필드별 렌더링 * 에러 바인딩 * 템플릿과의 연결 이 자연스럽게 이어집니다. 반면 Serializer를 사용할 때는: * `serializer.errors`를 직접 템플릿 구조에 맞게 풀어야 하고 * 기존 입력값도 별도로 넘겨야 하며 * HTML form 재렌더링과의 연결을 개발자가 더 많이 신경 써야 합니다 즉, **사용할 수는 있지만, 조금 더 수작업이 늘어나는 편**입니다. 바로 이 지점 때문에 [[HTMX]]와 함께 쓸 때 Serializer가 약간 억지스럽게 느껴질 수 있습니다. --- ## 마무리 {#sec-37e5d418cdb7} 지난 편에서 우리는 [[HTMX]]가 데이터를 어떻게 보내는지를 살펴보았습니다. 그리고 이번 편에서는 그 데이터를 Django에서 어떻게 검증하는 것이 가장 자연스러운지 정리해 보았습니다. * HTMX는 기본적으로 form-data와 잘 어울립니다 * Django Form은 바로 그 흐름을 위해 설계된 도구입니다 * 그래서 HTMX와의 궁합만 놓고 보면 Form이 가장 자연스럽습니다 * DRF Serializer는 사용할 수는 있지만, 보다 전략적인 선택지에 가깝습니다 개인적으로는 HTMX를 제대로 활용하려면, **"AJAX를 HTML스럽게 다루는 감각"** 뿐 아니라 **"Django Form과 form tags 를 활용하는 감각"** 도 함께 회복할 필요가 있다고 생각합니다. --- **관련글읽기** * [Django와 HTMX로 동적 웹 개발을 단순화하기 (1편)](/ko/whitedec/2025/1/27/django-htmx-dynamic-web-simplification/) * [Django와 HTMX로 동적 웹 개발을 단순화하기 - Ajax (2편)](/ko/whitedec/2025/1/27/django-htmx-dynamic-web-simplification-2/) * [Django와 HTMX로 동적 웹 개발 단순화하기 (3편): Django 통합 방법](/ko/whitedec/2025/1/27/django-htmx-dynamic-web-simplification-3/) * [Django와 HTMX로 동적 웹 개발을 단순화하기 (4편): payload는 어떻게?](/ko/whitedec/2025/1/27/django-htmx-advanced-features/)