-
네이버 회원가입 페이지 따라 만들기(클론코딩) - Javascript프론트엔드 2019. 12. 17. 00:29
HTML, CSS에 이어서 Javascript로 기능구현하는 과정까지 보여드릴게요.
저는 간단한 유효성검사 기능까지만 구현했습니다.
사용자가 데이터를 입력하지 않았거나, 잘못된 데이터를 입력했을 때(조건/형식에 맞지 않는 데이터를 입력했을 때)
입력폼 아래에 "필수 정보입니다", "8~16자 영문 대 소문자, 숫자, 특수문자를 사용하세요."와 같은
에러메세지를 출력할 것입니다.
기능을 구현하기 전에
먼저 네이버 실제 회원가입 페이지의 유효성 검사 로직을 직접 이것저것 입력해보면서 분석해봤습니다.
입력폼별로 로직을 정리한 내용입니다.
더보기1. 아이디 :
데이터 입력x : 필수 정보입니다.
5자미만/20자초과/영문소문자,숫자,특수기호(-),(_) 외의 문자 입력 : 5~20자의 영문 소문자, 숫자와 특수기호(_),(-)만 사용 가능합니다.
조건에 맞는 데이터 입력 : 멋진 아이디네요!
2. 비밀번호 :
데이터 입력x : 필수 정보입니다.
8자미만/16자초과/영문대소문자, 숫자, 특수문자 외의 문자 입력 :
8~16자 영문 대 소문자, 숫자, 특수문자를 사용하세요.
(네이버에서는 비밀번호를 위험, 보통, 안전, 사용불가 네 가지로 판단하는데 전부 다 구현하기에는 좀 어려워서ㅜㅜ그냥 안전, 사용불가 두가지로만 나눠서 구현했습니다)
3. 비밀번호 재확인 :
데이터 입력x : 필수 정보입니다.
비밀번호와 불일치 : 비밀번호가 일치하지 않습니다.
네이버 비밀번호 재확인 입력폼은
8자미만/16자초과/특정문자 입력불가 등의 조건이 없고 그냥 2번에 입력한 비밀번호와 일치하는지만
판단하더라구요. ("123"이라고 입력해도 위의 비밀번호 입력폼에 입력된 비밀번호와 동일하다면 에러메세지가 출력되지 않았습니다.)
4. 이름 :
데이터 입력x : 필수 정보입니다.
한글, 영문 대소문자 외의 문자 사용(특수기호, 공백 안됨. 자음이나 모음 단독입력도 안됨)x :
한글과 영문 대 소문자를 사용하세요. (특수기호, 공백 사용 불가)
5. 생년월일 :
가장 복잡한 부분이었습니다. 순서도를 그려야할 것 같은데 일단 그냥 써보겠습니다.
1) 연도가 입력되었는지 확인한다(숫자4개이기만 하면 ok)
1-1) 연도가 입력되었다면, 2)로 이동
1-2) 연도가 입력되지 않았다면, "태어난 년도 4자리를 정확하게 입력하세요." 출력
2) 월이 입력되었는지 확인한다.("월"외의 option을 선택하면 ok)
2-1) 월이 입력되었다면, 3)으로 이동
2-2) 월이 입력되지 않았다면, "태어난 월을 선택하세요." 출력
3) 날짜가 입력되었는지 확인한다.(숫자 1개~2개이기만 하면 ok)
3-1) 날짜가 입력되었다면, 4)로 이동
3-2) 날짜가 입력되지 않았다면, "태어난 일(날짜) 2자리를 정확하게 입력하세요." 출력
3-3) 날짜에 숫자1개~2개 외의 다른 데이터(문자, 숫자3개이상 등)가 입력되었다면, "생년월일을 다시 확인해주세요." 출력
4) 입력된 생년월일을 확인한다.
4-1) 나이가 만100세 이상이면 "정말이세요?" 출력.
(오늘 날짜 2019-12-17 기준, 100년전인 1919-12-17을 입력하면 에러메세지가 출력되고, 1919-12-18이후면 ok)
4-2) 나이가 만14세 미만이면 "만 14세 미만의 어린이는 보호자 동의가 필요합니다." 출력
4-3) 미래의 날짜(?)를 입력하면 "미래에서 오셨군요. ^^" 출력
6. 성별
데이터 입력x : 필수 정보입니다.
처음부터 이 에러메세지가 뜨지는 않고, "남자" 혹은 "여자"를 선택했다가 다시 "성별"을 선택하면
그 때 에러메세지가 뜹니다.
7. 본인 확인 이메일
데이터 입력x : 선택 입력사항이므로 아무것도 입력하지 않아도 에러메세지가 뜨지 않습니다.
이메일 형식이 아닌 데이터 입력: "이메일 주소를 다시 확인해주세요." 출력
8. 휴대전화
데이터 입력x : 필수 정보입니다.
휴대폰 번호 형식이 아닌 데이터 입력 : "형식에 맞지 않는 번호입니다." 출력
분석이랄 것도 없지만 어쨌든 이 분석내용을 바탕으로 자바스크립트 소스를 작성했습니다.
첫 번째로, DOM에서 입력폼들과 에러메세지 출력 부분의 아이디/클래스명을 가져와서 전부 변수에 담았습니다.
/*변수 선언*/ var id = document.querySelector('#id'); var pw1 = document.querySelector('#pswd1'); var pwMsg = document.querySelector('#alertTxt'); var pwImg1 = document.querySelector('#pswd1_img1'); var pw2 = document.querySelector('#pswd2'); var pwImg2 = document.querySelector('#pswd2_img1'); var pwMsgArea = document.querySelector('.int_pass'); var userName = document.querySelector('#name'); var yy = document.querySelector('#yy'); var mm = document.querySelector('#mm'); var dd = document.querySelector('#dd'); var gender = document.querySelector('#gender'); var email = document.querySelector('#email'); var mobile = document.querySelector('#mobile'); var error = document.querySelectorAll('.error_next_box');
이 부분은 특별히 설명하지 않아도 되겠죠? 사실 글로벌 변수 사용은 자제하는 게 좋다고 하던데 다른 방법을 아직 모르겠고 간단한 기능 구현이기 때문에 어쩔 수 없이 이렇게 글로벌 변수를 왕창 선언해줬습니다^^;
두 번째로, 변수에 담긴 입력폼 요소에 이벤트 핸들러를 연결해줬습니다.
id.addEventListener("change", checkId); pw1.addEventListener("change", checkPw); pw2.addEventListener("change", comparePw); userName.addEventListener("change", checkName); yy.addEventListener("change", isBirthCompleted); mm.addEventListener("change", isBirthCompleted); dd.addEventListener("change", isBirthCompleted); gender.addEventListener("change", function() { if(gender.value === "성별") { error[5].style.display = "block"; } else { error[5].style.display = "none"; } }) email.addEventListener("change", isEmailCorrect); mobile.addEventListener("change", checkPhoneNum);
gender 부분은 함수가 짧길래 그냥 저렇게 익명함수로 작성해봤고 나머지는 따로 함수를 작성했습니다.
여기서 제가 해결하지 못한 부분은,
네이버에서는 처음에 내용이 아무것도 입력되지 않은 텍스트 입력 폼에 마우스 커서만 눌렀다가 떼도
이벤트가 발생해서 "필수 정보입니다."가 출력되는데,
제가 작성한 소스는 "change"이벤트가 발생해야 함수가 실행되기 때문에 텍스트 입력폼 내용에 변경이 있어야만
에러메세지가 출력된다는 점입니다. ("필수 정보입니다."가 출력되게 하려면 뭔가 입력했다가 지워야합니다ㅜ)
다른 이벤트 종류가 있는지 찾아봤지만 잘 모르겠더라구요..
마지막으로, 콜백 함수들을 작성해줬습니다.
이 때 정규식이 필요한 함수의 경우 정규식 패턴을 만들어 함수 내부에서 변수에 담아 선언해줬습니다.
콜백 함수 소스코드 전체보기 ↓
더보기/*콜백 함수*/ function checkId() { var idPattern = /[a-zA-Z0-9_-]{5,20}/; if(id.value === "") { error[0].innerHTML = "필수 정보입니다."; error[0].style.display = "block"; } else if(!idPattern.test(id.value)) { error[0].innerHTML = "5~20자의 영문 소문자, 숫자와 특수기호(_),(-)만 사용 가능합니다."; error[0].style.display = "block"; } else { error[0].innerHTML = "멋진 아이디네요!"; error[0].style.color = "#08A600"; error[0].style.display = "block"; } } function checkPw() { var pwPattern = /[a-zA-Z0-9~!@#$%^&*()_+|<>?:{}]{8,16}/; if(pw1.value === "") { error[1].innerHTML = "필수 정보입니다."; pwMsg.style.display = "block"; pwMsgArea.style.paddingRight = "40px"; pwImg1.src = "m_icon_pass.png"; error[1].style.display = "block"; } else if(!pwPattern.test(pw1.value)) { error[1].innerHTML = "8~16자 영문 대 소문자, 숫자, 특수문자를 사용하세요."; pwMsg.innerHTML = "사용불가"; pwMsgArea.style.paddingRight = "93px"; error[1].style.display = "block"; pwMsg.style.color = "red"; pwMsg.style.display = "block"; pwImg1.src = "m_icon_not_use.png"; } else { error[1].style.display = "none"; pwMsg.innerHTML = "안전"; pwMsgArea.style.paddingRight = "93px"; pwMsg.style.color = "#03c75a"; pwMsg.style.display = "block"; pwImg1.src = "m_icon_safe.png"; } } function comparePw() { if(pw2.value === pw1.value) { pwImg2.src = "m_icon_check_enable.png"; error[2].style.display = "none"; } else if(pw2.value !== pw1.value) { pwImg2.src = "m_icon_check_disable.png"; error[2].innerHTML = "비밀번호가 일치하지 않습니다."; error[2].style.display = "block"; } if(pw2.value === "") { error[2].innerHTML = "필수 정보입니다."; error[2].style.display = "block"; } } function checkName() { var namePattern = /[a-zA-Z가-힣]/; if(userName.value === "") { error[3].innerHTML = "필수 정보입니다."; error[3].style.display = "block"; } else if(!namePattern.test(userName.value) || userName.value.indexOf(" ") > -1) { error[3].innerHTML = "한글과 영문 대 소문자를 사용하세요. (특수기호, 공백 사용 불가)"; error[3].style.display = "block"; } else { error[3].style.display = "none"; } } function isBirthCompleted() { var yearPattern = /[0-9]{4}/; if(!yearPattern.test(yy.value)) { error[4].innerHTML = "태어난 년도 4자리를 정확하게 입력하세요."; error[4].style.display = "block"; } else { isMonthSelected(); } function isMonthSelected() { if(mm.value === "월") { error[4].innerHTML = "태어난 월을 선택하세요."; } else { isDateCompleted(); } } function isDateCompleted() { if(dd.value === "") { error[4].innerHTML = "태어난 일(날짜) 2자리를 정확하게 입력하세요."; } else { isBirthRight(); } } } function isBirthRight() { var datePattern = /\d{1,2}/; if(!datePattern.test(dd.value) || Number(dd.value)<1 || Number(dd.value)>31) { error[4].innerHTML = "생년월일을 다시 확인해주세요."; } else { checkAge(); } } function checkAge() { if(Number(yy.value) < 1920) { error[4].innerHTML = "정말이세요?"; } else if(Number(yy.value) > 2019) { error[4].innerHTML = "미래에서 오셨군요. ^^"; } else if(Number(yy.value) > 2005) { error[4].innerHTML = "만 14세 미만의 어린이는 보호자 동의가 필요합니다."; } else { error[4].style.display = "none"; } } function isEmailCorrect() { var emailPattern = /[a-z0-9]{2,}@[a-z0-9-]{2,}\.[a-z0-9]{2,}/; if(email.value === ""){ error[6].style.display = "none"; } else if(!emailPattern.test(email.value)) { error[6].style.display = "block"; } else { error[6].style.display = "none"; } } function checkPhoneNum() { var isPhoneNum = /([01]{2,})([01679]{1,})([0-9]{3,4})([0-9]{4})/; if(mobile.value === "") { error[7].innerHTML = "필수 정보입니다."; error[7].style.display = "block"; } else if(!isPhoneNum.test(mobile.value)) { error[7].innerHTML = "형식에 맞지 않는 번호입니다."; error[7].style.display = "block"; } else { error[7].style.display = "none"; } }
위 소스코드에서 제가 사용한 정규표현식 패턴들만 모아보면 다음과 같습니다.
var idPattern = /[a-zA-Z0-9_-]{5,20}/; var pwPattern = /[a-zA-Z0-9~!@#$%^&*()_+|<>?:{}]{8,16}/; var namePattern = /[a-zA-Z가-힣]/; var yearPattern = /[0-9]{4}/; var datePattern = /\d{1,2}/; var emailPattern = /[a-z0-9]{2,}@[a-z0-9-]{2,}\.[a-z0-9]{2,}/; var isPhoneNum = /([01]{2,})([01679]{1,})([0-9]{3,4})([0-9]{4})/;
저는 이 회원가입 페이지를 만들면서 정규식이라는 걸 거의 처음 접했기 때문에
제가 이해할 수 있는 수준으로 아주아주 간단하게 작성했습니다. 사실 이게 맞는지 잘 모르겠지만 테스트 해봤을 때 작동은 잘 되더라구요.
따로 설명을 안해도 읽어보시면 이해가 될 정도이긴 하지만 그래도 몇 가지 설명하자면,
pwPattern은 [ ] 안에 있는 문자(영어 소문자(a-z), 대문자(A-Z), 숫자(0-9), 그외특수문자들)가 아닌 문자를 포함하거나 8자미만/16자초과하는 문자열을 테스트하면 false가 반환되는 식입니다.
namePattern에서 '가-힣'은 완성형 한글(자음+모음/자음+모음+자음)의 첫글자-마지막글자를 나타냅니다. 그래서 'ㄱ', 'ㅏ'와 같은 단독 한글 자음/모음을 입력하고 테스트했을 때 false가 뜹니다.
datePattern의 '\d'는 숫자를 의미합니다. [0-9]와 같다고 보시면 됩니다.
emailPattern의 '[a-z0-9]{2,}@' 부분은 @앞에 오는 문자열이 영어소문자나 숫자로 이루어져있고, 2글자 이상이라는 의미입니다.
생활코딩의 javascript강의 정규표현식 파트와 정규식 시각화 사이트(https://regexper.com/), 정규식 테스트 사이트(https://regexr.com/)를 참고했습니다.
다음은 함수 중에서 checkPw() 함수만 따로 가져왔습니다.
function checkPw() { var pwPattern = /[a-zA-Z0-9~!@#$%^&*()_+|<>?:{}]{8,16}/; if(pw1.value === "") { error[1].innerHTML = "필수 정보입니다."; pwMsg.style.display = "block"; pwMsgArea.style.paddingRight = "40px"; pwImg1.src = "m_icon_pass.png"; error[1].style.display = "block"; } else if(!pwPattern.test(pw1.value)) { error[1].innerHTML = "8~16자 영문 대 소문자, 숫자, 특수문자를 사용하세요."; pwMsg.innerHTML = "사용불가"; pwMsgArea.style.paddingRight = "93px"; error[1].style.display = "block"; pwMsg.style.color = "red"; pwMsg.style.display = "block"; pwImg1.src = "m_icon_not_use.png"; } else { error[1].style.display = "none"; pwMsg.innerHTML = "안전"; pwMsgArea.style.paddingRight = "93px"; pwMsg.style.color = "#03c75a"; pwMsg.style.display = "block"; pwImg1.src = "m_icon_safe.png"; } }
checkPw() 함수는
네이버 실제 회원가입 페이지에 있는 자물쇠 이미지를 다운받아서 썼습니다. (근데 며칠 후에 네이버가 자물쇠 이미지를 바꿨어요) 입력값에 따라 이미지가 바껴요.
비밀번호를 입력하면 입력폼 오른쪽에 사용불가 혹은 안전 이라는 메세지가 뜨는데, 이 때 메세지가 나타날 공간을 확보해주기 위해서 입력폼 오른쪽 padding값을 93px로 변경해줍니다. (pwMsgArea.style.paddingRight = "93px";) (원래 padding-right 값은 40px입니다.)
그리고 "사용불가" / "안전"이라는 메세지를 html 해당태그 안에 삽입하고, 컬러를 설정한 뒤(레드/그린),
display: none -> diplay: block으로 바꿔 메세지가 나타나게 합니다.
비밀번호 입력폼 아래쪽에 나타나는 에러메세지는 다른 폼들과 똑같이 처리했습니다. (에러메세지를 태그 안에 삽입하고, display:block;으로 스타일을 바꿔 메세지가 보이게 했습니다. 제대로 입력했을 때는 display 속성을 다시 none으로 바꿔줍니다. )
이 외에 다른 함수들 설명은 생략하고 넘어가겠습니다.
코드에 개선할 부분이 있으면 알려주시고 이해되지 않는 부분이 있다면 질문해주세요~
'프론트엔드' 카테고리의 다른 글
자바스크립트 소스 작성하고 실행하기 - Do it 자바스크립트 기본편 (0) 2019.12.24 자바스크립트와 웹 프로그래밍 - Do it 자바스크립트 기본편 (0) 2019.12.19 레이아웃 따라 만들기 - HTML/CSS (0) 2019.12.18 네이버 회원가입 페이지 따라 만들기(클론코딩) - HTML/CSS (10) 2019.12.16 자바스크립트 클로저(Closure) _속 깊은 Javascript 2장 (0) 2019.12.15