WWW가 세상에 처음 나온 이후로, 개발자들은 다양한 최신 기술을 적용하여 사용자의 관심을 끌고 훨씬 더 쉽게 웹 사이트를 사용할 수 있도록 하기 위해 웹 사이트에 새로운 기능과 위젯을 도입하려는 노력을 멈추지 않고 있다. 요즘의 일반 웹 사용자는 인터넷 상에서 다른 활동 중에서도 특히 쇼핑, 정보 검색, 포럼 및 커뮤니티 등록, 온라인 게임, 다른 사용자와의 통신에 많은 비중을 두고 있다. 이런 활동 중 대부분의 경우 사용자는 일반적으로 웹 사이트에 등록한 후 로그인할 필요가 있다. 따라서 이런 기본적인 기능을 구현할 때 더욱 각별한 주의가 요구된다. 즉, 사용자 입장에서 간단하고 신속하고 안전해야 하는 것이다. 개발자의 관점에서는 새로운 기술 덕분에 이런 기능을 더욱 쉽게 구현할 수 있다.
이 기사에서는 이런 기술들을 몇 가지 이용하여 웹 사이트에 매우 간단하게 로그인할 수 있는 기능을 구현하는 방법을 배울 수 있다.
구현 프로세스를 다음 4가지 섹션으로 나누었다.
- SQL 섹션에서는 데이터베이스에 사용자 정보를 저장하기 위한 테이블을 작성하는 방법을 정의한다.
- HTML 섹션에서는 로그인 양식뿐 아니라 CSS 및 JavaScript 참조를 구현한다.
- JavaScript 섹션에서는 jQuery와 JSON을 사용하여 Ajax 파트를 다룬다.
- 마지막으로, Perl 섹션에서는 사용자 입력과 데이터베이스 간의 상호작용에 대해 설명한다.
지시사항 섹션에서는 모든 것을 함께 조합하는 방법에 대한 단계별 지시사항을 확인할 수 있다. 이 기사에서는 독자가 앞서 거론한 기술들에 이미 익숙하고 웹 개발 경험이 있는 것으로 가정하고 설명한다. 이런 기술에 충분한 경험이 있다면 지시사항 섹션으로 바로 건너뛰어도 된다.
본 기사에 포함되지 않은 내용
예를 들어, 사용자가 입력한 데이터가 가능한 데이터인지 여부와 시스템 위협 검사 등과 같은 보안 문제는 본 기사의 주제가 아님을 분명히 밝혀둔다. 그래서 사용자의 비밀번호를 저장하기 위한 암호화 방법도 본 기사의 범위를 벗어나는 내용이다. 하지만, 반드시 시스템에서 발생 가능한 취약점에 대해 알아보고 이런 기능을 코드에 빌드해야 할 것이다.
매우 간단한 로그인 기술에 대해 다루는 것이므로, users 테이블은 고유 id,
고유 username 및 password의 3개 필드만으로 구성된다.
리스트 1. SQL 코드
CREATE TABLE `mydb`.`users` ( `id` INT NOT NULL AUTO_INCREMENT , `username` VARCHAR(45) NOT NULL , `password` VARCHAR(45) NOT NULL , PRIMARY KEY (`id`) , UNIQUE INDEX `id_UNIQUE` (`id` ASC) , UNIQUE INDEX `username_UNIQUE` (`username` ASC) ); COMMIT; |
실제 데이터 삽입과 고유성에 대한 유효성 검증은 등록 기능에서 맡게 되므로, 이 두 가지는 본 기사에서 다루지 않는다. users
테이블이 로그인 구현에서 요구될 적절한 사용자 정보로 이미 채워져 있다고 가정한다.
그러나 테스트 목적으로, 다음 코드를 사용하여 users 테이블에 2개의 간단한 항목을 삽입할 수 있다.
INSERT INTO `mydb`.`users` (`id`, `username`, `password`) VALUES(1, 'username1', 'password1'); INSERT INTO `mydb`.`users` (`id`, `username`, `password`) VALUES(2, 'username2', 'password2'); COMMIT; |
MySQL을 사용하지 않을 경우에는 데이터베이스의 SQL 구문이 MySQL 구문과 약간 다를 수 있다.
HTML 파일에는 로그인 양식은 물론이고, CSS 및 JavaScript 파일 참조도 포함된다.
간소화를 위해, 본 기사에서는 로그인 양식의 디자인보다는 거론된 기술을 구현하는 데 더 중점을 두므로, 크로스 브라우저 호환성도 보장하는 Blueprint CSS
프레임워크라는 수정된 MIT License로 이미 사용 가능한 소스를 사용한다. 물론, 인터넷 상에는 다양한 설계에 사용할 수 있는 훨씬 더 많은 무료 CSS 구현도
공개되어 있다.
Blueprint 프레임워크에 대한 자세한 정보는 참고자료를 참조한다.
<link rel="stylesheet" type="text/css" media="screen, projection" href="http://www.blueprintcss.org/blueprint/screen.css" /> <link rel="stylesheet" type="text/css" media="screen, projection" href="http://www.blueprintcss.org/blueprint/plugins/buttons/screen.css" /> <link rel="stylesheet" type="text/css" media="print" href="http://www.blueprintcss.org/blueprint/print.css" /> <!--[if IE]><link rel="stylesheet" type="text/css" media="screen, projection" href="http://www.blueprintcss.org/blueprint/ie.css"><![endif]--> |
jQuery 라이브러리 외에, 기사 후반부에 설명할 JavaScript 파일에 대한 참조도 있다(login.js).
<script type="text/javascript" src="http://code.jquery.com/jquery-1.4.4.min.js"></script> <script type="text/javascript" src="login.js"></script> |
본 기사를 게시하는 시점에서는 jQuery 버전이 1.4.4였으며, 최신 버전을 선택하려면 참고자료를 참조한다.
로그인 양식은 username 텍스트 필드, password 필드 및
submit 단추로 구성된다.
<form id="loginForm" name="loginForm" method="post" action="">
<fieldset>
<legend>Enter information</legend>
<p>
<label for="username">Username</label>
<br />
<input type="text" id="username" name="username" class="text" size="20" />
</p>
<p>
<label for="password">Password</label>
<br />
<input type="password" id="password" name="password" class="text" size="20" />
</p>
<p>
<button type="submit" class="button positive">
<img alt="ok" src=
"http://www.blueprintcss.org/blueprint/plugins/buttons/icons/tick.png" />
Login
</button>
</p>
</fieldset>
</form>
|
입력 데이터를 사람이 입력하거나 컴퓨터에서 생성되는 경우 그 출처를 테스트하기 위해 Captcha를 사용할 수 있다.
Captcha에 대한 자세한 정보는 참고자료를 참조한다.
그림 1. 로그인 양식의 화면 캡처
로그인 조치의 결과는 로드 시간에는 숨겨져 있다가 이후에 JavaScript에 의해 채워지는 div 태그에 표시된다.
<div id="loginResult" style="display:none;"> </div> |
그림 2. JavaScript에 의해 리턴되는 오류 메시지의 화면 캡처
그림 3. Perl 스크립트에 의해 리턴되는 오류 메시지의 화면 캡처
그림 4. Perl 스크립트에 의해 리턴되는 성공 메시지의 화면 캡처
리스트 2. login.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Very simple login using Perl, jQuery, Ajax, JSON and MySQL</title>
<link rel="stylesheet" type="text/css" media="screen, projection"
href="http://www.blueprintcss.org/blueprint/screen.css" />
<link rel="stylesheet" type="text/css" media="screen, projection"
href="http://www.blueprintcss.org/blueprint/plugins/buttons/screen.css" />
<link rel="stylesheet" type="text/css" media="print"
href="http://www.blueprintcss.org/blueprint/print.css" />
<!--[if IE]><link rel="stylesheet" type="text/css" media="screen, projection"
href="http://www.blueprintcss.org/blueprint/ie.css"><![endif]-->
<script type="text/javascript"
src="http://code.jquery.com/jquery-1.4.4.min.js"></script>
<script type="text/javascript" src="login.js"></script>
<style type="text/css">
#loginContent { width: 350px; margin: 100px auto; }
button[type] { margin: 0.5em 0; }
</style>
</head>
<body>
<div id="loginContent" class="container">
<div id="loginResult" style="display:none;">
</div>
<form id="loginForm" name="loginForm" method="post" action="">
<fieldset>
<legend>Enter information</legend>
<p>
<label for="username">Username</label>
<br />
<input type="text" id="username" name="username" class="text" size="20" />
</p>
<p>
<label for="password">Password</label>
<br />
<input type="password" id="password" name="password" class="text" size="20" />
</p>
<p>
<button type="submit" class="button positive">
<img alt="ok" src=
"http://www.blueprintcss.org/blueprint/plugins/buttons/icons/tick.png" />
Login
</button>
</p>
</fieldset>
</form>
</div>
</body>
</html>
|
CSS가 주 관심사는 아니지만, HTML 코드에서 양식과 단추의 모양을 좀 더 괜찮아 보이게 하기 위한 두 가지 추가 인라인 정의가 있다.
HTML 코드와 추가적인 CSS 정의는 W3C Validation Services에 의해 유효성 검증되었다.
W3C Validation Services에 대한 자세한 정보는 참고자료를 참조한다.
사용자가 로그인 양식을 제출하면 jQuery 라이브러리에서 입력 데이터를 읽는다. 빈 필드가 제출되었는지 확인하기 위한 매우 간단한 테스트를 통과한 후,
Perl 스크립트에 대해 Ajax 호출이 이루어진다(login.pl). Ajax 호출의 핵심은
dataType 매개변수를 json으로 설정하는 것이다.
스크립트 호출의 결과에 따라, error 또는 success 함수가 호출된다. 이 코드 샘플에서는
두 함수가 모두 loginResult ID를 포함한 div 태그에 결과를 쓴다.
실패한 경우 샘플 코드에는 jQuery에서 제공하는 XMLHttpRequest.responseText, textStatus 및
errorThrown이 표시된다.
스크립트 호출에 성공한 경우에는 그 응답을 테스트한다. 응답 변수 data에 오류 메시지가 포함되지 않은 경우에는 성공 메시지
외에도 Perl 스크립트에 의해 users 테이블에서 검색되는 사용자의 id가 표시된다. 성공한 경우
코드 샘플은 양식 자체를 숨기고 div 태그만 표시한다.
리스트 3. login.js
$(document).ready(function(){
$("form#loginForm").submit(function() { // loginForm is submitted
var username = $('#username').attr('value'); // get username
var password = $('#password').attr('value'); // get password
if (username && password) { // values are not empty
$.ajax({
type: "GET",
url: "/cgi-bin/login.pl", // URL of the Perl script
contentType: "application/json; charset=utf-8",
dataType: "json",
// send username and password as parameters to the Perl script
data: "username=" + username + "&password=" + password,
// script call was *not* successful
error: function(XMLHttpRequest, textStatus, errorThrown) {
$('div#loginResult').text("responseText: " + XMLHttpRequest.responseText
+ ", textStatus: " + textStatus
+ ", errorThrown: " + errorThrown);
$('div#loginResult').addClass("error");
}, // error
// script call was successful
// data contains the JSON values returned by the Perl script
success: function(data){
if (data.error) { // script returned error
$('div#loginResult').text("data.error: " + data.error);
$('div#loginResult').addClass("error");
} // if
else { // login was successful
$('form#loginForm').hide();
$('div#loginResult').text("data.success: " + data.success
+ ", data.userid: " + data.userid);
$('div#loginResult').addClass("success");
} //else
} // success
}); // ajax
} // if
else {
$('div#loginResult').text("enter username and password");
$('div#loginResult').addClass("error");
} // else
$('div#loginResult').fadeIn();
return false;
});
});
|
매우 간단한 구현의 경우 Perl에서는 CGI, DBI 및 DBD::mysql의 3가지 모듈만 필요하다. CGI 모듈의 도움을 받아, Perl 스크립트는 Ajax에서 보낸
username 및 password 값을 가져온다. 그런 다음, 스크립트는 데이터베이스에 연결하여 주어진 값에
대한 사용자 id를 선택하기 위한 쿼리를 만든다. 쿼리 결과를 바탕으로, 오류 메시지 또는 성공 메시지와 사용자 ID로 JSON 응답이
빌드된다. Perl 스크립트는 컨텐츠 유형을 application/json으로 설정하고 jQuery에 의해 data 변수에
캐치되는 JSON 문자열로 Ajax에 응답한다.
리스트 4. login.pl
#!/usr/bin/perl -T
use CGI;
use DBI;
use strict;
use warnings;
# read the CGI params
my $cgi = CGI->new;
my $username = $cgi->param("username");
my $password = $cgi->param("password");
# connect to the database
my $dbh = DBI->connect("DBI:mysql:database=mydb;host=localhost;port=2009",
"mydbusername", "mydbpassword")
or die $DBI::errstr;
# check the username and password in the database
my $statement = qq{SELECT id FROM users WHERE username=? and password=?};
my $sth = $dbh->prepare($statement)
or die $dbh->errstr;
$sth->execute($username, $password)
or die $sth->errstr;
my ($userID) = $sth->fetchrow_array;
# create a JSON string according to the database result
my $json = ($userID) ?
qq{{"success" : "login is successful", "userid" : "$userID"}} :
qq{{"error" : "username or password is wrong"}};
# return JSON string
print $cgi->header(-type => "application/json", -charset => "utf-8");
print $json;
|
유효한 JSON 응답을 빌드하기 위해, JSON 유효성 검증기를 사용하고 싶을 수도 있을 것이다.
JSON 유효성 검증에 대한 자세한 정보는 참고자료를 참조한다.
생성된 JSON 문자열 표시
브라우저에서 JSON 문자열을 확인할 수 있다.
$cgi->header(-type => "application/json", -charset => "utf-8");을print $cgi->header;로 변경한다.- 브라우저에 http://your-domain-name_or_localhost/cgi-bin/login.pl?username=username1&password=password1을 입력한다.
그림 5. JSON 문자열의 화면 캡처
- SQL 코드로 users 테이블을 작성하고(
mydb를 적당한 값으로 변경), 테이블을 테스트 데이터로 채운다. - login.html 및 login.js를 복사하여 웹 서버의 htdocs 폴더에 붙여넣는다. 폴더 이름은 웹 서버의 설정에 따라 달라질 수 있다.
- login.pl을 복사하여 웹 서버의 cgi-bin 폴더에 붙여넣는다. 폴더 이름은 웹 서버의 설정에 따라 달라질 수 있다.
- 버전에 따라 Perl 스크립트의 첫 행을 변경하고(일반적으로 Unix의 경우
#!/usr/bin/perl, Windows의 경우#!\Perl\bin\perl.exe),mydb,localhost,2009,mydbusername및mydbpassword도 알맞은 값으로 변경한다. - 브라우저에 http://your-domain-name_or_localhost/login.html을 입력한다.
- 올바른 값뿐 아니라 잘못된 값도 사용자 이름과 비밀번호로 입력하여 다양한 결과를 확인한다.
중요
HTML, JavaScript 및 Perl 파일이 동일한 도메인에 호스트되어 있는지 확인한다. 그렇지 않은 경우 Ajax가 동일한 도메인에 호출 스크립트로
호스트되어 있지 않은 Perl 스크립트를 호출하려 할 때 동일 출처 정책을 위반하게 되는 것이므로, 오류가 발생한다(data 변수가
null임). 더욱이, 똑같은 이유로 웹 서버에서 HTML 파일을 사용해야 한다.
동일 출처 정책에 대한 자세한 정보는 참고자료를
참조한다.
본 기사를 쓴 취지는 로그인 서비스를 구현하기 위한 훌륭한 시작점으로 사용할 수 있도록 현대적 기술을 적용한 정말 간단한 코드를 제시하는 것이다. 이렇듯 간단한 코드에 보안을 포함한 다양한 기능을 고안하여 빌드할 수 있다. 아이디어가 떠오른 후 다른 데이터베이스 및 프로그래밍 언어를 사용하고 싶을지도 모르겠다.
-
Blueprint는 크로스 브라우저 호환성을 보장하도록 수정된 MIT 라이센스
방식의 CSS 프레임워크이다.
-
jQuery는 다른 기능 중에서도 신속한 웹 개발을 위해 Ajax 상호작용을 단순화하는 크로스 브라우저 JavaScript
라이브러리이다.
-
Ajax API in jQuery에는 jQuery에서 비동기 HTTP(Ajax) 요청을 수행하는 방법이 설명되어 있다.
- jQuery의 최신 버전을 구할 수 있다.
-
JSON은 브라우저와 서버 간의 정보 교환을 위한 경량 데이터 형식(XML에 비해 경량임)이다.
-
JSONLint는 JSON 유효성 검증기이다.
-
Captcha는 웹 사이트에서 받는 입력 데이터의 출처를 테스트하기 위한 메소드이다.
-
동일 출처 정책(Same origin policy)은 다른 도메인 상의 메소드에 액세스하지 못하게 하는
보안 개념이다.
- developerWorks의
JSONP를 사용한 도메인 간 통신, Part 1: JSONP와 jQuery의 결합으로 강력한 매시업
빠르게 만들기 기사에서 "동일 출처 정책 제한 이해"에 대한 정보를 얻을 수 있다.
- 표준 준수 코드를 구현하기 위해, W3C에서는 Markup Validation Service와
CSS Validation Service를 제공한다.
