23. 09. 18
지난 시간에 배웠던 정규표현식을 회원가입에 적용시켜보는 시간을 가져보려고 해요.
먼저 Open with Live Server로 회원가입 페이지가 어떻게 구성되어 있는지 웹 브라우저에서 확인해볼게요.
전체 html, js 코드는 블로그 맨 아래에서 확인하실 수 있습니다.

필수로 있어야 할 유효성 목록입니다.
- 유효성 검사를 통과하지 못해 false를 반환할 때, 입력칸 리셋 및 마우스 커서 이동 처리
- 비밀번호는 아이디와 일치할 수 없도록 할 것
- 집주소는 API를 이용하여 zip-code를 입력받도록 처리
- 주민등록번호는 주민등록번호 계산식을 통해 올바른 주민등록번호만 입력되도록 처리
- 주민등록번호를 올바르게 입력했을 경우 자동으로 생년월일 데이터가 삽입될 것
- 관심분야(체크박스)는 최소 한 개 이상 체크할 것
- 자기소개는 입력 글자수에 제한을 둘 것
- 모든 유효성 검사를 통과했을 때만 가입하기 버튼을 눌렀을 때 메일창으로 넘어갈 것
아래는 html 코드입니다.
<form> 태그로 <table> 태그를 감쌌습니다.
<html>
<head> </head>
<body>
<form>
<table></table>
</form>
</body>
</html>
input type이 button인 "가입하기" 버튼을 눌렀을 때 유효성 검사를 실행할 수 있도록 onClick 이벤트를 넣어 userCheck() 함수가 실행되도록 했습니다.
<input align="center" type="button" onclick="userCheck()" value="가입하기" />
집주소 API는 다음(카카오)에서 제공하는 우편번호 검색 서비스를 이용했습니다.
우편번호 검색 버튼을 눌렀을 때 execDaumPostcode() 함수가 실행되고,
초기화 버튼을 눌렀을 때 execDaumPostcodeReset() 함수가 호출됩니다.
<td align="left">
<input type="text" id="zipCode" placeholder="우편번호" readonly required />
<input type="button" onclick="execDaumPostcode()" value="우편번호 찾기" />
<input type="button" onclick="execDaumPostcodeReset()" value="초기화" /><br />
<input type="test" id="address1" placeholder="도로명주소" size="40" readonly required /><br>
<input type="text" id="address2" placeholder="지번주소" size="40" readonly /><br>
<input type="text" id="address3" placeholder="상세주소" size="40" required />
<!-- 다음(카카오)에서 제공하는 우편번호 검색 제공 서비스 -->
<script src="//t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js"></script>
</td>
각 <input> 태그마다 required 속성을 추가하여 폼 데이터가 서버로 제출되기 전에 반드시 채워져 있어야 하는 입력 필드임을 지정했습니다.
<input type="" required />
아래는 JavaScript 코드입니다.
id="uid"인 아이디의 유효성 검사입니다.
// 아이디 검사
if (uid.value == "") {
alert("아이디를 입력하세요.");
uid.focus();
return false;
}
let uidRegExp = /^[a-zA-z0-9]{4,12}$/;
if (!uidRegExp.test(uid.value)) {
alert("아이디는 영문 대소문자와 숫자 4~12자리로 입력해야 합니다!");
uid.focus();
uid.value = "";
return false;
}
id="pw"인 비밀번호의 유효성 검사입니다
입력한 비밀번호의 값이 아이디(uid)의 값과 일치하면 경고창이 뜹니다.
// 비밀번호 검사
if (pw.value == "") {
alert("비밀번호를 입력하세요.");
pw.focus();
return false;
}
if (uid.value == pw.value) {
alert("비밀번호는 아이디와 다르게 입력해야 합니다.");
pw.focus();
pw.value = "";
return false;
}
let pwRegExp = /^(?=.*[a-zA-Z])(?=.*[!@#$%^*+=-])(?=.*[0-9]).{8,25}$/;
if (!pwRegExp.test(pw.value)) {
alert("비밀번호는 영문자+숫자+특수문자 조합으로 8~25자리 사용해야 합니다.");
pw.focus();
pw.value = "";
return false;
}
id="pwChenk"인 비밀번호 확인의 유효성 검사입니다.
위에서 입력한 비밀번호(pw)의 값과 일치하지 않으면 경고창이 뜹니다.
// 비밀번호 확인 검사
if (pwCheck.value == "") {
alert("비밀번호 확인을 입력하세요.");
pwCheck.focus();
return false;
}
if (pwCheck.value != pw.value) {
alert("비밀번호가 일치하지 않습니다.");
pwCheck.focus();
pwCheck.value = "";
}
let pwCheckRegExp = /^(?=.*[a-zA-Z])(?=.*[!@#$%^*+=-])(?=.*[0-9]).{8,25}$/;
if (!pwCheckRegExp.test(pwCheck.value)) {
alert("비밀번호는 영문자+숫자+특수문자 조합으로 8~25자리 사용해야 합니다.");
pwCheck.focus();
pwCheck.value = "";
return false;
}
id="mail"인 이메일의 유효성 검사힙니다. "@"와 "."이 반드시 포함되어야 합니다.
// 이메일 검사
if (mail.value == "") {
alert("이메일을 입력하세요.");
mail.focus();
return false;
}
let emailRegExp = /^[A-Za-z0-9_]+[A-Za-z0-9]*[@]{1}[A-Za-z0-9]+[A-Za-z0-9]*[.]{1}[A-Za-z]{1,3}$/;
if (!emailRegExp.test(mail.value)) {
alert("이메일 형식이 올바르지 않습니다.");
mail.focus();
mail.value = "";
return false;
}
id="userName"인 이름의 유효성 검사입니다. 한글만 입력할 수 있습니다.
// 이름 검사
if (userName.value == "") {
alert("이름을 입력하세요.");
userName.focus();
return false;
}
let userNameRegExp = /^[가-힣]{2,4}$/;
if (!userNameRegExp.test(userName.value)) {
alert("이름이 올바르지 않습니다. (2~4글자 한글만 입력)");
userName.focus();
userName.value = "";
return false;
}
id="zipCode"인 집주소 유효성 검사입니다.
// 집주소 검사
if (zipCode.value == "") {
alert("집주소를 입력하세요.");
zipCode.focus();
return false;
}
if (address3.value == "") {
alert("상세주소를 입력하세요.");
zipCode.focus();
return false;
}
let address3RegExp = /[가-힣0-9].{2,25}/;
if (!address3RegExp.test(address3.value)) {
alert("상세주소를 다시 입력해주세요 (ex. A동 101호)");
address3.focus();
address3.value = "";
return false;
}
집주소의 경우 "우편번호 검색" 버튼을 눌렀을 때 execDaumPostcode() 함수가 실행됩니다.
버튼을 누르면 우편번호를 검색할 수 있는 팝업창이 뜨고 주소를 선택했을 때, 선택한 우편번호(data.zonecode)를 getElementById("")로 해당 id 값을 불러와 데이터를 각각의 폼 양식에 자동으로 삽입하게 합니다.
const execDaumPostcode = () => {
new daum.Postcode({
oncomplete: function (data) {
// 우편번호(data.zonecode)를 ID가 zip-code인 양식에 삽입
document.getElementById("zipCode").value = data.zonecode;
document.getElementById("address1").value = data.address;
document.getElementById("address2").value = data.jibunAddress;
document.getElementById("address3").focus();
},
}).open();
};
또한, 초기화 버튼을 눌렀을 때는 우편번호의 모든 값이 null이 되면서 우편번호를 다시 검색할 수 있도록 합니다.
const execDaumPostcodeReset = () => {
document.getElementById("zipCode").value = null;
document.getElementById("address1").value = null;
document.getElementById("address2").value = null;
document.getElementById("address3").value = null;
};
주민등록번호는 앞자리와 뒷자리를 각각 id="num1", id="num2"로 나누어 계산식을 통해 유효성을 검사합니다
// 주민등록번호 검사
if (unm1.value == "" || unm2.value == "") {
alert("주민번호를 입력하세요.");
unm1.focus();
return false;
}
let num1 = document.getElementById("unm1");
let num2 = document.getElementById("unm2");
let arrNum1 = new Array();
let arrNum2 = new Array();
for (let i = 0; i < num1.value.length; i++) {
arrNum1[i] = num1.value.charAt(i);
}
for (let i = 0; i < num2.value.length; i++) {
arrNum2[i] = num2.value.charAt(i);
}
let tempSum = 0;
for (let i = 0; i < num1.value.length; i++) {
tempSum += arrNum1[i] * (2 + i);
} // 주민번호 검사방법을 적용하여 앞 번호를 모두 계산하여 더함
for (let i = 0; i < num2.value.length - 1; i++) {
if (i >= 2) {
tempSum += arrNum2[i] * i;
} else {
tempSum += arrNum2[i] * (8 + i);
}
} // 같은방식으로 앞 번호 계산한것의 합에 뒷번호 계산한것을 모두 더함
if ((11 - (tempSum % 11)) % 10 != arrNum2[6]) {
alert("올바른 주민번호가 아닙니다.");
num1.value = "";
num2.value = "";
num1.focus();
return false;
}
생년월일 유혀성 검사입니다.
(올바른) 주민등록번호를 기반으로, 년/월/일 정보를 추출하여 각각의 필드에 값을 자동으로 삽입합니다.
// 생일
let birthYear = arrNum1[0] === "1" || arrNum1[0] === "2" ? "19" : "20";
// 년도 정보를 완성하고 year 필드에 설정
year.value = birthYear + arrNum1[0] + arrNum1[1];
// 월, 일 정보를 추출하여 각각의 필드에 설정
let birthMonth = parseInt(arrNum1[2] + arrNum1[3]);
let birthDay = parseInt(arrNum1[4] + arrNum1[5]);
document.getElementById("Month").value = birthMonth;
document.getElementById("Day").value = birthDay;
관심분야 유효성 검사입니다.
체크박스의 갯수만큼 반복문을 돌고 체크된 게 없으면 경고창이 뜨고, 체크된 값을 찾으면 true를 반환하며 반복문을 빠져나갑니다.
// 관심분야
let checkboxes = document.getElementsByName("01");
let isChecked = false;
for (let i = 0; i < checkboxes.length; i++) {
if (checkboxes[i].checked) {
isChecked = true;
break;
}
}
if (!isChecked) {
alert("관심분야는 최소 1개 이상 선택해야 합니다.");
return false;
}
id="intro"인 자기소개 유효성 검사입니다.
사용자가 입력한 글자수가 조건을 만족하지 않으면 경고창이 뜹니다. (200자 이상, 1000자 이하)
// 자기소개
let intro = document.getElementById("my_intro");
if (intro.value == "") {
alert("자기소개를 입력하세요. (200자 이상, 1000자 이하)");
intro.focus();
return false;
} else if (!(intro.value.length >= 200 && intro.value.length <= 1000)) {
alert("자기소개는 200자 이상 1000자 이하로 입력해야 합니다.");
my_intro.focus();
return false;
}
마지막으로 모든 유효성 검사를 통과했을 때 "가입하기" 버튼을 눌렀을 때 회원가입이 완료되었다는 alert 창이 뜨면서 메일창으로 넘어가는 코드입니다.
alert("회원가입이 완료되었습니다!");
// ${mail.value}는 수신자의 이메일 주소, subject 파라미터는 이메일의 제목, 그리고 body 파라미터는 이메일 본문
window.location.href = `mailto:${mail.value}?subject=회원가입 완료&body=회원가입을 환영합니다.`;
return true;
?= 기호는?
- 정규표현식에서 사용하는 전방탐색(lookahead)입니다.
- 전방탐색은 특정 패턴이 나중에 따라오는 다른 패턴 앞에 있는지 확인하는 데 사용됩니다.
?= : 긍정적인 전방탐색 (Positive Lookahead)
이전의 주어진 패턴이 후속 패턴 앞에 있어야 일치한다는 것을 의미합니다.
?! : 부정적인 전방탐색 (Negative Lookahead)
이전의 주어진 패턴이 후속 패턴 앞에 없어야 일치한다는 것을 의미합니다.
".", "*" 기호는?
- "."은 임의의 단일 문자를 나타내며, "*"은 직전 요소가 0번 이상 반복되는 것을 의미합니다.
- 즉 *의 경우 있어도 되고 없어도 된다는 의미가 됩니다.
예를들어 (?=.*[a-zA-Z])라고 쓰면 "한 개 이상의 임의 문자가 뒤따르며, 그 중 하나 이상이 a-z 또는 A-Z인 문자를 포함해야 함"을 의미합니다. 즉, 해당 정규표현식은 비밀번호 등에서 영문자가 최소 한 개 포함되어야 한다는 조건을 체크할 때 사용됩니다.
아래는 전체 html, JavaScript 코드입니다.
<!-- <input> 태그의 required 속성: 폼 데이터(form data)가 서버로 제출되기 전 반드시 채워져 있어야 하는 입력 필드를 명시 -->
<!DOCTYPE html>
<html>
<head>
<title>유효성 검사 예제</title>
</head>
<body>
<form>
<table
width="720"
height="120"
border="1"
align="center"
bordercolor="#CFEFFC"
>
<tr>
<td colspan="2" align="center" bgcolor="#CFEFFC">
<b>회원 기본 정보</b>
</td>
</tr>
<tr align="center">
<td width="150px" bgcolor="#E8F7FD">
<b>아이디</b>
</td>
<td align="left">
<input type="text" id="uid" size="10" maxlength="15" required />
4~12자의 영문 대소문자와 숫자로만 입력
</td>
</tr>
<tr align="center">
<td bgcolor="#E8F7FD">
<b>비밀번호</b>
</td>
<td align="left">
<input type="password" id="pw" size="10" maxlength="15" required />
8~25자의 영문자+숫자+특수문자 조합
</td>
</tr>
<tr align="center">
<td bgcolor="#E8F7FD">
<b>비밀번호 확인</b>
</td>
<td align="left">
<input
type="password"
id="pwCheck"
size="10"
maxlength="15"
required
/>
</td>
</tr>
<tr align="center">
<td bgcolor="#E8F7FD">
<b>메일주소</b>
</td>
<td align="left">
<input type="text" id="mail" size="15" required />
예) id@domain.com
</td>
</tr>
<tr align="center">
<td bgcolor="#E8F7FD">
<b>이름</b>
</td>
<td align="left">
<input
type="text"
id="userName"
size="15"
maxlength="10"
required
/>
</td>
</tr>
<tr align="center">
<td bgcolor="#E8F7FD">
<b>집주소</b>
</td>
<td align="left">
<input
type="text"
id="zipCode"
placeholder="우편번호"
readonly
required
/>
<input
type="button"
onclick="execDaumPostcode()"
value="우편번호 찾기"
/>
<input
type="button"
onclick="execDaumPostcodeReset()"
value="초기화"
/><br />
<input
type="test"
id="address1"
placeholder="도로명주소"
size="40"
readonly
required
/><br />
<input
type="text"
id="address2"
placeholder="지번주소"
size="40"
readonly
/><br />
<input
type="text"
id="address3"
placeholder="상세주소"
size="40"
required
/>
<!-- 다음에서 제공하는 우편번호 검색 제공 서비스 -->
<script src="//t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js"></script>
</td>
</tr>
<tr>
<td colspan="2" align="center" bgcolor="#CFEFFC">
<b>개인 신상 정보</b>
</td>
</tr>
<tr>
<td align="center" bgcolor="#E8F7FD"><b>주민등록번호</b></td>
<td>
<input
type="password"
id="unm1"
size="10"
maxlength="6"
placeholder="6자리"
required
/>
-
<input
type="password"
id="unm2"
size="10"
maxlength="7"
placeholder="7자리"
required
/>
</td>
</tr>
<tr align="center">
<td bgcolor="#E8F7FD">
<b>생일</b>
</td>
<td align="left">
<input
type="text"
name="bir"
id="year"
size="5"
maxlength="4"
required
readonly
/>년
<select name="bir" id="Month">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
<option value="7">7</option>
<option value="8">8</option>
<option value="9">9</option>
<option value="10">10</option>
<option value="11">11</option>
<option value="12">12</option>
</select>
월
<select name="bir" id="Day">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
<option value="7">7</option>
<option value="8">8</option>
<option value="9">9</option>
<option value="10">10</option>
<option value="11">11</option>
<option value="12">12</option>
<option value="13">13</option>
<option value="14">14</option>
<option value="15">15</option>
<option value="15">16</option>
<option value="17">17</option>
<option value="18">18</option>
<option value="19">19</option>
<option value="20">20</option>
<option value="21">21</option>
<option value="22">22</option>
<option value="23">23</option>
<option value="24">24</option>
<option value="25">25</option>
<option value="26">26</option>
<option value="27">27</option>
<option value="28">28</option>
<option value="29">29</option>
<option value="30">30</option>
<option value="31">31</option>
</select>
일
</td>
</tr>
<tr align="center">
<td bgcolor="#E8F7FD">
<b>관심분야</b>
</td>
<td align="left" id="hobby" required>
<input type="checkbox" name="01" value="컴퓨터" />컴퓨터
<input type="checkbox" name="01" value="인터넷" />인터넷
<input type="checkbox" name="01" value="여행" />여행
<input type="checkbox" name="01" value="영화감상" />영화감상
<input type="checkbox" name="01" value="음악감상" />음악감상
</td>
</tr>
<tr align="center">
<td bgcolor="#E8F7FD">
<b>자기소개</b>
</td>
<td align="left">
<textarea
id="my_intro"
cols="85"
rows="8"
maxlength="1000"
required
></textarea>
<div id="text_count"></div>
</td>
</tr>
</table>
<table width="720" height="50" border="0" align="center">
<tr>
<td align="right">
<input
align="center"
type="button"
onclick="userCheck()"
value="가입하기"
/>
</td>
<td align="left"><input type="reset" value="다시 입력" /></td>
</tr>
</table>
</form>
<script src="test.js"></script>
</body>
</html>
const userCheck = () => {
// 아이디 검사
if (uid.value == "") {
alert("아이디를 입력하세요.");
uid.focus();
return false;
}
let uidRegExp = /^[a-zA-z0-9]{4,12}$/;
if (!uidRegExp.test(uid.value)) {
alert("아이디는 영문 대소문자와 숫자 4~12자리로 입력해야 합니다!");
uid.focus();
uid.value = "";
return false;
}
// 비밀번호 검사
if (pw.value == "") {
alert("비밀번호를 입력하세요.");
pw.focus();
return false;
}
if (uid.value == pw.value) {
alert("비밀번호는 아이디와 다르게 입력해야 합니다.");
pw.focus();
pw.value = "";
return false;
}
let pwRegExp = /^(?=.*[a-zA-Z])(?=.*[!@#$%^*+=-])(?=.*[0-9]).{8,25}$/;
if (!pwRegExp.test(pw.value)) {
alert("비밀번호는 영문자+숫자+특수문자 조합으로 8~25자리 사용해야 합니다.");
pw.focus();
pw.value = "";
return false;
}
// 비밀번호 확인 검사
if (pwCheck.value == "") {
alert("비밀번호 확인을 입력하세요.");
pwCheck.focus();
return false;
}
if (pwCheck.value != pw.value) {
alert("비밀번호가 일치하지 않습니다.");
pwCheck.focus();
pwCheck.value = "";
}
let pwCheckRegExp = /^(?=.*[a-zA-Z])(?=.*[!@#$%^*+=-])(?=.*[0-9]).{8,25}$/;
if (!pwCheckRegExp.test(pwCheck.value)) {
alert("비밀번호는 영문자+숫자+특수문자 조합으로 8~25자리 사용해야 합니다.");
pwCheck.focus();
pwCheck.value = "";
return false;
}
// 이메일 검사
if (mail.value == "") {
alert("이메일을 입력하세요.");
mail.focus();
return false;
}
let emailRegExp =
/^[A-Za-z0-9_]+[A-Za-z0-9]*[@]{1}[A-Za-z0-9]+[A-Za-z0-9]*[.]{1}[A-Za-z]{1,3}$/;
if (!emailRegExp.test(mail.value)) {
alert("이메일 형식이 올바르지 않습니다.");
mail.focus();
mail.value = "";
return false;
}
// 이름 검사
if (userName.value == "") {
alert("이름을 입력하세요.");
userName.focus();
return false;
}
let userNameRegExp = /^[가-힣]{2,4}$/;
if (!userNameRegExp.test(userName.value)) {
alert("이름이 올바르지 않습니다. (2~4글자)");
userName.focus();
userName.value = "";
return false;
}
// 집주소 검사
if (zipCode.value == "") {
alert("집주소를 입력하세요.");
zipCode.focus();
return false;
}
if (address3.value == "") {
alert("상세주소를 입력하세요.");
zipCode.focus();
return false;
}
let address3RegExp = /[가-힣0-9].{2,25}/;
if (!address3RegExp.test(address3.value)) {
alert("상세주소를 다시 입력해주세요 (ex. A동 101호)");
address3.focus();
address3.value = "";
return false;
}
// 주민등록번호 검사
if (unm1.value == "" || unm2.value == "") {
alert("주민번호를 입력하세요.");
unm1.focus();
return false;
}
let num1 = document.getElementById("unm1");
let num2 = document.getElementById("unm2");
let arrNum1 = new Array();
let arrNum2 = new Array();
for (let i = 0; i < num1.value.length; i++) {
arrNum1[i] = num1.value.charAt(i);
}
for (let i = 0; i < num2.value.length; i++) {
arrNum2[i] = num2.value.charAt(i);
}
let tempSum = 0;
for (let i = 0; i < num1.value.length; i++) {
tempSum += arrNum1[i] * (2 + i);
} // 주민번호 검사방법을 적용하여 앞 번호를 모두 계산하여 더함
for (let i = 0; i < num2.value.length - 1; i++) {
if (i >= 2) {
tempSum += arrNum2[i] * i;
} else {
tempSum += arrNum2[i] * (8 + i);
}
} // 같은방식으로 앞 번호 계산한것의 합에 뒷번호 계산한것을 모두 더함
if ((11 - (tempSum % 11)) % 10 != arrNum2[6]) {
alert("올바른 주민번호가 아닙니다.");
num1.value = "";
num2.value = "";
num1.focus();
return false;
}
// 생일
let birthYear = arrNum1[0] === "1" || arrNum1[0] === "2" ? "19" : "20";
// 년도 정보를 완성하고 year 필드에 설정
year.value = birthYear + arrNum1[0] + arrNum1[1];
// 월, 일 정보를 추출하여 각각의 필드에 설정
let birthMonth = parseInt(arrNum1[2] + arrNum1[3]);
let birthDay = parseInt(arrNum1[4] + arrNum1[5]);
document.getElementById("Month").value = birthMonth;
document.getElementById("Day").value = birthDay;
// 관심분야
let checkboxes = document.getElementsByName("01");
let isChecked = false;
for (let i = 0; i < checkboxes.length; i++) {
if (checkboxes[i].checked) {
isChecked = true;
break;
}
}
if (!isChecked) {
alert("관심분야는 최소 1개 이상 선택해야 합니다.");
return false;
}
// 자기소개
let intro = document.getElementById("my_intro");
if (intro.value == "") {
alert("자기소개를 입력하세요. (200자 이상, 1000자 이하)");
intro.focus();
return false;
} else if (!(intro.value.length >= 200 && intro.value.length <= 1000)) {
alert("자기소개는 200자 이상 1000자 이하로 입력해야 합니다.");
my_intro.focus();
return false;
}
alert("회원가입이 완료되었습니다!");
// ${mail.value}는 수신자의 이메일 주소, subject 파라미터는 이메일의 제목, 그리고 body 파라미터는 이메일 본문
window.location.href = `mailto:${mail.value}?subject=회원가입 완료&body=회원가입을 환영합니다.`;
return true;
// document.getElementById("frm").submit();
};
const execDaumPostcode = () => {
new daum.Postcode({
oncomplete: function (data) {
// 우편번호(data.zonecode)를 ID가 zip-code인 양식에 삽입
document.getElementById("zipCode").value = data.zonecode;
document.getElementById("address1").value = data.address;
document.getElementById("address2").value = data.jibunAddress;
document.getElementById("address3").focus();
},
}).open();
};
const execDaumPostcodeReset = () => {
document.getElementById("zipCode").value = null;
document.getElementById("address1").value = null;
document.getElementById("address2").value = null;
document.getElementById("address3").value = null;
};
'2023-02 몰입형 SW 정규 교육' 카테고리의 다른 글
웹 프로그래밍 개념 (Web Programming) (0) | 2023.09.19 |
---|---|
[JavaScript] 회원가입 예제 (정규표현식 미사용) (0) | 2023.09.19 |
[JavaScript] 정규표현식 (0) | 2023.09.18 |
[JavaScript] ver, let, const 차이점 (0) | 2023.09.18 |
JavaScript (2) (0) | 2023.09.15 |