한 줄 요약 — 한 줄짜리 입력 칸을 넘어, 목록을 펼쳐 하나 고르는 <select>, 여러 줄을 받는 <textarea>, 여러 개를 켜는 체크박스, 한 묶음에서 딱 하나만 고르는 라디오 버튼을 정리합니다. 그리고 <fieldset><legend>는 흩어진 칸들을 "이건 한 가지 질문을 위한 칸"이라고 한 묶음으로 엮어 줍니다.

학습 목표

  • <select>·<textarea>·체크박스·라디오 버튼이 각각 어떤 입력에 어울리는지 고를 수 있다.
  • 같은 name이 라디오 버튼 여러 개를 한 묶음으로 만든다는 것을 안다.
  • 체크박스와 라디오 버튼이 언제 갈리는지(여러 개 vs 하나) 설명할 수 있다.
  • <fieldset><legend>로 관련된 칸들을 한 묶음으로 엮을 수 있다.
  • 회원가입 설문 폼을 fieldset으로 묶어 키보드만으로 채울 수 있다.

오늘의 비유 — 객관식 시험지의 묶음 문항

객관식 시험지를 떠올려 봅니다. 시험지에는 답을 적는 방식이 한 가지가 아닙니다. 보기 네 개 중 하나에만 동그라미를 치는 문항이 있고, "맞는 것을 모두 고르시오"처럼 여러 개를 칠하는 문항도 있습니다. 어떤 문항은 빈 줄에 직접 문장을 적는 서술형이고, 또 어떤 문항은 여러 문제가 한 묶음으로 묶여 있습니다. 그 묶음 위에는 "다음 글을 읽고 1번부터 3번까지 물음에 답하시오" 같은 안내 문구가 한 줄 붙어 있습니다.

폼의 입력 칸도 똑같이 여러 모양입니다. 한 묶음에서 하나만 고르는 칸이 라디오 버튼, 칸마다 따로 켜는 칸이 체크박스, 목록을 펼쳐 하나 고르는 칸이 <select>, 여러 줄을 적는 칸이 <textarea>입니다. 그리고 관련된 칸을 한 묶음으로 두르는 테두리가 <fieldset>, 그 묶음의 안내 문구가 <legend>입니다. 오늘은 이 네 가지 칸과 그것을 묶는 테두리를 정리합니다.

핵심 개념

지난 회의 폼은 <input type="text">처럼 한 줄짜리 칸이 전부였습니다. 하지만 실제 폼에는 여럿 중에서 고르는 칸과 여러 줄을 받는 칸이 필요합니다. 객관식 문항, 모두 고르는 문항, 서술형 문항이 한 시험지에 섞여 있는 것과 같습니다. 하나씩 봅니다.

select — 목록을 펼쳐 하나 고르기

<select>는 보기를 접어 둔 목록입니다. 누르면 펼쳐지고, 그 안의 <option> 하나를 고르면 다시 접힙니다.

<label for="grade">학년</label>
<select id="grade" name="grade">
  <option value="1">1학년</option>
  <option value="2">2학년</option>
  <option value="3" selected>3학년</option>
</select>

<option>value가 서버로 가는 값이고, 태그 사이의 글자는 화면에 보이는 보기입니다. 어느 <option>selected를 붙이면 그 보기가 처음부터 골라진 채로 시작합니다. 보기가 대여섯 개를 넘어 자리를 많이 차지할 때, <select>는 칸 하나 크기로 접어 둘 수 있어 편합니다.

textarea — 여러 줄을 받는 칸

<input type="text">가 한 줄이라면, <textarea>는 문단을 통째로 받는 칸입니다. 서술형 답안 칸처럼 줄이 여러 개입니다.

<label for="reason">가입 이유</label>
<textarea id="reason" name="reason" rows="4"></textarea>

<input>과 달리 <textarea>는 여는 태그와 닫는 태그가 따로 있습니다. rows는 칸의 처음 높이를 줄 수로 정합니다. rows="4"면 네 줄 높이로 시작하고, 글이 길어지면 사용자가 칸 모서리를 끌어 늘릴 수 있습니다.

체크박스와 라디오 버튼 — 여러 개냐, 하나냐

체크박스와 라디오 버튼은 둘 다 <input>type으로 만듭니다. 생김새는 닮았지만 고르는 방식이 정반대입니다.

<input type="checkbox" id="news" name="news">
<label for="news">새 글 알림</label>

<input type="radio" id="m" name="gender" value="male">
<label for="m">남성</label>
<input type="radio" id="f" name="gender" value="female">
<label for="f">여성</label>

체크박스는 칸마다 따로따로 켜고 끕니다. "맞는 것을 모두 고르시오" 문항처럼 여러 개를 동시에 켤 수 있습니다. 라디오 버튼은 name이 같은 것끼리 한 묶음이 되어, 그 묶음에서는 하나만 골라집니다. 한 문항의 보기 중 하나에만 동그라미를 치는 것과 같습니다.

여기서 name이 핵심입니다. 위의 두 라디오 버튼은 name이 똑같이 gender라서 한 문항으로 묶입니다. 남성을 고르면 여성은 저절로 풀립니다. 만약 name을 다르게 주면 둘은 서로 다른 문항이 되어 둘 다 골라질 수 있습니다 — 이건 흔한 실수에서 다시 봅니다.

fieldset과 legend — 칸들을 한 묶음으로

지금까지 만든 칸들은 화면에 나열돼 있을 뿐, 어떤 칸들이 한 가지 질문을 위한 것인지는 표시돼 있지 않습니다. 시험지의 묶음 문항이 테두리와 안내 문구로 "여기부터 여기까지가 한 묶음"이라고 알려 주듯, 폼에서는 <fieldset><legend>가 그 일을 합니다.

<fieldset>
  <legend>성별</legend>
  <input type="radio" id="m" name="gender" value="male">
  <label for="m">남성</label>
  <input type="radio" id="f" name="gender" value="female">
  <label for="f">여성</label>
</fieldset>

<fieldset>은 관련된 칸들을 한 테두리로 두르고, <legend>는 그 묶음의 제목을 답니다. 화면에는 칸들을 감싼 얇은 테두리와 그 위에 걸친 제목으로 나타납니다. 특히 라디오 버튼 묶음에는 <fieldset>이 거의 필수입니다. "남성 / 여성"이라는 보기만으로는 무엇을 묻는 문항인지 알 수 없고, <legend>의 "성별"이 있어야 비로소 묶음 문항이 완성되기 때문입니다.

함께 따라하기 — fieldset으로 묶은 회원가입 설문

지난 회에 만든 폴더 옆에 day-10 폴더와 index.html을 두고, <body> 안에 제목 <h1>회원가입 설문</h1>을 하나 둔 다음 그 아래를 이렇게 채웁니다.

<form action="/signup" method="post">
  <fieldset>
    <legend>관심 분야</legend>
    <input type="radio" id="fe" name="field" value="frontend">
    <label for="fe">프론트엔드</label>
    <input type="radio" id="be" name="field" value="backend">
    <label for="be">백엔드</label>
  </fieldset>
  <fieldset>
    <legend>받고 싶은 알림</legend>
    <input type="checkbox" id="news" name="news">
    <label for="news">새 글 알림</label>
    <input type="checkbox" id="event" name="event">
    <label for="event">이벤트 알림</label>
  </fieldset>
  <button type="submit">제출</button>
</form>

저장하고 브라우저로 열어보면 테두리 상자 두 개가 보입니다. 첫 상자는 "관심 분야"라는 제목이 테두리 위에 걸려 있고 그 안에 라디오 버튼 두 개, 둘째 상자는 "받고 싶은 알림" 아래에 체크박스 두 개가 들어 있습니다.

이제 차이를 직접 확인해 봅니다. "관심 분야"의 프론트엔드를 고른 뒤 백엔드를 누르면 프론트엔드가 저절로 풀립니다. 두 라디오 버튼의 name이 둘 다 field라 한 묶음이기 때문입니다. 반면 "받고 싶은 알림"의 체크박스 두 개는 서로 무관해서 둘 다 켜 둘 수 있습니다. Tab 키로 칸 사이를 옮겨 보면, 보조기기는 "관심 분야, 프론트엔드"처럼 묶음 이름과 보기를 함께 읽어 줍니다 — <legend> 덕분입니다.

흔한 실수 3가지

1. radio 그룹의 name을 다르게 줘서 다중 선택이 가능해진다

라디오 버튼 여러 개를 두면서 name을 칸마다 다르게 주는 경우입니다.

<input type="radio" id="m" name="male" value="male">
<label for="m">남성</label>
<input type="radio" id="f" name="female" value="female">
<label for="f">여성</label>

(이 코드는 남성과 여성을 동시에 고를 수 있습니다.) 라디오 버튼을 한 묶음으로 만드는 것은 같은 name입니다. namemalefemale로 다르면 둘은 각각 혼자짜리 문항이 되어, 시험지로 치면 1번 문항과 2번 문항을 따로 칠하는 것과 같아집니다. "하나만 고르기"가 깨집니다.

<input type="radio" id="m" name="gender" value="male">
<label for="m">남성</label>
<input type="radio" id="f" name="gender" value="female">
<label for="f">여성</label>

한 묶음에 속하는 라디오 버튼은 name을 똑같이 맞춥니다. 대신 id<label>과 잇는 용도라 칸마다 달라야 하므로, name은 같게·id는 다르게가 라디오 버튼의 기본입니다.

2. checkbox의 checked 상태를 :checked가 아닌 JS로만 다룬다

체크박스를 처음부터 켜 두는 일이나 켜졌을 때 모양을 바꾸는 일을 JavaScript로만 처리하려는 경우입니다. JavaScript는 이 시리즈의 범위 밖이지만, 굳이 쓰지 않아도 되는 자리를 짚어 둡니다.

체크박스가 기본으로 켜진 채 시작해야 한다면, HTML이 이미 그 속성을 갖고 있습니다.

<input type="checkbox" id="agree" name="agree" checked>
<label for="agree">약관에 동의합니다</label>

checked 한 단어면 칸이 켜진 채로 페이지가 열립니다. 또 켜졌을 때만 다른 모양을 주고 싶다면 CSS의 :checked 선택자를 쓸 수 있습니다. "기본으로 켜기"는 checked 속성, "켜졌을 때 꾸미기"는 :checked — 이 둘은 JavaScript 없이 HTML과 CSS만으로 됩니다. 켜고 끌 때마다 다른 동작을 더하는 단계에 가서야 비로소 JavaScript가 필요해집니다.

3. fieldset/legend 없이 시각장애 사용자에게 폼의 맥락을 잃게 한다

라디오 버튼 묶음을 <fieldset> 없이 그냥 늘어놓는 경우입니다.

<p>성별</p>
<input type="radio" id="m" name="gender" value="male">
<label for="m">남성</label>
<input type="radio" id="f" name="gender" value="female">
<label for="f">여성</label>

(이 코드는 화면으로 보면 멀쩡하지만 묶음이라는 사실이 표시돼 있지 않습니다.) 눈으로 보는 사람은 "성별"이라는 글자가 위에 있으니 두 보기가 그 질문의 답이라고 짐작합니다. 하지만 화면을 읽어 주는 보조기기는 <p>의 "성별"과 라디오 버튼을 별개로 읽어, 사용자는 "남성, 라디오 버튼"만 듣게 됩니다. 무엇을 묻는 문항인지 맥락이 사라집니다.

<fieldset>
  <legend>성별</legend>
  <input type="radio" id="m" name="gender" value="male">
  <label for="m">남성</label>
  <input type="radio" id="f" name="gender" value="female">
  <label for="f">여성</label>
</fieldset>

<fieldset>으로 묶고 <legend>에 질문을 적으면, 보조기기는 "성별, 남성, 라디오 버튼"처럼 묶음 이름을 함께 읽어 줍니다. 묶음 문항의 안내 문구를 빠뜨리지 않는 것과 같습니다.

오늘 배운 것 체크리스트

  • <select>·<textarea>·체크박스·라디오 버튼이 각각 어떤 입력에 어울리는지 안다.
  • 라디오 버튼은 같은 name으로 묶어야 그 안에서 하나만 골라진다.
  • 체크박스는 칸마다 독립이라 여러 개를 동시에 켤 수 있다.
  • <fieldset><legend>로 관련된 칸들을 한 묶음으로 엮는다.
  • 기본 선택은 selected·checked 속성으로, JavaScript 없이 정한다.

자주 묻는 질문

Q. 라디오 버튼 그룹은 어떻게 만드나요? 그냥 여러 개 두면 되나요?

A. 라디오 버튼을 여러 개 두는 것만으로는 부족하고, 한 묶음으로 묶을 칸들의 name을 모두 똑같이 맞춰야 합니다. name이 같아야 그 안에서 하나만 골라지고, 다르면 각각 별개 문항이 되어 여러 개가 동시에 골라집니다. id<label>과 잇는 용도이므로 칸마다 다르게 둡니다.

Q. select에서 특정 보기를 처음부터 골라진 채로 두려면 어떻게 하나요?

A. 처음 골라진 채로 둘 <option>selected 속성을 붙입니다. 아무 <option>에도 selected가 없으면 목록의 첫 번째 보기가 골라진 채로 시작합니다. 한 <select> 안에서 selected는 하나에만 붙입니다.

Q. textarea의 줄 수, 그러니까 칸 높이는 어떻게 정하나요?

A. rows 속성에 줄 수를 적으면 그 줄 수만큼의 높이로 시작합니다. 예를 들어 rows="4"는 네 줄 높이입니다. 이건 처음 높이일 뿐이라 사용자가 칸 모서리를 끌어 더 늘릴 수 있고, 정확한 크기는 나중에 CSS로 다시 정할 수 있습니다.

다음 시간 예고

내일은 테이블과 글로벌 속성 — table은 표일 때만을 다룹니다. 폼을 마무리했으니, 다음은 자료를 행과 열로 보여 주는 <table>과 모든 태그가 공통으로 갖는 id·class 같은 속성으로 넘어갑니다.

더 알아보기