[HTML5] Form과 Client-Side Validation
by Jewoo.Song
1. HTML Form
HTML 폼은 사용자와 웹사이트 또는 애플리케이션이 서로 상호 작용하는 것 중 중요한 기술 중 하나이다. 폼은 사용자가 웹사이트에 데이터를 전송하기 위해 사용한다.
폼은 하나 이상의 위젯(입력 양식 태그)으로 만들어진다.
- input
- textarea
- button
- select
- checkbox
- radio button
- submit button
1-1. form
모든 HTML 폼은 <form>
엘리먼트로 시작된다.
<form>
엘리먼트는 일반적으로 form과 form의 동작을 결정하는 attribute를 정의한다.
<form action="/my-handling-form-page" method="post">
<!-- 위젯 -->
</form>
폼 엘리먼트를 사용할 때에는 폼이 동작하는 방식을 설정하는 일부 attribute들을 지정해야 하고, 대부분 선택적이지만 action, method는 필수적으로 설정해야 한다.
- action : 데이터를 전송할 URL을 지정한다.
- method : HTTP 전송 방식을 지정한다.(GET, POST)
GET
- GET 방식은 전송 URL에 입력 데이터를 쿼리스트링으로 보내는 방식이다. 예) http://jsonplaceholder.typicode.com/posts?userId=1&id=1
- REST API에서 GET 메서드는 모든 또는 특정 리소스의 조회를 요청한다.
POST
- POST 방식은 Request Body에 담아 보내는 방식이다. 예) http://jsonplaceholder.typicode.com/posts
- REST API에서 POST 메서드는 특정 리소스의 생성을 요청한다.
1-2. input
<input>
엘리먼트는 form 엘리먼트 중에서 가장 중요한 엘리먼트로 사용자로부터 데이터를 입력받기 위해 사용된다.
input 엘리먼트의 가장 중요한 attribute는 type이다.
type : input 엘리먼트가 어떻게 입력을 받을 정의한다.
사용할 수 있는 input type은 다음과 같다.
- button
- checkbox
- color
- date
- datetime-local
- file
- hidden
- image
- month
- number
- password
- radio
- range
- reset
- search
- submit
- tel
- text
- time
- url
- week
공통 속성
- autofocus : boolean 타입의 속성을 지정하여 페이지 로드 시 엘리먼트에 포커스가 자동으로 지정되도록 할 수 있다. document 내에서 오직 하나의 form 엘리먼트만이 이 속성을 지정할 수 있다. (default false)
- disabled : boolean 타입의 속성을 지정하여 엘리먼트가 사용자와 상호작용을 할 수 있을지 여부를 지정할 수 있다. disabled 속성은 따로 지정하지 않으면 상속되고 기본적으로는 enabled 상태이다.(default false)
- form : form 속성에 document 내에 다른 form 엘리먼트의 id 값을 입력하면, 다른 form 엘리먼트 안에 있더라도 form control을 외부에 있는 form과 연결할 수 있다.
- name
- value
Text
Text type field는 가장 기초적이고 많이 사용되는 폼 위젯이다. input 엘리먼트의 type 속성에 text를 입력하거나, type 속성을 생략하면(default) text type으로 동작한다.
Text input fields Text type을 포함하여 모든 text 입력하는 동작을 하는 폼 위젯들은 몇몇 공통된 속성을 가지고 있다.(text, password, email, hidden 등)
- readonly : 입력된 값을 수정할 수 없지만 전송 가능하다.
- disabled : 입력된 값을 수정할 수 없고, 전송할 수 없다.
- placeholder : textbox에 표시되는 텍스트로 textbox의 목적을 간략하게 설명하는데 사용한다.
- size : size를 지정한다.
- maxlength : 입력할 수 있는 maximum 글자 수를 지정한다.
- spellcheck : browser가 지원하면 spell check 기능을 사용할 수 있다.
<input type="text" id="comment" name="comment" value="I'm a text field" />
Password
password type을 지정해도 text 입력 시에 특별한 제약사항은 없지만, 입력된 text는 dot이나 별표와 같은 문자로 감춰진다.
주의할 점은 password type으로 지정하는 것은 단순히 UI 상의 기능이므로 데이터 전송 시 보안까지는 책임지지 않는다. user form data를 보호하기 위해서는 https를 사용해서 data 전송 전에 암호화를 해야 한다.
<input type="password" id="pwd" name="pwd" />
Hidden
또 다른 text control의 종류로는 hidden type이 있다. hidden type은 사용자에게 보이지 않는 폼 컨트롤을 생성할 때 사용한다.(사용자에게는 보이지 않지만 서버로는 전송된다.)
예를 들어, timestamp 등의 data를 사용자에게는 노출하지 않고 전송하기 위해 사용할 수 있다.
<input type="hidden" id="timestamp" name="timestamp" value="1286705410" />
Check box
input type에 checkbox를 지정하면 Check box control을 생성할 수 있다.
<input type="checkbox" id="carrots" name="carrots" value="carrots" checked />
checked 속성을 가지고 있으면 페이지가 로드될 때 확인란이 체크된다. 그리고 확인란 또는 연결된 label을 클릭하면 확인란이 토글 된다.
Checkable items : checkboxes and radio buttons check box와 radio button 컨트롤은 checked 속성을 사용한다. 이 속성은 위젯이 check되었는지를 나타낸다.
- Checkable item들은 일반적인 다른 폼 위젯과 같이 동작하지 않는다.
- 다른 위젯은 name 속성을 가지고 있는 경우 값이 채워지지 않았더라도 전송한다.
- Checkable item들은 오직 checked 상태일 때만 데이터를 전송한다.(name이 있더라도)
- 만약 check 되었지만 value가 없다면
on
으로 전송된다.- 일반적으로 lebel, fieldset/legend 엘리먼트와 함께 사용된다.
Radio button
input type에 radio를 지정하면 radio button control을 생성할 수 있다.
<input type="radio" id="soup" name="meal" checked />
라디오 버튼은 여러개를 묶어서 사용할 수 있다. name
속성에 동일한 값을 주면 같은 버튼 그룹으로 엮을 수 있다. 그리고 한 번에 오직 하나의 버튼만 선택할 수 있다. 즉, 버튼중에 하나가 선택되면 다른 버튼들은 자동으로 선택 해제되게 된다.
form이 전송될 때, 선택된 하나의 radio button만 전송된다. 만약 선택된 버튼이 없다면 전달되지 않는다. 그리고 그룹내에 라디오 버튼 중 하나가 선택되면 form을 reset하기 전에는 모든 버튼의 선택을 취소할 수 없다.
image
input type에 image를 지정하면 image button control을 생성할 수 있다. image button은 클릭하면 summit 버튼과 같이 동작한다는 점만 제외하고는 <img>
엘리먼트와 똑같이 렌더링된다.
그리고 img 엘리먼트의 모든 속성과 다른 form button들이 같은 속성을 지원한다.
<input type="image" alt="Click me!" src="my-img.png" width="80" height="30" />
image 엘리먼트로 submit 하면 value 대신에 이미지 기준의 x, y 좌표를 전송한다.
fname=song&lname=jewoo&x=36&y=44
file
input type에 file을 지정하면 file picker control을 생성할 수 있다. File을 서버로 전송하기 위해 사용하고, file picker 위젯을 사용하여 하나 이상의 파일을 선택할 수 있다.
선택 가능한 파일은 accept 속성을 사용해서 지정할 수 있다. 그리고 하나 이상의 파일을 선택하게 하려면 multiple 속성을 추가하면 된다.
<input type="file" name="file" id="file" accept="image/*" multiple />
1-3. label
label은 HTML forms의 label을 정의하는 공식적인 방법이다.
- 접근 가능한 form을 만들 때 중요한 엘리먼트다.
- for attribute는
<label>
엘리먼트와 폼 위젯을 연결하기 위해 사용되고, 폼 위젯의 id와 for의 값이 매핑되어야 한다. - for attribute로 연결되면 사용자가 라벨을 눌렀을 때 그거에 맞는 위젯을 활성화시킨다.
<form action="/my-handling-form-page" method="post">
<div>
<label for="name">Name:</label>
<input type="text" id="name" />
</div>
<div>
<label for="mail">E-mail:</label>
<input type="email" id="mail" />
</div>
</form>
1-4. select
여러 개의 리스트에서 여러 개의 아이템을 선택할 때 사용한다.
서버에 전송되는 데이터는 select 엘리먼트의 name 어트리뷰트를 키로 하고 option 엘리먼트의 value 어트리뷰트를 값으로 하여 key=value 형태로 전송된다.
optgroup 엘리먼트는 option 엘리먼트를 그룹화하는 역할을 한다.
<label for="cars">Choose a car:</label>
<select name="cars" id="cars" form="carform">
<optgroup label="Swedish Cars">
<option value="volvo">Volvo</option>
<option value="saab">Saab</option>
</optgroup>
<optgroup label="German Cars">
<option value="bmw">BMW</option>
<option value="audi">Audi</option>
</optgroup>
</select>
1-5. fieldset/legend
<fieldset>
엘리먼트를 사용하여 동일한 목적을 공유하는 위젯 그룹을 만들 수 있다.
opening fieldset 태그 바로 아래에 <legend>
엘리먼트를 포함시켜서 fieldset에 label을 지정할 수 있다.
legend 엘리먼트의 fieldset 태그 내에서 사용되어야 하고 그룹화된 fieldset의 제목을 정의한다.
<form>
<fieldset>
<legend>Fruit juice size</legend>
<p>
<input type="radio" name="size" id="size_1" value="small" />
<label for="size_1">Small</label>
</p>
<p>
<input type="radio" name="size" id="size_2" value="medium" />
<label for="size_2">Medium</label>
</p>
<p>
<input type="radio" name="size" id="size_3" value="large" />
<label for="size_3">Large</label>
</p>
</fieldset>
</form>
1-6. textarea
textarea 엘리먼트는 여러 줄의 text를 입력할 때 사용한다.
<textarea name="editor" rows="15" cols="20"></textarea>
1-7. button
라디오 버튼은 이름은 버튼이지만 실제로 버튼은 아니다. <button>
엘리먼트는 세 가지 타입이 존재한다.
<button>
엘리먼트를 사용하던 <input>
의 type 속성에 button을 지정하던 동일하게 동작한다.
그러나 button 엘리먼트를 사용하면 opening 태그와 closing 태그 사이에 컨텐츠를 입력할 수 있다. input 엘리먼트는 빈 엘리먼트로만 사용 가능하다.(value는 속성을 통해서 입력받으므로 오직 일반 텍스트만 가능하다.)
submit
form data를 서버로 전송하는 역할을 한다. (default)
<button type="submit">This is a <strong>submit button</strong></button>
<input type="submit" value="This is a submit button" />
reset
모든 form 위젯을 default value로 초기화한다.
<button type="reset">This is a <strong>reset button</strong></button>
<input type="reset" value="This is a reset button" />
button
버튼 type은 자동으로 적용되는 동작은 없고, Javascript code를 사용하여 사용자 정의 동작을 수행한다.
<button type="button">This is an <strong>anonymous button</strong></button>
<input type="button" value="This is an anonymous button" />
2. Client-side form validation
서버로 데이터를 전송하기 전에 전송에 필요한 모든 데이터를 올바른 포맷으로 form control 들에 채워넣는 것이 중요하다.
이렇게 올바른 포맷으로 form control에 데이터를 작성할 수 있도록 검증하는 과정을 client-side form validation
이라 한다.
client-side form validation은 높은 사용자 경험을 위해 중요한 기능이다.
client-side form validation이 보안을 책임지는 것은 좋지 않다. 앱은 항상 클라이언트 측의 데이터에 대해 서버에서 추가로 validation check를 해야 한다.
Validation type 별 특징
- Built-in form validation
- HTML5에 form validation 기능으로 많은 Javascript 코드를 요구하지 않는다.
- Javascript validation 보다 좋은 성능을 가지만 Custom에 용이하진 않다.
- Javascript validation
- 완전히 custom이 가능하지만 모두 직접 만들거나 library를 사용해야 한다.
2-1. Build-in From Validation
HTML5 form control의 가장 중요한 기능 중 하나는 validation이다.
Javascript에 의존하지 않고 대부분의 사용자 데이터의 유효성을 검증할 수 있다.
- required : 필수로 form field를 채워야 하는지
- minlength/maxlength : 최소/최대 길이
- min/max : 최소/최대 정수
- type : number, email등 input type 지정
- pattern : 입력 가능 패턴 정의(정규표현식)
<form>
<input name="email" required />
<button type="submit">제출</button>
</form>
CSS 가상 클래스
valid 상태일 때 엘리먼트가 :valid CSS 가상 클래스에 매치된다.
input:valid {
border-color: blue;
}
invalid 상태일 때 엘리먼트가 :invalid CSS 가상 클래스에 매치된다.
input:invalid {
border-color: red;
}
HTML5에서는 폼 입력을 아래와 같이 처리한다.
-
사용자 입력
- validation 결과에 따라 :valid/:invalid 가상 클래스 적용
- 폼 데이터 전송 시도
- validation 규칙에 따라 입력 값 검증
- 통과하면 전송
- 통과하지 못하면 전송 차단 후
오류 메세지
노출
오류 메세지
브라우저 기본 동작을 활용하여 오류 메세지를 나타낼 수 있다.
기본 오류 메세지는 다음과 같고 각 message는 locale에 따라 번역되어 표시된다.
- required : 이 입력란을 작성하세요.
- minlength/maxlength : 이 텍스트를 {maxlength}자 이상으로 늘리세요.
- min/max : 값은 {min}이상이어야 합니다.
- type : 이메일 주소에 ‘@’를 포함해주세요. ‘‘에는 ‘@’가 없습니다.
- pattern : 요청한 형식과 일치시키세요.
2-3. Javascript를 사용한 form validation
만약 기본 오류 메세지를 수정하거나 레거시 브라우저를 처리하려면 반드시 Javascript를 사용해야 한다.
이때 Constraint Validation API를 사용할 수 있다.
Constraint Validation API
대부분의 브라우저는 Constraint Validation API를 지원한다.
다음과 같은 form 엘리먼트 DOM interface에서 Constraint Validation API를 사용할 수 있다.
- HTMLButtonElement (<button>)
- HTMLFieldSetElement (<fieldset>)
- HTMLInputElement (<input>)
- HTMLOutputElement (<output>)
- HTMLSelectElement (<select>)
- HTMLTextAreaElement (<textarea>)
Constraint Validation API은 다음과 같은 property들과 메서드로 구성된다.
- property
- validity : 다음과 같은 property를 가지고 있는 ValidityState Object를 반환한다.
- patternMismatch : pattern에 맞는 value가 입력되었는지
- tooLong : maxLength 속성 만족 여부
- tooShort : minLength 속성 만족 여부
- rangeOverflow : max 속성 만족 여부
- rangeUnderflow : min 속성 만족 여부
- typeMismatch : 지정된 타입에 알맞은 value가 입력되었는지(email)
- valid : valid 인지 invalid 인지
- valueMissing : required 속성 만족 여부
- validationMessage : locale이 반영된 오류 메세지를 반환한다.
- willValidate : form이 summit 될 때 valid 한 상태라면 true, 아니라면 false를 반환한다.
- validity : 다음과 같은 property를 가지고 있는 ValidityState Object를 반환한다.
- method
- checkValidity() : 엘리먼트에 유효성 문제가 없는 경우 true를 반환한다.
- setCustomValidity(emssage) : 엘리먼트에 사용자 정의 오류 메세지를 추가한다. 오류 발생 시 지정된 오류가 표시된다.
아래 코드에서 이메일 입력의 validity.typeMismatch 속성을 확인하여 만약 typeMismatch가 true라면 setCustomValidity() 메서드를 사용하여 에러 메세지를 지정한다.
<form>
<label for="mail"
>I would like you to provide me with an e-mail address:</label
>
<input type="email" id="mail" name="mail" />
<button>Submit</button>
</form>
const email = document.getElementById("mail");
email.addEventListener("input", function (event) {
if (email.validity.typeMismatch) {
email.setCustomValidity("I am expecting an e-mail address!");
} else {
email.setCustomValidity("");
}
});
커스텀 메세지 형식
만약 메세지 내용뿐 아니라 나타내는 모습까지 제어하고 싶다면 기본 동작을 preventDefault로 막고 원하는 위치에 나타낼 수도 있다.
input.addEventListener("invalid", (e) => {
e.preventDefault();
//특정 메세지를 원하는 위치에 넣는 코드
});
참고
- https://developer.mozilla.org/ko/docs/Learn/HTML/Forms/
- https://developer.mozilla.org/ko/docs/Learn/HTML/Forms/Your_first_HTML_form
- https://developer.mozilla.org/en-US/docs/Learn/Forms/How_to_structure_a_web_form
- https://developer.mozilla.org/en-US/docs/Learn/Forms/Basic_native_form_controls
- https://developer.mozilla.org/en-US/docs/Learn/Forms/Form_validation
- https://poiemaweb.com/html5-tag-forms
- HTML5 폼 검증에 대해 정리해 보자
Subscribe via RSS