Skip to content

06. 폼 유효성 검증과 FormData

브라우저는 폼에 대해 이미 많은 일을 해 준다. 문제는 그 기본 기능을 모르고 전부 직접 다시 짜는 순간부터 시작된다.

학습 목표

  1. submit()requestSubmit()의 차이를 설명할 수 있다.
  2. Constraint Validation API의 핵심 메서드와 상태를 이해한다.
  3. SubmitEvent.submitter, FormData, formdata 이벤트의 역할을 설명할 수 있다.
  4. 폼 처리에서 브라우저 기본 기능과 커스텀 로직을 어떻게 나눌지 판단할 수 있다.

1. 폼은 아직도 브라우저의 강한 기본 기능이다

단순해 보이지만 폼은 아래를 기본 제공한다.

  • 입력 필드 그룹화
  • Enter 제출
  • 제출 버튼 식별
  • 기본 유효성 검사
  • URL 인코딩 또는 multipart 전송

현대 프레임워크에서도 이 기능을 완전히 버릴 이유는 많지 않다.
오히려 브라우저 기본을 적절히 살리고, 필요한 부분만 덧붙이는 편이 더 단단하다.


2. submit()requestSubmit()은 다르다

이 차이는 반드시 알고 있어야 한다.

메서드유효성 검사submit 이벤트 발생사용 추천도
form.submit()하지 않음발생하지 않음낮음
form.requestSubmit()수행함발생함높음

즉, "사용자가 제출 버튼을 눌렀을 때와 비슷하게" 동작시키고 싶다면 requestSubmit()이 맞다.

원문이 이 차이를 짚은 점은 좋고, 현재 기준에서는 이 메서드의 우선순위를 더 높게 두면 된다.


3. 브라우저 기본 유효성 검사를 먼저 활용하자

Constraint Validation API는 복잡해 보이지만 핵심은 단순하다.

  • HTML 속성으로 기본 규칙 정의
  • 브라우저가 검사
  • 필요할 때 JavaScript로 상태 확인이나 사용자 메시지 보강

대표 속성은 아래와 같다.

  • required
  • min, max, step
  • minlength, maxlength
  • pattern
  • type="email", type="url"

기본 규칙만으로도 많은 검증을 브라우저에게 맡길 수 있다.


4. 자주 쓰는 검증 메서드

메서드역할
checkValidity()유효하면 true, 아니면 false 반환
reportValidity()검사 후 오류 UI도 표시
setCustomValidity(message)사용자 정의 오류 메시지 설정

또 함께 봐야 할 것이 validity다.

ValidityState 안에는 아래 같은 상태가 담긴다.

  • valueMissing
  • typeMismatch
  • patternMismatch
  • rangeUnderflow
  • rangeOverflow
  • tooShort
  • tooLong

실무에서는 "어떤 규칙이 깨졌는가"를 이 객체로 판별해 사용자 메시지를 구체화할 수 있다.


5. invalid 이벤트와 사용자 경험

폼 검증은 단순히 막는 것이 아니라, 사용자가 어디를 어떻게 고치면 되는지 알려 주는 문제다.

좋은 흐름은 보통 이렇다.

  1. 사용자가 제출 시도
  2. 브라우저 기본 검증 실행
  3. 첫 오류 필드로 포커스 이동
  4. 필요하면 설명 텍스트를 추가

너무 이른 실시간 검증은 오히려 UX를 망칠 수 있다.
예를 들어 사용자가 아직 이메일 입력을 끝내지 않았는데 빨간 경고를 계속 띄우면 피로감만 커진다.


6. SubmitEvent.submitter는 실무에서 꽤 유용하다

폼에 제출 버튼이 여러 개 있을 수 있다.

  • 임시 저장
  • 게시
  • 미리보기

이때 submit 이벤트 안에서 event.submitter를 보면 어떤 버튼이 제출을 트리거했는지 알 수 있다.

js
form.addEventListener('submit', (event) => {
  event.preventDefault()
  const mode = event.submitter?.value
  console.log(mode)
})

이 기능을 알면 "버튼마다 별도 폼 만들기" 같은 비효율을 줄일 수 있다.


7. FormData는 서버 전송 직전의 구조화된 스냅샷이다

js
const formData = new FormData(form)
await fetch('/signup', {
  method: 'POST',
  body: formData,
})

장점은 분명하다.

  • 폼 필드 name과 값이 자동 수집된다
  • 파일 업로드도 함께 다룰 수 있다
  • append, set, delete, get, getAll로 조작할 수 있다

즉, 직접 JSON 객체를 새로 조립하기 전에 이미 폼이라는 좋은 소스가 있다면 FormData부터 검토할 가치가 크다.


8. formdata 이벤트는 전송 직전 개입 지점이다

브라우저가 FormData를 구성하는 시점에 맞춰 개입할 수 있다.

  • 숨겨진 메타데이터 추가
  • 체크박스 묶음 값 정리
  • 사용자 편집값 외의 보조 정보 합치기

이 패턴을 알면 HTML 폼 구조를 유지하면서도 전송 직전 데이터를 유연하게 가공할 수 있다.


9. 비동기 제출과 기본 제출을 어떻게 나눌까?

현대 앱에서는 fetch 기반 비동기 제출이 흔하다.
그래도 기본 제출 흐름을 버릴 필요는 없다.

기본 제출이 좋은 경우

  • 단순 검색 폼
  • 페이지 이동이 자연스러운 흐름
  • 복잡한 상태 관리가 불필요

비동기 제출이 좋은 경우

  • 제출 후 현재 화면 유지
  • 실시간 피드백과 부분 갱신
  • 업로드 진행 표시, 후속 액션 연계

핵심은 둘 중 하나를 맹목적으로 고르는 것이 아니라, 브라우저 기본을 얼마나 살릴지 판단하는 것이다.


10. 폼 연동 커스텀 엘리먼트도 가능하다

원문 후반의 ElementInternals까지 연결하면, 커스텀 엘리먼트도 폼과 자연스럽게 통합할 수 있다.

  • 폼 제출 데이터에 값 포함
  • 유효성 상태 연동
  • Shadow DOM 내부 구현을 유지하면서 외부 폼과 결합

다만 이 부분은 일반 폼보다 난도가 높다.
기본 폼 처리 흐름을 먼저 확실히 이해한 뒤 Web Components 장에서 읽는 편이 좋다.


11. 흔한 안티패턴

안티패턴문제점더 나은 방식
무조건 form.submit() 호출검증과 submit 이벤트를 건너뜀기본은 requestSubmit()
브라우저 검증을 전부 비활성화구현량 증가, 접근성 저하 가능네이티브 검증을 최대한 활용
입력 중 매 순간 공격적 에러 표시피로감 증가제출 시점 또는 적절한 타이밍 검증
폼 데이터 전부 수동 JSON 조립중복 작업 발생가능하면 FormData 활용

12. PR 리뷰 체크리스트

  • 제출 트리거가 requestSubmit()이 되어야 할 상황은 아닌가
  • 기본 유효성 검사 속성을 HTML에서 먼저 표현하고 있는가
  • 오류 메시지와 포커스 이동이 사용자에게 충분히 친절한가
  • 여러 제출 버튼이 있다면 event.submitter를 활용하고 있는가
  • 비동기 제출 시에도 브라우저 기본 폼 의미를 최대한 살리고 있는가

핵심 정리

  • 폼은 아직도 브라우저가 가장 잘하는 영역 중 하나다
  • requestSubmit()과 Constraint Validation API를 알면 불필요한 재구현을 크게 줄일 수 있다
  • FormDatasubmitter를 활용하면 현대적인 비동기 폼 처리도 더 깔끔하게 설계할 수 있다