아이디와 비밀번호를 입력할 때, '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자리 이상, 특수문자 포함' 이런 식으로 어떤 조건인지 알 수 있게 해줄 수 있다!
근데 그러면.. 배열 안에서 값을 또 찾아야 하니까.. 더 이상 생각하고 싶지는 않은ㅋㅋㅋㅋ
오늘은 여기까쥐.
이러고 코드스테이츠의 유효성 검사 레퍼런스를 확인하고 허탈.....
역시 난 갈 길이 멀었다...ㅋㅋㅋㅋㅋㅋㅋㅋ