SOYOYU
기술 SEO

type=text는 거의 항상 잘못된 선택입니다 — input 타입별 코드 리뷰

type 속성 한 글자가 모바일 키보드를 바꾸고, AI 자동완성 정확도를 결정하고, 한국 전화번호 입력의 함정을 만듭니다. input 타입 13개의 실무 결정 트리를 정리합니다.

전승엽2026년 8월 15일8 min read
A11y 코드 리뷰시맨틱 HTMLaccessibility treeAI 에이전트웹 접근성input 타입모바일 키보드autocompleteKWCAG

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"

다른 글 읽기

기술 SEO

이미지 검색에 우리 제품 사진이 하나도 안 나오는 이유

구글 이미지·시각 검색(Google Lens)에 제품 사진이 안 뜨는 원인과 대응. Alt Text·파일명·이미지 사이트맵·주변 텍스트까지 이커머스 시각 검색 최적화 실무.

4 min read
기술 SEO

커스텀 dropdown은 거의 항상 잘못된 선택입니다 — select와 combobox 코드 리뷰

native <select>는 모바일에서 압도적으로 좋은 UX를 줍니다. 커스텀 dropdown으로 갈아탈 정당한 이유는 검색 가능, 멀티 선택, 가상화 셋 정도뿐입니다. ARIA combobox의 진짜 비용을 봅니다.

9 min read
기술 SEO

검색 결과에 내 사이트 설명이 이상하게 나오는 이유

메타 디스크립션을 써도 구글이 다르게 보여주는 이유와 대응 방법. 구글이 스니펫을 재작성하는 구조, 자주 하는 실수, 최소화할 수 있는 기술.

4 min read
기술 SEO

고성 송지호·봉수대 오토캠핑장 예약 사이트가 검색에 안 나오는 이유

송지호·봉수대 등 고성군 공공캠핑장 6곳 예약 방법 정리. 그리고 공식 예약 사이트가 구글 색인에 0건인 이유를 구글 오픈소스 파서까지 돌려 실측으로 진단합니다.

19 min read
기술 SEO

신규 페이지를 만들어도 구글이 며칠째 안 가져가는 이유

사이트맵을 제출해도 신규 페이지가 바로 색인되지는 않습니다. 발견·크롤·색인 단계를 구분하고 공식 도구로 원인을 점검하는 대응 순서.

5 min read
기술 SEO

Radix Dialog도 깨지는 그 케이스 — focus trap 코드 리뷰

포커스 트랩은 native <dialog>가 무료로 해주는 일이지만, 라이브러리·SPA 라우팅·autoFocus가 섞이면 미세하게 깨집니다. 깨지는 다섯 시나리오를 코드로 짚어봅니다.

10 min read

검색 최적화가 필요하신가요?

무료 상담을 통해 비즈니스에 맞는 최적화 전략을 확인하세요.