상태·렌더 구조 위에 실사용에 가까운 입력 검증 로직을 얹습니다. 서버가 다시 검증하더라도 클라이언트에서 규칙을 명확히 하면 사용자 피로를 줄일 수 있습니다.
이번 글에서 새로 나오는 용어
- 폼 검증: 제출 전에 입력값이 규칙을 지켰는지 확인하는 과정입니다.
- 패턴 정규식: 이메일처럼 특정 형식을 검사하기 위해 쓰는 규칙 표현식입니다.
- blur: 입력 칸에서 포커스가 빠져나갈 때 발생하는 이벤트로, 입력 이후 검증 시점에 활용합니다.
- aria-live: 화면 변화 문구를 보조기기가 읽어 주도록 표시하는 접근성 속성입니다.
- aria-invalid: 해당 입력이 지금 잘못된 상태임을 보조기기에 알려 주는 속성입니다.
핵심 개념
- 검증 규칙: 필수 여부, 길이, 패턴, 서로 일치해야 하는 값 등을 정리합니다.
- 검증 시점: 입력 중 실시간, 입력 후
blur, 제출 순간 중 어떤 시점에 메시지를 보여줄지 결정합니다. - 피드백 요소: 경고 텍스트, 테두리 색,
aria-live영역 같은 시각·청각 힌트를 계획합니다. - 접근성 고려:
aria-invalid,aria-live="polite"같은 속성을 통해 보조기기가 메시지를 읽을 수 있게 합니다.
코드로 확인하기
const constraints = {
name: { required: true, minLength: 2 },
email: { required: true, pattern: /^[\w.-]+@([\w-]+\.)+[\w-]{2,}$/ },
password: { required: true, minLength: 8 },
confirmPassword: { matches: "password" },
};
function validateField(name, value, form) {
const rules = constraints[name];
if (!rules) return { valid: true };
if (rules.required && !value.trim()) return { valid: false, message: "필수 입력입니다." };
if (rules.minLength && value.length < rules.minLength)
return { valid: false, message: `${rules.minLength}자 이상 입력해주세요.` };
if (rules.pattern && !rules.pattern.test(value))
return { valid: false, message: "형식이 올바르지 않습니다." };
if (rules.matches) {
const target = form.elements.namedItem(rules.matches);
if (target && value !== target.value) return { valid: false, message: "값이 일치하지 않습니다." };
}
return { valid: true };
}
const form = document.querySelector(".signup-form");
const feedbackEl = document.querySelector(".form-feedback");
form?.addEventListener("input", (event) => {
const target = event.target;
if (!(target instanceof HTMLInputElement) || !form) return;
const { valid, message } = validateField(target.name, target.value, form);
target.classList.toggle("is-invalid", !valid);
target.setAttribute("aria-invalid", String(!valid));
const hint = target.nextElementSibling;
if (hint) hint.textContent = message ?? "";
});
form?.addEventListener("submit", (event) => {
event.preventDefault();
if (!form) return;
const fields = Array.from(form.elements).filter((el) => el instanceof HTMLInputElement);
const invalid = fields
.map((field) => ({ field, result: validateField(field.name, field.value, form) }))
.find(({ result }) => !result.valid);
if (invalid) {
if (feedbackEl) feedbackEl.textContent = invalid.result.message;
invalid.field.focus();
return;
}
if (feedbackEl) {
feedbackEl.textContent = "회원가입 요청을 전송했습니다.";
feedbackEl.classList.add("is-success");
}
});
실시간 입력과 제출 시 검증을 분리하면 사용자에게 언제 무엇을 고쳐야 하는지 명확히 알려 줄 수 있습니다.
.signup-form input {
border: 1px solid #c8d0da;
transition: border-color 0.2s ease;
}
.signup-form input.is-invalid {
border-color: #ff5757;
background: rgba(255, 87, 87, 0.1);
}
.signup-form .hint {
font-size: 0.85rem;
color: #ff5757;
min-height: 1.2rem;
}
.form-feedback {
margin-top: 1rem;
font-weight: 600;
}
.form-feedback.is-success {
color: #1ba784;
}
짧은 CSS만으로도 오류 필드를 한눈에 강조할 수 있습니다.
BrowserMock로 검증 흐름 리플레이
폼 검증은 입력 → 메시지 → 제출 차례를 따라야 하므로 BrowserMock으로 UI 상태를 되짚어 보는 것이 도움이 됩니다.
const { document, window } = mock(`
<form class="signup-form">
<input name="email" />
<span class="hint"></span>
<div class="form-feedback"></div>
</form>
`);
const form = document.querySelector(".signup-form");
const feedbackEl = document.querySelector(".form-feedback");
form.addEventListener("submit", (event) => {
event.preventDefault();
const { valid, message } = validateField("email", form.email.value, form);
if (!valid) feedbackEl.textContent = message;
});
form.email.value = "broken";
form.dispatchEvent(new window.Event("submit"));
console.log(feedbackEl.textContent); // "형식이 올바르지 않습니다."
이렇게 하면 실제 브라우저를 띄우지 않고도 오류 메시지가 필요한 순간에 나타나는지 즉시 확인할 수 있습니다. 팀 리뷰에서는 BrowserMock 콘솔 로그를 캡처해 상태 변화를 설명하면 UI 합의가 빨라집니다.
왜 중요한가
- 클라이언트에서 검증을 선행하면 서버까지 요청을 보내기 전에 실수를 바로잡을 수 있어 네트워크 비용과 사용자 좌절감을 줄입니다.
- 명확한 피드백 구조는 신입생 모집 폼, 동아리 신청서처럼 중요한 입력 흐름에서 신뢰를 높여 줍니다.
- 접근성을 고려한 메시지는 보조기기를 사용하는 사용자에게도 동일한 정보를 전달합니다.
실습
- 따라 하기:
validateField와 입력/제출 이벤트를 그대로 붙여 하나의 가입 폼에서 동작시키세요. - 확장하기: 비밀번호 강도 게이지, 전화번호 마스킹, 서버 오류 메시지 통합 중 두 가지를 추가합니다.
- 디버깅: 정규식이나
matches규칙을 일부러 깨서 어떤 메시지가 노출되는지 확인하고, 이벤트가 중복 실행되지 않는지 DevTools로 점검합니다. - 완료 기준: 모든 필드가 유효하지 않으면 제출이 막히고, 성공 시 상태 텍스트가 녹색으로 바뀌며 콘솔 경고가 없다면 실습이 끝납니다.
마무리
폼 검증이 탄탄할수록 사용자는 믿음을 느낍니다. 다음 글에서는 웹 스토리지와 데이터 지속성을 연결해 입력값을 안전하게 보관하는 방법을 정리해 보겠습니다.
💬 댓글
이 글에 대한 의견을 남겨주세요