UI Laboratory

UI 개발을 위한 레퍼런스

INDEX

Node.js 개요


이벤트 기반 비동기 방식

기존 웹 서버는 대부분 스레드를 기반으로 하는 동기방식으로 네트워크 입출력을 처리한다. 반면에 node.js는 이벤트를 기반으로 하는 비동기 방식으로 네트워크 입출력을 처리한다. 따라서 대규모 네트워크 프로그램을 개발하기 적합한 형태이다.

Node.js의 전역객체


전역변수

문자열 자료형의 전역변수

변수 이름 설명
__filename 현재 실행중인 코드의 파일 경로를 나타낸다.
__dirname 현재 실행중인 코드의 폴더 경로를 나타낸다.

전역객체

변수 이름 설명
console 콘솔화면과 관련된 기능을 다루는 객체이다.
exports 모듈과 관련된 기능을 다루는 객체이다.
process 프로그램과 관련된 기능을 다루는 객체이다.

console 객체

console 객체의 메서드

변수 이름 설명
log() 화면에 출력한다.
time(label) 시간 측정을 시작한다.
timeEnd(label) 시간 측정을 종료한다.

exports 객체와 모듈

Node.js는 모듈을 사용하여 기능을 확장한다. 모듈은 기능을 쉽게 사용하고자 메서드와 속성을 미리 정의해 모아 놓은 것이다.

모듈을 생성하려면 별도로 자바스크립트 파일을 생성해야 한다.

예를 들면 module.js 파일이 생성할 모듈 파일이고 main.js 파일은 생성한 모듈을 불러올 파일이라고 가정하자.

module.js

			//절대값을 구하는 메서드
			exports.abs = function(number){
				 if(0 < number){
					  return number;
				 }else {
					  return -number;
				 }
			};

			//원의 넓이를 구하는 메서드
			exports.circleArea = function(radius){
				 return radius * radius * Math.PI;
			};
		

모듈을 생성할 때는 exports 객체를 사용한다. exports 객체에 속성이나 메서드를 지정한다.

main.js

			//모듈을 추출한다.
			var module = require('./module.js');

			//모듈을 사용한다.
			console.log('abs(-273) = %d', module.abs(-273));
			console.log('circleArea(3) = %d', module.circleArea(3));
		

생성한 모듈을 다른 자바스크립트 파일에서 추출할 때는 require() 함수를 사용한다.

기본 내장 모듈


url 모듈

url 모듈의 메서드

메서드명 설명
url.parse(urlStr, [parseQueryString], [slashesDenoteHost]) URL 문자열을 URL 객체로 변환해 리턴한다.
url.format(urlObj) URL 객체를 URL 문자열로 변환해 리턴한다.
url.resolve(from, to) 매개변수를 조합하여 완전한 URL 문자열을 생성해 리턴한다.
			// 모듈을 추출한다.
			var url = require('url');

			// 모듈을 사용한다.
			var parsedObject = url.parse('http://www.hanb.co.kr/trackback/978-89-7914-874-9');
			console.log(parsedObject);

		

parse() 메서드의 두번째 매개변수에 true를 전달하면 query 속성이 객체로 출력되어 쿼리 문자열을 쿼리 객체로 변환해 얻을 수 있다.

			// 모듈을 추출한다.
			var url = require('url');

			// 모듈을 사용한다.
			var parsedObject = url.parse('http://www.hanb.co.kr/book/look.html?isbn=978-89-7914-874-9', true);
			console.log(parsedObject.query);
		

결과 : {isbn : '978-89-7914-874-9'}

util 모듈

util 모듈의 메서드

메서드명 설명
format() 매개변수로 입력한 문자열을 조합해 리턴한다.

format() 메서드는 console.log() 메서드와 비슷하지만 차이점이 있다면 출력하지 않고 문자열을 반환한다.

			//모듈을 추출한다.
			var util = require('util');

			//모듈을 사용한다.
			var data = util.format('%d + %d = %d', 52, 273, 52+273);
			console.log(data);
		

결과 : 52 + 273 = 325

File System 모듈

File System 모듈을 다른 모듈과 함께 활용하여 웹 서버를 구현한다.

File Systme 모듈의 메서드

메서드명 설명
readFile(file, encoding, callback) 파일을 비동기적으로 읽는다.
readFileSync(file, encoding) 파일을 동기적으로 읽는다.
writeFile(file, data, encoding, callback) 파일을 비동기적으로 쓴다.
writeFileSync(file, data,) encoding 파일을 동기적으로 쓴다.

| 파일 읽기

readFileSync() 메서드

			var fs = require('fs');

			var text = fs.readFileSync('textfile.txt', 'utf8');
			console.log(text);
		

readFile() 메서드를 만나는 순간 이벤트 리스너를 등록하고 파일을 모두 읽으면 이벤트 리스너를 실행한다.

			var fs = require('fs');

			fs.readFile('textfile.txt', 'utf8', function(error, data){
				 console.log(data);
			});
		

| 파일 쓰기

			var fs = require('fs');
			var data = 'Hello World..!';

			fs.writeFile('TextFileOtherWrite.txt', data, 'utf8', function(error){
				 console.log('WRITE FILE ASYNC COMPLETE');
			});

			fs.writeFileSync('TextFileOtherWriteSync.txt', data, 'utf8');
			console.log('WRITE FILE SYNC COMPLETE');
		

코드를 실행하면 Hello World..!' 라는 내용이 쓰여진 두개의 파일 (TextFileOtherWrite.txt 와 TextFileOtherWriteSync.txt)이 생성된다.

이벤트


이벤트 연결

이벤트 연결 메서드

메서드명 설명
on(eventName, eventHandler) 이벤트를 연결한다.

다음은 process 객체에 exit 이벤트를 연결하는 방법이다.

			process.on('exit', function(){

			});
		

이벤트 연결 개수 제한

node.js는 한 이벤트에 10개가 넘는 이벤트 리스너를 연결할 경우 이를 개발자 실수로 간주하고 경고를 발생 시킨다. 많은 이벤트 리스너를 연결하고 경고 메세지를 제거하려면 아래 메서드를 사용한다.

이벤트 연결 개수 제한 메서드

메서드명 설명
setMaxListeners(limit) 이벤트 리스너 연결 개수를 조절한다.

만약 이벤트 리스너를 무한 개 연결하고 싶을 때는 setMaxListeners() 메서드의 매개변수에 0을 입력한다.

이벤트 제거

[이벤트를 제거할 때 사용하는 메서드]

메서드명 설명
removeListeners(eventName, handler) 특정 이벤트의 이벤트 리스너를 제거한다.
removeAllListeners([eventName]) 모든 이벤트 리스너를 제거한다.

[이벤트를 한번만 연결]

메서드명 설명
once(eventName, eventHandler) 이벤트 리스너를 한번만 연결한다.

이벤트 강제 발생

[이벤트 강제 발생 메서드]

메서드명 설명
emit(event, [arg1], [arg2], [...]) 이벤트를 실행한다.

emit() 메서드를 사용하여 exit 이벤트를 강제로 호출해도 프로그램이 종료되지 않는다. 이벤트를 강제로 호출하면 리스너만 실행된다. 프로그램을 종료할 때는 process 객체의 exit() 메서드를 사용한다.

			process.exit();
		

이벤트 생성

node.js에서 이벤트를 연결할 수 있는 모든 객체는 EventEmitter 객체의 상속을 받는다. process 객체도 EventEmitter 객체의 상속을 받기 때문에 이벤트를 연결할 수 있는 것이다.

EventEmitter 객체는 process 객체 안에 있는 생성자 함수로 생성할 수 있는 객체이다.

[EventEmitter 객체의 메서드]

메서드명 설명
addEventListener(eventName, eventHandler) 이벤트를 연결한다.
on(eventName, eventHandler) 이벤트를 연결한다.
setMaxListener(limit) 이벤트 연결개수를 조절한다.
removeListener(eventName, eventHandler) 특정 이벤트의 이벤트 리스너를 제거한다.
removeAllListener([eventName]) 모든 이벤트 리스너를 제거한다.
once(eventName, eventHandler) 이벤트를 한번만 연결한다.

| EventEmitter 객체 생성

			var custom = new process.EventEmitter();
		

원래 EventEmitter 객체는 events 모듈안에 들어 있었지만 편하게 사용하고자 process 객체안에도 추가되었다.

| EventEmitter 객체 생성의 예

			var custom = new process.EventEmitter();

			custom.on('tick', function(){
				 console.log('이벤트를 실행합니다.');
			});

			custom.emit('tick');
		

http 모듈


HTTP는 TCP/IP를 기반으로 하는 프로토콜이다. http모듈은 Node.js의 가장 기본적인 웹 모듈이며 HTTP 웹서버와 클라이언트를 생성하는 것과 관련된 모든 기능을 담당한다.

요청과 응답

웹서버가 하는 일은 요청과 응답의 연속이다. 요청하는 대상을 클라이언트라고 부르고 응답하는 대상을 서버라고 부른다. 클라이언트와 서버는 편지를 주고받는 것처럼 요청과 응답을 처리한다. 클라이언트가 서버로 보내는 편지를 요청 메시지라고 부르며 서버가 클라이언트로 보내는 편지를 응답 메시지라고 부른다.

server 객체

http 모듈의 createServer() 메서드를 사용하면 server 객체를 생성할 수 있다.

[server 객체의 메서드]

메서드명 설명
listen(port[, callback]) 서버를 실행한다.
close() 서버를 종료한다.

아래 코드는 서버를 실행하여 10초 후 서버를 종료하는 예이다.

			//모듈을 추출
			var http = require('http');

			//웹서버 생성
			var server = http.createServer();

			//웹서버 실행
			server.listen(52273, function(){
				 console.log('Server Running at http://127.0.0.1:52273');
			});

			//10초후 서버를 종료
			setInterval(function(){
				 server.close();
			}, 10000);
		

server 객체는 EventEmitter 객체를 기반으로 만들어졌으므로 이벤트를 연결할 수 있다.

[server 객체의 이벤트]

메서드명 설명
request 클라이언트가 요청할 때 발생하는 이벤트이다.
connection 클라이언트가 접속할 때 발생하는 이벤트이다.
close 서버가 종료될 때 발생하는 이벤트이다.
checkContinue 클라이언트가 지속적인 연결을 하고 있을 때 발생하는 이벤트이다.
upgrade 클라이언트가 HTTP 업그레이드를 요청할 때 발생하는 이벤트이다.
clientError 클라이언트에서 오류가 발생할 때 발생하는 이벤트이다.
			//모듈 추출
			var http = require('http');

			//server 객체 생성
			var server = http.createServer();

			//server 객체에 이벤트 생성
			server.on('request', function(){
				 console.log('Request On');
			});

			server.on('connection', function(){
				 console.log('Connection On');
			});

			server.on('close', function(){
				 console.log('Close On');
			});

			//서버 실행
			server.listen(52273);
		

response 객체

클라이언트에 웹 페이지를 제공하려면 응답 메세지를 작성해야 한다. 응답 메세지를 작성할 때는 response 객체를 사용한다. response 객체는 request 이벤트 리스너의 두번째 매개변수로 전달되는 객체이다.

[response 객체의 메서드]

메서드명 설명
writeHead(statusCode, object) 응답헤더를 작성한다.
end([data], [encoding]) 응답 본문을 작성한다.

writeHead() 메서드와 end() 메서드를 사용하여 응답 메시지를 작성하고 클라언트에 제공한다.

아래 코드는 간단한 응답 메시지를 작성한 예이다.

			//웹서버 생성하고 실행
			require('http').createServer(function(request, response){
				 //응답메시지
				 response.writeHead(200, {'Cotent-Type':'text/html'});
				 response.end('

Hello Web Server with Node.js

'); }).listen(52273, function(){ console.log('Server Running at http://127.0.0.1:52273'); });

| File System 모듈을 사용한 HTML 페이지 제공

File System 모듈을 사용하여 서버에 존재하는 HTML 페이지를 클라이언트에 제공할 수 있다.

			//모듈 추출
			var fs = require('fs');
			var http = require('http');

			//웹서버 생성 및 실행
			http.createServer(function(request, response){
				 //HTML 파일을 읽는다.
				 fs.readFile('javascript.oop.html', function(error, data){
					  response.writeHead(200, {'Content-Type':'text/html'});
					  response.end(data);
				 });
			}).listen(52273, function(){
				 console.log('Server Running at http://127.0.0.1:52273');
			});	
		

HTTP 웹서버를 생성하고 실행하며 File System 모듈도 추출한다. request 이벤트가 발생할 때 readFile() 메서드를 사용하여 클라이언트에 HTML 페이지를 제공한다.

| 이미지와 음악 파일 제공

이미지와 음악파일을 제공하는 웹서버를 만들어 보자. 52273번 포트는 이미지 파일을 제공하고 52274번 포트는 음악파일을 제공한다.

특정 형식 파일을 제공할 때 가장 중요한 것은 응답 헤더의 Content-Type 속성이다. Content-Type 속성은 MIMI 형식을 입력한다.

이미지 형식의 Content-Type 속성을 입력하면 이미지 파일을 제공할 수 있다.
'Content-Type' : 'image/jpeg'

음악파일 형식의 Content-Type 속성을 입력하면 음악 파일을 제공할 수 있다.
'Content-Type':'audio/mp4'

			//모듈 추출
			var fs = require('fs');
			var http = require('http');

			//52273번 포트에 서버를 생성하고 실행
			http.createServer(function(request, response){
				 //이미지 파일을 읽는다
				 fs.readFile('khy.jpg', function(error, data){
					  response.writeHead(200, {'Content-Type' : 'image/jpeg'});
					  response.end(data);
				 });
			}).listen(52273, function(){
				 console.log('Server Running at http://127.0.0.1:52273');
			});

			//52274번 포트에 서버를 생성하고 실행
			http.createServer(function(request, response){
				 fs.readFile('Stronger.mp4', function(error, data){
					  response.writeHead(200, {'Content-Type':'audio/mp4'});
					  response.end(data);
				 });
			}).listen(52274, function(){
				 console.log('Server Running at http://127.0.0.1:52274');
			});
		

코드를 실행하고 http://127.0.0.1:52273 에 접속하면 이미지를 제공받거나 다운로드할 수 있으며 http://127.0.0.1:52274 에 접속하면 음악 파일을 제공받거나 다운로드 할 수 있다.

[MIMI 형식의 예]

Content Type 설명
text/plain 기본적인 텍스트
text/html HTML 문서
text/css CSS 문서
text/xml XML 문서
image/jpeg JPG/JPEG 그림 문서
image/png PNG 그림문서
video/mpeg MPEG 비디오 파일
audio/mp3 MP3 음악 파일

| 쿠키 생성

쿠키는 키와 값이 들어 있는 작은 데이터 조각으로 이름, 값, 날짜와 경로 정보를 가지고 있다. 쿠키는 서버와 클라이언트에서 모두 저장하고 사용할 수 있다. 쿠키는 일정 기간동안 데이터를 저장할 수 있으므로 일정 기간 동안 로그인을 유지하는 페이스북과 같은 웹사이트에 사용한다.

response 객체를 사용하면 클라이언트에 쿠키를 할당할 수 있다. 쿠키를 할당할 때는 응답 헤더의 Set-Cookie 속성을 사용한다. Set-Cookie 속성에는 쿠키의 배열을 넣는다. 쿠키는 형태가 다음과 같은 문자열이다.
Name = Value; Expires = 날짜; Domain = 도메일; Path = 경로; Secure

아래 코드는 쿠키 저장 및 출력 예제이다.

			//모듈 추출
			var http = require('http');

			//서버를 생성하고 실행
			http.createServer(function(request, response){
				 //변수를 선언
				 var date = new Date();
				 date.setDate(date.getDate() + 7);

				 //쿠키를 입력
				 response.writeHead(200, {
					  'Content-Type' : 'text/html',
					  'Set-Cookie' : [
						   'breakfast =  toast; Expires = ' + date.toUTCString(),
						   'dinner = chicken'
					  ]
				 });

				 //쿠키를 출력
				 response.end('

' + request.headers.cookie + '

'); }).listen(52273, function(){ console.log('Server Running at http://127.0.0.1:52273'); });

| 페이지 강제 이동

웹페이지에 접속했을 때 서버에서 강제적으로 페이지를 이동하는 경우가 있다. 페이지를 강제로 이동할 때는 응답 헤더의 Location 속성을 사용한다. Location 속성에 이동하고자 하는 웹 페이지 주소를 입력하기만 하면 된다.

다음은 Location 속성을 사용한 페이지 강제 이동을 보여주는 예이다.

			//모듈 추출
			var http = require('http');

			//웹서버를 생성 및 실행
			http.createServer(function(reqeust, response){
				 response.writeHead(302, {'Location' : 'http://hanb.co.kr'});
				 response.end();
			}).listen(52273, function(){
				 console.log('Server Running at http://127.0.0.1:52273');
			});
		

writeHead() 메서드의 첫번째 매개변수에 200이 아닌 다른 숫자를 입력했다. writeHead() 메서드의 첫번째 매개변수를 Status Code라고 부른다. Status Code 302는 이러한 강제 페이지 이동을 구현할 때 가장 많이 사용되는 코드이다.

[HTTP Status Code]

HTTP Status Code 설명
1XX 처리중 100 Continue
2XX 성공 200 OK
3XX 리다이렉트 300 Multiple Choices
4XX 클라이언트 오류 400 Bad Request
5XX 서버 오류 500 Internal Server Error

request 객체

Server 객체의 request 이벤트가 발생할 때 이벤트 리스너의 첫번째 매개변수에 request 객체가 들어간다.

[request 객체의 속성]

속성이름 설명
method 클라이언트의 요청 방식을 나타낸다.
url 클라이언트가 요청한 URL을 나타낸다.
headers 요청 메시지 헤더를 나타낸다.
trailers 요청 메시지 트레일러를 나타낸다.
httpVersion HTTP 프로토콜 버전을 나타낸다.

| url 속성을 사용한 페이지 구분

요청 메시지의 URL에 따라 서로 다른 웹페이지를 제공할 수 있다.

			//모듈 추출
			var http = require('http');
			var fs = require('fs');
			var url = require('url');

			//서버 생성 및 실행
			http.createServer(function(request, response){
				 var pathname = url.parse(request.url).pathname;

				 //페이지를 구분
				 if(pathname == '/'){
					  //파일을 읽는다.
					  fs.readFile('http://mylko72.maru.net/jquerylab/bookList.html', function(error, data){
						   //응답한다
						   response.writeHead(200, {'Content-Type' : 'text/html'});
						   response.end(data);
					  });
				 }else if(pathname == '/JavaScript'){
					  //파일을 읽는다.
					  fs.readFile('http://mylko72.maru.net/jquerylab/javascript/javascript.oop.html?hn=1&sn=2', function(error, data){
						   //응답한다.
						   response.writeHead(200, {'Content-Type' : 'text/html'});
						   response.end(data);
					  });
				 }
			}).listen(52273, function(){
				 console.log('Server Running at http://127.0.0.1:52273');
			});
		

request 객체의 url 속성을 사용하면 페이지를 구분할 수 있다. url 모듈을 사용하여 pathname을 추출하고 조건문을 사용하여 페이지를 구분한다. 참고로 Node.js는 기본적으로 페이지를 요청할 때 대소문자를 구분하다. 대소문자를 구분하지 않게 만들고 싶다면 문자열 객체의 toUpperCase() 메서드 또는 toLowerCase() 메서드를 사용한다.

| method 속성을 사용한 페이지 구분

GET 요청인지 POST 요청인지에 따라 서로 다른 페이지를 제공하는 것도 굉장히 중요한 일이다. request객체의 method 속성을 사용하면 요청방식에 따라 페이지를 쉽게 구분할 수 있다. request 객체의 method 속성은 알파벳 대문자로 이루어진 문자열이다.

			//모듈 추출
			var http = require('http');

			//서버 생성 및 실행
			http.createServer(function(request, response){
				 if(request.method == 'GET'){
					  console.log('GET 요청입니다');
				 }else if(request.method == 'POST'){
					  console.log('POST 요청입니다.');
				 }
			}).listen(52273, function(){
				 console.log('Server Running at http://127.0.0.1:52273');
			});		
		

| GET 요청 매개변수 추출

GET 방식으로 요청을 받았을 경우 매개변수를 추출하는 방법은 다음과 같다.

			//모듈 추출
			var http = require('http');
			var url = require('url');

			//서버 생성 및 실행
			http.createServer(function(request, response){
				 //요청 매개변수를 추출
				 var query = url.parse(request.url, true).query;

				 //GET 요청 매개변수 추출
				 response.writeHead(200, {'Content-Type':'text/html'});
				 response.end('

' + JSON.stringify(query) + '

'); }).listen(52273, function(){ console.log('Server Running at http://127.0.0.1:52273'); });

코드를 실행하고 http://127.0.0.1:52273/?name=lko&region=seoul 에 접속하면 다음과 같이 JSON 형식으로 출력된다.

결과 : {"name":"lko","region":"seoul"}

| POST 요청 매개변수 추출

POST 방식은 GET 방식과 달리 데이터를 더 많이 담을 수 있고 보안 측면에서도 좋다. GET 방식은 요청하면서 데이터를 전달하지만 POST 방식은 요청한 후 데이터를 별도로 전달하기 때문이다. request 이벤트가 발생한 후 request 객체의 data 이벤트로 데이터가 전달된다. 다음과 같이 POST 요청 매개변수를 추출할 수 있다.

			//모듈 추출
			var http = require('http');

			http.createServer(function(request, response){
				 request.on('data', function(data){
					  console.log('POST Data:', data);
				 });
			}).listen(52273);
		

POST 요청으로 추출된 데이터는 쿼리 문자열 형태로 들어온다. Query String 모듈을 사용하면 분해하여 원하는 대로 사용할 수 있다.

			//모듈 추출
			var http = require('http');
			var fs = require('fs');

			//서버 생성 및 실행
			http.createServer(function(req, res){
				if(req.method == 'GET'){
					//GET 요청
					fs.readFile('HTMLPage.html', function(error, data){
						res.writeHead(200, {'Content-Type' : 'text/html'});
						res.end(data);
					});
				}else if(req.method == 'POST'){
					//POST 요청
					req.on('data', function(data){
						res.writeHead(200, {'Content-Type' : 'text/html'});
						res.end('

' + data + '

'); }); } }).listen(52273, function(){ console.log('Server Running at http://127.0.0.1:52273'); });

| 쿠키 추출

쿠키는 request 객체의 headers 속성 안 cookie 속성에서 추출할 수 있다.

아래 코드는 쿠키 생성 및 추출의 예를 보여준다.

			//모듈 추출
			var http = require('http');

			//서버 생성 및 실행
			http.createServer(function(request, response){
				 //GET COOKIE
				 var cookie = request.headers.cookie;

				 //SET COOKIE
				 response.writeHead(200, {
					  'Content-Type' : 'text/html',
					  'Set-Cookie' : ['name = mylko', 'region = Seoul']
				 });

				 response.end('

' + JSON.stringify(cookie) + '

'); }).listen(52273, function(){ console.log('Server Running at http://127.0.0.1:52273'); });

결과 : "name=mylko; region=Seoul"

request 객체의 headers 속성에 있는 cookie 속성은 문자열이다. 쿠키를 쉽게 사용하려면 문자열을 분해하여 객체의 배열로 생성한다.

쿠키 분해는 아래와 같이 한다.

			//모듈 추출
			var http = require('http');

			//서버 생성 및 실행
			http.createServer(function(request, response){
				 //쿠키를 추출하고 분해
				 var cookie = request.headers.cookie;
				 cookie = cookie.split(';').map(function(element){
					  var element = element.split('=');
					  return {
						   key : element[0],
						   value : element[1]
					  };
				 });

				 //쿠키를 생성
				 response.writeHead(200, {
					  'Content-Type' : 'text/html',
					  'Set-Cookie' : ['name = mylko', 'region = Seoul']
				 });

				 response.end('

' + JSON.stringify(cookie) + '

'); }).listen(52273, function(){ console.log('Server Running at http://127.0.0.1:52273'); });

외부 모듈


Node.js는 npm(Node Package Manager)을 기반으로 모듈을 공유한다.

외부 모듈을 설치할 때는 콘솔화면에 다음 명령어를 입력한다.

			$ npm install 모듈명
		

ejs 모듈

ejs 모듈은 다음 방법으로 설치한다.

			$ npm install ejs
		

ejs 모듈은 다음과 같이 추출한다.

			//모듈 추출
			var ejs = require('ejs');
		

ejs 모듈은 템플릿 엔진 모듈이다. 템플릿 엔진 모듈은 특정 형식의 문자열을 HTML 형식의 문자열로 변환하는 모듈을 의미한다.

| ejs 모듈의 메서드

우선 ejs 페이지를 HTML 페이지로 바꾸어 제공하는 서버를 만든다. http 모듈을 사용하여 서버를 생성하고, File System 모듈을 사용하여 ejs 파일을 읽는다.

			//모듈 추출
			var http = require('http');
			var fs = require('fs');

			//서버 생성 및 실행
			http.createServer(function(request, response){
				//ejsPage.ejs 파일을 읽는다.
				fs.readFile('EJSPage.ejs', 'utf8', function(error, data){

				});
			}).listen(52273, function(){
				console.log('Server Running at http://127.0.0.1:52273');
			});
		

ejs 페이지를 HTML 페이지로 변환할 때는 ejs 모듈의 render() 메서드를 사용한다.

ejs 모듈의 메서드
render(string[, option]) - ejs 문자열을 HTML 문자열로 변경한다.

ejs모듈을 추출하고 render() 메서드로 ejs 페이지를 HTML 페이지로 변환하여 클라이언트에 제공한다.

			//모듈 추출
			var http = require('http');
			var fs = require('fs');
			var ejs = require('ejs');

			//서버 생성 및 실행
			http.createServer(function(request, response){
				//ejsPage.ejs 파일을 읽는다.
				fs.readFile('EJSPage.ejs', 'utf8', function(error, data){
					response.writeHead(200, {'Content-Type' : 'text/html'});
					response.end(ejs.render(data));
				});
			}).listen(52273, function(){
				console.log('Server Running at http://127.0.0.1:52273');
			});
		

| ejs 파일 형식

			<% var name = 'RintIanTta'; %>
			

<%= name %>

<%= 52 * 273 %>


<% for(var i=0; i < 10; i++){ %> <h2>The Squae of <%= i %> is <%= i*i%></h2> <% } %>
ejs 파일의 특수태그
<% Code % > - 자바스크립트 코드를 입력한다.
<%= Value % > - 데이터를 출력한다.

이렇게 ejs 페이지 내부에서 변수를 생성하여 출력할 수도 있지만 일반적으로 Node.js 코드에서 데이터를 생성한 뒤 ejs 페이지로 전달해 출력한다.

| 데이터 전달

ejs 페이지에 데이터를 전달하는 방법은 간단하다. render() 메서드의 두번째 매개변수에 전달하고자 하는 데이터를 입력한다.

			//모듈 추출
			var http = require('http');
			var fs = require('fs');
			var ejs = require('ejs');

			//서버 생성 및 실행
			http.createServer(function(request, response){
				//ejsPage.ejs 파일을 읽는다.
				fs.readFile('EJSPage.ejs', 'utf8', function(error, data){
					response.writeHead(200, {'Content-Type' : 'text/html'});
					response.end(ejs.render(data, {
						name : 'RintIanTta',
						description : 'Hello ejs With Node.js...!'
					}));
				});
			}).listen(52273, function(){
				console.log('Server Running at http://127.0.0.1:52273');
			});
		
			

<%= name %>

<%= description %>


<% for(var i=0; i < 10; i++){ %> <h2>The Squae of <%= i %> is <%= i*i%></h2> <% } %>

변수 name과 description 은 ejs 페이지 내부에서 생성한 것이 아니라 Node.js 코드에서 전달한 것이다.

ejs 모듈과 같은 템플릿 엔진은 동적 웹 페이지를 생성할 때 사용한다.

jade 모듈

jade 모듈은 다음과 같이 추출한다.

			//모듈 추출
			var jade = require('jade');
		

| jade 모듈의 메서드

			//모듈 추출
			var http = require('http');
			var fs = require('fs');
			var jade = require('jade');

			//서버 생성 및 연결
			http.createServer(function(request, respond){
				// jadePage.jade 파일을 읽는다.
				fs.readFile('JadePage.jade', 'utf-8', function(error, data){
					
				});
			}).listen(52273, function(){
				console.log('Server Running at http://127.0.0.1:52273');
			});
		

jade 페이지를 HTML 페이지로 변환할 때는 compile() 메서드를 사용한다. ejs 모듈의 render() 메서드와 다르게 문자열을 리턴하는 것이 아니라 함수를 리턴한다.

jade 모듈의 메서드
compile(string) - jade 문자열을 HTML 문자열로 바꿀 수 있는 함수를 생성한다.

compile() 메서드로 생성한 함수를 아래와 같이 실행하면 jade 페이지를 HTML 페이지로 변환할 수 있다.

			//모듈 추출
			var http = require('http');
			var fs = require('fs');
			var jade = require('jade');

			//서버 생성 및 연결
			http.createServer(function(request, respond){
				// jadePage.jade 파일을 읽는다.
				fs.readFile('JadePage.jade', 'utf-8', function(error, data){
					//jade 모듈을 사용한다.
					var fn = jade.compile(data);

					//출력한다.
					response.writeHead(200, {'Content-Type' : 'text/html'});
					response.end(fn());
				});
			}).listen(52273, function(){
				console.log('Server Running at http://127.0.0.1:52273');
			});
		

| jade 기본 형식

jade 기본형식은 아래와 같으며 가장 중요한 것은 들여쓰기이다. 들여쓰기로 각 태그의 HTML 구조를 형성한다.

			doctype 5
			html
				head
					title	Index Page
					style
						* {margin:0; padding:0;}
						h1 {color:red}
					script(src="http://code.jquery.com/jquery-1.10.2.js")
					script
						$(document).ready(function(){
							var alpha = 10;
						});
				body
					// JADE String
					#header
						h1	Hello jade...!
						h2	Lorem ipsum
					hr
					.article
						a(href="http://hanb.co.kr", data-test="multiple Attribute") Go To Hanbit Media
		

태그 안에 글자를 입력하고 싶을 때는 지정한 태그 뒤에 원하는 글자를 입력한다. 또한 태그에 속성을 입력하고 싶을 때는 지정한 태그 뒤에 괄호를 사용하고 같은 방식으로 속성을 입력한다. 태그에 속성을 여러개 입력하고 싶을 때는 쉼표를 사용하여 구분한다. 또 doctype 5는 <!Doctype html> 로 변환되고 //JADE String은 <!--JADE String-->로 변환된다.

div 태그는 직접 입력하지 않고 #header 태그를 입력하는 것만으로도 id 속성이 header인 div 태그를 생성할 수 있다. 마찬가지로 .article태그를 입력하는 것만으로도 class 속성이 article인 div 태그를 생성할 수 있다.

마지막으로 style태그와 script 태그도 위와 같이 입력할 수 있다.

| jade 특수 기호

jade 페이지는 HTML 페이지 위에 다음과 같은 특수 태그를 사용하여 페이지를 구성한다.

jade 파일의 특수태그
- Code - 자바스크립트 코드를 입력한다.
#{Value} - 태그 중간에 데이터를 출력한다.
= Value - 태그 전체에 데이터를 출력한다.

생성한 함수의 매개변수에 jade 페이지에 전달할 데이터를 입력한다.

			//모듈 추출
			var http = require('http');
			var fs = require('fs');
			var jade = require('jade');

			//서버 생성 및 연결
			http.createServer(function(request, response){
				// jadePage.jade 파일을 읽는다.
				fs.readFile('JadePage.jade', 'utf-8', function(error, data){
					//jade 모듈을 사용한다.
					var fn = jade.compile(data);

					//출력한다.
					response.writeHead(200, {'Content-Type' : 'text/html'});
					response.end(fn({
						name : 'RintIanTta',
						description : 'Hello Jade With Node.js...'
					}));
				});
			}).listen(52273, function(){
				console.log('Server Running at http://127.0.0.1:52273');
			});
		

name 속성과 description 속성을 출력하고 for 반복문을 사용하여 p태그와 a태그를 여러개 생성한다.

			doctype 5
			html
				head
					title	Index Page
					style
						* {margin:0; padding:0;}
						h1 {color:red}
					script(src="http://code.jquery.com/jquery-1.10.2.js")
					script
						$(document).ready(function(){
							var alpha = 10;
						});
				body
					// JADE String
					#header
						h1	#{name} ... !
						h2=	description
					hr
					- for(var i=0; i < 10; i++){
						p
							a(href="http://hanb.co.kr", data-test="multiple Attribute") Go To Hanbit Media #{i}
					- }
		

서버 실행 모듈

| supervisor 모듈

supervisor 모듈은 파일의 변경을 자동으로 인식하고 종료 후에 실행해 준다.

supervisor 모듈의 기본 명령어는 다음과 같이 supervisor 명령어를 입력해서 확인할 수 있다.

			$ supervisor
		

파일을 작성했다면 다음과 같이 supervisor 모듈을 사용할 수 있다.

			$ supervisor test-server.js
		

파일을 변경하고 저장하면 자동으로 supervisor 모듈이 서버를 재시작한다. 따라서 화면을 새로고침하면 변경된 내용을 출력한다.

| forever 모듈

웹서비스를 잘 구축해도 예외가 발생할 수 있다. 기존의 멀티스레드 기반의 웹서비스는 예외가 발생해도 전체 웹 서비스에 크게 영향을 주지 않는다. 하지만 node.js 같은 단일 스레드 기반의 웹서비스는 예외 하나로 웹서비스가 죽어버린다. 이러한 예외 상황을 대비하고자 만들어진 모듈이 forever 모듈이다. forever 모듈을 사용하면 웹 서버를 안정적으로 유지할 수 있다.

			$ supervisor test-server.js
		

express 모듈


기본 서버

express 모듈로 서버를 실행하려면 http 모듈이 함께 필요하다.

express 모듈은 express() 함수로 애플리케이션 객체를 생성하고 애플리케이션 객체로 웹서버를 설정 및 실행한다.

[express 모듈을 사용한 서버 생성 및 실행]

			// 모듈 추출
			var http = require('http');
			var express = require('express');

			// 서버 생성
			var app = express();

			// request 이벤트 리스너를 설정
			app.use(function(request, response){
				response.writeHead(200, {'Content-Type' : 'text/html'});
				response.end('

Hello Express

'); }); // 서버를 실행 http.createServer(app).listen(52273, function(){ console.log('Server Running at http://127.0.0.1:52273'); });

기본 응답 메서드

request 이벤트 리스너의 매개변수에는 request 객체와 response 객체가 들어간다.

			// request 이벤트 리스너를 설정
			app.use(function(request, response){
				response.writeHead(200, {'Content-Type' : 'text/html'});
				response.end('

Hello Express

'); });

express 모듈로 서버를 생성하면 request 객체와 response 객체에 다양한 기능이 추가된다.

[reponse 객체의 메서드]

메서드 이름 설명
reponse.send() 매개변수의 자료형에 따라 적적한 형태로 응답한다.
response.json() JSON 형태로 응답한다.
response.jsonp() JSONP 형태로 응답한다.
response.redirect() 웹페이지 경로를 강제로 이동한다.

[send() 메서드의 매개변수]

자료형 설명
문자열 HTML
배열 JSON
객체 JSON.

response.send() 메서드는 매개변수의 자료형에 따라 적절한 형태로 응답한다.

			// 모듈 추출
			var http = require('http');
			var express = require('express');

			// 서버 생성
			var app = express();

			// request 이벤트 리스너를 설정
			app.use(function(request, response){
				// 데이터를 생성
				var output = [];
				for(var i=0; i<3; i++){
					output.push({
						count : i,
						name : 'name-' + i
					});
				}

				// 응답
				response.send(output);
			});

			// 서버를 실행
			http.createServer(app).listen(52273, function(){
				console.log('Server Running at http://127.0.0.1:52273');
			});
		

코드를 실행하면 response.send() 메서드로 자바스크립트 객체를 입력했으므로 JSON 형식으로 출력한다.

response.send() 메서드는 첫번째 매개변수에 Stauts Code를 입력하고 두번째 매개변수에 응답 데이터를 입력할 수 있다.

			// 모듈 추출
			var http = require('http');
			var express = require('express');

			// 서버 생성
			var app = express();

			// 미들웨버를 설정
			app.use(function(request, response){
				// 응답
				response.send(404, '

ERROR

'); }); // 서버를 실행 http.createServer(app).listen(52273, function(){ console.log('Server Running at http://127.0.0.1:52273'); });

기본 요청 메서드

express 모듈을 사용하면 response 객체와 마찬가지로 request 객체에도 아래와 같은 메서드가 추가된다.

[request 객체의 메서드]

메서드 이름 설명
header() 요청 헤더의 속성을 지정 또는 추출한다.
accepts() 요청 헤더의 Accept 속성을 확인한다.
param() 요청 매개변수를 추출한다.
is() 요청 헤더의 Content-Type 속성을 확인한다.

| 요청 헤더의 속성 추출

header() 메서드를 사용하면 손쉽게 요청 헤더의 속성을 지정하거나 추출할 수있다. 웹브라우저로 HTTP 요청을 하면 반드시 User-Agent 속성이 따라오므로 User-Agent 속성을 추출할 수 있다.

			// 모듈 추출
			var http = require('http');
			var express = require('express');

			// 서버 생성
			var app = express();

			// 미들웨어 설정
			app.use(function(request, response){
				// User-Agent 속성을 추출
				var agent = request.header('User-Agent');

				console.log(request.headers);
				console.log(agent);

				// 응답
				response.send(200);
			});

			// 서버 실행
			http.createServer(app).listen(52273, function(){
				console.log('Server Running at http://127.0.0.1:52273');
			});		
		

이렇게 요청 헤더의 속성을 추출하면 웹브라우저에 따라 별도의 처리를 할 수 있다. 아래는 사용자의 웹브라우저가 크롬인지 확인한다.

			// 모듈 추출
			var http = require('http');
			var express = require('express');

			// 서버 생성
			var app = express();

			// 미들웨어 설정
			app.use(function(request, response){
				// User-Agent 속성을 추출
				var agent = request.header('User-Agent');

				//브라우저를 구분
				if(agent.toLowerCase().match(/chrome/)){
					// 페이지를 추출한다.
					response.send('

Hello Chrome...

'); }else{ // 페이지를 추출한다. response.send('

Hello express...

'); } }); // 서버 실행 http.createServer(app).listen(52273, function(){ console.log('Server Running at http://127.0.0.1:52273'); });

| 요청 매개변수 추출

param() 메서드로 요청 매개변수를 쉽게 추출할 수 있다. 아래는 사용자의 요청에서 요청 매개변수 name과 region을 추출해 출력한다.

			// 모듈 추출
			var http = require('http');
			var express = require('express');

			// 서버 생성
			var app = express();

			// 미들웨어 설정
			app.use(function(request, response){
				// 변수선언
				var name = request.param('name');
				var region = request.param('region');

				// 응답
				response.send('

' + name + '-' + region + '

'); }); // 서버 실행 http.createServer(app).listen(52273, function(){ console.log('Server Running at http://127.0.0.1:52273'); });

코드를 실행하고 http://127.0.0.1:52273/?name=lko&region=ansan 에 접속하며 요청 매개변수를 추출할 수 있다.

param() 메서드는 GET 요청 방식의 요청 매개변수뿐만 아니라 다른 형태의 요청 매개변수도 처리할 수 있다.

미들웨어 개요

express 모듈은 request 이벤트 리스너를 연결하는데 use() 메서드를 사용한다. use() 메서드는 여러번 사용할 수 있다. 그리고 이때 use() 메서드의 매개변수에는 function(request, response, next){} 형태의 함수를 입력한다. 매개변수 next는 다음에 위치하는 함수를 의미한다. 요청의 응답을 완료하기 전까지 요청 중간 중간에서 여러가지 일을 처리할 수 있다. 그래서 use() 메서드의 매개변수에 입력하는 함수를 '미들웨어 middleware'라고 부른다.

미들웨어에서 request 객체와 response 객체에 속성 또는 메서드를 추가하면 다음 미들웨어에서 추가한 속성과 메서드를 사용할 수 있다.

			//모듈 추출
			var http = require('http');
			var express = require('express');

			//서버 생성
			var app = express();


			//미들웨어 설정
			app.use(function(request, response, next){
				//데이터 추가
				request.number = 52;
				response.number = 273;
				next();
			});

			app.use(function(request, response, next){
				//응답
				response.send('

' + request.number + ' : ' + response.number + '

'); }); //서버실행 http.createServer(app).listen(52273, function(){ console.log('Server Running at http://127.0.0.1:52273'); });

따라서 미들웨어를 사용하면 특정한 일을 수행하는 모듈을 분리해서 만들 수 있다.

express 모듈은 이미 다양한 미들웨어를 가지고 있다.

logger 미들웨어

logger 미들웨어는 웹 요청이 들어왔을 때 로그를 출력하는 미들웨어이다.

			//모듈 추출
			var http = require('http');
			var express = require('express');

			//서버 생성
			var app = express();

			//미들웨어 설정
			app.use(express.logger());
			app.use(function(request, response){
				response.send('

express Basic

'); }); //서버실행 http.createServer(app).listen(52273, function(){ console.log('Server Running at http://127.0.0.1:52273'); });

코드를 실행하고 웹브라우저로 웹요청을 보내면 콘솔화면에 로그를 출력된다.

logger() 메서드의 매개변수에 문자열을 입력해 출력 로그를 원하는 형태로 지정할 수 있다. 매개변수로 입력할 수 있는 문자열은 다음의 토큰을 사용해 특정 정보를 출력한다.

[logger 미들웨어의 토큰]

토큰 설명 토큰 설명
:req[header] 요청 헤더를 나타낸다. :res[header] 응답 헤더를 나타낸다.
:http-version HTTP 버전을 나타낸다. :response-time 응답시간을 나타낸다.
:remote-addr 원격 주소를 나타낸다. :date 요청 시간을 나타낸다.
:method 요청 방식을 나타낸다. :url 요청 URL을 나타낸다.
:referer 이전 URL을 나타낸다. :User-Agent 사용자 에이전트를 나타낸다.
:status 상태 코드를 나타낸다.

예를 들어 요청 방식과 시간을 출력하고 싶다면 다음 코드처럼 입력한다.

			//모듈 추출
			var http = require('http');
			var express = require('express');

			//서버 생성
			var app = express();

			//미들웨어 설정
			app.use(express.logger(':method + :date'));
			app.use(function(request, response){
				response.send('

express Basic

'); }); //서버실행 http.createServer(app).listen(52273, function(){ console.log('Server Running at http://127.0.0.1:52273'); });

직접 토큰을 입력할 수도 있지만 다음 처럼 기본 형식을 사용할 수 있다. 매개변수에 기본 형식을 입력하면 형태가 다양한 로그를 쉽게 출력할 수 있다.

[logger 미들웨어의 기본형식]

형식 설명
default :remote-addr - - [:date] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":User-Agent"
short :remote-addr - :method :url HTTP/:http-version :status :res[content-length] - :response-time ms
tiny :method :url :status :res[content-length] - :response-time ms

short를 사용하여 형식을 지정하고 싶다면 다음과 같이 입력한다.

			//모듈 추출
			var http = require('http');
			var express = require('express');

			//서버 생성
			var app = express();


			//미들웨어 설정
			app.use(express.logger('short'));
			app.use(function(request, response){
				response.send('

express Basic

'); }); //서버실행 http.createServer(app).listen(52273, function(){ console.log('Server Running at http://127.0.0.1:52273'); });

static 미들웨어

static 미들웨어는 웹서버에서 손쉽게 파일을 제공하는 방법을 제공한다.

만약 public 폴더에 그림파일, 자바스크립트 파일, css 파일 등이 있다면 전역변수 __dirname을 사용하여 폴더 위치를 지정하면 나머지는 expess 모듈이 전부 알아서 해준다.

			//모듈 추출
			var http = require('http');
			var express = require('express');

			//서버생성
			var app = express();

			//미들웨어 설정
			app.use(express.logger());
			app.use(express.static(__dirname + '/public'));
			app.use(function(request, response){
				//응답
				response.writeHead(200, {'Content-Type' : 'text/html'});
				response.end('');
			});

			//서버 실행
			http.createServer(app).listen(52273, function(){
				console.log('Server Running at http://127.0.0.1:52273');
			});		
		

static 미들웨어를 사용하면 지정한 폴더에 있는 내용을 모두 웹서버 루트 폴더에 올린다. 코드를 실행하면 지정한 이미지 파일을 출력한다.

router 미들웨어

router 미들웨어는 페이지 라우팅을 구현하는 미들웨어이다. 페이지 라우팅은 클라이언트 요청에 적절한 페이지를 제공하는 기술이다.

router 미들웨어는 app.router 속성을 사용한다. 다른 미들웨어와 다르게 express 객체에 들어있는 것이 아니고 함수를 호출하는 것이 아니다.

			//모듈 추출
			var http = require('http');
			var express = require('express');

			//서버생성
			var app = express();

			//미들웨어 설정
			app.use(express.logger());
			app.use(app.router);

			//서버 실행
			http.createServer(app).listen(52273, function(){
				console.log('Server Running at http://127.0.0.1:52273');
			});		
		

위와 같이 router 미들웨어를 사용하면 아래와 같은 app 객체의 메서드를 사용할 수 있다.

[app 객체의 메서드]

메서드 이름 설명
get(path, callback) GET 요청이 발생했을 때의 이벤트 리스너를 지정한다.
post(path, callback) POST 요청이 발생했을 때의 이벤트 리스너를 지정한다.
put(path, callback) PUT 요청이 발생했을 때의 이벤트 리스너를 지정한다.
del(path, callback) DEL 요청이 발생했을 때의 이벤트 리스너를 지정한다.
all(path, callback) 모든 요청이 발생했을 때의 이벤트 리스너를 지정한다.

다음은 get() 메서드를 사용한 예제이다. 첫번째 매개변수에는 요청 URL을 입력하고 두번째 매개변수에는 요청 URL을 입력했을 때 실행할 이벤트 리스너를 입력한다.

			//모듈추출
			var http = require('http');
			var express = require('express');

			//서버생성
			var app = express();


			//미들웨어 설정
			app.use(express.logger());
			app.use(app.router);
			app.use(express.static(__dirname + '/public'));

			//라우터 설정
			app.get('/a', function(request, response){
				response.send('Go to B');
			});

			app.get('/b', function(request, response){
				response.send('Go to A');
			});

			//서버실행
			http.createServer(app).listen(52273, function(){
				console.log('Server Running at http://127.0.0.1:52273');
			});	
		

코드를 실행하고 http://127.0.0.1:52273/a에 접속한다.

router 미들웨어를 사용하면 굉장히 유용한 기능이 있다. 바로 라우팅을 할 때 토큰을 사용한다는 것이다. 다음은 get() 메서드의 첫 번째 매개변수에 문자열 /:id를 입력했다.

			//모듈추출
			var http = require('http');
			var express = require('express');

			//서버생성
			var app = express();


			//미들웨어 설정
			app.use(express.logger());
			app.use(app.router);

			//라우터 설정
			app.get('/page/:id', function(request, response){
				//변수 선언
				var name = request.param('id');
				//응답
				response.send('

' + name + ' Page

'); }); //서버실행 http.createServer(app).listen(52273, function(){ console.log('Server Running at http://127.0.0.1:52273'); });

코드를 실행하고 http://127.0.0.1:52273/page/52273에 접속한다. id 속성에 52273을 입력했으므로 문자열 '52273 Page'를 출력한다.

cookie parser 미들웨어

cookie parser 미들웨어는 요청 쿠키를 추출하는 미들웨어이다. cookie parser 미들웨어를 사용하면 request 객체에 cookies 속성이 부여된다.

다음과 같이 router 미들웨어를 사용하여 쿠키를 저장할 수 있는 setCookie 페이지와 쿠키를 볼 수 있는 getCookie 페이지를 생성한다.

			//모듈 추출
			var http = require('http');
			var express = require('express');

			//서버생성
			var app = express();

			//미들웨어 설정
			app.use(express.cookieParser());
			app.use(app.router);

			//라우터 설정
			app.get('/getCookie', function(request, response){
				//응답
				response.send(request.cookies);
			});

			app.get('/setCookie', function(request, response){
				//쿠키생성
				response.cookie('string', 'cookie');
				response.cookie('json', {
					name : 'cookie',
					property : 'delicious'
				});

				//응답
				response.redirect('/getCookie');
			});

			//서버 실행
			http.createServer(app).listen(52273, function(){
				console.log('Server Running at http://127.0.0.1:52273');
			});
		

코드를 실행한 후 http://127.0.0.1:52273/setCookie에 접속하면 string 쿠키와 join 쿠키를 생성한 후 이어서 getCookie 페이지에 자동으로 이동하여 화면에 출력한다.

cookie() 메서드의 세번째 매개변수에 다음과 같이 옵션 객체를 입력할 수 있다.

			response.cookie('string', 'cookie', {
				maxAge : 6000,
				secure : true
			});
		

[cookie 메서드의 옵션 속성]

속성 이름 설명
httpOnly 클라이언트의 쿠키 접근 권한을 지정한다.
secure secure 속성을 지정한다.
expires expires 속성을 지정한다.
maxAge 상대적으로 expires 속성을 지정한다.
path path 속성을 지정한다.

body parser 미들웨어

body parser 미들웨어는 POST 요청 데이터를 추출하는 미들웨어이다. body parser 미들웨어를 사용하면 request 객체에 body 속성이 부여된다.

| 입력 양식 데이터 추출

cookie parser 미들웨어와 body parser 미들웨어를 사용하여 로그인을 구현할 수 있다.

			<!DOCTYPE html>
			<html>
			 <head>
			   New Document 
			 </head>
			 <body>
				

Login Page


</body> </html>

GET 방식으로 요청했을 경우와 POST 방식으로 요청했을 경우 두 가지로 나누어 GET 방식일때는 HTML 페이지를 출력하고 POST 방식일 때는 클라이언트가 입력한 아이디와 비밀번호를 확인하여 auth 쿠키를 생성한다.

			//모듈 추출
			var http = require('http');
			var fs = require('fs');
			var express = require('express');

			//서버 생성
			var app = express();

			//미들웨어 설정
			app.use(express.cookieParser());
			app.use(express.bodyParser());
			app.use(app.router);

			//라우터를 설정
			app.get('/', function(request, response){
				if(request.cookies.auth){
					response.send('

Login Success

'); }else{ response.redirect('/login'); } }); app.get('/login', function(request, response){ fs.readFile('login.html', function(error, data){ response.send(data.toString()); }); }); app.post('/login', function(request, response){ //쿠키 생성 var login = request.param('login'); var password = request.param('password'); //출력 console.log(login, password); console.log(request.body); //로그인을 확인 if(login=='rint' && password=='1234'){ //로그인 성공 response.cookie('auth', true); response.redirect('/'); }else{ //로그인 실패 response.redirect('/login'); } }); //서버 실행 http.createServer(app).listen(52273, function(){ console.log("Running Server at http://127.0.0.1:52273"); });

로그인에 성공하며 auth 쿠키가 생성되므로 웹브라우저를 종료하고 다시 실행하기 전까지는 계속 로그인 상태를 유지할 것이다.

| 파일 업로드

일반적인 입력 양식은 application/x-www-form-urlencoded 인코딩 방식을 사용한다. 그런데 파일은 일반적인 입력 양식 데이터에 비해 용량이 크다. 따라서 웹브라우저는 파일을 전송할 때에 multipart/form-data 인코딩 방식을 사용한다.

			<!DOCTYPE html>
			<html>
			 <head>
			  <title> New Document </title>
			 </head>
			 <body>
				

File Upload

Comment :
File :
</body> </html>

반드시 form 태그의 enctype속성을 "multipart/form-data"로 지정해야 한다.

			//모듈 추출
			var http = require('http');
			var fs = require('fs');
			var express = require('express');

			//서버 생성
			var app = express();

			//미들웨어 설정
			app.use(express.cookieParser());
			app.use(express.limit('10mb'));
			app.use(express.bodyParser({uploadDir: __dirname + '/multipart'}));
			app.use(app.router);

			//라우터를 설정
			app.get('/', function(request, response){
				fs.readFile('HTMLPage.html', function(error, data){
					response.send(data.toString());
				});
			});
			app.post('/', function(request, response){
				//변수 선언
				var comment = request.param('comment');
				var imageFile = request.files.image;

				if(imageFile){
					//변수 선언
					var name = imageFile.name;
					var path = imageFile.path;
					var type = imageFile.type;

					//이미지 파일 확인
					if(type.indexOf('image') != -1){
						// 이미지 파일의 경우 : 파일 이름을 변경
						var outputPath = __dirname + '/multipart/' + Date.now() + '_' + name;

						fs.rename(path, outputPath, function(error){
							response.redirect('/');
						});
					}else{
						//이미지 파일이 아닌 경우 : 파일 이름을 제거
						fs.unlink(path, function(error){
							response.send(400);
						});
					}

				}else{
					//파일이 없을 경우
					response.send(404);
				}

				console.log(request.body);
				console.log(request.files);

				response.redirect('/');
			});

			//서버 실행
			http.createServer(app).listen(52273, function(){
				console.log("Running Server at http://127.0.0.1:52273");
			});
		

서버의 코드는 위처럼 작성을 하는데 body parser 미들웨어에 uploadDir 속성을 입력한다. uploadDir 속성을 지정하면 사용자가 파일을 업로드했을 때 해당 경로로 파일이 업로드된다. 파일 업로드가 완료되면 multipart 폴더에 파일이 추가된다. limit 미들웨어를 사용해 업로드 용량을 조절할 수 있다. 파일이 업로드되면 request 객체에 files 속성이 전달된다. 파일과 관련된 정보는 모두 files 속성에 있다.

그리고 같은 이름으로 파일을 올리면 덮어 씌어지므로 이미지 파일인지 확인하고 rename() 메서드를 사용해 시간을 기반으로 파일의 이름을 변경한다. 또한 이미지 파일이 아니라면 파일을 제거한다.

session 미들웨어

쿠키는 정보를 클라이언트의 웹 브라우저에 저장하고 세션은 정보를 서버에 저장하는 기술이다. 일반적으로 세션은 클라이언트에 세션 식별자 쿠키를 부여한다. 그리고 부여한 세션 식별자 쿠키와 대응되는 서버에 위치하는 별도 저장소에 데이터를 저장한다.

예를 들어 클라이언트는 식별자 쿠키 a10을 부여받는다. 만약 클라이언트 정보를 사용한다면 클라이언트의 식별자 쿠키를 읽어 서버에 대응되는 위치에 저장된 데이터를 사용한다.

session 미들웨어는 세션을 쉽게 생성할 수 있게 도와주는 미들웨어이다. session 미들웨어를 사용하면 request 객체에 session 속성을 부여한다. session 미들웨어는 자체적으로 cookie parser 미들웨어를 사용하므로 cookie parser 미들웨어와 session 미들웨어가 순서대로 추가되어야 한다.

다음 예제는 request 객체의 cookies 속성과 session 속성을 출력한다. 접속할 때마다 현재 시간을 now 세션에 저장한다.

			//모듈 추출
			var http = require('http');
			var express = require('express');

			//서버생성
			var app = express();


			//미들웨어 설정
			app.use(express.cookieParser());
			app.use(express.session({secret : 'secret key'}));
			app.use(express.bodyParser());
			app.use(function(request, response){
				//변수 선언
				var output = {};
				output.cookies = request.cookies;
				output.session = request.session;

				//세션을 저장
				request.session.now = (new Date()).toUTCString();

				//응답
				response.send(output);
			});

			//서버실행
			http.createServer(app).listen(52273, function(){
				console.log("Running Server at http://127.0.0.1:52273");
			});
		

코드를 실행하고 http://127.0.0.1:52273/에 접속하면 다음처럼 쿠키와 세션정보를 출력한다. 이 상태에서 새로고침하면 connect.sid 쿠키가 생성된 것을 확인할 수 있는데 express 모듈은 connect.sid 쿠키로 서버에 저장된 데이터를 확인한다.

			{
			  "cookies": {
				"auth": "true",
				"connect.sid": "s:uXL9pZCAVdio24o0lD0JK82X.AOK+EPPD9iwonPaqbK0wZZPFy9n1wbWzx9XrsB9MwDw"
			  },
			  "session": {
				"cookie": {
				  "originalMaxAge": null,
				  "expires": null,
				  "httpOnly": true,
				  "path": "/"
				},
				"now": "Wed, 20 Nov 2013 13:36:16 GMT"
			  }
			}
		

브라우저를 종료하고 다시 실행하면 connect.sid 쿠키가 소멸되므로 클라이언트는 자신이 소유한 정보를 읽게 된다. 예를 들어 데스크톱 웹페이지는 대부분 로그인을 하고 나서 웹브라우저를 종료하면 세션 식별자 쿠키가 소멸되므로 자동으로 로그아웃된다.

그렇다면 세션은 언제까지 유지될까? express 모듈은 웹 브라우저가 켜져 있는 동안만 세션을 유지한다. 만약 쿠키가 사라지는 시간과 쿠키의 name 속성을 바꾸고 싶다면 다음과 같이 session() 메서드의 매개변수에 옵션 객체를 입력한다.

			//모듈 추출
			var http = require('http');
			var express = require('express');

			//서버생성
			var app = express();


			//미들웨어 설정
			app.use(express.cookieParser());
			app.use(express.session({
				secret : 'secret key',
				key : 'rint',
				cookie : {
					maxAge : 60*1000
				}
			}));
			app.use(express.bodyParser());
			app.use(function(request, response){
				//변수 선언
				var output = {};
				output.cookies = request.cookies;
				output.session = request.session;

				//세션을 저장
				request.session.now = (new Date()).toUTCString();

				//응답
				response.send(output);
			});

			//서버실행
			http.createServer(app).listen(52273, function(){
				console.log("Running Server at http://127.0.0.1:52273");
			});
		

session() 메서드의 매개변수에 입력할 수 있는 옵션은 다음과 같다.

[session 메서드의 옵션]

옵션 이름 설명
key 쿠키의 name 속성을 지정한다.
store 세션 저장소를 지정한다.
cookie 생성할 cookie와 관련된 정보를 지정한다.

cookie 속성을 입력하지 않으면 다음과 같은 객체가 입력된다.

			{ path: '/', httpOnly: true, maxAge: null }
		

session 미들웨어를 사용하여 만들어지는 request 객체의 session 속성은 다음의 메서드를 갖는다.

[session 객체의 메서드]

메서드 이름 설명
regenerate() 세션을 다시 생성한다.
destroy() 세션을 제거한다.
reload() 세션을 다시 불러온다.
save() 세션을 저장한다.

RESTful 웹 서비스 개발

RESTful 웹 서비스는 REST 규정을 맞춰 만든 웹서비스를 의미한다. REST 규정은 일관된 웹서비스 인터페이스 설계를 위한 규정이다.

[RESTful 웹 서비스의 구조]

경로 /collection collection
GET 방식 컬렉션을 조회한다. 컬렉션의 특정 요소를 조회한다.
POST 방식 컬렉션에 새로운 데이터를 추가한다. 사용하지 않는다.
PUT 방식 컬렉션 전체를 한꺼번에 변경한다. 컬렉션에 특정 요소를 수정한다.
DELETE 방식 컬렉션 전체를 삭제한다. 컬렉션의 특정 요소를 삭제한다.

예를 들어 사용자 관련 정보라면 다음과 같은 행태로 사용한다.

MySQL 데이터베이스


mysql 모듈

mysql 모듈은 다음과 같은 방법으로 추출한다.

			//모듈을 추출
			var mysql = require('mysql');
		

[mysql 모듈의 메서드]

메서드 이름 설명
createConnection(options) 데이터베이스에 접속한다

createConnection() 메서드의 options 객체에는 다음 속성을 입력한다. 이 속성중 user 속성과 password 속성은 필수적으로 입력해야 한다.

[Connection 객체의 속성]

속성 이름 설명
host 연결할 호스트를 나타낸다
port 연결할 포트를 나타낸다
user 사용자 이름을 나타낸다
password 사용자 비밀번호를 나타낸다
database 연결할 데이터베이스를 나타낸다
debug 디버그 모드를 사용할지 나타낸다

createConnection() 메서드는 다음과 같이 사용한다. 이렇게 createConnection() 메서드를 사용하면 Client 객체가 생성된다. mysql 모듈은 이렇게 생성한 Connection 객체를 사용하여 데이터베이스에 있는 데이터에 접근한다.

			//모듈을 추출
			var mysql = require('mysql');

			//데이터베이스에 연결
			var client = mysql.createConnection({
				user : 'root',
				password : '비밀번호'
			});
		

[Connection 객체의 메서드]

메서드 이름 설명
query(sql[, callback]) 쿼리 문장을 실행합니다.

query() 메서드로 MySQL 쿼리 문장을 직접 입력할 수 있다. query() 메서드는 다음처럼 사용한다.

			//모듈을 추출
			var mysql = require('mysql');

			//데이터베이스에 연결
			var client = mysql.createConnection({
				user : 'root',
				password : '비밀번호'
			});

			//데이터베이스 쿼리 사용
			client.query('USE Company');
		

query() 메서드도 이벤트 기반 비동기 처리 방식을 사용한다. 다음 처럼 두번째 매개변수에 이벤트 리스너를 입력하면 SELECT * FROM products 쿼리 문장을 실행한 이후에 매개변수 result로 결과가 입력된다.

			//데이터베이스 쿼리 사용
			client.query('USE Company');
			client.query('SELECT * FROM products', function(error, result, fields){

			});
		

쿼리 문장에 오류가 있을 경우 매개변수 error에 오류와 관련된 정보가 입력된다. 따라서 다음과 같은 방법으로 예외처리를 할 수있다.

			//모듈을 추출
			var mysql = require('mysql');

			//데이터베이스에 연결
			var client = mysql.createConnection({
				user : 'root',
				password : '비밀번호'
			});

			//데이터베이스 쿼리 사용
			client.query('USE Company');
			client.query('SELECT * FROM products', function(error, result, fields){
				if(error){
					console.log('쿼리 문장에 오류가 있습니다.');
				}else{
					console.log(result);
				}
			});
		

express 프레임워크


express 프레임워크는 express 모듈로 만든 프레임워크이다. express 프레임워크는 프로젝트를 손쉽게 만들어주며 기본적인 뷰 지원과 세션 지원을 한다.

설치

다음 명령어로 express 프레임워크를 설치한다.

			$ npm install -g express
		

express 프레임워크 설치가 완료됐으면 프로젝트를 생성한다. 'expess 프로젝트' 형태 명령어를 사용하여 프로젝트를 생성한다.

			$ express Helloexpress
		

express 프레임워크가 요청하는 대로 다음 명령어를 입력하면 자동으로 express 프레임워크와 관련된 모듈이 설치된다.

			$ cd Helloexpress && npm install
		

express 프레임워크로 생성한 프로젝트를 실행할 때는 프로젝트 안에 있는 app.js 파일을 실행하면 된다.

			$ node app
			express server listening on port 3000
		

express 프레임워크는 기본 설정으로 3000번 포트를 사용한다. 따라서 http://127.0.0.1:3000에 접속하면 express 프레임워크로 생성된 웹페이지를 확인할 수 있다.

프로젝트 생성 및 설정

express 프레임워크는 프로젝트를 생성할 때 여러가지 설정을 할 수 있다. 다음 명령어로 express 프레임워크 도움말을 출력한다.

			$ express --help
		

[express 프레임워크 옵션]

매개변수 설명
-h 또는 --help 도움말을 출력한다
-v 또는 --version 프레임워크 버전을 출력한다
-s 또는 --sessions 세션을 사용한다
-e 또는 --ejs ejs 템플릿 엔진을 사용한다.
-j 또는 --jshtml jshtml 템플릿 엔진을 사용한다
-h 또는 --hogan hogan.js 템플릿 엔진을 사용한다
-c <engine> 또는 --css <engine> 스타일시트 엔진을 지정한다
-f 또는 --force 프로젝트가 이미 존재해도 새로 작성한다.

이러한 속성은 다음 명령어처럼 지정한다. 다음 명령어는 세션을 사용하고 템플릿 엔진으로 ejs 모듈을 사용하는 express 프로젝트를 생성한다.

			$ express -s -e Helloexpress
		

기본 프로젝트

express HelloExpress 명령어를 사용해서 생성한 프로젝트 파일은 다음처럼 구성된다.

[ app.js ]

			/**
			 * 모듈 추출
			 */

			var express = require('express');
			var routes = require('./routes');
			var user = require('./routes/user');
			var http = require('http');
			var path = require('path');

			// 서버 생성
			var app = express();

			// 미들웨어 설정
			app.set('port', process.env.PORT || 3000);
			app.set('views', path.join(__dirname, 'views'));
			app.set('view engine', 'jade');
			app.use(express.favicon());
			app.use(express.logger('dev'));
			app.use(express.json());
			app.use(express.urlencoded());
			app.use(express.methodOverride());
			app.use(app.router);
			app.use(express.static(path.join(__dirname, 'public')));

			// 개발모드
			if ('development' == app.get('env')) {
			  app.use(express.errorHandler());
			}

			app.get('/', routes.index);
			app.get('/users', user.list);

			http.createServer(app).listen(app.get('port'), function(){
			  console.log('Express server listening on port ' + app.get('port'));
			});
		

| 미들웨어 설정

위 코드에서 set() 메서드는 express 프레임워크의 설정 옵션을 지정하는 메서드이다.

[표. express 프레임워크 설정]

설정옵션 설명
basepath 기본 URL을 지정한다.
views 뷰 폴더를 지정한다.
view engine 뷰 엔진을 지정한다.
view options 뷰 엔진 옵션을 지정한다.
view cache 뷰 캐시를 사용할지 지정한다.
case sensitive routes 페이지 라우트를 할 때 대소문자를 구분할지 지정한다.
strict routing 엄격 경로 확정을 사용할지 지정한다.

express 프레임워크는 기본적으로 URL 요청을 할 때 대소문자를 구분하지 않는다. 대소문자를 구분하고 싶으면 express 프레임워크 설정 부분에서 case sensitive routes 속성을 true로 입력한다.

			app.set('case sensitive routes', true);
		

| 실행 환경 설정

express 프레임워크는 기본적으로 2가지 실행환경을 제공한다. development 환경으로 실행할 때 error handler 미들웨어가 추가된다.

			// 개발모드
			if ('development' == app.get('env')) {
			  app.use(express.errorHandler());
			}
		

development 모드는 기본 설정이며 다음과 같이 실행하면 된다.

			$ node app
		

production 모드로 실행하고 싶다면 윈도환경에서는 코드에 직접 모드를 선택한다.

			var app = express();

			//서버환경 설정
			app.settings.env = 'production';
		

| 페이지 라우트

			app.get('/', routes.index);
			app.get('/users', user.list);
		

사용자가 GET 방식으로 '/' URL에 요청했을 경우 routes 모듈의 index() 메서드를 제공한다는 의미이다. routes 모듈은 다음 처럼 구성되어 있다.

			exports.index = function(req, res){
				res.render('index', {title : 'Express'});
			};
		

페이지 렌더링

| 단일 페이지 렌더링

express 프레임워크는 자체적으로 File System 모듈을 사용해 페이지를 제공하는 render() 메서드를 제공한다.

views 폴더에 product.jade 파일을 생성하여 다음 코드를 입력한다.

[ product.jade ]

			doctype 5
			html
				head
					title= title
					link(rel='stylesheet', href='/stylesheets/style.css')
				body
					h1 Hello Express
					p Node.js Programming for Modern Web Development
					hr
		

이어서 render() 메서드를 사용하여 페이지를 제공한다.

			app.get('/', routes.index);
			app.get('/users', user.list);
			app.get('/product', function(request, response){
				response.render('product', {title : 'Product Page'});
			});
		

render() 메서드는 ejs 모듈의 render() 메서드와 비슷하다. 첫번째 매개변수에는 views 폴더에 존재하는 파일 이름을 입력하고 두번째 매개변수에는 뷰에 전달하고자 하는 데이터를 입력한다. 코드를 실행하고 http://127.0.0.1:3000/product에 접속한다.

| 폴더를 사용한 페이지 분류

웹 사이트 페이지가 복잡해지면 폴더를 생성해 파일을 분류하는 것이 좋다. views 폴더를 다음과 같이 구성한다.

[ jade 페이지 ]

			doctype 5
			html
				head
					title= title
					link(rel='stylesheet', href='/stylesheets/style.css')
				body
					h1 Hello Express
					p Node.js Programming for Modern Web Development
					hr
		
			// GET - /
			app.get('/', routes.index);

			// GET - /product
			app.get('/product', function(request, response){
				response.render('product', {title : 'Product Page'});
			});

			// GET - /product/insert
			app.get('/product/insert', function(request, response){
				response.render('product/insert', {title : 'Insert Page'});
			});

			// GET - /product/edit
			app.get('/product/edit', function(request, response){
				response.render('product/edit', {title : 'Edit Page'});
			});
		

코드를 실행하고 http://127.0.0.1:3000/product/edit 페이지에 접속한다.

레이아웃 페이지

| jade 모듈 레이아웃

express 프레임워크는 jade 모듈을 뷰 엔진으로 사용할 때는 기본적으로 레이아웃 페이지를 지원한다. 다음과 같이 express 프로젝트를 생성한다.

			$ express jadeLayout
			$ cd jadeLayout
			$npm install
		

views 폴더에 layout.jade 파일이 레이아웃 파일이다. index.jade 파일은 다음과 같은 코드가 입력되어 있다. 파일 내부에 html 태그와 head 태그가 없고, extends 태그와 block 태그라는 알 수 없는 키워드가 써있다.

			extends layout

			block content
			  h1= title
			  p Welcome to #{title}
		

jade 모듈은 extends 태그를 사용해 페이지의 레이아웃을 지정한다. 현재 폴더에서 layout.jade 파일을 레이아웃으로 사용하므로 extends layout이라고 입력한 것이다. block 태그는 변수라고 생각하면 쉽다. block content라고 입력하면 content 변수가 생성된다.

layout.jade 파일은 다음과 같다. 레이아웃 파일은 전체적인 HTML 파일을 정의하며 block 태그를 사용해 변수를 출력한다.

			doctype 5
			html
			  head
				title= title
				link(rel='stylesheet', href='/stylesheets/style.css')
			  body
				block content
		

코드를 실행하면 두 페이지가 합쳐져서 출력된다.

| ejs 모듈 레이아웃

ejs 모듈은 레이아웃 기능을 지원하지 않는다. 대신 include라는 방식을 사용해 레이아웃 페이지처럼 만들 수 있다.

			$ express -e ejsLayout
			$ cd ejsTemplate
			$ npm install
		

views 폴더에 다음처럼 구성한다.

[ index.ejs ]

			<% include headerLayout.ejs %>
			

Torrentz is a free, fast and powerful meta-search engine combining results from dozens of torrent search engines

Torrentz is a free, fast and powerful meta-search engine combining results from dozens of torrent search engines

Torrentz is a free, fast and powerful meta-search engine combining results from dozens of torrent search engines

<% include footerLayout.ejs %>

이렇게 include 키워드를 사용하면 해당 파일의 내용이 그대로 옯겨진다. 따라서 headerLayout.ejs 파일과 footerLayout.ejs 파일이 레이아웃 페이지가 된다.

[ headerLayout.ejs ]

			<!DOCTYPE html>
			<html>
			<head>
				<title><%= title %></title>
			</head>
			<body>
				<header>
					<h1><%= title %></h1>
				</header>
				<hr />
		

[ footerLayout.ejs ]

			</body>
			</html>
		

socket.io 모듈


socket.io 모듈 기본

socket.io 모듈은 다음 명령어를 사용하여 설치한다.

			$ npm install socket.io
		

다음과 같이 폴더를 구성한다.

| 웹소켓 서버

웹소켓 서버를 생성할 때는 socket.io 모듈의 listen() 메서드를 사용하여 서버를 생성한다. listen() 메서드의 매개변수에는 웹소켓 서버 포트 번호를 입력할 수 있지만 일반적으로 server 객체를 매개변수로 입력한다.

[표. socket.io 모듈의 메서드]

메서드 이름 설명
listen() 서버를 생성 및 실행한다.
			//모듈 추출
			var http = require('http');
			var fs = require('fs');
			var socketio = require('socket.io');

			//웹서버 생성
			var server = http.createServer(function(request, response){
				fs.readFile('HTMLPage.html', function(error, data){
					response.writeHead(200, {'Content-Type' : 'text/html'});
					response.end(data);
				});
			}).listen(52273, function(){
				console.log('Server running at http://127.0.0.1:52273');
			});

			//소켓 서버를 생성 및 실행
			var io = socketio.listen(server);
		

소켓 서버를 생성한 이후에는 io.sockets 객체에 connection 이벤트를 연결한다. connection 이벤트는 클라이언트가 웹소켓 서버에 접속할 때 발생한다.

			//소켓서버를 생성 및 실행
			var io = socketio.listen(server);
			io.sockets.on('connection', function(socket){

			});
		

| 웹소켓 클라이언트

이제 HTMLPage.html을 작성한다. script 태그를 2개 추가하는데 하나는 socket.io.js 파일을 추가할 script 태그이고, 다른 하나는 입력할 script 태그이다. io 객체의 connect() 메서드를 호출하면 자동으로 웹 소켓 서버에 연결된다.

			<!DOCTYPE HTML>
			<html>
			<head>
			  <script src="/socket.io/socket.io.js"></script>
			  <script>
				window.onload = function(){
					// 소켓을 생성
					var socket = io.connect();
				};
			  </script>
			 </head>
			 <body>
			  
			 </body>
			</html>
		

프로젝트를 실행하고 웹브라우저로 http://127.0.0.1:52273/에 접속하면 콘솔 화면에 클라이언트가 접속했음을 출력한다.

위코드를 보면 웹서버에 socket.io.js 파일을 올린적이 없는데 사용하고 있는 것을 볼 수 있다. socket.io 모듈을 사용하면 socket.io.js 파일이 자동으로 등록된다. 서버를 실행하고 http://127.0.0.1:52273/socket.io/socket.io.js에 접속하면 자바스크립트 파일을 볼 수 있다.

| 웹소켓 이벤트

socket.io 모듈은 서버와 클라이언트 사이에 데이터를 교환할 때 이벤트를 사용한다.

[표. socket.io 모듈의 이벤트]

메서드 이름 설명
connection 클라이언트가 연결할 때 발생한다.
disconnect 클라이언트가 연결을 해제할 때 발생한다.

socket.io 모듈은 사용자 정의 이벤트를 굉장히 많이 사용한다. Node.js 이벤트와 비슷하게 socket.io 모듈은 이벤트를 생성하고 이벤트를 발생시킨다.

[표. socket 객체의 메서드]

메서드 이름 설명
on() 소켓 이벤트를 연결한다.
emit() 소켓 이벤트를 발생시킨다.

예를 들어 클라이언트가 서버로 데이터와 함께 rint 이벤트를 발생시키면 서버는 smart 이벤트를 발생시켜 사용자가 전달한 데이터를 그대로 클라이언트에 전달한다.여기서 rint 이벤트와 smart 이벤트는 사용자 정의 이벤트이다.

다음은 socket 객체에 rint 이벤트를 연결하고 이벤트가 발생할 때 smart 이벤트를 클라이언트에 전달한다.

			//소켓 서버를 생성 및 실행
			var io = socketio.listen(server);
			io.set('log level', 2);
			io.sockets.on('connection', function(socket){
				//rint 이벤트
				socket.on('rint', function(data){
					//클라이언트가 전송한 데이터를 출력한다.
					console.log('Client Send Data: ', data);

					//클라이언트에 smart 이벤트를 발생시킨다.
					socket.emit('smart', data);
				});
			});
		

다음은 클라이언트 파일이다.

			<body>
				<input type="text" id="text" />
				<input type="button" id="button" value="echo" />
			</body>
		

다음은 script 태그 안에서 이벤트를 연결한다. 서버에서 smart 이벤트가 전달될 때의 이벤트와 버튼을 클릭했을 때 2가지 이벤트를 연결한다. smart 이벤트가 발생할 때는 서버에서 전달된 데이터를 경고 화면에 출력한다. 또한 버튼을 클릭했을 경우에는 서버에 rint 이벤트를 발생시키며 데이터를 서버에 전달한다.

			<script>
				window.onload = function(){
					//소켓을 생성한다.
					var socket = io.connect();

					//소켓 이벤트를 연결한다.
					socket.on('smart', function(data){
						alert(data);
					});

					//문서 객체 이벤트를 연결한다.
					document.getElementById('button').onclick = function(){
						var text = document.getElementById('text').value;

						//데이터 전송
						socket.emit('rint', text);
					};
				};
			</script>
		

코드를 실행하고 http://127.0.0.1:52273/에 접속하여 글자를 입력한 후 echo 버튼을 누르면 경고화면이 뜬다.

소켓 통신 종류

socket.io 모듈로 생성할 수 있는 소켓 통신 방법은 3가지가 있다.

[표. socket 통신 종류]

통신 방법 이름 설명
public 자신을 포함한 모든 클라이언트에 데이터를 전달한다.
broadcast 자신을 제외한 모든 클라이언트에 데이터를 전달한다.
private 특정 클라이언트에 데이터를 전달한다.

| public 통신

클라이언트가 이벤트를 발생시키면 서버에서 모든 클라이언트에 이벤트를 발생시키는 방식이다.

public 통신을 구현하는 방법은 굉장히 간단하다. io.sockets 객체의 emit() 메서드를 사용하면 된다. 아래 코드처럼 io.sockets 객체의 emit() 메서드를 사용하여 smart 이벤트를 발생시킨다. 이렇게 하면 현재 접속한 모든 사용자에게 메시지를 보낼 수 있다.

			//소켓 서버를 생성 및 실행
			var io = socketio.listen(server);
			io.sockets.on('connection', function(socket){
				//rint 이벤트
				socket.on('rint', function(data){

					//public 통신
					io.sockets.emit('smart', data);
				});
			});
		

코드를 실행하고 브라우저를 여러 개를 실행해 http://127.0.0.1:52273/에 접속하여 메세지를 전달하면 모든 웹브라우저에서 경고 화면이 출력된다.

| broadcast 통신

broadcast 통신은 소켓 이벤트를 발생한 당사자에게는 이벤트를 전달하지 않는다. broadcast 통신을 할 때는 socket 객체의 broadcast 속성을 사용한다. broadcast 객체의 emit() 메서드를 사용하면 자신을 제외한 모든 사용자에게 이벤트를 전달한다.

			//소켓 서버를 생성 및 실행
			var io = socketio.listen(server);
			io.sockets.on('connection', function(socket){
				//rint 이벤트
				socket.on('rint', function(data){

					//broadcast 통신
					socket.broadcast.emit('smart', data);
				});
			});
		

코드를 실행하고 브라우저를 여러 개를 실행해 http://127.0.0.1:52273/에 접속한다. 한 브라우저에서 메세지를 전달하는 순간 자신을 제외한 모든 웹브라우저에서 경고 화면이 출력된다.

| private 통신

private 통신은 소켓 이벤트를 발생한 당사자가 선택된 특정 대상에게만 데이터를 전달하는 방법을 뜻한다. 채팅에서 귓속말을 하는 경우가 이에 해당한다.

다음은 가장 최근 접속한 클라이언트 한명에게 데이터를 전달하는 예제이다. 변수 id를 선언하고 connection 이벤트가 발생할 때 socket 객체의 id 속성을 저장한다. 이어서 io.sockets 객체의 sockets 속성에서 특정 id를 선택하여 emit() 메서드를 호출하면 가장 최근에 접속한 클라이언트에 데이터가 전달된다.

			//소켓 서버를 생성 및 실행
			var id = 0;
			var io = socketio.listen(server);
			io.sockets.on('connection', function(socket){
				// id를 설정한다.
				id = socket.id;

				//rint 이벤트
				socket.on('rint', function(data){
					//private 통신
					io.sockets.sockets[id].emit('smart', data);
				});
			});		
		

클라이언트 정보 저장

클라이언트 정보를 저장할 때는 다음 메서드를 사용한다.

[표. 클라이언트 정보 저장]

메서드 이름 설명
set() 클라이언트 데이터를 저장한다.
get() 클라이언트 데이터를 추출한다.

다음과 같이 파일을 구성한다.

웹서버는 request 이벤트가 발생하면 간단한 HTML 페이지를 클라이언트에 제공한다. HTMLPage.html은 실행되자마자 사용자에게 이름을 입력받고 setname 이벤트와 함께 서버에 전달한다. 이어서 html 페이지에서 '이름 가져오기' 버튼을 누르면 getname 이벤트를 서버에 전달한다. 이때 서버는 개별적인 클라이언트에 저장된 데이터를 클라이언트에 responsename 이벤트와 함께 전달한다.

			//모듈 추출
			var fs = require('fs');

			//서버 생성
			var server = require('http').createServer();
			var io = require('socket.io').listen(server);

			//서버 실행
			server.listen(52273, function(){
				console.log('Server running at http://127.0.0.1:52273');
			});

			//웹서버 이벤트를 연결
			server.on('request', function(request, response){
				fs.readFile('HTMLPage3.html', 'utf8', function(error, data){
					response.writeHead(200, {'Content-Type' : 'text/html'});
					response.end(data);
				});
			})

			//소켓서버 이벤트를 연결
			io.sockets.on('connection', function(socket){
				//setname 이벤트가 발생할 때
				socket.on('setname', function(data){
					//클라이언트 데이터를 저장
					socket.set('name', data);
				});

				//getname 이벤트가 발생할 때
				socket.on('getname', function(){
					//데이터를 추출
					socket.get('name', function(error, data){
						socket.emit('responsename', data);
					});;
				});
			});	
		

소켓 서버 객체에 connection 이벤트를 연결하고 사용자 정의 이벤트(setname과 getname)를 연결한다. 각 이벤트 안에서 set() 메서드와 get() 메서드로 데이터를 저장하고 추출한다.

			<!DOCTYPE html>
			<html>
			 <head>
			  <title> New Document </title>
			  <meta charset="utf-8">
			  <script src="/socket.io/socket.io.js"></script>
			  <script>
				window.onload = function(){
					//소켓을 생성한다.
					var socket = io.connect();
					 
					//변수를 선언한다
					var name = prompt('이름을 입력해주세요.', '이름');

					//데이터를 전송한다
					socket.emit('setname', name);

					//데이터를 전달받는다
					socket.on('responsename', function(data){
						alert(data);
					});

					//문서 객체 이벤트를 연결한다.
					document.getElementById('button').onclick = function(){
						socket.emit('getname');
					};
				};
			</script>
			 </head>

			<body>
			<button id="button">이름 가져오기</button>
			</body>

			</html>
		

방 생성

socket.io 모듈은 방을 생성하는 기능을 포함하고 있다.

[표. 방을 만들 때 사용하는 메서드]

메서드 이름 설명
socket.join() 클라이언트를 방에 집어 넣는다.
io.sockets.in() 특정 방에 있는 클라이언트를 추출한다.

다음과 같이 파일을 구성한다.

먼저 소켓 서버를 생성하고 connection 이벤트를 연결한다.

			// 모듈을 추출합니다.
			var fs = require('fs');

			// 서버를 생성합니다.
			var server = require('http').createServer();
			var io = require('socket.io').listen(server);

			// 서버를 실행합니다.
			server.listen(52273, function () {
				console.log('Server Running at http://127.0.0.1:52273');
			});

			// 웹 서버 이벤트를 연결합니다.
			server.on('request', function (request, response) {
				// HTMLPage.html 파일을 읽습니다.
				fs.readFile('HTMLPage4.html', function (error, data) {
					response.writeHead(200, { 'Content-Type': 'text/html' });
					response.end(data);
				});
			});

			// 소켓 서버 이벤트를 연결합니다.
			io.sockets.on('connection', function (socket) {
				// join 이벤트
				socket.on('join', function (data) {
					socket.join(data);
					socket.set('room', data);
				});

				// message 이벤트
				socket.on('message', function (data) {
					socket.get('room', function (error, room) {
						io.sockets.in(room).emit('message', data);
					});
				});
			});
		

join 이벤트는 클라이언트가 특정한 방에 접속하게 만드는 이벤트이며 message 이벤트는 같은 방에 속한 클라이언트에 메세지를 전달하는 이벤트 이다.

join 이벤트에서 join() 메서드를 사용하여 클라이언트를 접속하게 만들고 set() 메서드로 클라이언트에 자신의 방 번호를 부여한다.

message 이벤트는 get() 메서드를 사용하여 클라이언트 방 번호를 추출한다. 이어서 io.sockets 객체의 in() 메서드로 현재 접속한 클라이언트 중 같은 방을 공유하는 클라이언트를 추출한다. 추출한 클라이언트에 emit() 메서드를 사용하여 이벤트를 발생시키면 같은 방에 속하는 클라이언트에 데이터를 전달할 수 있다.

HTMLPage.html 페이지는 먼저 사용자에게 방 이름을 입력받고 방 이름을 join 이벤트와 함께 서버로 전달한다. 또한 버튼을 클릭하면 message 이벤트를 발생시키고 message 이벤트가 서버로 부터 전달될 때 데이터를 body 태그에 출력한다.

			<!DOCTYPE html>
			<html>
			<head>
				<meta charset="utf8" />
				<script src="http://code.jquery.com/jquery-1.9.1.js"></script>
				<script src="/socket.io/socket.io.js"></script>
				<script>
					window.onload = function () {
						// 변수를 선언합니다.
						var room = prompt('방 이름을 입력하세요.', '');
						var socket = io.connect();

						// 소켓 이벤트를 연결합니다.
						socket.emit('join', room);
						socket.on('message', function (data) {
							$('<p>' + data + '</p>').appendTo('body');
						});

						// 문서 객체 이벤트를 연결합니다.
						document.getElementById('button').onclick = function () {
							socket.emit('message', 'socket.io room message');
						};
					};
				</script>
			</head>
			<body>
				<button id="button">EMIT</button>
			</body>
			</html>
		

코드를 실행하고 웹 브라우저 3개를 실행해 2개는 같은 방 이름을 입력하고 1개는 다른 방 이름을 입력한다