[Vanilla JS] 실시간 회원가입 폼 유효성 검사

세하·2025년 9월 23일

JavaScript

목록 보기
9/10

Vanilla JS를 사용하여, 사용자가 입력하는 순간 실시간으로 유효성을 검사해주도록 해보자.

  • 아이디, 닉네임, 비밀번호의 유효성을 실시간으로 검사한다.
  • 유효성 메시지는 필요할 때만 나타나고, 평소에는 레이아웃에 영향을 주지 않는다.
  • 폼 전체의 모든 입력이 유효할 때만 [회원가입] 버튼이 활성화된다.

HTML - 에러 메시지 보여줄 영역 설정

먼저 회원가입 폼의 기본 HTML 구조를 만들자.
각 <input> 필드 바로 아래에 에러 메시지를 보여줄 <p class="error-message"> 태그를 배치한다.

  <form action="/register" method="POST" class="register-form">
    <div class="input-form">
        <label for="userid">아이디</label>
        <input type="text" id="userid" name="userId" required>
        <p class="error-message" id="userid-error"></p>
    </div>

    <div class="input-form">
        <label for="usernickname">닉네임</label>
        <input type="text" id="usernickname" name="userNickname" required>
        <p class="error-message" id="usernickname-error"></p>
    </div>

    <div class="input-form">
        <label for="userpw">비밀번호</label>
        <input type="password" id="userpw" name="userPw" autocomplete="off" required>
        <p class="error-message" id="userpw-error"></p>
    </div>

    <button type="submit" class="submit-btn" disabled><strong>회원가입</strong></button>
</form>

제출 버튼은 초기에 disabled 속성으로 비활성화해두는 것이 좋음

CSS - 메시지 숨기기/보여주기

에러 메시지가 평소에는 보이지 않고 공간도 차지하지 않게 display: none;으로 설정
이후 자바스크립트로 .show 클래스를 추가하여 display: block;으로 상태를 전환할 것.

.error-message {
    display: none; /* 평소에는 숨김 */
    color: #ed4956;
    font-size: 12px;
    margin-top: 4px;
}

.error-message.show {
    display: block; /* .show 클래스가 붙으면 보이게 함 */
}

.submit-btn:disabled {
    background-color: #b2dffc;
    cursor: not-allowed;
}

JS

자바스크립트 로직을 작성하자.
여기서 브라우저에 내장된 Constraint Validation API를 활용하면 코드를 훨씬 효율적으로 관리할 수 있다.

📍 Constraint Validation API

  • 웹 표준 기술
  • 폼 데이터의 유효성을 검증하는 도구 제공

종류

  • input.setCustomValidity(message)
    • 유효성 상태를 설정한다.
    • 빈 문자열('')을 넣으면 유효함으로, 에러 메시지 문자열을 넣으면 유효하지 않음으로 상태가 설정된다.
    • 이 함수 자체는 메시지를 화면에 보여주지는 않음
  • input.validity.valid
    • 해당 입력 필드의 개별 유효 여부를 true 또는 false로 알려줌
    • setCustomValidity로 설정한 상태를 그대로 반영
  • form.checkValidity()
    • 폼 전체의 모든 자식 요소가 유효한지 검사하여 true 또는 false를 반환
    • 버튼 활성화/비활성화 로직에 유용
  • form.reportValidity()
    • 폼의 유효성을 검사하고, 유효하지 않은 필드가 있다면 브라우저가 기본적으로 제공하는 오류 메시지 말풍선을 출력한다.
    • 나는 따로 커스텀 메시지를 사용하므로 이 함수를 사용하진 않음

어떻게 checkValidity()가 폼 전체의 유효성을 판단?

form.checkValidity()가 폼 전체의 유효성을 판단할 수 있는 이유는, 개발자가 각 <input> 요소에 setCustomValidity()를 통해 브라우저의 내부 유효성 상태를 미리 업데이트해줬기 때문이다.

  1. 각 <input>의 상태 기록
  • 브라우저는 모든 input 요소에 대해 보이지 않는 '성적표' 같은 것을 가지고 있다.
  • 이 성적표에는 "현재 이 학생(input)은 유효한가?"라는 항목이 있으며, 기본값은 '유효함(valid)'이다.
  1. 개발자의 상태 업데이트 (setCustomValidity)
  • 내가 작성한 자바스크립트 유효성 검사 로직(if/else)은 '채점' 과정이다.
  • 채점 결과, 오류가 발견되면 setCustomValidity('에러 메시지')를 호출하여 브라우저에게 "이 학생의 성적표에 '유효하지 않음(invalid)'이라고 기록해줘!"라고 명령하는 것과 같다.
  • 반대로, 오류가 없으면 setCustomValidity('')를 호출하여 "다시 '유효함'으로 바꿔줘!"라고 알려준다.
  1. 폼 전체의 상태 확인 (form.checkValidity)
  • form.checkValidity()는 '최종 확인' 과정이다.
  • 이 함수가 호출되면, 폼은 자신의 모든 자식 input 요소들을 쭉 둘러보며 "너의 성적표에 뭐라고 적혀있니?"라고 하나씩 물어본다.
  • 모든 input이 '유효함'이라고 대답해야만 form.checkValidity()는 true를 반환한다.
  • 단 하나라도 '유효하지 않음' 상태라면 즉시 false를 반환한다.

결론적으로, setCustomValidity는 쓰기 작업이고 checkValidity는 읽기 작업이라고 생각하면 된다.
코드에서 각 input의 상태를 직접 써줬기 때문에, form은 그 결과를 읽기만 하면 되는 것이다.

📍 실시간 검증 및 버튼 활성화 로직

input 이벤트가 발생할 때마다 각 필드의 유효성을 검사하고, 그 결과를 커스텀 에러 메시지로 보여준다.
그리고 이벤트 위임(Event Delegation) 을 활용하여 form 태그에서 모든 input 이벤트를 감지하고, form.checkValidity()로 전체 폼의 유효성을 검사하여 버튼 상태를 업데이트한다.

// 각 입력 필드에 대한 유효성 검사 로직 (아이디, 닉네임, 비밀번호)
const idInput = document.getElementById('userid');
const idError = document.getElementById('userid-error');
idInput.addEventListener('input', (e) => {
    let message = '';
    // 아이디 유효성 검사 조건
    if (e.target.value.length < 8 || e.target.value.length > 16) {
        message = '비밀번호는 8자 이상 16자 이하로 입력해주세요.';
    } else if (!/(?=.*[a-zA-Z])(?=.*\d)(?=.*[!@#$%^&*])/.test(e.target.value)) {
        message = '비밀번호는 영문 대소문자, 숫자, 특수문자를 모두 포함해야 합니다.';
    }
    // 브라우저에 내장된 유효성 상태를 설정
    e.target.setCustomValidity(message); 
    // 개발자가 만든 <p> 태그에 에러 메시지를 표시
    idError.textContent = message;
    // CSS 클래스로 메시지 보이기/숨기기 제어
    message ? idError.classList.add('show') : idError.classList.remove('show');
});

...

const registerForm = document.querySelector('.register-form');
const submitBtn = document.querySelector('.submit-btn');

// 이벤트 위임을 이용한 버튼 활성화 로직
registerForm.addEventListener("input", () => {
    // form.checkValidity()는 form 안의 모든 input 요소의 유효성을 검사
    // 모든 필드가 유효하면 true, 하나라도 유효하지 않으면 false를 반환
    const isFormValid = registerForm.checkValidity();
    submitBtn.disabled = !isFormValid;
});

// 폼 제출 시, 브라우저의 기본 유효성 검증 UI가 뜨지 않도록 방지
registerForm.addEventListener('submit', (e) => {
    if (!registerForm.checkValidity()) {
        e.preventDefault(); // 폼 제출을 막고, 커스텀 메시지가 보이도록 유지
    }
});

💡 왜 이벤트 위임을 사용?
input 요소 하나하나에 이벤트 리스너를 다는 대신, 부모인 form에 한 번만 달아도 이벤트 버블링 덕분에 모든 자식 input의 이벤트를 감지할 수 있다.
코드가 더 간결해지고 효율적이게 된다.

이렇게 HTML5의 표준 API + 커스텀 메세지 + 이벤트 위임 패턴을 활용하여 사용자 친화적인 실시간 유효성 검사 폼을 완성했다!

0개의 댓글