한 줄 요약<form>은 사용자가 입력한 값을 한 묶음으로 모아 보내는 양식이고, 그 안의 <input>은 값을 적는 칸, <label>은 칸 옆 안내 문구, <button>은 다 적은 양식을 제출하는 단추입니다. 이 네 태그는 for/idname이라는 보이지 않는 연결로 이어져 있어, 그 연결을 빠뜨리면 화면은 멀쩡한데 폼이 동작하지 않습니다.

학습 목표

  • <form>·<input>·<label>·<button>이 각각 어떤 역할을 하는지 한 줄로 설명할 수 있다.
  • <label>for/id<input>에 연결하는 이유와 방법을 안다.
  • <input>name이 없으면 값이 서버로 가지 않는 이유를 설명할 수 있다.
  • <button>type 속성을 빠뜨렸을 때 무슨 일이 생기는지 안다.
  • 로그인 폼을 마우스 없이 키보드만으로 끝까지 입력할 수 있다.

오늘의 비유 — 우체국 송장의 칸 나눔

우체국에서 소포를 부칠 때 송장 한 장을 받아 듭니다. 그 종이에는 빈칸이 여러 개 인쇄돼 있습니다. "보내는 분 성함", "받는 분 주소", "물품 내용"처럼 칸마다 무엇을 적어야 하는지가 칸 옆에 작은 글씨로 안내돼 있습니다. 우리는 안내 글씨를 보고 그 옆 빈칸을 채웁니다. 다 채우면 송장 한 장을 통째로 창구에 내밉니다. 그러면 우체국은 그 한 장에 적힌 정보를 모아 소포를 처리합니다.

HTML 폼이 정확히 이 구조입니다. 송장 한 장 전체가 <form>, 글씨를 적는 빈칸 하나하나가 <input>, 칸 옆 안내 문구가 <label>, 다 적은 송장을 창구에 내미는 동작이 <button>입니다. 오늘은 이 네 칸이 서로 어긋나지 않게 묶는 법을 정리합니다.

핵심 개념

가장 단순한 폼은 이렇게 생겼습니다.

<form action="/login" method="post">
  <label for="email">이메일</label>
  <input id="email" name="email" type="email">

  <button type="submit">로그인</button>
</form>

<form>은 안쪽의 입력 칸들을 한 묶음으로 감싸고, 제출하면 그 값들이 어디로(action) 어떤 방식으로(method) 갈지를 정합니다. <input>은 값을 적는 빈칸, <label>은 그 칸이 무엇을 받는 칸인지 알려 주는 안내, <button type="submit">은 다 적은 양식을 한꺼번에 내보내는 단추입니다.

label과 input 연결 — for와 id로 잇는다

송장의 안내 문구는 바로 옆 빈칸을 가리킵니다. "받는 분 주소"라는 글씨와 그 아래 빈칸은 한 짝입니다. HTML에서는 이 짝을 forid로 명시해 줍니다.

<label for="email">이메일</label>
<input id="email" name="email" type="email">

<label>for 값과 <input>id 값을 똑같이 맞추면 둘이 한 쌍으로 묶입니다. 이렇게 묶으면 두 가지가 좋아집니다. 첫째, 화면을 읽어 주는 보조기기가 "이메일, 입력 칸"이라고 안내 문구와 칸을 함께 읽어 줍니다. 둘째, 안내 문구를 클릭해도 옆 칸으로 커서가 들어갑니다. 누를 수 있는 면적이 칸 하나에서 글씨까지 넓어지는 셈이라, 작은 칸에서 특히 편해집니다.

<input><label> 안에 넣어 감싸는 방법도 있습니다.

<label>
  이메일
  <input name="email" type="email">
</label>

감싸기만 해도 짝은 묶이지만, 보조기기 호환이 한결 확실하고 CSS로 다루기도 쉬운 for/id 방식을 기본으로 삼는 편이 무난합니다.

input type 종류 — 칸의 성격을 정한다

<input>type 속성으로 어떤 값을 받는 칸인지를 정합니다.

<input type="text">      <!-- 일반 한 줄 글자 -->
<input type="email">     <!-- 이메일 주소 -->
<input type="password">  <!-- 가려지는 비밀번호 -->

type을 맞춰 두면 칸의 성격이 따라옵니다. type="password"는 적은 글자가 점으로 가려지고, type="email"은 휴대폰에서 키보드에 @가 먼저 나오며 형식이 어긋나면 제출할 때 가벼운 검사를 해 줍니다. 칸마다 받을 값에 맞는 type을 고르는 것이 첫 단추입니다.

name과 value — 우체국이 읽는 항목 이름

여기가 초보자가 가장 자주 놓치는 부분입니다. 송장의 빈칸에는 글씨만 적혀 있는 게 아닙니다. 그 칸이 "받는 분 주소 칸"이라는 항목 이름이 함께 인쇄돼 있어서, 우체국은 그 항목 이름을 보고 적힌 글씨가 무슨 정보인지 압니다.

<input>name이 바로 그 항목 이름입니다. 사용자가 칸에 적은 글자는 value가 되고, 폼을 제출하면 name=value 꼴로 묶여 서버로 갑니다.

<input name="email" type="email">

이메일 칸에 me@site.com을 적고 제출하면 email=me@site.com이 서버로 전달됩니다. name이 없으면 칸에 글씨는 적히지만 항목 이름이 빠진 송장 칸과 같아서, 우체국은 그 글씨를 그냥 흘려보냅니다.

button — 송장을 내미는 단추, 그리고 type

<button>은 폼 안에서 누를 수 있는 단추입니다. 그런데 폼 안의 <button>type을 적지 않으면 자동으로 type="submit"이 됩니다. 즉 폼을 제출하는 단추가 됩니다.

<button type="submit">로그인</button>          <!-- 폼을 제출 -->
<button type="button">비밀번호 보기</button>   <!-- 아무것도 제출하지 않음 -->

제출용 단추는 type="submit", 제출과 상관없는 단추(비밀번호 표시 토글 같은)는 type="button"을 분명히 적어 줍니다. 이 한 글자를 빠뜨리면 의도치 않은 제출이 일어납니다. 자세한 사례는 흔한 실수에서 다시 봅니다.

함께 따라하기 — 키보드만으로 입력하는 로그인 폼

지난 회에 만든 폴더에 day-09 폴더와 index.html을 두고, <body> 안을 이렇게 채웁니다.

<h1>로그인</h1>

<form action="/login" method="post">
  <p>
    <label for="email">이메일</label>
    <input id="email" name="email" type="email">
  </p>

  <p>
    <label for="password">비밀번호</label>
    <input id="password" name="password" type="password">
  </p>

  <button type="submit">로그인</button>
</form>

저장하고 브라우저로 열어보면 이메일 칸, 비밀번호 칸, 로그인 단추가 세로로 놓입니다. 이제 마우스를 치우고 키보드만 써 봅니다. Tab 키를 한 번 누르면 커서가 이메일 칸으로 들어가고, 글자를 적은 뒤 Tab을 다시 누르면 비밀번호 칸으로, 한 번 더 누르면 로그인 단추로 이동합니다. 단추에서 EnterSpace를 누르면 폼이 제출됩니다.

안내 문구도 눌러 봅니다. "이메일"이라는 글씨를 클릭하면 커서가 옆 칸으로 들어갑니다. forid를 맞춰 둔 덕분입니다. 이 폼은 마우스가 없어도, 화면을 보지 못해도 끝까지 채울 수 있습니다. 폼을 "올바르게 연결한다"는 말의 실제 모습이 이것입니다.

흔한 실수 3가지

1. label과 input을 for/id로 연결하지 않는다

안내 문구와 입력 칸을 화면상 나란히 두기만 하고 for/id로 잇지 않는 경우입니다.

<label>이메일</label>
<input name="email" type="email">

(이 코드는 눈으로 보면 멀쩡하지만 둘이 짝으로 묶여 있지 않습니다.) 보조기기로 이 폼을 읽으면 "이메일"이라는 글씨와 입력 칸이 따로 읽혀, 사용자는 비어 있는 칸 앞에서 무엇을 적어야 할지 알 수 없습니다. 안내 문구를 클릭해도 칸으로 커서가 들어가지 않습니다.

<label for="email">이메일</label>
<input id="email" name="email" type="email">

forid에 같은 값을 적어 둘을 한 쌍으로 묶습니다. id는 한 페이지 안에서 유일해야 하므로, 칸이 여럿이면 email·password처럼 서로 다른 값을 줍니다.

2. button 안의 type 속성을 빼서 의도치 않게 submit된다

폼 안에 단추를 하나 더 두면서 type을 적지 않는 경우입니다.

<form action="/login" method="post">
  <input id="password" name="password" type="password">
  <button>비밀번호 보기</button>
  <button>로그인</button>
</form>

(이 코드는 "비밀번호 보기"를 누르는 순간 폼이 제출돼 버립니다.) 폼 안의 <button>type이 없으면 type="submit"으로 동작합니다. 비밀번호를 잠깐 확인하려고 누른 단추가 폼 전체를 제출하고 페이지를 새로고침해 버립니다.

<form action="/login" method="post">
  <input id="password" name="password" type="password">
  <button type="button">비밀번호 보기</button>
  <button type="submit">로그인</button>
</form>

제출하는 단추는 type="submit", 제출과 무관한 단추는 type="button"을 분명히 적습니다. 폼 안에 단추가 하나뿐이라도 type을 적어 두는 습관을 들이면 나중에 단추가 늘어도 안전합니다.

3. input의 name을 빼서 서버로 값이 전송되지 않는다

id만 적고 name을 빠뜨리는 경우입니다. idname이 비슷하게 생겨서 하나면 충분하다고 오해하기 쉽습니다.

<label for="email">이메일</label>
<input id="email" type="email">

(이 코드는 입력은 잘 되지만 제출하면 이메일 값이 사라집니다.) id는 같은 페이지 안에서 <label>과 칸을 잇는 용도이고, 서버로 값을 보내는 일은 하지 않습니다. 값을 name=value 꼴로 묶어 보내는 것은 name의 몫입니다. name이 없는 칸은 항목 이름이 빠진 송장 칸과 같아, 우체국이 그 글씨를 그냥 흘려보냅니다.

<label for="email">이메일</label>
<input id="email" name="email" type="email">

for/id는 안내 문구와 칸을 잇기 위해, name은 값을 서버로 보내기 위해 — 둘은 역할이 다르므로 둘 다 적어야 합니다. 값이 한 글자도 안 넘어온다면 name부터 확인합니다.

오늘 배운 것 체크리스트

  • <form>·<input>·<label>·<button>의 역할을 각각 한 줄로 댈 수 있다.
  • <label for><input id>에 같은 값을 적어 둘을 묶는다.
  • 서버로 값을 보내려면 <input>name이 있어야 한다.
  • 제출용 단추는 type="submit", 그 밖의 단추는 type="button"을 적는다.
  • 폼을 마우스 없이 키보드만으로 끝까지 채울 수 있다.

자주 묻는 질문

Q. idname을 둘 다 적어야 하나요? 같은 값인데 하나로 안 되나요?

A. 역할이 다르므로 둘 다 필요합니다. id는 같은 페이지 안에서 <label for>와 칸을 잇는 연결고리이고, name은 제출할 때 값을 name=value 꼴로 서버에 보내는 항목 이름입니다. 값이 같아도(id="email", name="email") 쓰임이 달라서, name을 빠뜨리면 칸은 멀쩡한데 값만 사라집니다.

Q. <form>actionmethod는 무엇을 정하나요?

A. action은 제출한 값이 갈 주소, method는 보내는 방식입니다. method="get"은 값을 주소창 뒤에 붙여 보내 검색 폼처럼 결과를 공유·북마크할 때 어울리고, method="post"는 값을 요청 본문에 담아 보내 로그인·회원가입처럼 노출되면 안 되는 값에 씁니다. 비밀번호가 든 폼은 post를 씁니다.

Q. 단추를 만들 때 <button><input type="submit"> 중 무엇을 쓰나요?

A. 둘 다 폼을 제출하지만 <button>을 권합니다. <button>은 여는 태그와 닫는 태그 사이에 글자뿐 아니라 아이콘 같은 다른 요소도 넣을 수 있어 자유롭습니다. <input type="submit">value 속성에 적은 글자만 단추 표면에 보일 수 있어 제약이 큽니다.

다음 시간 예고

내일은 폼 심화 — select, textarea, checkbox, radio, fieldset/legend를 다룹니다. 오늘은 한 줄짜리 입력 칸을 묶는 법을 익혔으니, 다음은 여러 줄 입력과 목록에서 고르는 칸, 그리고 그 칸들을 주제별로 묶는 법으로 들어갑니다.

더 알아보기