웹 서버는 다른 서버 기능을 추가할 수 있는 서버이다. 노드에는 웹 서버를 만들 때 필요한 http모듈이 들어 있는데 이 모듈을 사용하면 HTTP프로토콜로 요청하는 내용과 응답을 모두 처리할 수 있다. 그러나 좀 더 쉽고 빠르게 웹 서버를 구축하려면 익스프레스를 사용하는 것이 좋다. 익스프레스는 웹 서버 기능을 쉽게 만들 수 있게 코드를 자동으로 만들어 준다
05-1 간단한 웹 서버 만들기
노드에 기본으로 들어있는 http 모듈을 사용하면 웹 서버 기능을 담당하는 서버 객체를 만들 수 있다.
var http =require('http');//웹 서버 객체 생성var server =http.createServer();//웹 서버를 시작해 3000번 포트에서 대기var port =3000;server.listen(port,function(){console.log('웹 서버가 시작되었다. %s',port);});
createServer() 메소드
이더넷(Ethernet)카드가 여려 개 있는 경우에는 서버에서 사용할 수 있는 IP주소가 여러개 존재한다. 특정 IP를 지정해서 서버를 실행해야할 때에는 listen()메소드를 호출하면서 IP주소를 직접 지정해준다. backlog란 실질적으로 동시 접속 연결 수를 결정하는 정보이다.
$ ifconfig명령어를 실행하면 사용하고 있는 IP를 확인할 수 있다.
var http =require('http');//웹 서버 객체 생성var server =http.createServer();// 웹 서버를 시작해 127.0.0.1 IP와 3000번 포트에서 대기var host ='127.0.0.1';var port =3000;server.listen(port,host,'50000',function(){console.log('웹 서버가 시작되었다. %s %d',host,port);});
클라이언트가 웹 서버에 요청할 때 발생하는 이벤트 처리하기
서버 객체에서 사용할 수 있는 주요 이벤트
var http =require('http');//웹 서버 객체 생성var server =http.createServer();//웹 서버를 시작해 3000번 포트에서 대기var port =3000;server.listen(port,function(){console.log('웹 서버 시작됨 %d',port);});// 클라이언트 연결 이벤트 처리server.on('connection',function(socket){var addr =server.address();console.log('클라이언트가 접속했습니다 : %s %d',addr.address,addr.port);});//클라이언트 요청 이벤트 처리server.on('request',function(req,res){console.log('클라이언트 요청이 들어왔습니ㅏㄷ.');console.dir(req);});//서버 종료 이벤트server.on('close',function(){console.log('서버 종료');});
그런데 웹 브라우저에서 페이지를 열어도 서버에서 아무런 응답을 보내지 않기 때문에 웹브라우저에서는 결과를 볼 수 없다.
express모듈을 사용하면 간단한 코드로 웹 서버의 기능을 구현할 수 있다. 미들웨어와 라우터를 사용하면 우리가 만들어야 하는 기능을 훨씬 편리하게 구성할 수 있다.
새로운 익스프레스 서버 만들기
express 모듈은 http 모듈 위에서 동작한다.
//Express 기본 모듈 불러오기var express =require('express'),http =require('http');// 익스프레스 객체 생성var app =express();// 기본 포트를 app 객체에 속성으로 설정// process.env객체에 PORT속성이 있으면 그 속성 사용, 아님 3000포트 사용app.set('port',process.env.PORT||3000);// Express 서버 시작http.createServer(app).listen(app.get('port'),function(){console.log('익스프레스 서버를 시작했습니다.'+app.get('port'));});
익스프레스 서버 객체
서버 설정을 위해 미리 정해진 app 객체 주요속성
미들웨어로 클라이언트에 응답 보내기
use()메소드를 사용해 미들웨어 설정하는 방법. 노드에서는 미들웨어를 사용해 필요한 기능을 순차적으로 실행할 수 있다.
익스프레스에서는 웹 요청과 응답에 관한 정보를 사용해 필요한 처리를 진행할 수 있도록 독립된 함수(미들웨어)로 분리한다. 각각의 미들웨어는 next()메소드를 호출해 그다음 미들웨어가 처리할 수 있도록 순서를 넘길 수 있다. 라우터는 클라이언트의 요청 패스를 보고 이 요청 정보를 처리할 수 있는 곳으로 기능을 전달해주는 역할을 한다. 이러한 역할을 흔히 라우팅이라 부른다.
var express =require('express') , http =require('http');var app =express();app.use(function(req,res,next){console.log('첫 번재 미들웨어에서 요청을 처리함');res.writeHead('200',{'Content-Type':'text/html;charset=utf-8'});res.end('<h1>서버에 응답한 결과입니다</h1>');});http.createServer(app).listen(3000,function(){console.log('3000포트에서 시작');});
미들웨어 함수는 클라이언트 요청을 전달받을 수 있다. 클라이언트 요청은 등록된 미들웨어를 순서대로 통과하며, 요청 정보를 사용해 필요한 기능을 수행할 수 있다.
여러 개의 미들웨어를 등록해 사용하는 방법 알아보기
var express =require('express') , http =require('http');var app =express();app.use(function(req,res,next){console.log('첫 번재 미들웨어에서 요청을 처리함');req.user ='seongwoo';next();res.writeHead('200',{'Content-Type':'text/html;charset=utf-8'});res.end('<h1>서버에 응답한 결과입니다</h1>');});app.use('/',function(req,res,next){console.log('두 번재 미들웨어에서 요청을 처리함');res.writeHead('200',{'Content-Type':'text/html;charset=utf-8'});res.end('<h1>서버에 응답한 결과입니다</h1>'+req.user);});http.createServer(app).listen(3000,function(){console.log('3000포트에서 시작');});
반드시 next()메소드를 호출해 두 번째 미들웨어로 처리 순서를 넘겨줘야한다. 미들웨어 안에서는 기본적으로 요청 객체인 req와 응답 객체인 res객체를 파라미터로 전달받아 사용할 수 있다.
익스프레스의 요청 객체와 응답 객체 알아보기
주요 메소드
app.use(function(req,res,next){console.log('첫 번재 미들웨어에서 요청을 처리함');res.send({name:'박우진',age:20});});
JSON데이터만 받아와서 게시물을 보여 줄 때 해당 데이터만 업데이트 하는 것이 효율적이다.
// public폴더의 모든 파일을 웹 서버의 루트패스로 접근하기var static =require('serve-static');...app.use(static(path.join(__dirname,'public')));app.use('/public',static(path.join(__dirname,'public')));
body-parser 미들웨어
클라이언트가 POST 방식으로 요청할 때 본문 영역에 들어 있는 요청 파라미터들을 파싱하여 요청 객체의 body속성에 넣어준다.
var express =require('express') , http =require('http'), path =require('path');//익스프레스 미들웨어 불러오기var bodyParser =require('body-parser'),static =require('serve-static');//익스프레스 객체 생성var app =express();// 기본 속성설정app.set('port',process.env.PORT||3000);// body-parser를 사용해 application/x-www-form-urlencoded 파싱app.use(bodyParser.urlencoded({extended:false}));// body-parser를 사용해 application/json 파싱app.use(bodyParser.json());app.use(static(path.join(__dirname,'public')));//미들웨어 파라미터 확인app.use(function(req,res,next){console.log('첫 번재 미들웨어에서 요청을 처리함');var paramId =req.body.id ||req.query.id;var paramPassword =req.body.password ||req.query.password;res.writeHead('200',{'Content-Type':'text/html;charset=utf-8'});res.write('<h1>Express 서버에서 응답한 값</h1>');res.write('<div><p>User-ID :'+paramId+'</p></div>');res.write('<div><p>param password :'+paramPassword+'</p></div>');res.end();});http.createServer(app).listen(3000,function(){console.log('3000포트에서 시작');});
05-4 요청 라우팅하기
**라우터 미들웨어(router middleware)**는 요청 url을 일일이 확인해야하는 번거로운 문제를 해결한다.
라우터 미들웨어 사용하기
라우터 미들웨어는 익스프레스에 포함되어있다.
//라우터 객체 참조var router =express.Router();//라우팅 함수 등록router.route('/process/login').get(...);router.route('/process/login').post(...);//라우터 객체를 app객체에 등록app.use('/',router);
/process/login/:name은 뒤에오는 파라미터를 req.params.name으로 접근 할 수 있다. 이것을 **토큰(Token)**이라한다.
오류 페이지 보여주기
app.all('*',function(req,res){res.status(404).send('<h1>ERROR - 페이지를 찾을 수 없습니다.</h1>');});
다른 사람이 만들어 둔 미들웨어를 사용할 수도 있다.
express-error-handler 미들웨어로 오류페이지 보내기
//404.html<!DOCTYPE html><htmllang="en"><head> <metacharset="UTF-8"> <title>오류페이지</title></head><body> <h3>ERROR - 페이지를 찾을 수 없습니다.</h3> <hr> <p>/public/404.html 파일의 오류 페이지 표시</p></body></html>
var errorHandler=expressErrorHandler({ static: {'404':'./public/404.html' }});app.use(expressErrorHandler.httpError(404));app.use(errorHandler);
토큰과 함께 요청한 정보 처리하기
//라우터 객체 참조var router =express.Router();//라우팅 함수 등록router.route('/process/users/:id').get(function(req,res){console.log('/process/users/:id 처리함');var paramid =req.params.id;console.log('/process/users와 토큰 %s를 이용해 처리',paramid);res.writeHead('200',{'Content-Type':'text/html;charset=utf8'});res.write('<h1>Express서버응답</h1>');res.write('<div><p>User-ID :'+paramid+'</p></div>');res.end();});//라우터 객체를 app객체에 등록app.use('/',router);
http://localhost:3000/process/users/2 이렇게 토큰을 사용하면 사용자 리스트 중에서 특정 사용자 정보를 id 값으로 조회하기에 편리하다.
05-5 쿠키와 세션 관리하기
사용자가 로그인한 상태인지 아닌지 확인하고 싶을 때에는 쿠키나 세션을 사용한다. 쿠키는 클라이언트 웹 브라우저에 저장되는 정보이며, 세션은 웹 서버에 저장되는 정보이다.
쿠키 처리하기
cookie-parser 미들웨어를 사용해 쿠키를 설정하거나 확인할 수 있다.
var cookieParser =require('cookie-parser');...app.use(cookieParser());...//라우터 객체 참조var router =express.Router();//라우팅 함수 등록router.route('/process/showCookie').get(function(req,res){console.log('/process/showCookie 처리함');res.send(req.cookies);});router.route('/process/setUserCookie').get(function(req,res){console.log('/process/setUserCookie 처리함');res.cookie('user',{ id:'dahye', name:'정다혜', authorized:true });res.redirect('/process/showCookie');});//라우터 객체를 app객체에 등록app.use('/',router);
쿠키가 브라우저에 제대로 되었는지 확인하기 위해서는 [개발자도구-Application -Cookies]에서 확인할 수 있다.
세션 처리하기
<!DOCTYPE html><htmllang="en"><head> <metacharset="UTF-8"> <title>상품 페이지</title></head><body> <h3>상품 정보 페이지</h3> <hr> <p>로그인 후 볼 수 있는 상품정보 페이지</p> <br><br> <ahref="/process/logout">로그아웃하</a></body></html>
로그인하여 세션이 만들어지면 connect.sid 쿠키가 브라우저에 저장된다. 이 쿠키는 세션 정보를 저장할 때 만들어진 것이다.
05-6 파일 업로드 기능 만들기
파일을 업로드할 때는 멀티파트포맷으로 된 파일 업로드 기능을 사용하여 파일 업로드 상태를 확인할 수 있다.
multer 미들웨어 설치해서 파일 업로드하기
파일을 업로드할 때는 클라이언트에서 POST 방식으로 데이터를 전송하므로 body-parser 미들웨어 함께 사용한다.
multer미들웨어 사용 : 미둘웨어 사용 순서가 중요하다. body-parser->multer->router
주요속성
클라이언트의 요청 처리 함수 추가하기
//Express 기본 모듈var express =require('express') , http =require('http'), path =require('path');//익스프레스 미들웨어 불러오기var bodyParser =require('body-parser'),cookieParser =require('cookie-parser'),static =require('serve-static'),errorHandler=require('errorhandler');//오류 핸들러 모듈 사용var expressErrorHandler =require('express-error-handler');//Session 미들웨어var expressSession =require('express-session');//파일 업로드용 미들웨어var multer =require('multer');var fs =require('fs');//클라이언트에서 ajax로 요청했을 때 CORS(다중서버) 지원var cors =require('cors');//익스프레스 객체 생성var app =express();// 기본 속성설정app.set('port',process.env.PORT||3000);// body-parser를 사용해 application/x-www-form-urlencoded 파싱app.use(bodyParser.urlencoded({extended:false}));// body-parser를 사용해 application/json 파싱app.use(bodyParser.json());//public,uploads 폴더 오픈app.use('/public',static(path.join(__dirname,'public')));app.use('/uploads',static(path.join(__dirname,'uploads')));//cookie - parser 설정app.use(cookieParser());//session설정app.use(expressSession({ secret:'my kye', resave:true, saveUninitialized:true}));//ajax 요청했을때 다중서버접속 지원app.use(cors());//multer미들웨어 사용 : 미둘웨어 사용 순서가 중요하다. body-parser->multer->router//파일 제한 10개,1Gvar storage =multer.diskStorage({destination:function(req,file,callback){callback(null,'uploads'); },filename:function(req,file,callback){callback(null,file.originalname+Date.now()); }});var upload =multer({ storage: storage, limits: { files:10, filesize:1024*1024*1024 }});//라우터 사용해 라우팅 함수 등록var router =express.Router();router.route('/process/photo').post(upload.array('photo',1),function(req,res){console.log('/process/photo 처리함');try{var files =req.files;console.dir('#===== 업로드된 첫번째 파일 정보 =====#');console.dir(req.files[0]);console.dir('#================================#');//현재의 파일 정보를 저장할 변수 선언var originalname ='', filename ='', mimetype='', size=0;//배열에 들어가 있는 경우(설정에서 1개의 파일도 배열에 넣음)if(Array.isArray(files)){console.log('배열에 들어있는 파일 수 : %d',files.length);for (var index =0; index<files.length; index ++) { originalname=files[index].originalname; filename=files[index].filename; mimetype=files[index].mimetype; size=files[index].size; } }else{ // 배열에 들어가 있지않은 경우console.log('파일 갯수 : 1'); originalname=files[index].originalname; filename=files[index].filename; mimetype=files[index].mimetype; size=files[index].size; }console.log('현재 파일 정보 : '+originalname+','+filename+','+mimetype+','+size);//클라이언트에 응답 전송res.writeHead('200',{'Content-Type':'text/html;charset=utf8'});res.write('<h3>파일업로드 성공</h3>');res.write('<hr>');res.write('<p>원본 파일 이름 :'+originalname+'-> 저장 파일명 : '+filename+'</p>');res.write('<p>mime type :'+mimetype+'</p>');res.write('<p>파일 크기 :'+size+'</p>');res.end(); }catch(err){console.dir(err.stack); }});//라우터 객체를 app객체에 등록app.use('/',router);http.createServer(app).listen(3000,function(){console.log('3000포트에서 시작');});