개 요
주민등록번호의 타당성 검사는 사이트를 구축할 때 꼭 빠지지 않고 사용될 정도로 그 활용도도 높지만, 자바스크립트 프로그래밍의 예제로서도 널리 알려진 유명한 소재이다. 공개된 많은 자바스크립트 예제에서 이 내용을 다루고 있지만, 알고리즘 설명이나 매끄러운 구현 기법들에 대한 언급까지는 잘 다루어지지 않고 있다. 이 글에서는 HTML 문서의 폼에서 사용자가 입력한 주민등록번호를 검사하는 원리(알고리즘)와 구현방법, 그리고 예제에 대해서 살펴보도록 하겠다.
* [PHP 버전] 보기
검사 원리 (알고리즘)
주민등록번호는 앞자리가 6자리의 숫자로 구성되며, 태어난 날의 연도, 월, 일을 나타내는 숫자이다. 뒷자리는 일련번호로서, 7자리로 구성되며 첫번째 숫자는 성별을 나타내는 의미를 가지고 있다. 구분자인 '-' 기호를 빼면 총 13자리의 숫자로 구성되며, 정상적인 번호인지를 가려낼 수 있는 자체적인 정보를 담고 있다.
정상적인 주민등록번호인지를 판별하기 위해서는 먼저 주민등록번호 맨 뒷자리를 제외한 각 자릿수의 숫자들에 각각 지정된 숫자들을 곱해서, 이 결과들을 더해야 한다. 각 자릿수에 지정된 승수들은 다음과 같다. (아래에서 진하게 표시된 행은 주민등록번호를 나타낸다)
1 | 2 | 3 | 4 | 5 | 6 | - | 1 | 2 | 3 | 4 | 5 | 6 | 7 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
X | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 2 | 3 | 4 | 5 | ||
n1 | n2 | n3 | n4 | n5 | n6 | n7 | n8 | n9 | n10 | n11 | n12 |
각 자릿수에 지정된 승수들을 더한 값을 N이라고 하면,
N = n1 + n2 + n3 + ... + n12
N을 11로 나눈 나머지를 11에서 뺀 수가 주민등록번호 마지막 자릿수와 일치하면 정상적인 주민등록번호이다.
11 - (N % 11) = 마지막 자릿수
N의 값이 11로 나누어 떨어지거나 나머지가 1이라면 위 식의 값은 10 또는 11이 된다. 마지막 자릿수는 1자리이기 때문에 이런 경우에는 비교할 때 같지 않은 것으로 처리되기 때문에 위 식을 다시 한번 10으로 나누어 그 나머지를 취하여 마지막 자릿수와 비교해야 한다. 따라서, 위 식을 다음과 같이 수정해야 한다.
(11 - (N % 11)) % 10 = 마지막 자릿수
구 현
테스트용 입력양식
이제 테스트용 문서를 먼저 만들어보자. 폼 태그가 삽입된 HTML의 형태로서, 주민등록번호 입력란과 검사 버튼만을 준비한다. 주민등록번호는 편의상 '-' 기호를 포함하여 단일 입력란으로 받아들이는 것으로 한다.
<html> <head><title>주민등록번호 검사</title> </head> <body> <h1>주민등록번호 검사</h1> <form onSubmit="chkresno(this.resno.value);return false"> 주민등록번호 <input type="text" name="resno"> <input type="submit" value="지금 검사"> </form> </body> </html>
이 문서는 브라우저 상의 검사만을 목적으로 하므로, onSubmit() 이벤트 핸들러에서는 편의상 무조건 return false로 폼의 전송을 취소하였다. 검사 루틴은 chkresno()이며, 주민등록번호 입력값인 resno 항목의 값을 직접 매개변수로 전달하였다.
기본 입력형태의 검사
입력된 내용의 검사는 먼저 형태 검사부터 시작한다. 주민등록번호의 정해진 형태인 숫자 6자리와 '-' 기호, 그리고 뒤이은 숫자 7자리의 형태를 가져야만 한다. 이렇게 정해진 형태의 내용을 검사하는 좋은 방법으로는 정규표현식을 이용하는 방법이 있다.
function chkresno(resno) { fmt = /^\d{6}-\d{7}$/; if (!fmt.test(resno)) { alert("잘못된 주민등록번호입니다."); return; } alert("정상적인 주민등록번호입니다."); }
정규표현식은 '/' 기호로 그 시작과 끝을 구분한다. '^' 기호는 해당 기호 뒤에 나오는 표현이 제일 처음에 나와야 한다는 것을 의미하며, '$' 기호는 지정된 표현으로 종료되어야 함을 나타낸다. '\d' 지시자는 주어진 표현이 숫자임을 나타내고, {n}의 표현은 앞의 지시자 또는 문자가 주어진 개수만큼 반복되어진다는 것을 의미한다. 정규표현식에 대한 보다 자세한 내용은 매뉴얼 또는 참조 사이트의 온라인 문서들을 참고하기 바란다.
위의 정규표현식을 이용한 검사방법으로는 전체 자릿수와 형태에 대한 검사를 수행할 수 있으나, 여기서 한가지 검사를 더 하자면, 주민등록번호의 뒷부분 첫자리는 성별을 나타내는 숫자로서, 1은 남자, 2는 여자를 가리킨다. (2000년도 이후 출생자의 경우에는 3이 남자, 4가 여자의 의미를 가진다.) 따라서, 기왕 검사하는 김에 뒷부분 첫자리가 1 부터 4 까지의 숫자만 입력되어야 함을 정규표현식을 이용하여 체크하도록 다음과 같이 수정하자.
function chkresno(resno) { fmt = /^\d{6}-[1234]\d{6}$/; if (!fmt.test(resno)) { alert("잘못된 주민등록번호입니다."); return; } alert("정상적인 주민등록번호입니다."); }
입력된 날짜의 유효성 검사
주민등록번호의 앞부분은 생년월일을 나타내는 6자리의 일련의 숫자이다. 6자리의 일련의 숫자가 실제로 존재하지 않으면서도 주민등록번호의 검사루틴을 빠져나갈 수 있기 때문에 이런 경우를 점검하기 위해서는 앞부분의 숫자들이 실제로 존재하는 날짜인가의 여부를 반드시 점검해야 한다. (990229-1234567 라는 가상의 주민등록번호는 1999년 2월 29일이 존재하지 않음에도 불구하고 날짜 체크를 하지 않은 경우에는 유효한 주민등록번호라고 오인될 수 있다.)
실제로 존재하는 일자인지를 간단히 판단하기 위해서는 자바스크립트에서 기본으로 제공하는 Date() 객체의 생성자를 이용하여 주어진 날짜 정보로 Date 객체를 생성한 다음에 그 객체가 가지는 값이 원래 값과 일치하는지 비교하는 것으로 간단하게 구현할 수 있다. Date() 생성자는 잘못된 일자정보로 생성되었을 경우에는 자동으로 정상적인 일자정보로 재조정되게 되어있다.
birthYear = (resno.charAt(7) <= "2") ? "19" : "20"; birthYear += resno.substr(0, 2); birthMonth = resno.substr(2, 2) - 1; birthDate = resno.substr(4, 2); birth = new Date(birthYear, birthMonth, birthDate); if ( birth.getYear() % 100 != resno.substr(0, 2) || birth.getMonth() != birthMonth || birth.getDate() != birthDate) { alert("잘못된 주민등록번호입니다."); return; }
Checksum 코드의 계산과 비교
이제 각 자릿수별 곱셈을 하기 위해서, 먼저 주민등록번호 각 자릿수들을 하나의 배열에 담도록 하겠다. '-' 기호는 제외하고 나머지 숫자들만 길이가 13인 배열에 저장한다.
buf = new Array(13); for (i = 0; i < 6; i++) buf[i] = parseInt(resno.charAt(i)); for (i = 6; i < 13; i++) buf[i] = parseInt(resno.charAt(i + 1));
각 자릿수 별로 지정된 승수들도 마찬가지로 배열로 준비한다.
multipliers = [2,3,4,5,6,7,8,9,2,3,4,5];
이제 각 자릿수를 모두 곱하고, 이 곱한 내용들을 모두 합한 값을 구한다.
for (i = 0, sum = 0; i < 12; i++) sum += (buf[i] *= multipliers[i]);
대입문은 그 자체로서도 값을 가진다. 위 코드에서 괄호로 묶인 *= 연산자를 사용하는 문장 자체는 오른쪽의 값을 왼쪽의 변수에 대입시키는 역할도 하지만, 그 자체로서도 대입시키는 값을 가지게 된다. 이제 합한 값을 11로 나눈 나머지를 11에서 뺀 값의 마지막 자릿수가 주민등록번호의 마지막 자릿수와 일치하면 정상적인 주민등록번호라고 판단할 수 있다.
if ((11 - (sum % 11)) % 10 != buf[12]) { alert("잘못된 주민등록번호입니다."); return; }
결 론
다음은 지금까지 설명한 내용을 모두 포함하는 전체 소스코드이다.
<html> <head><title>주민등록번호 검사</title> <script language="javascript"> function chkresno(resno) { // 주민번호의 형태와 7번째 자리(성별) 유효성 검사 fmt = /^\d{6}-[1234]\d{6}$/; if (!fmt.test(resno)) { alert("잘못된 주민등록번호입니다."); return; } // 날짜 유효성 검사 birthYear = (resno.charAt(7) <= "2") ? "19" : "20"; birthYear += resno.substr(0, 2); birthMonth = resno.substr(2, 2) - 1; birthDate = resno.substr(4, 2); birth = new Date(birthYear, birthMonth, birthDate); if ( birth.getYear() % 100 != resno.substr(0, 2) || birth.getMonth() != birthMonth || birth.getDate() != birthDate) { alert("잘못된 주민등록번호입니다."); return; } // Check Sum 코드의 유효성 검사 buf = new Array(13); for (i = 0; i < 6; i++) buf[i] = parseInt(resno.charAt(i)); for (i = 6; i < 13; i++) buf[i] = parseInt(resno.charAt(i + 1)); multipliers = [2,3,4,5,6,7,8,9,2,3,4,5]; for (i = 0, sum = 0; i < 12; i++) sum += (buf[i] *= multipliers[i]); if ((11 - (sum % 11)) % 10 != buf[12]) { alert("잘못된 주민등록번호입니다."); return; } alert("정상적인 주민등록번호입니다."); } </script> </head> <body> <h1>주민등록번호 검사</h1> <form onSubmit="chkresno(this.resno.value);return false"> 주민등록번호 <input type="text" name="resno"> <input type="submit" value="지금 검사"> </form> </body> </html>
'IT_Programming > JavaScript' 카테고리의 다른 글
[스크랩] Document 객체 (0) | 2006.04.27 |
---|---|
[스크랩] 이벤트 렌더링 (0) | 2006.04.27 |
JavaScript에서 return의 의미 (0) | 2006.04.27 |
[스크랩] 베너랜덤하게나타내기 (0) | 2006.04.27 |
[스크랩] 마우스 버튼 양쪽모두 사용금지 시키기 (0) | 2006.04.27 |