본문 바로가기
백엔드/Django

[Django] 폼(Form)에 관하여

by 토마토베이컨수프 2021. 10. 5.

From 이란?

폼은 사용자가 입력한 데이터를 서버로 전송하기 위한 방식입니다.

 

이렇게 사용자의 데이터를 입력 받을 수 있는 양식이라는 것이 존재하고, 데이터를 입력하게 되면 폼의 내부 전송 방식에 따라 서버로 데이터를 보내게 됩니다.

 

form html 코드의 예시를 한번 볼까요?

<form action="input/" method="post">{% csrf-token %}
    <label for="name">이름</label>
    <input type='text' id="name" name="name"/>
    <label for="birth">생일</label>
    <input type='date' id="birth" name="birth" />
  <input type="submit" value="제출">                       
</form>

각각의 입력란이 <label>태그와 <input>의 태그의 쌍으로 이루어져 있는 것을 확인할 수 있습니다.

 

위에서 확인할 수 있는 키워드들은 다음과 같습니다.

  • for, id : <label>태그의 for 속성과 <input>태그의 id 속성은 이 두 태그를 쌍으로 묶어주기 위한 식별자 같은 것입니다. 
  • name : name 속성은 데이터들이 서버로 전송될 때, 데이터들을 구분하기 위한 값으로 꼭 필요한 요소입니다.
  • type : 입력할 값에 따른 유형을 나타내는 속성입니다.
  • action : 입력된 데이터를 전송할 서버의 url을 지정해줍니다. 아무것도 없으면 현재 경로로 전송합니다.
  • method : 데이터의 전달 방식을 지정해줍니다. post와 get의 두 종류가 있습니다.
  • csrf-token : 교차 사이트 위조를 검증하는 보안 기능을 수행합니다.

 

POST / GET

클라이언트가 서버로 보내는 request의 종류에는 GETPOST가 있습니다. GET은 서버로부터 데이터를 조회할 때 사용하는 request로, url에 쿼리 스트링을 이용하여 정보를 주고 받습니다. 이 때문에 데이터가 url을 통해 노출될 수 있습니다.

 

쿼리스트링(Query String)은 string)은 URL Path의 끝을 '?'로 표시하고, 데이터는 '='를 이용하여 key와 value의 쌍으로 나타내며 각각의 데이터는 '&'로 구분하는 url 형태입니다. 
ex) 도메인/send/data?name='kim'&email='exemail@naver.com'

 

그에 반해 POST는 서버에 데이터의 변경을 요청할 때 쓰는 request로 데이터 생성, 수정, 삭제할 때 사용됩니다. 데이터 노출 또한 없습니다.

 

 

전반적인 폼 처리 과정

  1. 클라이언트가 GET으로 서버에게 폼 양식을 요청하면 서버는 폼을 리턴합니다.
  2. 데이터 입력 후 POST 방식으로 데이터를 서버로 보냅니다.
  3. 서버가 바인딩이라는 과정을 통해 데이터와 폼을 합쳐 '바운드폼'이라는 것을 만들어 냅니다.
  4. 입력받은 데이터 양식이 잘못 되었다면 서버는 다시 클라이언트에게 입력을 요청합니다.
  5. 제대로 입력한 데이터가 전송되었을 때 서버는 내부 로직을 수행합니다. (저장, 가공 등)
  6. 내부 로직에 따라 완성된 결과(새로운 페이지 등)을 클라이언트에게 안내합니다.

 


폼을 통한 데이터 생성

폼 작성

폼 작성은 모델을 만드는 것과 매우 비슷합니다.

프로젝트의 앱 디렉토리 내에 forms.py를 만들고 다음과 같은 코드를 작성합니다.

from django import forms

class MyForm(forms.Form):
    title = forms.CharField(max_length=50, label='제목')
    content = forms.CharField(label='내용', widget=forms.Textarea, label='내용')

widget은 데이터에 따라 달라지는 입력 형식으로, 기본적으로 해당 필드에 해당하는 디폴트값이 존재하지만, 이렇게 따로 설정해줄 수도 있습니다.

 

하지만 저렇게 굳이 따로 만들어주지 않고 이미 만들어준 모델의 구조를 기반으로 자동으로 폼을 생성할 수도 있습니다. 바로 아래 코드와 같이 말이죠.

from django import forms
from .models import MyModel

class MyForm(forms.ModelForm):
    class Meta:
        model = MyModel
        # fields 값으로 모델의 필드들을 리스트 형태로 넣어주면 되지만 이렇게 모두 다 받아 올 수 있습니다.
        fields = "__all__"

 

그리고 폼을 입력 받는 페이지와 입력 받아 최종적으로 만들어진 상세 정보를 나타내는 페이지에 연결하는 url을 urls.py에 추가합시다.

urlpatterns = [
    path("create/", new_create.index, name="create"),
    path("detail/<int:page_id>", detail.index, name="detail"),
]

name은 부여된 url을 나타내는 속성입니다. 

 

이제 views.py에서 뷰 함수를 만듭니다.

from .forms import MyForm
from django.shortcuts import redirect

def new_create(request):
    if request.method == "POST":
        my_form = MyForm(request.POST)
        new_post = my_form.save()
        return redirect("detail", page_id=new_post.id)
    else:
        my_form = MyForm()
        return render(request, "my_app/form.html", {"form": my_form})

이 뷰 함수에는 두 가지 로직을 처리합니다. 하나는 처음 이 폼 페이지로 들어왔을 때로, 빈 폼 형태를 보여주며 폼 html파일을 랜더링 합니다.

또 다른 하나는 폼을 다 작성한 후 제출버튼을 눌렀을 때 입니다. 'post'형태로 제출을 하게 되면 request.method 값은 "POST"가 될테고, 그렇다면 이렇게 폼 구조에 데이터를 합친 후 저장한 다음, redirect를 통해 상세 페이지로 이동하게 해줍니다.

 

데이터 유효성 검사

여기서 잠깐, 데이터를 무작정 save하는 것 보다는 데이터의 유효성 검증을 한번 해준 뒤 데이터를 저장하는 것이 데이터베이스 관리상 안전할 텐데요, 데이터의 유효성을 체크하는 로직을 추가해주도록 합시다.

validations.py파일을 만들고 이 안에 '제목의 길이는 10자 이하로 해야 한다' 라는 제약 함수를 만듭니다.

from django.core.exceptions import ValidationError

def validate_title(value):
    if len(value) > 10:
        raise ValidationError("제목은 10자 이하로 입력해 주세요.", code="name-err")

 

그리고 models.py로 이동한 뒤, title 필드에 다음과 같이 인자를 추가합니다.

from django.db import models
from .validations import validate_name

class MyModel(models.Model):
    title = models.CharField(max_length=20, validators=[validate_name])
    content = models.CharField(max_length=50)
    
    def __str__(self):
        return self.title

 

뷰 함수 또한 데이터 유효성 검증 과정을 거치도록 수정해줍니다.

def new_create(request):
    if request.method == "POST":
        my_form = MyForm(request.POST)
        if my_form.is_valid():
        	new_post = my_form.save()
        	return redirect("detail", page_id=new_post.id)
    else:
        my_form = MyForm()
    return render(request, "my_app/form.html", {"form": my_form})

 

템플릿 구성

마지막으로 랜더링하는 form.html 파일을 작성해 줍시다.

<form method="post">{% csrf-token %}
    {{form}}
    <input type="submit" value="전송">
</form>

사실 이렇게만 해줘도 알아서 폼 양식을 labelinput 태그의 쌍으로 랜더링하여 표시해 줍니다. 

그리고 추가적으로, {{form.as_ul}} 처럼 코드를 작성하면 폼 내부 태그들을 알아서 ul(unordered list) 형태로 바꿔서 표시해 줍니다. 이 말고도 as_table이나 as_p처럼 많은 태그 변경 기능 속성이 있습니다.

 

또한 폼을 {{form.title}}, {{form.title.errors}} 처럼 컴포넌트로 나눠 개별적으로 css처리를 해줄 수도 있습니다.

 


데이터 수정

수정 페이지와 관련된 페이지의 url을 추가합니다.

path('edit/<int:page_id>', views.update, name="update")

그리고 템플릿상에 수정하기 전용 버튼도 만듭니다.

<a href="{% url 'update' page.id %}">수정하기</a>

 

뷰 함수는 다음과 같이 작성해줍니다.

def update(request, page_id):
    page = MyModel.objects.get(id=page_id)
    if request.method == "POST":
        page_form = MyForm(request.POST, instance=post)  
        if page_form.is_valid():
            page_form.save()
            return redirect('detail', page_id=page.id)
    else:
        page_form = MyForm(instance=page)
    return render(request, "my_app/form.html", {"form": page_form})

데이터 수정 함수도 처음 수정 페이지에 들어갔을 때와 글을 수정하고 제출 버튼을 눌렀을 때의 경우 두 가지로 나눠서 로직을 처리합니다. 

 

수정 후 제출 버튼을 눌렀을 때, 데이터 생성과는 달리 수정은 새로 MyForm 모델을 만들어서 저장하는 것이 아니라 기존 데이터 모델을 수정해야 하므로, 기존 모델인 instance와 수정된 데이터를 갖는 폼을 만들어서 저장합니다.

그리고 처음으로 수정 페이지에 들어가는 경우에는 page_form = MyForm(instance=page)로 기존 모델 형태만 폼에 넘겨줍니다.

 


데이터 삭제

삭제 페이지와 관련된 페이지의 url을 추가합니다.

path('delete/<int:page_id>', views.delete, name="delete")

그리고 템플릿상에 삭제하기 전용 버튼도 만듭니다.

<a href="{% url 'delete' page.id %}">삭제하기</a>

 

뷰 함수는 다음과 같이 작성해줍니다.

def delete(request, page_id):
    page = MyModel.objects.get(id=page_id)
    if request.method == 'POST':
        page.delete()
        return redirect('page-list')         # 글 리스트로 돌아갑니다.
    else:		# 처음 '삭제하기' 버튼을 눌렀을 때
        return render(request, 'my_app/page_confirm_delete.html', {'page': page})

 

page_confirm_delete.html은 글 삭제 여부를 한번 더 묻는 form형 html 파일입니다.

<form method="post">{% csrf-token %}
    <input type="submit" value="삭제하기">
</form>

 


참고 자료

'백엔드 > Django' 카테고리의 다른 글

[Django] allauth 패키지  (0) 2021.10.25
[Django] 제네릭 뷰  (0) 2021.10.11
[Django] 페이지네이션(Pagination) 기능  (0) 2021.10.10
[Django] MVT 구조  (0) 2021.10.02
[Django] 장고 프로젝트 시작하기(Windows)  (0) 2021.09.30