HTML 폼 구성 방법
============

HTML폼을 만들떄 구조화 하는것은 중요한 것이다. 이것은 두가지 이유로 중요하다. 폼이 사용 할수 있다는 것을 보장하고 접근성도 늘릴수 있기 떄문이다.(즉 장애인들도 쉽게 사용할 수 있다.) HTML 폼의 접근성은 중요한 점이고 어떻게 폼 접근성을 높일 수 있는지 볼것이다.

* 이전
* Overview: Forms
* 다음

기본 사항을 다룬 후에는 이제 폼의 다른 부분에 구조와 의미를 부여하는 데 사용되는 요소들을 좀 더 자세히 살펴보겠습니다.

|  |  |
| --- | --- |
| 전제 조건: | 기본적인 컴퓨터 사용능력과 HTML에 대한 기본적인 이해. |
| 목표: | HTML 폼을 구조화하고 의미를 부여하여 사용가능하고 접근성있는 폼을 만드는 방법을 이해합니다. |

폼의 유연성은 폼을 HTML의 가장 복잡한 구조 중 하나로 만듭니다. 전용 요소들과 특성들로 모든 종류의 기본적인 폼을 만들 수 있습니다. HTML 폼을 만들때 올바른 구조를 사용하게되면 사용성과 접근성을 확보 할 수 있습니다.

<form> 요소
---------

`<form>` 요소는 폼을 형식적으로 정의하는 폼의 동작 방식을 결정하는 속성들을 포함합니다. HTML폼을 생성할떄마다 반드시 이 요소로 시작하고 모든 내용을 내부에 중첩해야 합니다. 많은 보조 기술이나 브라우저 플러그인이 `<form>` 요소를 발견하고 사용하기 쉽게 만들기 위해 특별한 기능을 제공 할 수 있습니다.

우리는 저번 문서에서 이미 이 내용을 다루었습니다.

**경고:** 폼을 다른 폼으로 중첩하는 것은 엄격하게 제한되어 있습니다. 그렇게 하면 사용자가 사용하는 브라우저에 따라 예측할 수 없는 방식으로 작동하므로 좋은 방법이 아닙니다.

언제든지 `<form>` 요소 바깥에서 폼 컨트롤을 사용할 수 있습니다. 이럴 경우 기본적으로 컨트롤은 `form` (en-US) 특성을 사용하여 연결해주지 않는한 어떤 폼과도 관련이 없습니다. 이것은 컨트롤이 폼 요소 내부에 중첩되어있지 않더라도 명시적으로 컨트롤을 명시적으로 바인딩할 수 있도록 하기위해 도입됐습니다.

다음으로 폼 내부의 중첩되어있는 구조요소에 대해 다루겠습니다.

<fieldset> 와 <legend> 요소
------------------------

`<fieldset>` 요소는 같은 목적을 가진 위젯들을 편리하게 그룹화 하는 방법입니다. `<fieldset>`에 레이블을 붙이려면 `<fieldset>` 태그 바로 아래에 `<legend>` 요소를 포함하면 됩니다. `<legend>`의 텍스트 컨텐츠는 `<fieldset>` 요소의 목적을 형식적으로 설명합니다.

많은 보조 기술들이 `<legend>` 요소를 해당 `<fieldset>` 요소내의 각 컨트롤의 레이블의 일부인것 처럼 사용합니다. 예를 들어 Jaws 와 NVDA 같은 스크린 리더는 각 컨트롤의 라벨을 읽기전에 legend를 읽을 것입니다.

아래 작은 예제가 있습니다.


```html
<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>
```


**참고:** 예제는 여기서 찾을수 있습니다. fieldset-legend.html (see it live also).

위 예제를 스크린 리더가 읽을때 Fruit juice size small을 먼저 읽고 Fruit juice size medium을 읽은 다음 마지막으로 Fruit juice size large을 읽을 것입니다.

이 예제에서의 사용 사례는 가장 중요한것 중 하나입니다. 라디오 버튼의 셋트는 항상 `<fieldset>` 요소 내부에 중첩해야 합니다. 다르게 사용하는 사례도 있으며, 흔히 `<fieldset>` 요소는 폼의 구획을 나누는데도 사용 할 수 있습니다. 이상적으로는 긴 폼을 여러 페이지에 걸쳐 나누는 것이 좋지만, 폼이 길어지고 단일 페이지에 위치해야 하는 경우, 서로 다른 관련 섹션을 각각 fieldset 내에 넣는것은 사용성을 향상 시킵니다.

보조기술에 대한 영향력으로 인해, `<fieldset>` 요소는 접근성 있는 폼을 구축하기위한 주요 요소 중 하나입니다. 그러나 이를 남용하지 않는것은 여러분의 책임입니다. 가능하다면 폼을 작성할 때마다 스크린리더가 어떻게 해석하는지 들어보는것 (en-US)이 좋습니다. 스크린리더가 읽는것이 이상하게 들린다면 폼 구조를 개선해보세요.

<label> 요소
----------

이전 글에서 볼 수 있듯이 `<label>` 요소는 HTML 폼 위젯에 대한 레이블을 정의하는 공식적인 방법입니다. 올바르게 구현되면 스크린 리더는 폼 요소의 레이블과 관련된 지시사항을 읽어주며 시각 장애인 사용자 뿐만 아니라 시각적으로 볼 수 있는 사용자에게도 유용합니다. 이전 글에서 본 것처럼, 다음과 같은 예시를 살펴보겠습니다.


```html
<label for="name">Name:</label> <input type="text" id="name" name="user\_name" />
```


`<label>` 요소가 `<input>`요소와 `for` 속성을 통해 (`<input>` 요소의 `id` 속성을 사용) 올바르게 연결되어 있을 때 스크린 리더는 "이름, 텍스트 수정"과 같은 내용을 읽어줍니다.

다른 방법으로도 폼 컨트롤과 레이블을 연결할 수 있습니다. 폼 컨트롤을 `<label>` 내부에 중첩하여 암묵적으로 연결할 수 있습니다.


```html
<label for="name">
  Name: <input type="text" id="name" name="user\_name" />
</label>
```


심지어 이러한 경우에도, 레이블과 위젯간의 관계를 보조기술이 이해할 수 있도록 `for` 속성을 설정하는 것이 가장 좋은 방법으로 권장됩니다.

레이블이 없거나 폼 컨트롤이 암묵적으로나 명시적으로 레이블과 연결되지 않은 경우, 스크린 리더는 "텍스트 수정 공백"과 같은 내용을 읽어주는데, 이는 전혀 도움이 되지 않습니다.

### 레이블도 클릭할 수 있습니다!

레이블을 올바르게 설정했을때의 또다른 장점은 해당 위젯을 활성화 하기위해 레이블을 클릭하거나 탭할 수 있다는 것입니다. 이는 텍스트입력과 같은 컨트롤에 유용합니다. 텍스트 입력의 경우 레이블 뿐만 아니라 입력란을 클릭하여 포커스를 활성화할 수 있지만, 라디오버튼과 체크박스 같은 경우애는 특히 유용합니다. 이러한 컨트롤의 클릭 영역은 매우 작을 수 있으므로 활성화하기 쉽도록 만드는 것이 유용합니다.

예를 들어 아래 예시의 "I like cherry"레이블 텍스트를 클릭하면 *taste\_cherry* 체크박스의 선택 상태가 토글됩니다.


```html
<form>
  <p>
    <input type="checkbox" id="taste\_1" name="taste\_cherry" value="cherry" />
    <label for="taste\_1">I like cherry</label>
  </p>
  <p>
    <input type="checkbox" id="taste\_2" name="taste\_banana" value="banana" />
    <label for="taste\_2">I like banana</label>
  </p>
</form>
```


> 
> **Note:** 예제는 여기서 찾을수 있습니다. checkbox-label.html (see it live also).
> 
> 
> 

### 여러개의 레이블

엄밀히 말하자면, 하나의 위젯에 여러개의 레이블을 넣을 수도 있지만, 이는 일부 보조기술에서 처리하는데 문제가 발생할 수 있으므로 좋은 아이디어는 아닙니다. 여러 레이블을 사용하는 경우 위젯과 해당 레이블을 하나의 `<label>` 요소안에 중첩시켜야 합니다.

다음 예시를 살펴보겠습니다.


```html
<p>Required fields are followed by <span aria-label="required">*</span>.</p>

<!-- 따라서 이렇게 작성합니다. -->
<!--div>
 <label for="username">Name:</label>
 <input id="username" type="text" name="username" required>
 <label for="username"><span aria-label="required">\*</label>
</div-->

<!-- 이렇게 작성하는것이 더 좋습니다. -->
<!--div>
 <label for="username">
 <span>Name:</span>
 <input id="username" type="text" name="username" required>
 <span aria-label="required">\*</span>
 </label>
</div-->

<!-- 하지만 이렇게 작성하는 것이 가장 좋을것입니다. -->
<div>
  <label for="username">Name: <span aria-label="required">*</span></label>
  <input id="username" type="text" name="username" required />
</div>
```


위의 문단은 필수 요소에대한 규칙을 설명합니다. 이 규칙은 필수요소를 만나기 전에 시각적으로 확인하는 사용자와 스크린 리더와 같은 보조 기술을 사용하는 사용자가 해당 규칙을 알 수 있도록 '이전'에 포함되어야 합니다. 이 방법은 사용자에게 애스터리스크의 의미를 알려주는데 도움이 됩니다. 그러나 이것으로는 부족합니다. 스크린리더는 애스터리스크를 '별표'라고 읽어줍니다. 시각적인 마우스 사용자가 마우스 호버하면 '필수'가 표시되어야 하는데, 이는 `title` 속성을 사용하여 달성할 수 있습니다. `title` 속성이 스크린 리더에서 읽히는 것은 스크린 리더의 설정에 다라 달라지므로 항상 스크린 리더에서 읽히는 `aria-label` 속성 또한 추가하는 것이 더 신뢰할 수 있습니다.

위의 예시들은 효과적으로 사용하기 위해 순서대로 개선되었습니다.

* 첫 번째 예시에서는 입력필드와 함께 레이블이 읽히지 않고 "텍스트 수정 공백"만 읽힙니다. 또한 실제 레이블들이 따로 읽히기 때문에 여러개의 `<label>` 요소가 스크린 리더를 더 혼란스럽게 만듭니다.
* 두 번째 예시에서는 조금 더 명확해집니다. 입력필드와 함께 읽히는 레이블은 "이름 별표 이름 텍스트수정 리콰이어드"로 읽히고, 레이블들은 여전히 따로 읽힙니다. 여전히 약간 혼동스럽지만, 이번에는 `<input>` 에 레이블이 연결되어있어서 조금 더 나아진 것입니다.
* 세 번째 예시가 가장 좋습니다. 실제 레이블이 모두 함께 읽히고, 입력필드와 함께 읽히는 레이블은 "이름 리콰이어드 텍스트 수정"으로 읽힙니다.

**참고:** 실제로 사용하는 스크린 리더에 따라 결과가 약간 다를 수 있습니다. 이 예시는 VoiceOver에서 테스트 되었으며, NVDA도 비슷하게 작동합니다. 여러분에 경험에 대해서도 듣고 싶습니다.

**참고:** 이 예시는 GitHub에서 required-labels.html (see it live also)로 찾을 수 있습니다. 다만, 주의해야 할 점은 두개이상의 버전을 주석 해제하여 테스트하지 않아야 한다는 점입니다. 동일한 ID를 가진 여러개의 레이블과 입력 필드가 있는 경우 스크린 리더가 혼란스러워질 수 있습니다!

폼과 함께 사용되는 일반 HTML 구조
---------------------

웹 폼과 관련된 구조 이외에도, 폼 마크업은 단지 HTML일 뿐이라는 것을 기억하는것이 좋습니다. 이는 HTML의 모든 기능을 활용하여 웹 폼을 구조화할 수 있다는 것을 의미합니다.

예시에서 확인할 수 있듯이, 레이블과 해당 위젯을 `<ul>` 또는 `<ol>` 리스트 내에서 `<li>` 요소로 감싸는 것이 흔한 방법입니다. `<p>`와 `<div>` (en-US) 요소도 흔히 사용됩니다. 여러 개의 체크박스 또는 라디오 버튼을 구조화하기 위해 리스트를 사용하는 것이 권장됩니다.

`<fieldset>` 요소 외에도, 복잡한 폼을 구조화 하기 위해 HTML 제목 (예: h1, h2) 과 섹션화 (e.g. `<section>`) 을 사용하는 것이 흔한 방법입니다.

무엇보다도, 접근 가능하고 사용하기 쉬운 폼을 만들기 위해 편안한 코딩 스타일을 찾는것은 여러분에게 달려있습니다. 각각의 기능 섹션은 별도의 `<section>` 요소에 포함되어야 하며, 라디오 버튼을 포함하는 `<fieldset>` 요소를 사용해야 합니다.

학습 활동: 폼 구조 만들기
---------------

자 그럼 아이디어를 실제로 적용해보고 약간 더 복잡한 폼의 결제 폼을 만들어보겠습니다. 이 폼에는 아직 이해하지 못할 수도 있는 여러가지 컨트롤 유형이 포함됩니다. 현재는 걱정하지 마세요. 다음 글에서 이들이 어떻게 작동하는지 알게될 것입니다 (기본 네이티브 폼 컨트롤). 지금은 아래 지침을 주의 깊게 읽으며, 폼을 구조화 하는데 사용하는 래퍼 요소와 그 이유를 천천히 파악해보세요.

1. 컴퓨터의 새로운 경로에 빈 템플릿 파일 과 결제 폼을 위한 CSS 의 로컬 복사본을 만드는것으로 시작해 봅시다.
2. HTML에 CSS를 적용하기 위해 HTML `<head>` 내부에 아래의 코드를 추가하세요

```html
<link href="payment-form.css" rel="stylesheet" />
```

3. 다음으로, `<form>` 요소를 추가해 폼을 생성하세요.

```html
<form>
...
</form>
```

4. 유저에게 필수 입력 필드가 어떻게 표시될지 알려주기 위해 `<form>` 요소 안에 제목과 문단을 추가하세요

```html
<h1>Payment form</h1>
<p>
  Required fields are followed by
  <strong><span aria-label="required">*</span></strong>.
</p>
```

5. 다음으로, 이전 항목 아래에 더 큰 코드 섹션을 폼에 추가하겠습니다. 여기에서는 연락처 정보를 필드를 별도의 `<section>` 요소로 감싸고 있는것을 볼 수 있습니다. 또한, 세 개의 라디오 버튼을 각각 자체적인 목록(`<li>`) 요소 내에 넣고 있습니다. 또한, 두 개의 일반 텍스트가 `<input>` (en-US) 과 그에 해당하는 `<label>` 요소가 각각 `<p>` 안에 포함되어 있으며, 비밀번호를 입력을 위한 비밀번호 입력란도 있습니다. 아래 코드를 폼에 추가하세요.

```html
<section>
  <h2>Contact information</h2>
  <fieldset>
    <legend>Title</legend>
    <ul>
      <li>
        <label for="title\_1">
          <input type="radio" id="title\_1" name="title" value="K" />
          King
        </label>
      </li>
      <li>
        <label for="title\_2">
          <input type="radio" id="title\_2" name="title" value="Q" />
          Queen
        </label>
      </li>
      <li>
        <label for="title\_3">
          <input type="radio" id="title\_3" name="title" value="J" />
          Joker
        </label>
      </li>
    </ul>
  </fieldset>
  <p>
    <label for="name">
      <span>Name: </span>
      <strong><span aria-label="required">*</span></strong>
    </label>
    <input type="text" id="name" name="username" required />
  </p>
  <p>
    <label for="mail">
      <span>Email: </span>
      <strong><span aria-label="required">*</span></strong>
    </label>
    <input type="email" id="mail" name="usermail" required />
  </p>
  <p>
    <label for="pwd">
      <span>Password: </span>
      <strong><span aria-label="required">*</span></strong>
    </label>
    <input type="password" id="pwd" name="password" required />
  </p>
</section>
```

6. 우리 폼의 두번째 `<section>` 은 결제정보 입니다.
 여기에서는 각각의 라벨과 함께 세 가지 독립적인 컨트롤이 포함된 `<p>` 가 있습니다.
 첫 번째는 신용카드 종류를 선택하기 위한 드롭다운 메뉴 (`<select>` (en-US)) 입니다.
 두번째는 신용카드 번호를 입력하기 위한 `tel` 타입의 `<input>` 요소입니다. 우리는 `number` 타입을 사용할 수 도 있지만, 우리는 번호입력기 UI(number's spinner UI)를 사용하고 싶지 않습니다.
 마지막으로 카드의 만료 날짜를 입력하기 위한 `text` 타입의 `<input>` 요소입니다. 이는 올바른 형식을 나타내는 *placeholder* 속성과 입력된 날짜가 올바른 형식인지 검사하는 *pattern* 속성이 포함되어 있습니다.
 이런 최신 입력 타입은 The HTML5 input types 에서 다시 소개됩니다.
 이전 섹션 아래에 다음 코드를 입력해주세요.

```html
<section>
  <h2>Payment information</h2>
  <p>
    <label for="card">
      <span>Card type:</span>
    </label>
    <select id="card" name="usercard">
      <option value="visa">Visa</option>
      <option value="mc">Mastercard</option>
      <option value="amex">American Express</option>
    </select>
  </p>
  <p>
    <label for="number">
      <span>Card number:</span>
      <strong><span aria-label="required">*</span></strong>
    </label>
    <input type="tel" id="number" name="cardnumber" required />
  </p>
  <p>
    <label for="expiration">
      <span>Expiration date:</span>
      <strong><span aria-label="required">*</span></strong>
    </label>
    <input
 type="text"
 id="expiration"
 required="true"
 placeholder="MM/YY"
 pattern="^(0[1-9]|1[0-2])\/([0-9]{2})$" />
  </p>
</section>
```

7. 마지막 섹션에서 추가할 섹션은 훨씬 간단합니다, 이 섹션에서는 데이터를 제출하기 위한 `submit` 타입의 `<button>` 이 포함되어 있습니다. 이를 폼 가장 아래에 추가해주세요.

```html
<section>
  <p>
    <button type="submit">Validate the payment</button>
  </p>
</section>
```

8. 마지막으로, 폼을 완성하기 위해 닫는 `<form>` 태그를 추가해주세요.

```html
</form>
```


아래에서 완성된 폼을 확인할 수 있습니다. (GitHub에서도 확인할 수 있습니다. — payment-form.html source 와 running live를 확인 하세요):

실력을 시험해 보세요!
------------

이 글을 끝까지 읽었지만 가장 중요한 내용을 기억할 수 있나요? 이동하기 전에 Test your skills: Form structure (en-US) 를 방문해 확인 해보세요!

요약
--

이제 웹 폼을 올바르게 구조화하는 데 필요한 모든 지식을 갖게 되었습니다. 다음 기사에서는 사용자로부터 정보를 수집하기 위해 사용할 다양한 종류의 폼 위젯에 대해 자세히 알아볼 것입니다. 앞으로 몇 가지 기사에서 소개된 기능들을 자세히 다룰것 입니다.

함께 보기
-----

* A List Apart: *Sensible Forms: A Form Usability Checklist*
* 이전
* Overview: Forms
* 다음
### 고급 주제

* How to build custom form controls (en-US)
* Sending forms through JavaScript (en-US)
* Property compatibility table for form widgets (en-US)