Skip to main content

[dB'S NODE.JS] 노드 기본 개념 및 기능 (BASIC CONCEPT)

01. 기본개념

노드는 크롬의 V8 자바스크립트 엔진 위에서 실행됩니다. 엔진에 필요한 기능을 병렬로 실행하는 '스레드풀'과 이벤트를 받아 처리하는 '이벤트루프'를 기본구조로, 그 위에 네트워킹을 담당하는 소켓, http 라이브러리가 있습니다. 그 위에는 표준 라이브러리가 있으며 자바스크립트를 사용해서 라이브러리를 사용할 수 있습니다.

Process 객체
: 프로그램을 실행했을 때 만들어지는 프로세스 정보를 다루고 있는 객체입니다.
  • argv: 프로세스를 실행할 때 전달되는 파라미터 정보
  • env: 환경 변수 정보
  • exit(): 프로세스를 끝내는 메소드

console.log(process.argv.length);
console.dir(process.argv);

//result
[ '/usr/local/bin/node',
  '/Users/naver/Desktop/nodejs/ch02_test2.js' ]
node.exe 파일이 첫 번째 파라미터이고 실행된 자바스크립트 파일은 두 번째 파라미터가 됩니다. 노드를 설치하면 미리 정의된 내장모듈 사용이 가능합니다. 그 중에서 os 모듈, path 모듈 등이 있습니다. 파일 패스를 다루는 path 모듈은 아래와 같습니다. (path 문서)
console.log(process.argv.length);
var path = require('path');

var directories = ["users", "mike", "docs"];
var docsDirectory = directories.join(path.sep);
console.log(docsDirectory);

var curPath = path.join('/Users/mike', 'notepad.exe');
console.log(curPath);
주소 문자열과 요청파라미터
: url 모듈을 사용하면 일반 주소 문자열을 URL 객체로 만들 수 있으며 일반 문자열로 변환이 쉬어집니다.
  • parse(): 주소 문자열을 파싱하여 URL 객체를 만들어 줍니다.
  • format(): URL 객체를 주소 문자열로 변환합니다.
var url = require('url');

var curURL = url.parse('https://www.youtube.com/watch?v=Wgs2ahWHjHQ&index=24&list=RDEMN0b_oSABQRVM34ADsmrK2w');
console.log(curURL);

// result
Url {
  protocol: 'https:',
  slashes: true,
  auth: null,
  host: 'www.youtube.com',
  port: null,
  hostname: 'www.youtube.com',
  hash: null,
  search: '?v=Wgs2ahWHjHQ&index=24&list=RDEMN0b_oSABQRVM34ADsmrK2w',
  query: 'v=Wgs2ahWHjHQ&index=24&list=RDEMN0b_oSABQRVM34ADsmrK2w',
  pathname: '/watch',
  path: '/watch?v=Wgs2ahWHjHQ&index=24&list=RDEMN0b_oSABQRVM34ADsmrK2w',
  href: 'https://www.youtube.com/watch?v=Wgs2ahWHjHQ&index=24&list=RDEMN0b_oSABQRVM34ADsmrK2w' }
querystring 모듈을 사용해서 요청 파라미터를 쉽게 분리할 수 있습니다.
  • parse(): 요청 파라미터 문자열을 파싱하여 요청 파라미터 객체로 만들어 줍니다.
  • stringify(): 요청 파라미터 객체를 문자열로 변환합니다.
간단 모듈 만들어보기

//file1
var util = require('util');
var EventEmitter = require('events').EventEmitter;

var Calc = function() {
 var self = this;

 this.on('stop',function(){
  console.log('event occured!');
 });
};

util.inherits(Calc, EventEmitter);

Calc.prototype.add = function(a, b) {
 return a + b;
}

module.exports = Calc;
module.exports.title = 'calculator';

//file2
var Calc = require('./file1');

var calc = new Calc();
calc.emit('stop');
파일 다루기
파일 및 디렉터리를 다루는 기능이 있으며, 동기식, 비동기식을 함께 제공합니다. 파일을 비동기식으로 불러오는 방법을 더 많이 사용합니다.

//파일 읽기
var fs = require('fs');

fs.readFile('./package.json', 'utf8', function(err, data){
 console.log(data)
});
//파일 쓰기
fs.writeFile('./package.json', 'hello world', function(err){
 console.log('completed')
});
실제로 파일에 데이터를 기록하기 위해서는 open -> write -> close 단계가 필요합니다.

var fs = require('fs');

fs.open('./output.txt', 'w', function(err, fd){
 if(err) throw err;

 var buf = new Buffer('hello\n'); // 기본 utf-8 인코딩.
 fs.write(fd, buf, 0, buf.length, null, function(err, written, buffer){
  if(err) throw err;
   console.log(err, written, buffer);
  fs.close(fd, function(){
   console.log('completed')
  })
 })
});
데이터는 필요한 만큼 Buffer 객체 안에 기입합니다. 파일이 열리면 파일을 구별하는 fd객체를 전달 받습니다. Buffer 객체는 바이너리 데이터를 읽고 쓰는데 사용됩니다. new로 생성한 다음 안에 들어갈 바이트 데이터의 크기만 지정하면 됩니다. 자바스크립트에서 바이너리 데이터 관리를 위해 Buffer을 사용합니다.

winston 라이브러리를 사용하면 로그 파일을 남길 수 있습니다.

02. 웹 서버 만들기

노드에서 웹 서버는 다른 서버 기능을 추가할 수 있습니다. 웹 서버는 일반적으로 웹 브라우저(클라이언트)에서 HTTP 프로토콜로 요청한 정보를 처리한후 응답을 보내주는 역할을 합니다.

var http = require('http');

var server = http.createServer();

var port = 3000;
server.listen(port, function(){
 console.log('test')
})
클라이언트가 웹 서버에 요청할 때 발생하는 이벤트 처리가 필요하며 주로, connection, request 이벤트가 자주 사용됩니다. connection은 클라이언트가 접속되었을 때, request는 요청을 발생할 때 생기는 이벤트입니다.

var http = require('http');

var server = http.createServer();

var port = 3000;
server.listen(port, function(){
 console.log('works!');
});

server.on('connection', function(socket){
 var addr = socket.address();
 console.log('cconnection', addr.address, addr.port);
});

server.on('request', function(req, res){
 console.log('request');

 res.writeHead(200, {"Content-Type": "text/html; charset=utf-8"});
 res.write("<!DOCTYPE html>");
 res.write("<html>");
 res.write("<h1>Hello World!</h1>")
 res.write("</html>");
});
request 요청이 오면, writeHead(), write() 메소드로 데이터를 만들어 클라이언트로 보냅니다. 이제부터는 좀 더 편하게 express로 웹 서버를 만들어 보도록 하겠습니다.

var express = require('express');
var http = require('http');

var app = express();

app.set('port', process.env.Port || 3000);

http.createServer(app).listen(app.get('port'), function(){
 console.log('start!')
});
express 모듈은 웹서버를 만들기위한 것으로 http 모듈 위에서 작동합니다. 그렇기 때문에 이 두 가지 모듈은 항상 함께 불러들어야 합니다. 여기서 app 객체는 express() 메소드 호출로 만들어지는 익스프레스 서버 객체로서 주요 메소드를 가지고 있습니다.
set(서버설정 속성지정), get(설정속성을 가져옴), use(미들웨어사용), get(특정 패스로 요청된 정보 처리)


03. 미들웨어와 라우터

미들웨어와 라우터는 하나의 독립된 함수입니다. 예를들어, 로그를 남기는 간단한 함수를 만든 후 use() 메소드를 사용해 미들웨어로 등록해 사용이 가능합니다. next()를 사용해서 여러개의 미들웨어를 순차적으로 사용할 수 있습니다.

라우터는 클라이언트의 요청 패스를 보고 정보를 처리하는 것을 말하며 흔히 라우팅이라고 합니다. use()의 사용법을 보도록 하겠습니다.

var express = require('express');
var http = require('http');

var app = express();

app.use(function(req, res, next){
 console.log('first middleware');
});

http.createServer(app).listen(3000, function(){
 console.log('start!');
});
미들웨어를 연속적으로 사용하기 위해서는 next()가 필요합니다.

var express = require('express');
var http = require('http');

var app = express();

app.use(function(req, res, next){
 console.log('first middleware');

 req.user = 'Ryan';

 next();
});

app.use('/', function(req, res, next){
 console.log('second middleware');

 res.end('My name is ' + req.user);
});

http.createServer(app).listen(3000, function(){
 console.log('start!');
});
클라이언트로부터 요청을 받으면 미들웨어로 요청이 가고 그 안에 정의된 함수가 실행되고 완료가 되면 다시 클라이언트로 응답이 갑니다. 익스프레스에서 사용하는 응답/요청 객체의 종류는 아래와 같습니다. send(), status(), sendStatus(), redirect(), render(). 다음은 요청 객체에서 사용가능한 헤더와 파라미터를 살펴보도록 하겠습니다.

  • query: 클라이언트에서 GET 방식으로 전송한 요청 파라미터를 확인 함.
  • body: 클라이언트에서 POST 방식으로 전송한 요청 파라미터를 확인 함. 단, body-parser와 같은 외장 모듈을 사용해야함.
  • header: 헤더를 확인 함.

04. 미들웨어 사용하기

1. static 미들웨어
특정 폴더의 파일들을 특정 패스로 접근할 수 있도록 함. 예를들어, public 폴더를 웹서버의 루트 패스로 접근할 수 있도록 만들기 위해서는 아래와 같이 설정하면 public 폴더 안에 있는 파일들을 클라이언트에서 바로 접근이 가능합니다.

var static = require('serve-static');

app.use('/public', static(path.join(__dirname, 'public')));
2. body-parser
미들웨어 POST 요청시 주소 문자열에 요청 파라미터가 들어오는 것이 아닌, 본문영역(body)에 요청 파라미터가 들어가 있기 때문에 body-parser를 사용하여 파라미터를 파싱하여 body 속성에 넣어줍니다.

app.js
public
 |_login.html
app.js 파일을 만들고 public 폴더 안에 login.html 을 생성합니다. body-parser 모듈을 이용하여 파싱을합니다(링크)

var express = require('express');
var http = require('http');
var path = require('path');

var bodyParser = require('body-parser');
var static = require('serve-static');

var app = express();

app.use(bodyParser.urlencoded({extended: false}));
app.use(bodyParser.json());

app.use(static(path.join(__dirname, 'public')));

app.use(function(req, res, next){
 var paramId = req.body.id || req.query.id;
 var paramPassword = req.body.password || req.query.password;

 res.writeHead('200', {'Content-Type' : 'text/html;charset=utf8'});
 res.write('<h1>' + paramId + '</h1>');
 res.write('<h1>' + paramPassword + '</h1>');
 res.end();
});

http.createServer(app).listen(3000, function(){
 console.log('start!');
});

05. 요청 라우팅 & 쿠키, 섹션

라우터 미들웨어를 사용하면 사용자가 요청한 기능을 패스로 구별하여합니다. 요청 패스를 라우터 객체에 등록할 때 메소드는 다음과 같습니다. 
  • get
  • post
  • put
  • delete
  • all
var router = express.Router();

router.route('/process/login').get(...);
router.route('/process/login').post(...);

app.use('/', router);

<form method="post" action="/process/login">

클라이언트에서 요청시 URL뒤에 ? 를 추가하면 요청 파라미터를 추가하여 보낼 수 있습니다. 이외에도 URL파라미터 방식도 있습니다. 다른점이 있다면 URL주소의 일부로 들어갑니다.

var router = express.Router();

router.route('/process/login').post(function(req, res){

 var paramId = req.body.id || req.query.id;
 var paramPassword = req.body.password || req.query.password;

 res.writeHead('200', {'Content-Type' : 'text/html;charset=utf8'});
 res.write('<h1>' + paramId + '</h1>');
 res.write('<h1>' + paramPassword + '</h1>');
 res.end();
});

app.use('/', router);
router를 사용할 때 중요한 것이 라우터 객체를 app 객체에 등록해야 사용 가능합니다. URL 파라미터는 아래와 같이 사용할 수 있습니다.



router.route('/process/login/:name').post(function(req, res){

 var paramName = req.params.name;

 var paramId = req.body.id || req.query.id;
 var paramPassword = req.body.password || req.query.password;

 res.writeHead('200', {'Content-Type' : 'text/html;charset=utf8'});
 res.write('<h1>' + paramName + '</h1>');
 res.write('<h1>' + paramId + '</h1>');
 res.write('<h1>' + paramPassword + '</h1>');
 res.end();
});
페이지오류를 처리하는 미들웨어도 추가할 수 있습니다. express-error-handler 모듈을 설치해줍니다. expressErrorHandelr가 404 오류를 감지하면 미리 등록해놓은 404.html 페이지를 노출시킵니다.

var expressErrorHandler = require('express-error-handler');

var errorHandler = expressErrorHandler({
 static: {
  '404': './public/404.html'
 } 
});

app.use(expressErrorHandler.httpError(404));
app.use(errorHandler);
쿠키와 세션을 통해 사용자가 로그인한 상태인지 아닌지 확인이 가능합니다. 쿠키는 클라이언트 웹 브라우저에 저장되는 정보이며, 세션은웹 서버에 저장됩니다. 세션을 만들어 보도록 하겠습니다. 익스프레스에서 세션을 사용하기 위해서는 express-session 모듈을 사용합니다.

참고링크
https://github.com/shlee1353/react-contents

Comments

Popular posts from this blog

[dB's 리액트] Render Prop 사용하기

HOC는 코드 재사용 문제를 해결하기 위해 고안되었습니다. createClass 패러다임에서는 이러한 문제를 mixins으로 해결하였습니다. ES6 classes 가 등장하고 createClass는 사라지기 시작했습니다. 하지만 ES6 클래스는 mixins을 지원하지 않는다는 문제점이 있었습니다. 그 결과 HOC가 등장하였으며 재사용에 대한 이슈를 해결한 듯 보였지만, 기존 mixins이 가지고 있던 모든 이슈들을 해결한 건 아니였습니다. Render Props를 사용하면 mixins과 HOC가 가지고 있는 문제점들을 해결할 수 있습니다. Render Props 패턴은 함수로 이루어진 props를 이용하여  컴포넌트 간 코드를 공유하는 방법입니다. <DataProvider render={data => ( <h1>Hello {data.target}</h1> )}/> import React from 'react' import ReactDOM from 'react-dom' import PropTypes from 'prop-types' // Instead of using a HOC, we can share code using a // regular component with a render prop! class Mouse extends React.Component { static propTypes = { render: PropTypes.func.isRequired } state = { x: 0, y: 0 } handleMouseMove = (event) => { this.setState({ x: event.clientX, y: event.clientY }) } render() { return ( <div style={{ height: '100%' }} on...

[dB's 리액트] 리액트 16.3 무엇이 바뀌었나?!

리액트 16.3 버전이 새롭게 업데이트 되었습니다. 새로운 라이프 사이클과 API들이 소개 되었습니다: context API, ref forwarding API, ergonomic ref API 입니다. Context API Context API는 기존에도 제공되었습니다. 하지만, 상속 문제로 인해 사용 빈도가 적었으며 더 좋은 방법들로 대체되었습니다. 예로, redux, react-router, styled-components 등이 Context API를 기반으로 제작 되었습니다. Context는 CreateContext 함수를 이용해서 제작합니다. 이 함수는 Provider와 Consumer를 호출할 수 있습니다. Provider는 Context에서 사용할 값을 설정하고 Consumer는 설정한 값을 불러올 때 사용합니다. const ThemeContext = React.createContext('light'); class ThemeProvider extends React.Component { state = {theme: 'light'}; render() { return ( <ThemeContext.Provider value={this.state.theme}> {this.props.children} </ThemeContext.Provider> ); } } class ThemedButton extends React.Component { render() { return ( <ThemeContext.Consumer> {theme => <Button theme={theme} />} </ThemeContext.Consumer> ); } } 링크 https://hackernoon.com/how-to-use-the-new-react-co...

[dB's 몽고DB] MongoDB 데이터베이스 사용하기

오라클, MySQL 을 관계형 데이터베이스 라고 합니다. 이는 데이터를 담고 있는 복수 개의 테이블들이 긴밀하게 연결되어 있기 때문입니다. 이와 다르게, 몽고DB는 NoSQL 입니다. "SQL이 필요없다"가 아니라 "비관계형 데이터베이스 SQL" 이라는 뜻을 내포하고 있습니다. 이유는, DB에 접근하기 위해서는 결국 몽고DB에서 사용하는 쿼리문법이 필요하기 때문입니다. 특별히, 몽고DB는 자바스크립트 객체를 그대로 저장할 수 있어서 자바스크립트를 사용하는 노드에서 데이터를 저장하기에 적합합니다. 그렇기 때문에 데이터 저장, 조회 방식도 기존 SQL과 다릅니다. 이는 성능을 최우선으로 생각하기 때문에 고안된 방법이며 실시간 서비스나 대용량 트래픽을 감당할 수 있는 메시징 시스템등에 사용됩니다. 몽고DB는 데이터베이스의 테이블 대신, 여러 데이터가 모인 컬렉션(Collection)을 사용합니다. 즉, 데이터베이스는 컬렉션의 집합이고 각각의 컬렉션은 여러개의 문서객체(Document)를 가질 수 있습니다. 1. 몽고DB 설치하기 몽고DB 다운로드 센터로 이동 Installing with Homebrew 몽고DB 설치 brew install mongodb 데이터베이스 저장 폴더 만들기 mkdir -p /data/db 권한 부여하기 chown $USER /data/db 몽고DB 활성화가 되면서 27017 포트에서 연결을 기다린다는 메시지가 표시됨   mongod 몽고Shell 접속하기 새로운 터미널에서 mongo 2. 데이터베이스 생성과 입력, 삭제 데이터베이스 지정 use local 컬렉션 만들기 db.users.insert({name: '소녀시대'}) 컬렉션 문서조회 db.users.find().pretty() 문서삭제 d b.users.remove({name:/소녀시대/}) 로그인에 필요한 데이터 입력 db.users.insert({id: 'tes...