Skip to Content
WebHTML모던 HTML12. Constraint Validation API로 실시간 검증 메시지 만들기

이번 편의 결과물: 문의 폼에 잘못된 값을 입력하면 브라우저 기본 메시지 대신 우리가 정한 문구가 표시됩니다. 다루는 개념: ValidityState와 제약 플래그, setCustomValidity로 커스텀 오류 메시지, checkValidity/reportValidity, :valid/:invalid/:user-invalid

이 편에서 만드는 파일

web-practice/portfolio/ ├─ contact.html ~ (각 필드에 오류 문구를 담을 요소 추가, novalidate 유지) ├─ js/ │ └─ main.js ~ (Constraint Validation API로 실시간 검증 로직 추가) └─ css/ └─ styles.css ~ (:user-invalid 스타일 추가)

개념 정리

브라우저 기본 검증 말풍선은 위치·언어·디자인을 바꿀 수 없습니다. Constraint Validation API를 쓰면 검증 로직은 브라우저에 맡기면서 메시지와 표시 방식만 직접 만들 수 있습니다.

ValidityState

모든 <input>/<textarea>/<select>.validity 속성에 ValidityState 객체를 갖습니다. 각 값이 true면 해당 문제가 있다는 뜻입니다.

플래그참(true)인 경우
valueMissingrequired인데 값이 비어 있음
typeMismatchemail/url 등 형식에 안 맞음
tooShortminlength보다 짧음
patternMismatchpattern과 안 맞음
valid모든 제약을 통과함

setCustomValidity

input.setCustomValidity('메시지')를 호출하면 그 입력은 메시지가 있는 동안 항상 무효 상태가 됩니다. 빈 문자열('')을 넣으면 오류가 해제됩니다. 즉, 검사할 때마다 먼저 ''로 초기화한 뒤 문제가 있으면 메시지를 다시 넣는 순서를 지켜야 합니다.

checkValidity vs reportValidity

메서드하는 일
checkValidity()유효성만 검사하고 true/false 반환. 화면에 아무것도 표시 안 함
reportValidity()검사 후 유효하지 않으면 브라우저 기본 말풍선(또는 우리가 그린 오류)을 화면에 표시

:valid / :invalid / :user-invalid

CSS로 상태에 따라 스타일을 줄 수 있습니다. :invalid는 페이지를 열자마자(사용자가 아직 아무것도 안 건드려도) 빈 필수 필드에 바로 걸립니다. 사용자가 손대지도 않았는데 빨간 테두리가 뜨면 어색하므로, 사용자가 값을 입력한 뒤에만 걸리는 :user-invalid를 씁니다.

실습

1. 파일 열기

web-practice/portfolio/contact.html, js/main.js, css/styles.css를 엽니다.

2. 코드 작성

각 필드 아래에 오류 문구를 담을 <p>를 추가합니다(이름 필드 예시, 나머지 필드도 같은 패턴으로 추가).

<!-- web-practice/portfolio/contact.html --> <div class="field"> <label for="name">이름</label> <input type="text" id="name" name="name" autocomplete="name" required aria-describedby="name-error" /> <p class="error" id="name-error" aria-live="polite"></p> </div>

js/main.js에 검증 로직을 추가합니다.

// web-practice/portfolio/js/main.js const form = document.querySelector('.contact-form') const messages = { name: { valueMissing: '이름을 입력해 주세요.', }, email: { valueMissing: '이메일을 입력해 주세요.', typeMismatch: '올바른 이메일 형식이 아닙니다.', }, message: { valueMissing: '메시지를 입력해 주세요.', tooShort: '메시지는 10자 이상 입력해 주세요.', }, agree: { valueMissing: '개인정보 수집·이용에 동의해야 제출할 수 있습니다.', }, } function validateField(field) { field.setCustomValidity('') const rules = messages[field.name] if (!rules) return const validity = field.validity let text = '' for (const [key, msg] of Object.entries(rules)) { if (validity[key]) { text = msg break } } field.setCustomValidity(text) const errorEl = document.getElementById(`${field.id}-error`) if (errorEl) { errorEl.textContent = text } } if (form) { const fields = form.querySelectorAll('input[name], textarea[name]') fields.forEach((field) => { field.addEventListener('input', () => validateField(field)) field.addEventListener('blur', () => validateField(field)) }) form.addEventListener('submit', (event) => { fields.forEach((field) => validateField(field)) if (!form.checkValidity()) { event.preventDefault() form.reportValidity() } }) }

css/styles.css에 사용자가 값을 건드린 뒤에만 오류 스타일이 보이도록 규칙을 추가합니다.

/* web-practice/portfolio/css/styles.css */ .contact-form input:user-invalid, .contact-form textarea:user-invalid { border-color: var(--color-accent); } .contact-form .error { color: var(--color-accent); font-size: var(--text-sm); min-height: 1.2em; }

3. 실행

VS Code Live Server로 contact.html을 엽니다.

4. 확인

  • 이메일 필드에 abc를 입력하고 다른 필드로 포커스를 옮기면(blur) “올바른 이메일 형식이 아닙니다.”가 필드 아래에 나타나는지 확인합니다.
  • 메시지 필드에 5자만 입력하면 “메시지는 10자 이상 입력해 주세요.”가 나타나는지 확인합니다.
  • 아무것도 입력하지 않은 채 “보내기”를 누르면 각 필수 필드 아래 문구가 채워지고 제출이 막히는지 확인합니다.
  • 개발자 도구 콘솔에서 document.querySelector('#email').validity.typeMismatch를 실행해 true/false 값을 직접 확인합니다.

직접 해보기

  1. 전화번호 필드(pattern 사용)에도 patternMismatch용 메시지를 추가해 보세요.

답 보기

messages 객체에 phone: { patternMismatch: '010-1234-5678 형식으로 입력해 주세요.' }를 추가하면 됩니다. phone은 선택 입력이라 valueMissing은 필요 없습니다.

  1. 참고 링크(url 타입) 필드가 비어 있어도 되지만 값이 있으면 반드시 https://로 시작하게 만들고 싶다면 어떻게 할지 생각해 보세요.

답 보기

pattern="https://.*"를 추가하면 값이 있을 때만 검사되고(빈 값은 required가 없으면 통과), patternMismatch 메시지로 “https://로  시작하는 주소를 입력해 주세요.”를 보여줄 수 있습니다.

자주 하는 실수

증상원인고치는 법
한 번 오류가 뜨면 값을 고쳐도 안 사라짐setCustomValidity를 검사 전에 ''로 초기화하지 않음매 검사 시작 시 setCustomValidity('')부터 호출
페이지를 열자마자 모든 필수 필드가 빨갛게 표시됨:invalid를 써서 사용자가 손대기 전에도 스타일 적용:user-invalid로 교체
제출 버튼을 눌러도 폼이 그대로 전송됨submit 이벤트에서 checkValidity만 하고 preventDefault를 안 함유효하지 않을 때 event.preventDefault() 호출
오류 문구가 스크린 리더에 안 읽힘aria-livearia-describedby 연결 누락오류 요소에 aria-live="polite", 입력에 aria-describedby 연결

확인 문제

문제 14지선다
setCustomValidity를 검사할 때마다 먼저 빈 문자열로 초기화해야 하는 이유는
문제 24지선다
사용자가 아직 값을 입력하지 않은 필수 필드에 페이지 로드 즉시 오류 스타일이 걸리는 것을 막는 선택자는
문제 34지선다
checkValidity와 reportValidity의 차이는

참고 자료

Last updated on