폼 다루기

폼 다루기 (Working with forms)

웹 애플리케이션이 콘텐츠를 보여주기만 하지 않는다면, 방문자의 입력을 받아 처리해야 하는 순간이 반드시 와요. Django는 HTML 폼을 만들고, 제출된 데이터를 검증하고, 응답을 처리하는 데 필요한 도구와 라이브러리를 제공해요. 이 문서는 그 흐름을 끝에서 끝까지 따라가며 설명하는 입문서예요.

출처: Django 공식 문서 - Working with forms

HTML 폼이란

HTML에서 폼은 <form>...</form> 안에 있는 요소들의 모음이에요. 방문자가 텍스트를 입력하고, 옵션을 선택하고, 컨트롤을 조작한 뒤 그 정보를 서버로 보낼 수 있게 해 주죠. 텍스트 입력이나 체크박스 같은 일부 요소는 HTML 자체에 내장돼 있지만, 날짜 선택기처럼 복잡한 인터페이스는 보통 JavaScript와 CSS까지 함께 사용해요.

폼은 <input> 요소 외에도 두 가지를 반드시 지정해야 해요. 첫째는 데이터를 보낼 URL(action), 둘째는 데이터를 보내는 HTTP 메서드(method)예요.

GET과 POST

폼을 다룰 때 쓰는 HTTP 메서드는 GETPOST 두 가지뿐이에요. Django 관리자의 로그인 폼은 POST로 데이터를 보내요. 브라우저가 폼 데이터를 묶어 인코딩한 뒤 서버로 보내고 응답을 받아오는 방식이에요.

반면 GET은 제출된 데이터를 URL 문자열에 담아요. URL 안에 데이터의 키와 값이 그대로 들어가니까, 검색 폼처럼 북마크하거나 공유하기 좋은 경우에 어울려요. 하지만 상태를 바꾸는 요청(예: 데이터베이스 변경)이나 비밀번호처럼 민감하거나 큰 데이터에는 부적절해요. 비밀번호가 URL에 들어가면 브라우저 기록과 서버 로그에 평문으로 남을 수 있거든요.

정리하면, 시스템의 상태를 바꿀 수 있는 요청은 반드시 POST를 쓰고, 상태에 영향을 주지 않는 요청에만 GET을 쓰는 게 원칙이에요. POST는 Django의 CSRF 보호 같은 보호 장치와 함께 쓰면 접근 통제를 더 잘할 수 있어요.

Django에서 폼의 역할

폼 처리는 생각보다 복잡한 일이에요. 데이터를 표시할 준비를 하고, HTML로 렌더링하고, 사용자가 편집한 뒤 서버로 돌려보내고, 검증과 정리를 거쳐 저장하거나 다음 단계로 넘기는 과정이 필요하죠. Django의 폼 기능은 이 작업의 많은 부분을 자동화하고, 대부분의 개발자가 직접 짠 코드보다 더 안전하게 처리해 줘요. Django는 폼 작업을 크게 세 부분으로 나눠서 다뤄요.

  • 렌더링할 수 있게 데이터를 준비하고 재구성하기
  • 데이터를 위한 HTML 폼 만들기
  • 클라이언트가 제출한 폼과 데이터 받아 처리하기

Django의 Form 클래스

이 시스템의 중심에는 Django의 Form 클래스가 있어요. 모델 클래스가 객체의 논리적 구조를 설명하듯, Form 클래스는 폼이 어떻게 동작하고 어떻게 보이는지를 결정해요. 모델 클래스의 필드가 데이터베이스 필드에 대응하듯, 폼 클래스의 필드는 HTML <input> 요소에 대응해요. (ModelForm은 모델 필드를 Form을 통해 HTML <input> 요소에 연결하는데, Django 관리자가 이 방식으로 만들어져 있어요.)

폼의 필드는 그 자체가 클래스예요. 제출된 폼 데이터를 관리하고 검증하는 역할을 하죠. 예를 들어 DateFieldFileField는 아주 다른 종류의 데이터를 다루고, 각각 다르게 동작해요. 필드는 브라우저에서는 HTML "위젯(widget)"으로 표현되는데, 각 필드 타입에는 알맞은 기본 위젯 클래스가 있지만 필요하면 덮어쓸 수 있어요.

폼 인스턴스화, 처리, 렌더링

객체를 렌더링할 때는 보통 뷰에서 객체를 얻어, 템플릿 컨텍스트에 넘기고, 템플릿 변수로 HTML로 펼쳐요. 폼을 렌더링하는 것도 거의 같은 작업이지만 차이가 있어요. 데이터가 없는 모델 인스턴스를 템플릿에서 쓰는 일은 드물지만, 채워지지 않은 폼을 렌더링하는 것은 아주 자연스러운 일이에요. 사용자가 채우도록 하기 위해서죠.

그래서 모델 인스턴스는 뷰에서 데이터베이스로부터 가져오는 경우가 많은 반면, 폼은 보통 뷰에서 인스턴스화해요. 폼을 만들 때 비워 두거나 미리 채울 수 있는데, 그 데이터는 저장된 모델 인스턴스에서 오거나, 다른 소스에서 모은 값이거나, 이전 HTML 폼 제출에서 받은 값이에요. 마지막 경우가 특히 흥미로운데, 사용자가 웹사이트를 읽기만 하는 게 아니라 정보를 다시 보내도록 만드는 핵심이기 때문이에요.

폼 만들어 보기

사용자의 이름을 받는 간단한 폼을 템플릿에 직접 만들면 이렇게 생겼어요.

<form action="/your-name/" method="post">
    <label for="your_name">Your name: </label>
    <input id="your_name" type="text" name="your_name" value="{{ current_name }}">
    <input type="submit" value="OK">
</form>

이 폼은 데이터를 /your-name/ URL로 POST 방식으로 보내고, 텍스트 필드와 "OK" 버튼을 보여줘요. 컨텍스트에 current_name 변수가 있으면 your_name 필드에 미리 채워져요. 이 폼을 렌더링하는 뷰와, 제출을 받아 처리하는 뷰도 필요하겠지요. 실제 폼은 수십~수백 개의 필드를 가질 수 있고, 여러 번의 편집·제출 사이클을 거칠 수도 있어요. 이때부터는 Django에 많은 부분을 맡기는 게 훨씬 수월해져요.

Form 클래스로 폼 만들기

Django에서 폼을 정의하는 출발점은 이렇게 생겼어요.

from django import forms

class NameForm(forms.Form):
    your_name = forms.CharField(label="Your name", max_length=100)

이렇게 하면 your_name 필드 하나를 가진 Form 클래스가 정의돼요. 필드에 사람이 읽기 좋은 label을 붙였는데, 렌더링하면 <label>로 나타나요. max_length는 두 가지 일을 해요. HTML <input>maxlength="100"을 넣어 브라우저가 입력을 막게 하고, 서버에서도 데이터 길이를 검증해요.

Form 인스턴스에는 is_valid() 메서드가 있어요. 이 메서드는 모든 필드에 대해 검증을 실행해요. 모든 필드가 유효하면 True를 돌려주고, 데이터를 cleaned_data 속성에 넣어 줘요.

뷰와 템플릿

Django로 보내진 폼 데이터는 폼을 표시했던 것과 같은 뷰가 처리하는 게 일반적이에요. 이렇게 하면 로직 일부를 재사용할 수 있어요. 폼을 렌더링할 때는 템플릿에 {{ form }} 변수를 넣는 것만으로 폼 위젯들이 HTML로 출력돼요. 다만 폼 자체에는 <form> 태그와 제출 버튼이 포함되지 않으므로, 그것들은 템플릿에서 직접 제공해야 해요.

<form action="/your-name/" method="post">
    {% csrf_token %}
    {{ form }}
    <input type="submit" value="Submit">
</form>

{% csrf_token %} 태그는 CSRF 보호를 위한 숨은 필드를 넣어 주는 태그예요. 폼의 각 필드는 템플릿에서 {{ field.html_name }}, {{ field.id_for_label }}, {{ field.label }}, {{ field.label_tag }}, {{ field.value }} 같은 속성으로 개별 접근할 수도 있어요. 예를 들어 {{ field.label_tag }}label_suffix(기본값은 콜론)를 포함한 <label> 태그를 만들어 줘요. 전체 속성과 메서드 목록은 BoundField 레퍼런스에서 볼 수 있어요.

더 알아보기 (Learn more)