JavaScript 유효성 검사 | 한글, 영어, 숫자, 특수문자 구분 정규식 모음 및 활용

Hailey Song·2020년 8월 12일
1

DEVLOG_JavaScript

목록 보기
2/6

아이디와 비밀번호를 입력할 때, '8자리 이상, 특수문자 포함' 등의 조건을 본 적이 있을 것이다.
이처럼 유효성 검사(form vaildation)란 입력값이 유효한지 아닌지 판단하는 검사로, 한글, 영어, 숫자, 특수문자의 정규식과 test() 함수를 알고 있으면 쉽게 검사를 할 수 있다.

// 한글, 영어, 숫자, 특수문자 구분 정규식 모음
let checkNum = /[0-9]/;
let checkEng = /[a-zA-Z]/;
let checkSpc = /[~!@#$%^&*()_+|<>?:{}]/;
let checkKor = /[ㄱ-ㅎ|ㅏ-ㅣ|가-힣]/;

// 영어 대문자와 소문자는 다음과도 같이 구분할 수 있다.
let checkEnga = /[a-z]/;
let checkEngA = /[A-Z]/;

이 정규식은 test() 함수를 활용해서 쉽게 검사가 가능하다.

checkNum.test('012345') // true
checkNum.test('0123asdf') // true
checkSpc.test('0123456') // false
checkSpc.test('012345!!6') // true

.
.
.
.
.
.
.
.
.
.
.
.
주의!
이 아래로는 개인적으로 짠 코드를 일기 형식으로 적기 위한 글이다.
코린이가 짠 코드임에 주의!
그리고 혹시 같은 문제를 풀고 있는 동료라면(흑흑..) 스스로의 힘으로 해결한 후 참고하길 바란다.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

이제 본격적으로 아이디와 비밀번호 유효성 검사를 해보자.
먼저 아이디와 비밀번호를 확인하기 위해 다음과 같은 HTML 코드를 작성해 주었다.
(CSS는 넘어가자)

<!DOCTYPE html>
<html lang="ko">
<head>
</head>
<body>
    <div>아이디 <input type="text" id="id_input"> 
        <button id="submit_id">Submit</button>
    </div> 

    <div>패스워드 <input type="text" id="ps_input">
        <button id="submit_ps">Submit</button>
    </div> 
    <script src="validation.js"></script>
</body>
</html>

가장 먼저 생각나는 것은 if문으로 아이디의 조건을 나열하는 것.
결과는 submit 버튼에 addEventListner의 click 이벤트를 추가하여 알림창으로 확인할 수 있게 했다.

let whenTrue = '사용할 수 있는 아이디입니다.'

document.querySelector("#submit_id").addEventListener('click', function(){
    if(document.querySelector('#id_input').value.length >= 8 
       && document.querySelector('#id_input').value.length <= 20) {
        alert('제출되었습니다.');
    } else {
        alert('아이디는 8자리 이상, 20자리 이하여야 합니다.'); 
    }
})

그러나 만약 아이디의 조건이 '8자리 이상 20자리 이하, 특수문자 미포함, 영문 대문자 미포함, 한글 미포함 등등 어마무시하게 늘어난다면?' 거기에 비밀번호도 대여섯가지의 조건이 붙을 수 있다.
또 알람메세지는 어떤가. 비밀번호가 8자리 이상이 아니라면 8자리 이상을 입력하라는 메세지를 보내야 하지만, 8자리 이상이 아니면서 특수문자도 포함되지 않고 영문도 포함되지 않는다면... 경우의 수는 더더욱 많아지게 된다.
귀차니스트인 나는 그 때마다 if문을 늘려주는 게 끔찍했다.
차라리 아이디와 비밀번호의 조건을 모두 처리하는 함수를 만들 수는 없을까?
알람메세지와 아이디의 조건, 패스워드의 조건을 모두 처리하는 함수를!

우선 문자 구분 정규식을 객체로 만들어주었다.

// 문자 구분 객체
const letterCheck = {
    'checkNum': /[0-9]/,
    'checkEngA': /[A-Z]/,
    'checkEnga': /[a-z]/,
    'checkEngAll': /[a-zA-Z]/,
    'checkSpc': /[~!@#$%^&*()_+|<>?:{}]/,
    'checkKor': /[ㄱ-ㅎ|ㅏ-ㅣ|가-힣]/
}

그리고 그에 따른 조건 메세지들도 따로 객체로 만들었다.

// 조건메세지
const conditionMessage = {
    'minLength': '자리 이상',
    'maxLength': '자리 이하',
    'withNum': '숫자 포함',
    'withNoNum': '숫자 미포함',
    'withEngA': '영문 대문자 포함',
    'withNoEngA': '영문 대문자 미포함',
    'withEnga': '영문 소문자 포함',
    'withNoEnga': '영문 소문자 미포함',
    'withSpc': '특수문자 포함',
    'withNoSpc': '특수문자 미포함',
    'withKor': '한글 포함',
    'withNoKor': '한글 미포함'
}

우와우.. 이게 오히려 더 노가다같은 느낌이 드는 건 왜일까?

그리고 이제 id와 ps 각각의 조건도 객체로 묶어주었다.
(지금 생각해보니 이것을 더 큰 배열로 묶을 수도 있겠다는 생각이 들었다. 그건 나중에 시도해보는 걸로.)

// id의 조건
const idCondition = {
    'minLength': 8, // 8자리 이상
    'maxLength': 20, // 20자리 이하
    'withNum': true, // 숫자 포함
    'withEngA': false, // 영문 대문자 미포함
    'withEnga': true, // 영문 소문자 포함
    'withSpc': false, // 특수문자 미포함
    'withKor': false, // 한글 미포함
}

// ps의 조건
const psCondition = {
    'minLength': 8,
    'maxLength': 20,
    'withNum': true,
    'withEngA': true,
    'withEnga': true,
    'withSpc': false,
    'withKor': true,
}

지금은 비록 id와 ps 두 가지 조건만 있어서 오히려 이렇게 만드는 게 더 비효율적인 것 같긴 하지만..
나중에 검사할 것들이 더 많아지면 괜찮아질거라고 위로를 해 보자.

그 다음은 대망의 함수!

// 유효성 검사 : input의 id, 조건객체를 매개변수로 갖는 함수
function checkConditionValue(inputCssSelector, conditionObj){
    // 알림메세지에 넣을 조건들을 넣을 배열
    let conditionAlert = [];
  
  	// input value의 표현식이 너무 길어서 변수로 할당해주었다.
    let idInput = document.querySelector(inputCssSelector).value;
  
  	// 만약 minLength보다 작으면 -> 해당 숫자와 조건 메세지를 같이 묶어서
  	// conditionAlert 배열에 push
    if (!(idInput.length >= conditionObj.minLength)) {
        conditionAlert.push(`${conditionObj.minLength}` + conditionMessage.minLength);
    } 
  	// 이제부턴 주석 생략! 추측해보시라
    if (!(idInput.length <= conditionObj.maxLength)) {
        conditionAlert.push(`${conditionObj.maxLength}` + conditionMessage.maxLength);
    }
    if (letterCheck.checkEngA.test(idInput) !== conditionObj.withEngA) {
        if (conditionObj.withEngA) {
            conditionAlert.push(conditionMessage.withEngA);
        } else {
            conditionAlert.push(conditionMessage.withNoEngA);
        }
    }
    if (letterCheck.checkSpc.test(idInput) !== conditionObj.withSpc) {
        if (conditionObj.withSpc) {
            conditionAlert.push(conditionMessage.withSpc);
        } else {
            conditionAlert.push(conditionMessage.withNoSpc);
        }
    }
    if (letterCheck.checkKor.test(idInput) !== conditionObj.withKor) {
        if (conditionObj.withKor) {
            conditionAlert.push(conditionMessage.withKor);
        } else {
            conditionAlert.push(conditionMessage.withNoKor);
        }
    }
  	// conditionAlert에 아무 것도 들어가 있지 않는다면 모든 조건이 충족한 것!
    // 성취감을 느끼며 다음 메세지를 띄운다.. '제출되었습니다아......'
    if (conditionAlert.length === 0) {
        alert('제출되었습니다.');
    } else {
  	// 아니면 conditionAlert의 모든 엘리먼트들을 join해서 내보낸다.
        alert('조건: ' + conditionAlert.join(', '));
    }
    
}

먼 여행이었지만 아직 함수는 호출되지 않았다.
여기서 좀 헤맸던게, 인자를 받는 함수를 addEventListner에 넣으면 이벤트가 발생될 때가 아니라 페이지가 로드될 때 바로 실행된다. 즉,

// addEventListner에 들어가는 함수에 ()가 들어가지 않으면 이벤트가 발생될 때 호출된다,
document.querySelector("#submit_id").addEventListener('click', checkConditionValue)

// 그러나 ()가 들어간다면 이벤트는 커녕 페이지가 시작될 때 호출된다. 네가 왜 이때 나와..
document.querySelector("#submit_id").addEventListener('click', checkConditionValue())

// 문제는! 나의 함수는 인자(inputCssSelector, conditionObj)를 필요로 하기 때문에
// ()를 쓸 수밖에 없는 것이다!
document.querySelector("#submit_id").addEventListener('click', checkConditionValue("#id_input", idCondition))

여기서 어떻게 할까 고민하다가 제로초님의 블로그에서 힌트를 얻었다.

function idConditionAlert() {
    checkConditionValue("#id_input", idCondition) 
}
document.querySelector("#submit_id").addEventListener('click', idConditionAlert)

호출되지 않게 함수 안에 함수를 만들면 된다! 그럼 addEventListener에서 얌전히 있지만 이벤트가 발생할 때 호출되면서 안에 있는 함수 역시 호출시킨다!

이렇게 함수를 바꿔끼우면(?) 비밀번호에 대한 이벤트도 발생시킬 수 있다.
앞으로는 유효성 검사 항목을 하나씩 추가할 때 조건 객체 하나와 호출 함수 하나씩만 추가하면 편리하게 사용할 수 있다.

function psConditionAlert() {
    checkConditionValue("#ps_input", psCondition) 
}
document.querySelector("#submit_ps").addEventListener('click', psConditionAlert)

다음은 결과!!

음! 잘 들어갔다. 뿌듯

앞으로 하고 싶은건 조건들을 하나의 큰 배열로 묶는 것. 이를테면

const allCondition = [
  {'name': '아이디',
   'minLength': 8, 
   'maxLength': 20, 
   'withNum': true, 
   'withEngA': false, 
   'withEnga': true, 
   'withSpc': false, 
   'withKor': false
  },
  {'name': '비밀번호'
   //생략//
  },
  //생략
]

이러면 알람에도 '아이디 조건 : 8자리 이상', '비밀번호 조건 : 8자리 이상, 특수문자 포함' 이런 식으로 어떤 조건인지 알 수 있게 해줄 수 있다!
근데 그러면.. 배열 안에서 값을 또 찾아야 하니까.. 더 이상 생각하고 싶지는 않은ㅋㅋㅋㅋ
오늘은 여기까쥐.

이러고 코드스테이츠의 유효성 검사 레퍼런스를 확인하고 허탈.....
역시 난 갈 길이 멀었다...ㅋㅋㅋㅋㅋㅋㅋㅋ

0개의 댓글