A11y 코드 리뷰 #011 · 오늘 도감에 등록할 패턴 —
<input>의 13가지 type이번 카드는 기본값에 머무르는 게 가장 자주 잘못된 선택인 한 속성에 관한 글입니다.
type="text"는 거의 항상 다른 type으로 바꿔야 하고, 그 결정이 모바일·자동완성·AI 에이전트 모두에 영향을 줍니다.
TL;DR
<input>의 기본값type="text"는 입력이 자유 텍스트일 때만 정당합니다. 90%의 한국 사이트 input은 그 외 type이어야 합니다.- type은 모바일 키보드 레이아웃을 결정합니다 — type=email이면 @ 키, type=tel이면 숫자 키패드, type=url이면 .com 키.
inputmode속성은 키보드만 바꾸고 검증은 안 합니다. 한국 휴대폰 번호처럼 숫자+하이픈 입력은type="tel"+inputmode="numeric"조합이 안전합니다.autocomplete표준값은 AI 에이전트의 폼 자동 입력 정확도를 직접 결정합니다. type만 맞춰도 부족하고 autocomplete까지 채워야 합니다.- 시리즈 #2와 보완 관계 — label·fieldset·legend가 폼의 시맨틱이라면 type·inputmode·autocomplete는 각 input의 데이터 의미입니다.
1. 어디서나 본 그 type="text"
다음 다섯 패턴은 한국 사이트의 폼 안티패턴 top 5입니다.
전화번호 — type="text"
<input type="text" placeholder="010-1234-5678" />
이메일 — type="text"
<input type="text" placeholder="이메일 주소" />
카드 번호 — type="number"
<input type="number" placeholder="카드 번호 16자리" />
검색 — type="text"
<input type="text" placeholder="검색어를 입력하세요" />
비밀번호 확인 — autocomplete 없음
<input type="password" placeholder="비밀번호 확인" />
다섯 케이스 모두 시각 사용자에게 보기에는 일반 입력 칸으로 보입니다. 모바일에서 입력해보면 — 전화번호 칸인데 영문 키보드가 뜨고, 카드 번호 칸인데 하이픈을 못 넣고, 이메일 칸인데 @ 키가 없습니다. AI 에이전트가 자동 입력할 때는 — 각 칸의 데이터 의미를 추측해야 합니다.
2. 한 가지 질문
이 칸에 들어가는 데이터의 의미는 무엇인가요? 자유 텍스트인가, 이메일인가, 전화번호인가, 숫자인가, URL인가, 검색어인가?
이 질문의 답이 곧 type 결정입니다. 답을 자유 텍스트로 시작하지 마세요 — 데이터 의미로 시작하세요.
3. input type — 점진적 해부
3.1 13가지 type 결정 트리
이 칸에 들어가는 데이터가...
이메일이다 → type="email"
URL이다 → type="url"
전화번호다 → type="tel" ⚠️ type="number"가 아님
숫자다 (계산용) → type="number" + inputmode 명시
숫자다 (식별 코드) → type="text" + inputmode="numeric"
검색어다 → type="search"
비밀번호다 → type="password"
날짜다 → type="date"
시간이다 → type="time"
색상이다 → type="color"
파일이다 → type="file"
범위 슬라이더다 → type="range"
체크박스/라디오 → type="checkbox" / type="radio"
그 외 자유 텍스트 → type="text"
3.2 type="email"
<label for="email">이메일</label>
<input
id="email"
type="email"
autocomplete="email"
inputmode="email"
/>
type="email"이 주는 것:
- 모바일 키보드에
@와.com키 자동 추가 - 브라우저 native validation (선택적)
- autocomplete 표준값 활용 가능
- 스크린리더가 "이메일 입력"으로 발화
3.3 type="tel" — 한국 전화번호의 정답
한국 휴대폰 번호는 010-1234-5678 형식입니다. 하이픈이 들어갑니다. type="number"로 두면 하이픈 입력이 불가능합니다.
<label for="phone">휴대폰 번호</label>
<input
id="phone"
type="tel"
autocomplete="tel"
inputmode="numeric"
placeholder="010-0000-0000"
/>
type="tel"이 주는 것:
- 모바일에 숫자 키패드 표시 (텐키)
- 하이픈·괄호·플러스 모두 허용
- autocomplete=tel로 자동 입력 지원
- 스크린리더가 "전화번호 입력"으로 발화
3.4 type="number"의 함정
type="number"는 계산 가능한 숫자에만 써야 합니다 — 가격, 수량, 나이 같은.
<!-- ✅ -->
<input type="number" min="1" max="100" value="1" />
<!-- ❌ 카드 번호, 우편번호, 전화번호 -->
<input type="number" placeholder="카드 번호" />
type="number"의 알려진 문제:
- 위/아래 화살표 스피너 표시 (디자인적으로 보기 싫음)
- 하이픈·공백 입력 불가
- 휠 스크롤로 값 변경 (실수로 값 바뀜)
- iOS에서 미세한 키보드 차이
- 입력 길이 제한이 안 됨 (
maxLength가 안 먹음)
해결책 — 식별 코드 같은 숫자는 type="text" + inputmode="numeric":
<label for="card">카드 번호</label>
<input
id="card"
type="text"
autocomplete="cc-number"
inputmode="numeric"
pattern="[0-9]{16}"
maxlength="19"
placeholder="0000 0000 0000 0000"
/>
이 조합이 — 모바일 숫자 키패드를 띄우고, 길이 제한을 걸고, 자동완성을 지원하면서, 스피너의 단점은 피합니다.
3.5 type="search"
검색창에 type="search"를 쓰면:
- 모바일 키보드의 "이동" 키가 "검색" 키로 바뀜
- 일부 브라우저에서 우측에 "지우기 X" 버튼 자동 표시
- 시맨틱하게 검색 input으로 인식 (스크린리더가 "검색 편집"으로 발화)
<form role="search">
<label for="q" class="sr-only">사이트 내 검색</label>
<input
id="q"
type="search"
name="q"
autocomplete="off"
placeholder="검색어 입력"
/>
<button type="submit">검색</button>
</form>
검색창은 시리즈 #2에서 본 visually-hidden 라벨 + type="search" + role="search" (form에) 조합이 표준.
3.6 inputmode 속성 — 키보드만 바꾸기
<input type="text" inputmode="decimal" /> <!-- 소수점 키패드 -->
<input type="text" inputmode="numeric" /> <!-- 숫자 키패드 -->
<input type="text" inputmode="tel" /> <!-- 전화 키패드 -->
<input type="text" inputmode="search" /> <!-- 검색 키패드 -->
<input type="text" inputmode="email" /> <!-- 이메일 키패드 -->
<input type="text" inputmode="url" /> <!-- URL 키패드 -->
<input type="text" inputmode="none" /> <!-- 키보드 안 띄움 (자체 키패드 구현) -->
inputmode는 모바일 키보드 레이아웃만 바꿉니다. 검증과 시맨틱은 type에 맡깁니다. 둘 다 적용하는 게 표준.
4. 왜 문제인가 — 3중 영향
4a. 스크린리더 사용자
<input type="text" placeholder="이메일">
→ NVDA: "편집"
<input type="email">
→ NVDA: "이메일 편집"
<input type="tel">
→ NVDA: "전화번호 편집"
type이 명시되면 스크린리더가 어떤 종류의 입력인지를 자동으로 발화합니다. 라벨이 없는 경우(검색창 등)에도 type이 있으면 어떤 형식을 입력해야 하는지 단서가 됩니다.
4b. 키보드 + 모바일 사용자
모바일 사용자에게 type의 영향이 가장 큽니다.
type="text" → 일반 키보드 (모든 입력 가능, 비효율)
type="email" → 일반 + @ + .com 키
type="tel" → 숫자 키패드 (텐키)
type="number" → 숫자 + 위/아래 스피너 (데스크탑) / 숫자 키패드 (모바일)
type="url" → 일반 + / + . + .com 키
전화번호 칸을 type="text"로 두면 — 모바일 사용자는 영문 키보드에서 숫자 모드로 토글한 후 입력합니다. 화면 두 번 더 누릅니다. 이게 입력 이탈률의 직접 원인.
4c. AI 에이전트
AI 에이전트의 폼 자동 입력 정확도는 세 신호의 곱입니다.
정확도 ≈ type 명시 × autocomplete 표준값 × label 시맨틱
세 신호가 모두 있으면 에이전트가 거의 100% 정확하게 입력합니다. 하나라도 빠지면 추측이 시작되고 — 추측은 자주 틀립니다.
특히 카드 번호, 이메일, 전화번호는 에이전트가 자주 자동 입력하는 필드. autocomplete 표준값(cc-number, email, tel)이 있으면 — 에이전트의 메모리에 저장된 사용자 정보를 정확한 칸에 넣습니다. 없으면 placeholder OCR로 추측.
선행 글 #2 label·fieldset·legend에서 본 폼 자동 입력 메커니즘이 — type/inputmode/autocomplete까지 합쳐서 완성됩니다.
5. SEO/GEO/AEO 영향
5.1 SEO — 폼 UX 신호
폼의 모바일 친화성은 모바일 페이지 평가에 영향을 줍니다. type이 잘 맞춰진 폼은 — 모바일 사용자의 입력 완료율이 높고, 이탈률이 낮고, 페이지의 task success rate가 좋아집니다.
5.2 GEO — 에이전트 자동화 성공률 (#2와 보완)
GEO에서 에이전트의 폼 자동 입력 성공률은 사이트의 GEO 친화성을 직접 측정하는 지표입니다. type + autocomplete가 맞으면 성공률이 올라가고, 결과적으로 에이전트가 우리 사이트에서 작업을 완료하는 비율이 올라갑니다.
5.3 AEO — 비교 답변에서의 인용
"이메일 가입 폼이 잘 만들어진 사이트는?" 같은 질문에 AI가 답할 때 — 폼의 시맨틱이 좋은 사이트가 인용 후보가 됩니다. 직접적이지 않지만 task-oriented 답변의 신호.
6. React에서
6.1 react-hook-form + type/autocomplete 패턴
function ContactForm() {
const { register, handleSubmit } = useForm();
return (
<form onSubmit={handleSubmit(onSubmit)}>
<label htmlFor="email">이메일</label>
<input
id="email"
type="email"
autoComplete="email"
inputMode="email"
{...register("email", { required: true })}
/>
<label htmlFor="phone">휴대폰</label>
<input
id="phone"
type="tel"
autoComplete="tel"
inputMode="numeric"
{...register("phone", { required: true })}
/>
<label htmlFor="company">회사</label>
<input
id="company"
type="text"
autoComplete="organization"
{...register("company")}
/>
<button type="submit">제출</button>
</form>
);
}
세 input 각각 — type, autocomplete, inputmode가 모두 다릅니다. 내용에 맞는 세 속성이 표준 조합.
6.2 카드 결제 폼의 정답 조합
function CardPaymentForm() {
return (
<form>
<label htmlFor="cc-name">카드 소지자 이름</label>
<input
id="cc-name"
type="text"
autoComplete="cc-name"
/>
<label htmlFor="cc-number">카드 번호</label>
<input
id="cc-number"
type="text"
autoComplete="cc-number"
inputMode="numeric"
pattern="[0-9 ]{13,19}"
maxLength={19}
/>
<label htmlFor="cc-exp">만료일 (MM/YY)</label>
<input
id="cc-exp"
type="text"
autoComplete="cc-exp"
inputMode="numeric"
pattern="[0-9]{2}/[0-9]{2}"
maxLength={5}
/>
<label htmlFor="cc-csc">CVC</label>
<input
id="cc-csc"
type="text"
autoComplete="cc-csc"
inputMode="numeric"
maxLength={4}
/>
</form>
);
}
이 폼의 autocomplete 값(cc-name, cc-number, cc-exp, cc-csc)은 WHATWG가 정한 표준입니다. 브라우저 패스워드 매니저와 AI 에이전트가 모두 인식합니다.
type="number"가 아닌 type="text" + inputMode="numeric"인 이유 — 카드 번호는 식별 코드이지 계산 가능한 숫자가 아니기 때문입니다.
6.3 한국 전화번호 자동 포맷
function PhoneInput() {
const [value, setValue] = useState("");
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
const raw = e.target.value.replace(/\D/g, "");
if (raw.length <= 3) {
setValue(raw);
} else if (raw.length <= 7) {
setValue(`${raw.slice(0, 3)}-${raw.slice(3)}`);
} else {
setValue(`${raw.slice(0, 3)}-${raw.slice(3, 7)}-${raw.slice(7, 11)}`);
}
};
return (
<input
type="tel"
autoComplete="tel"
inputMode="numeric"
value={value}
onChange={handleChange}
maxLength={13}
placeholder="010-0000-0000"
/>
);
}
자동 포맷은 시각 보조입니다. 검증·저장은 raw 값으로. 사용자 입력 경험이 매끄러워집니다.
7. 한국 맥락 — 가볍게
KWCAG 관련 항목:
- 1.3.5 입력의 목적 식별: 사용자에 대한 정보를 수집하는 input의 목적이 프로그래밍 방식으로 결정 가능해야 합니다. autocomplete 표준값이 이 항목의 표준 구현.
- 3.3.2 레이블·설명 제공: type이 라벨을 보완합니다 — type=email이면 입력 기대값이 명확해집니다.
한국 사이트 특수성 — 주민등록번호 input. type="text" + inputmode="numeric" + maxLength={14} 조합이 표준. autocomplete는 한국 특화 값이 없어 OFF로 둡니다 — 민감 정보이므로 자동완성을 일부러 꺼야 합니다.
8. "type=text가 가장 안전하지 않나요?"
자주 듣는 반론들:
"type을 자세히 정하면 디자인이 깨지지 않나요?" — type="search"의 X 버튼이나 type="number"의 스피너는 CSS로 제거 가능합니다. 시맨틱은 보존, 시각은 자유.
"native validation이 너무 엄격해요" — noValidate로 끄거나 react-hook-form 같은 라이브러리에 검증을 위임. type은 키보드와 시맨틱을 위해, 검증은 별도로.
"autocomplete가 보안에 안 좋지 않나요?" — 민감 정보(주민번호, 비밀번호 확인)는 autoComplete="off". 일반 폼은 켜는 게 사용자 경험과 AI 에이전트 호환성에 좋습니다.
"inputmode만 쓰면 type은 text로 둬도 되지 않나요?" — 가능하지만 시맨틱이 약합니다. 스크린리더 발화, 자동완성, 브라우저 동작이 type 기반입니다. 둘 다 명시.
"카드 번호에 type=number를 써도 되지 않나요?" — 위 3.4에서 본 함정들이 있습니다. type=text + inputMode=numeric이 표준.
9. 체크리스트
- 모든 input의 type이 데이터 의미에 맞게 명시돼 있는가
- 이메일은 type="email", 전화번호는 type="tel", URL은 type="url"인가
- 식별 코드(카드번호, 우편번호 등)는 type="text" + inputmode="numeric"인가
- 모든 input에 적절한 autocomplete 표준값이 있는가
- 검색창에 type="search"와 부모 form에 role="search"가 있는가
- 모바일에서 type별로 적절한 키보드가 뜨는가 (실제 디바이스 검증)
- 카드 결제 폼이 cc-name, cc-number, cc-exp, cc-csc 표준 autocomplete를 쓰는가
- 민감 정보(주민번호, 비밀번호 확인)는 autocomplete="off"인가
마무리
이 글이 16편 시리즈의 열한 번째 도감 카드입니다. 다음 카드는 <select>와 ARIA combobox — 네이티브 select의 모바일 UX와 커스텀 dropdown의 함정을 비교합니다.
input type의 단 한 줄 규칙 — type="text"는 자유 텍스트에만 씁니다. 이메일·전화·URL·검색·숫자에는 그에 맞는 type을 명시하고, autocomplete까지 함께 챙깁니다.
폼 a11y·전환율·AI 자동 입력 최적화 컨설팅이 필요하시면 문의하기로 연락 주세요.
codex:
primary_tag: input
secondary_tags: [type, inputmode, autocomplete]
aria_roles: [textbox, searchbox]
aria_attributes: [aria-label, aria-describedby, aria-required, autocomplete]
related_patterns: [card-payment-form, phone-number-input, korean-phone-format, search-form]
kwcag: ["1.3.5", "3.3.2"]
wcag: ["1.3.5", "3.3.2", "1.3.1"]
ai_agent_impact: high
seo_geo_impact: medium
difficulty: beginner
test_envs:
- "Mobile Safari iOS 17"
- "Mobile Chrome Android 14"
- "NVDA 2024.x + Chrome 125"
references:
- "WHATWG HTML Living Standard, States of the type attribute"
- "WHATWG HTML Living Standard, autofill values"
- "MDN, <input>: The Input element"
- "Adrian Roselli, Avoid Default Field Values"