본문 바로가기
Django

[Django] 폼 처리하기

by dio-han 2022. 1. 7.

 

 

1. HTML에서의 폼

 

우리는 웹 사이트를 개발할 때 사용자로부터 입력을 받기 위해서 폼을 사용한다. HTML로 표현하면 폼은 <form>...</form> 사이에

있는 엘리먼트들의 집합이다. 웹 사이트 사용자는 폼을 통해 텍스트를 입력할 수도 있고, 항목을 선택할 수도 있다.

 

이렇게 폼에 입력된 데이터는 서버로 보내진다. 텍스트 입력이나 체크 박스 등과 같은 간단한 폼의 엘리먼트들은 기본 위젯을 사용

하지만, 달력 위젯, 슬라이드 바 등의 복잡한 엘리먼트들은 자바스크립트나 CSS 를 사용하기도 한다. 폼은 <input> 엘리먼트 외에

폼 데이터를 어디로 보낼지 지정해주는 action 속성과 어떤 HTTP 메소드로 보낼지 지정해주는 method 속성을 설정해줘야 한다.

 

HTTP 프로토콜 중 폼에서 사용할 수 있는 HTTP 메소드는 GET과 POST뿐이다. 장고는 이 중에서도 폼의 데이터를 전송할 때는

POST 방식만을 사용하고 있다. 일반적으로 GET과 POST 방식은 다른 용도로 사용된다. 
GET 방식은 폼 데 이터량이 많거나 이미지와 같은 2진 데이터를 보내는 경우에도 부적합하고 보안에도 취약학디 때문에 위와 같은

경우에는 모두 POST 방식을 사용한다.

 

 

2. 장고의 폼 기능

 

웹 서버에서의 폼 처리는 복잡한 과정이지만, 공통적인 절차를 갖고 있다. 예를 들어 Admin 사이트를 생각해보면 여러 가지 타입의

많은 위젯이 폼 화면 출력용으로 준비되어야 하고, HTML로 렌더링되며, 적절한 인터페이스를 사용하여 입력 및 수정되고, 서버로 

보내져서 데이터가 유효한지 검증을 거친 후에 적절한 처리를 위해 저장되거나 전달된다. 장고는 이러한 폼 기능들을 단순화하고

자동화해서 개발자가 직접 코딩하는 것보다 훨씬 안전하게 처리해준다. 

폼처리를 위하여 다음의 3가지 기능을 제공

 

- 폼 생성에 필요한 데이터를 폼 클래스로 구조화하기

- 폼 클래스의 데이터를 렌더링하여 HTML 폼 만들기

- 사용자로부터 제출된 폼과 데이터를 수신하고 처리하기

 

웹 개발에 있어서 폼이라는 용어는 HTML 의 <form>을 지칭할 수도 있고, <form>을 만들어내는 장고의 Form 클래 스일 수도 있고, 서버로 제출된 구조화된 데이터일 수도 있다. 

가장 핵심적인 컴포넌트는 장고의 폼 클래스이다. 장고의 모델 클래스가 데이터베이스 테이블의 논리적인 구조 및 동작 기능, 

우리에게 보여지는 방식들을 기술하는 것과 마찬가지로, 폼 클래스는 폼을 기술하고 폼이 어떻게 작동하고 어떻게 보이는지를 결정

 

모델 클래스의 필드가 데이터베이스의 필드로 매핑되듯이, 폼 클래스의 필드도 HTML 폼의 <input> 엘리먼트에 매핑된다.

그리고 폼 클래스의 필드도 역시 클래스이다. 필드는 폼 데이터를 저장하고 있으며, 폼이 제출되면 자신의 데이터에 대한 유효성

검사를 실시한다. 필드는 저장하는 데이터의 종류에 따라 자신의 타입을 가지낟. 또한 폼의 필드는 브라우저에서 HTML 위젯으로

표현되고, 필드 타입 마다 디폴트 위젯 클래스를 가지고 있으며 필요 시 오버라이딩될 수 있다.

 

예를 들어, 장고에서는 DateField와 FileField라는 필등 타입을 제공하는데, DateField 타입은 날짜와 관련된 데이터를 다루고, FileField 타입은 파일과 관련된 데이터를 다룬다. 이 둘은 이렇게 서로 다른 종류의 데이터를 처리할 뿐 아니라 유효성 검사 방식도

다르고, 디폴트 위젯 클래스도 다르다. 즉, 폼 클래스의 필드를 정할 때도 대상이 되는 데이터에 맞는 적절한 타입을 지정해 줄 수

있어야 한다.

 

폼도 결국은 템플릿의 일부이므로 템플릿 코드에 포함되어서 렌더링 절차를 거친다.

장고에서 객체를 렌더링할 때 3가지 과정

 

- 렌더링할 객체를 뷰로 가져오기(데이터베이스로부터 객체를 추출하기)

- 그 객체를 템플릿 시스템으로 넘겨주기

- 템플릿 문법을 처리해서 HTML 마크업 언어로 변환하기

 

폼도 객체이기 때문에 템플릿에서 폼을 렌더링하는 작업은 위와 같은 객체를 렌더링하는 것과 거의 동일한 작업이지만, 한 가지

추가적인 고려사항이 있다. 폼 객체에는 데이터가 없을 수도 있다는 점이다.

 

예를 들어, 모델 객체를 렌더링하는 경우에 데이터가 없는 빈 객체를 렌더링하는 경우는 매우 드문일이다. 반면, 폼 객체는 렌더링

이후에 사용자가 데이터를 채우는 것이 보통이므로, 빈 객체를 렌더링하는 일이 자주 발생한다. 그래서 폼 객체를 생성할 때는 

이와 같은 사항을 고려해야 한다.

즉, 폼 객체는 보통 뷰 함수에서 생성하는데, 뷰 함수에서 폼 객체를 생성할 때는 데이터 없이 만들 것인지, 아니면 데이터를 채워서

만들 것인지 적절히 구분해서 코딩해야 한다. 데이터를 채울 때는 저장된 모델 객체를 채울 수도 있고, 또는 직전에 제출된 HTML

폼으로부터 채울 수도 있다. 후자는 폼 필드가 여러 개인 경우 하나의 필드에서 에러가 발생하여 다시 폼 데이터를 입력할 때 에러가

없는 다른 필드들은 직전에 제출된 폼으로부터 데이터를 채우고 사용자에게 보여주는 경우를 하나의 예로 들 수 있다.

 

데이터가 없는 폼을 언바운드(unbound) 폼이라고 하며, 언바운드 폼은 렌더링되어 사용자에게 보여질 때 비어있거나 디폴트 값으로 채워진다. 바운드(bound) 폼은 제출된 데이터를 갖고 있어서 데이터의 유효성 검사를 하는데 사용된다.

 

 

3. 폼 클래스로 폼 생성

폼도 클래스로 정의해서 간편하게 만들 수 있다. 

 

폼 클래스의 정의

위 예제는 필드가 your_name 하나인 폼 클래스이다. label 속성도 정의했다. 이는 렌더링되면 <label> 엘리먼트로 나타날 것이다. 필드의 최대 길이도 max_length="100" 이라는 문구를 넣어서 사용자가 100글자 이상 입력하는 것을 브라우저가 방지할 수

있도록 해준다. 또 다른 하나는 장고가 브라우저로부터 폼 데이터를 받았을 때 데이터의 길이가 유효한지 검사하는 데 사용된다.

 

각각의 폼 필든느 위젯 클래스를 갖고 있고, 이 위젯 클래스는 <input type = 'text'>와 같은 HTML 폼 위젯으로 대응된다.

대부분의 폼 필드는 디폴트 위젯을 갖고 있다. 위 예제에 사용된 CharField 필드 타입은 TextInput 위젯이 디폴트 위젯이며,

HTML <input type = 'text'>로 변환된다. 만일 디폴트 위젯을 <textarea>로 변경하려면 아래와 같이 폼 필드를 정의할 때 명시

적으로 지정하면 된다.

 

참고로 장고의 폼 클래스는 모든 필드에 대해 유효성 검사 루틴을 실행시키는 is_valid() 메소드를 갖고 있다. 이 메소드가 호출되어

유효성 검사를 하고, 그 결과 만일 모든 필드가 유효하다면 is_valild() 메소드는 다음과 같이 2가지 일을 한다.

 

- True를 반환

- 폼 데이터를 cleaned_data 속성에 넣는다.

 

위의 폼 클래스가 템플릿 시스템에 의해 렌더링되면 다음과 같은 결과가 나온다.

렌더링 결과에 <form> 태그나 submit 버튼은 없는데, 이들은 개발자가 직접 템플릿에 넣어줘야 한다. 그래서 템플릿 코드에는

다음과 같이 코딩한다.

폼 클래스는 {{ form }} 변수로 사용하였다. {{ form }} 변수는 뷰에서 컨텍스트 변수에 포함하여 템플릿 시스템으로 넘겨주게 된다.

 

 

4. 뷰에서 폼 클래스 처리

 

작성한 NameForm 폼 클래스와  name.html 템플릿을 사용하여 폼을 보여주고 폼 데이터를 수신하여 처리하는 뷰를 작성해보겠다. 폼을처리 하는 뷰는 2개가 필요하다. 하나는 폼을 보여주는 뷰이고, 다른 하나는 제출된 폼을 처리하는 뷰이다. 2개의 뷰는 하나의 뷰로 통합하여 처리할 수 있는데, 장고에서는 하나의 뷰로 통합하여 폼을 처리하는 것을 권장하고 있다. 

 

이렇게 하나의 뷰에서 2가지 기능을 처리하면, 처음 사용자에게 보여주는 폼과 사용자에게 데이터를 입력한 후 제출된 폼을 구분하여 처리할 수 있어야 한다. 장고에서는 이를 HTTP 메소드로 구분한다. 즉, 뷰가 GET 방식으로 요청을 받은 경우에는 사용자에게 

처음으로 폼을 보여주도록 처리하고, 뷰가 POST 방식으로 요청을 받은 경우에는 데이터가 담긴 제출된 폼으로 간주하여 처리하게 된다. 

- 1. 뷰 함수 이름은 get_name()이고, request 인자는 필수다.

- 2. 요청 방식에 따라, 즉 GET 요청과 POST 요청을 구분하여 처리

- 3. GET 요청이 도착하면 빈 폼 객체를 생성하고, 렌더링을 위해 템플릿 시스템으로 전달한다. 이 부분이 사용자가 해당 URL을

      처음 방문 시 일어나는 동작이다.

- 4. 사용자가 폼에 데이터를 입력하고 제출하면 POST 요청으로 도착한다.

- 5. 폼이 POST 요청으로 제출되면 뷰는 다시 한 번 폼 객체를 생성하는데, 이번에는 요청에 포함된 데이터로 채워준다.

      이러한 폼을 바운드 폼(bound form)이라고 부른다.

- 6. 폼의 is_valid() 메소드를 호출한다.

- 7. True가 아니면 마지막 라인의 render() 함수를 호출한다. 이때 템플릿 시스템으로 전달되는 컨텍스트 변수 'form'에는

      직전에 제출된 폼으로 채워진다.

- 8. 만일 True이면 유효한 폼 데이터가 cleaned_data 속성에 담겨 있게 되고, 이 데이터를 사용하여 데이터베이스를 변경하거나

      로직에 따른 다른 처리를 하게 된다. 그 이후에 브라우저에게 HTTP 리다이렉트를 전송하여 다음 페이지로 이동하게 된다.

- 9. HTTPResponseRedirect()를 리턴하는 경우가 아니면 마지막 라인이 실행된다. render() 함수는 템플릿 코드 name.html에

     컨텍스트 변수를 적용하여 사용자에게 보여줄 최종 템플릿 파일을 만들고, 이를 담아서 HttpResponse 객체를 반환한다.

 

 

 

5. 폼 클래스를 템플릿으로 변환

 

폼 클래스를 템플릿으로 변환하기 위해서는 폼 객체를 생성해서 이를 템플릿 시스템에 넘겨주면 된다. 템플리 시스템에서는 템플릿

문법 및 폼 객체를 해석해서 HTML 템플릿 파일을 만들어준다.

 

{{ form }} 구문은 HTML의 <label>과 <input> 엘리먼트 쌍으로 렌더링 된다.

HTML <label> / <input> 쌍으로 변환 시, {{ form }} 이외에도 3가지 옵션이 더 있다.

 

- {{ form.as_tabel }} : <tr> 태그로 감싸서 테이블 셀로 렌더링된다. {{ form }} 과 동일함

- {{ form.as_p }} : <p> 태그로 감싸도록 렌더링 된다.

- {{ form.as_ul }} : <li> 태그로 감싸도록 렌더링 된다.

 

여기서도 <label> / <input> 태그 쌍을 감싸는 <table> 혹은 <ul> 태그는 개발자가 직접 추가해야 한다. 물론 <form> 태그와

submit 컨트롤도 자동으로 추가되지 않으므로 개발자가 직접 작성해야 한다.

 

{{ form.as_p }} 옵션으로 변환하는 경우,