Node.js

[Node.js] 1. 시작 및 주요개념 정리

반응형

서버 측 자바스크립트와 nodejs 소개


자바스크립트가 생기면서, 웹을 동적으로 변화시키게 되었다.

사용자와 상호작용하게 되는 복합적인 기능들을 사용할 수 있게 되면서 웹에 대한 중요한 발전 계기가 된 것이 바로 자바스크립트!

 

최초의 자바스크립트에서는, 웹에서만 동작했다. 따라서 당시에는 크게 효용성이 없던 언어였지만, 2004년 구글의 gmail, gmap 서비스에 의해 다른 변화가 일어나기 시작한다. 지금까지 전세계적으로 인기를 끌며 사용되고 있는 이 서비스에서 자바스크립트를 사용한 것이다. 그리고 2008년, 다시한번 구글에서 크롬 브라우저를 개발하기 위한 자바스크립트 V8엔진을 만들었다. 구글은 이걸 오픈 소스로 공개하면서, 앞으로 웹이 아닌 다른 영역에서도 자바스크립트가 들어가게 되는 혁명과도 같은 시초가 되었다.

웹에서는 자바스크립트만 사용 가능, 하지만 자바스크립트로 다른 언어 대체 가능. 이것은 자바스크립트가 점점 엄청난 발전을 가져올 수 있었던 이유다.

 

nodejs는 구글의 v8엔진을 기본으로 사용하고, event-driven 자바스크립트 개발 방식, non-blocking 10 방식을 결합해서 만든 프로젝트다.

 

즉, 웹 뿐만아니라 서버에서도 동작하는 자바스크립트라고 할 수 있다.

 

Web Browser vs Nodejs

언어 측면의 자바스크립트 - web browser

자바스크립트가 동작하는 환경(런타임) - nodejs

 

쉽게 예시를 들어보자.

자바스크립트를 사용하는 것 -> 한국어를 사용하는 것

자바스크립트를 통해 웹브라우저를 제어하거나 서버쪽의 nodejs를 제어하는 것 -> 한국어를 통해 병원, 법원에 가서 이야기하는 것

 

웹브라우저와 nodejs는 다른 영역에서 동작하는 기술이고, 협력적인 관계에 있는 기술이다. 이 두 기술에서 자바스크립트를 이용해 하나의 완결된 웹 애플리케이션을 만들 수 있는 것이다.

 

서버쪽에 동작하는 Nodejs의 경쟁 언어 - 파이썬, Ruby, Java 등


그러면 Nodejs의 장점은?

성능이 상당히 좋아서 속도가 빠름. 여러 패러다임을 사용해서 경우에 따라 적합한 경우 굉장하게 빠른 퍼포먼스를 만들 수 있음. 무엇보다 좋은 장점은 클라이언트와 서버의 자바스크립트 활용으로 하나의 완결된 애플리케이션을 만들 수 있다는 점이다. (웹브라우저와 서버쪽에서 같은 언어로 구현한다는 것은 굉장히 매력적인 일이다.)

 

Nodejs 시작

 

  • node를 이용해 터미널에서 출력해보기

사용자 폴더에 dev/js/server_side_javascript 폴더 생성

편집기를 사용해 해당 폴더 열기

hello.js파일을 만들고 아래와 같이 작성 후 저장

1
console.log('Hello world');
cs

터미널(cmd)을 열어서 해당 폴더까지 경로 이동

(ex. cd dev/js/server_side_javascript)

이제 node를 이용해 hello.js를 불러보자

1
 node hello.js
cs

엔터를 치면, Hello world라고 출력되는 것을 볼 수 있다.

 

 

- 간단한 서버 애플리케이션 만들어보기

편집기에서 webserver.js라는 새로운 파일을 만들어보자.

webserver.js
1
2
3
4
5
6
7
8
9
10
11
const http = require('http');
 
const hostname = '127.0.0.1';
const port = 1337;
 
http.createServer((req, res) => {
  res.writeHead(200, { 'Content-Type''text/plain' });
  res.end('Hello World\n');
}).listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});
cs
해당 코드는 nodejs 홈페이지에 올라와있는 코드다.

 

이 파일을 저장하고, 이전과 동일하게 터미널에서 node로 실행시켜본다. 그러면 아래와 같은 글이 출력된다.

1
 Server running at http://127.0.0.1:1337/
cs

우리 컴퓨터에서 작동하는 서버의 주소를 보여주는 모습이다.

이를 그대로 웹브라우저 주소창에 쳐서 접속해보면, 해당 webserver.js에 입력한 res.end('Hello World\n');의 Hello World가 그대로 출력되는 모습을 볼 수 있다.

 

- 인터넷의 동작 방법

이 수업에서 다루는 내용은 총 4가지 개념: 서버, 클라이언트, IP, Port

1
2
http://a.com은 도메인 주소 : 접속하기 쉽도록 만들어진 이름
실제로 접속하는 것은 52.192.173.151과 같은 IP주소


cs

사용자가 a.com으로 접속을 하면?

만약 사용자의 컴퓨터에는 데이터베이스 서버, 채팅 서버, 웹 서버 등 다양한 서버 애플리케이션들이 있다면 사용자가 a.com에 접속했을 때, 서버 컴퓨터는 이 중에 어떤 서버를 연결시켜주어야 하는가. 이것이 이번 수업에서 중요한 부분이다.

 

포트는 0 ~ 65535까지 존재. 우리는 이중에서 80이라는 포트 번호를 통해 웹 서버에 들어간다. 접속할 때 이 포트번호는 생략이 가능하다. 사용자가 http로 접속했기 때문에 서버에서는 자동으로 웹 서버인 80포트에 들어가는 것이다.

1
http://a.com:80
cs

따라서 포트번호를 1337로 주고 접속을 하면, 아무런 일이 일어나지 않는 것을 경험할 수 있다. 만약에 이 포트번호로 접속하고 싶다면?

포트번호를 1337로 주고 접속할 수 있도록 따로 서버를 만들어주어야 한다.

 

다시한번 Nodejs에서 제공하는 기본 코드를 살펴보자

1
2
3
4
5
6
7
8
9
10
11
const http = require('http');
 
const hostname = '127.0.0.1';
const port = 1337;
 
http.createServer((req, res) => {
  res.writeHead(200, { 'Content-Type''text/plain' });
  res.end('Hello World\n');
}).listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});
cs

코드를 살펴보면, 아직 정확한 의미는 모르지만 createServer를 통해 서버를 생성하고, listen을 통해 기존에 const로 지정한 port와 hostname 변수를 가지고 서버에 접속하는 것을 이해할 수 있다.

즉, 127.0.0.1이라는 IP 주소에 1337포트 번호로 서버를 생성하는 것이다.

1
http://127.0.0.1:1337/
cs




모듈과 NPM


 

  • 모듈

모듈이라는 말이 어렵게 느껴질 수 있다. 부품이라고 생각하자

1
2
3
4
5
6
const http = require('http');
http라는 모듈을 요구하는 상황. 
이를 http라는 상수(const)에 담는 것
// 
const를 사용하면, 
그 값을 이후에 변경할 수 없는 수라고 생각하면 된다.

cs


1
2
3
4
5
6
7
8
9
http.createServer((req, res) => {
  res.writeHead(200, { 'Content-Type''text/plain' });
  res.end('Hello World\n');
}).listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});
//
http라는 모듈에 createServer라는 함수를 호출하면, 
http의 서버에 해당하는 객체를 리턴한다. 
이 객체는 listen이라는 메소드를 가지고 있어서, 
이 객체를 리턴해서 호출하는 모습이다.
cs


os.platform() -> os라는 모듈에 platform이라는 메소드를 호출하는 것

module.js라는 파일을 만들고 다음과 같이 작성해보자

1
2
3
4
5
var o = require('os'); // os라는 모듈을 리턴
console.log(o.platform());
출력값
win32
cs

현재 진행하는 컴퓨터의 os 플랫폼이 출력된다.

 

이번 시간에 http와 os라는 모듈을 사용해보았다. 다양한 모듈은 nodejs 홈페이지에서 확인할 수 있다. 이 밖에도 자바스크립트에서 제공하는 모듈도 많으니 여러가지를 접해보도록 하자.

 

  • NPM

NPM = Node Package Manager

Node계의 앱스토어라고 생각하자. Node에서 사용할 수 있는 여러 패키지들을 관리하는 것이다. 이를 찾은 다음 설치만 하면 사용이 가능하다!

모듈을 설치, 삭제, 업그레이드, 의존성(프로젝트에서 사용하고 있는 다른 사람의 모듈들은 우리의 프로젝트가 그 모듈들의 의존관계에 있다고 할 수 있다.) 관리

 

npm으로 만들어진 모듈을 검색하는 사이트 : https://www.npmjs.com

 

이제 npm을 이용해서 nodejs의 소프트웨어를 이용하는 방법을 알아보자

해당 사이트에서 uglify-js를 검색해보자

설치방법은 두 가지가 있다.

1
2
3
4
5
6
7
8
1) npm install uglify-js -g
2) npm install uglify-js
//
g는 global을 뜻한다. 
1)처럼 -g를 붙여서 설치하면, 
이 컴퓨터 전역에서 사용하는 독립적인 소프트웨어로 설치함을 뜻한다.
2)로 그냥 설치하는 것은, 
현재 있는 프로젝트 안에서 부품으로 사용하겠다는 뜻이다.
cs

1번을 이용해서 터미널에 입력해보자.

 

uglify를 npm으로 설치한 이후에 편집기를 통해서 프로젝트에 pretty.js 파일을 만들어보자. 그리고 다음과 같이 함수 하나를 작성한다.

1
2
3
4
5
function hello(name) {
    console.log('Hi,'+name);
}
hello('kim');
cs

저장 후, 터미널로 돌아와 이전에 설치한 uglify 모듈을 사용해보자.

터미널에 uglifyjs pretty.js라고 입력해보면 다음과 같은 출력 결과가 나온다.

uglifyjs pretty.js

1
function hello(name){console.log("Hi,"+name)}hello("kim");
cs

못생김을 뜻하는 이 npm 모듈은, 가독성이 떨어지도록 위처럼 다 붙여진 상태로 출력하게 만든다. 이건 실제로 프로그램을 실행할 필수적인 코드를 제외하고, 나머지 메모리에 해당하는 걸 다 지워주는 역할을 해준다. 따라서 실행 속도를 빠르게 해주는 도움을 받을 수 있다.

 

이번엔 뒤에 추가로 -m을 붙여보자

uglifyjs pretty.js -m

1
function hello(l){console.log("Hi,"+l)}hello("kim");
cs

이는 우리가 hello라는 메소드에 설정한 name이라는 변수를 l이라고 수정시켜 크기를 줄여주는 모습을 볼 수 있다. 따라서 메모리를 최대한 줄이고 싶을 때 사용하면 효과를 볼 수 있는 모듈이라고 할 수 있다.

이 파일을 저장하고 싶다면?

uglifyjs pretty.js -o uglified.js -m

터미널에 이를 입력하고, 프로젝트 폴더에 가보면 uglified라는 js파일이 생긴걸 확인할 수 있다.

실제로 사용할 때는, 사용자나 다른 사람이 알아보기 쉬워야 한다. 따라서 기존의 해당 파일에 축소화라는 의미의 min을 덧붙여서 저장시키곤 한다.

uglifyjs pretty.js -o pretty.min.js -m

이렇게 저장하면, 프로젝트의 파일이 많더라도 min이라는 단어로 해당 파일의 축소형이라는 것을 인식할 수 있을 것이다.

 

 

 

이번에는 다른 사람이 만들어놓은 모듈을 프로젝트에서 부품으로 이용하는 것을 해보자

 

npmjs.com 사이트에서 underscore를 찾아보자

코드가 나오지 않고 해당 홈페이지가 나올 것이다. http://underscorejs.org/

사이트를 보면 Installation에서 Node.js용 설치가 안내되어있다.

  • Node.js npm install underscore

이걸 이용해서 터미널로 설치가 가능하다. 하지만 이전에 해야 할 일이 있다.

지금 우리의 프로젝트 디렉토리를 npm의 패키지로 지정을 먼저 해야한다.

현재 우리는 지금 underscore라는 다른 사람이 만든 패키지를 우리 것으로 가져오려고 하고 있다. 따라서 npm상에서 현재 디렉토리를 패키지로 지정하는 명령인 npm init을 먼저 해주어야 한다.

현재 가지고 있는 이름, 버전 등을 나타내주는데, Enter를 치면서 내려가면 된다.

여기서 설정할 것

  • description에 Server side javascript tutorials라고 치고 Enter
  • git repository는 자신이 프로젝트를 저장한 github 주소를 입력하거나, 이미 존재하면 자동으로 저장됨
  • 이 밖에도 무언가 저장된 값이 아닌 입력하라는 화면이 나오는 test command는 테스트를 시행할 때 사용할 커맨드 작성인데 아직은 사용하지 않을 것이므로, 그냥 enter치고 넘어간다. keywords와 author도 마찬가지

마지막에 yes를 치고 나간 이후, 프로젝트 파일을 열어놓은 편집기로 가보자.

package.json이라는 새로운 파일이 생긴 것을 확인 할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
  "name""server_side_javascript",
  "version""1.0.0",
  "description""Server side javascript tutorials",
  "main""hello.js",
  "scripts": {
    "test""echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type""git",
    "url""(https://github.com/kim6394/nodejs-server_side_javascript.git)"
  },
  "author""",
  "license""ISC"
}
cs

이제 우리는 다른 사람들이 만든 것을 우리 프로젝트에 포함시킬 준비가 되었다!

npm init을 하기전에 준비했던 underscore를 설치 해보자

1
npm install underscore
cs

설치가 끝나고 확인해보면, 프로젝트에 node_modules이라는 폴더가 생긴 것을 확인할 수 있다. 폴더를 눌러보면 안에 설치한 underscore가 제대로 내 프로젝트 안에 들어온 것을 볼 수 있을 것이다.

하지만 현재 이러한 설치는 외부에서 가져온 underscore를 온전하게 모두 가져온 것은 아니다. 온전하게 가져오려면 뒤에 --save를 붙여서 다시 설치하고 비교해보자

1
npm install underscore --save
cs

이제 다시 package.json을 열어보자

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
  "name""server_side_javascript",
  "version""1.0.0",
  "description""Server side javascript tutorials",
  "main""hello.js",
  "scripts": {
    "test""echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type""git",
    "url""(https://github.com/kim6394/nodejs-server_side_javascript.git)"
  },
  "author""",
  "license""ISC",
  "dependencies": {
    "underscore""^1.8.3"
  }
}
cs

마지막에 dependencies, 의존성이 추가된 모습을 볼 수 있다.

현재 underscore의 1.8.3 버전을 의존하고 있다는 뜻이다.

 

이게 있고 없고의 차이는 무엇인데요?

언제든지 프로젝트 내에서 underscore 1.8.3 버전을 포함시킬 수 있다는 뜻

'지금은 일단 npm을 설치할 때 모두 --save를 붙이고 해보자.'

--save가 필요 없는 상황은 일시적으로만 모듈을 사용할 때이므로, 나중에 익숙해지면 상황에 따라 설치하도록 합시다

 

 

 

모듈 사용법


프로젝트에 underscore.js를 만들어보자

1
2
var _ = require('underscore');
var arr = [369112];
cs

_라는 변수에 underscore 모듈을 요구했다.

그리고 arr이라는 이름의 배열에 다음과 같은 값을 넣어놓았다.

 

첫번째 값을 출력할 때는 배열을 이용해 이렇게 출력할 수 있다.

1
 console.log(arr[0]);
cs

이번에는 underscore 모듈을 이용해서 첫번째 값을 가져와보자

1
console.log(_.first(arr));
cs

지금처럼 underscore 모듈이 가진 first라는 메소드를 통해 arr 배열의 첫번째 값을 가져올 수 있다.

 

이번엔 제일 마지막 값을 가져와보도록 한다. 배열을 이용해 가져오려면 조금 복잡해진다.

1
 console.log(arr[arr.length-1]);
cs

가독성도 별로 좋지 못하다. 이번엔 underscore 모듈을 이용해보자.

1
console.log(_.last(arr));
cs

이번에는 last 메소드를 이용한 모습이다. 이처럼 더 효율적인 코드 작성이 가능하다.

 

 

콜백(Callback) 함수


콜백 함수란?

사용자에 의해 호출되는 것이 아닌, 특정 함수에서 호출돼 필요할 때 코드 내에서 사용되는 함수

 

커맨드 창에 node라고만 작성해보면 > 표시로 내용을 작성하도록 화면이 나온다.

 

1) 계속 사용할 함수 (이름을 붙여준다.)

1
= [3,1,2]; function b(v1, v2){ return v2-v1;} 
a.sort(b); console.log(a);
cs

지금같이 sort함수 안에 들어가는 인자인 b 함수는 특정함수에서 사용되는 '콜백'함수라고 말할 수 있다.

 

2) 일회성으로 사용할 함수 : 익명 함수 (이름이 생략 가능하다.)

1
= [3,1,2]; a.sort(function(v1, v2){ return v2-v1;}); 
console.log(a);
cs

코드가 더 간결해지는 효과를 볼 수 있다.

 

 

콜백 함수에 대해 좀 더 알아보기 위해 아래와 같이 해보자

1
function sort(callback){callback();};
cs

이건 무슨 뜻일까? sort라는 함수안에 callback이라는 매개변수를 넣고, callback이라는 이름을 가진 함수를 호출하는 모습이다.

 

sort 함수를 실행하기 위해선 인자를 넣어주어야만 가능하다.

1
sort(function(){console.log('Hello Callback')});
cs

sort함수 안에, Hello Callback을 출력하는 함수를 통째로 집어넣었다. 이제 sort는 이 함수를 인자로 받고, 함수형태로 실행을 시켜주면서 결과로 Hello Callback이라는 글이 출력되는 걸 볼 수 있다.

 

콜백은 nodejs와 javascript에서 너무나도 중요하게 사용되는 것이므로 잘 알고 있어야 한다.

 

 

 

동기와 비동기 프로그래밍


동기(Synchronous) & 비동기 (Asynchronous)

 

10000명의 사람에게 이메일을 보내야 한다고 가정해보자

  • 동기적

10000명의 사람들에게 1명씩 이메일을 발행한다. 한 명당 이메일을 보내는데 1초씩 걸린다면 총 1만초가 소요된다.

ex. readFileSync : 동기 방식

  • 비동기적

10000명에게 발행 버튼 한번으로 동시에 보낼 수 있다.

ex. readFile : 비동기 방식

 

즉, 동기는 시작하면 끝이 나야 다음 작업으로 넘어가고,

비동기는 시작과 동시에 다음 작업으로 넘어간다.

 

예로 들었던 readFile의 메소드를 이용해 동기와 비동기를 활용하는 연습을 해보자

파일을 읽어오기 위해 프로젝트에 data.txt파일을 하나 만들어서 저장한다.

1
2
3
//data.txt
Hello Sync And Async
cs

이제 동기와 비동기를 구현할 sync_async.js 파일을 만든다.

1
 var fs = require('fs');
cs

fs 모듈을 요구해서 fs라는 변수에 저장시키고, Sync방식과 Async방식을 통해 data.txt에 들어있는 데이터를 읽어오는 모습을 보여주고 있다.

 

1
2
3
4
// Sync 방식
console.log(1);
data = fs.readFileSync('data.txt', {encoding:'utf8'});
console.log(data);
cs

Sync 방식은 readFileSync를 이용해서 첫번째 인자 값으로 출력을 원하는 파일을, 두번째로는 인코딩을 원하는 형태로 utf8을 작성해서 console로 불러온 결과다.

 

1
2
3
4
5
6
7
// Async 방식
console.log(2);
fs.readFile('data.txt', {encoding:'utf8'}, function(err, data1){
    console.log(3);
    console.log(data1);
})
console.log(4);
cs

Async 방식은 readFile을 이용해서 Sync와 두 인자(파일, 인코딩)를 똑같이 주고, 세번째 인수로 함수를 만들어 console 값을 불러온다.

function(err, data){}는, 만약 파일에 문제가 있을 때 오류를 나타내고 이상이 없으면 data값을 출력하라는 의미를 갖고있다.

 

이때 비동기식은 작동 순서를 잘 봐야한다. 위에 Async 방식만 보면 출력형태가 2,3,4 순으로 나와야 할 것만 같다. 하지만 출력결과는 2,4,3 순서로 나타난다.

동작하는 순서대로 살펴보자.

1
console.log(2); 로 2가 가장 먼저 출력
cs

1
fs.readFile이 실행. 이와 동시에 다음작업으로 넘어감
cs

1
console.log(4); 로 4가 다음으로 출력
cs

1
2
3
4
5
readFile 안에 있는 함수가 실행되면서 내부적인 일처리를 끝내기 위한
동작이 시작됨. 따라서 이 과정에서 3이 마지막으로 출력

function(err, data1){
    console.log(3);
    console.log(data1);
})
cs

이처럼 비동기가 사용될 때는, 실행 순서가 보이는 것과 다른 점을 확실하게 알고 넘어가야 한다. readfile에서는 함수가 내부적으로 일을 모두 처리할 때까지 출력할 수가 없으므로, 동시에 시작된 console.log(4);가 먼저 출력되는 것이다!

 data값을 출력하라는 의미를 나타내고 있다.


이처럼 비동기가 사용될 때는, 실행 순서가 보이는 것과 다른 점을 확실하게 알고 넘어가야 한다. readfile에서는 함수가 내부적으로 일을 모두 처리할 때까지 출력할 수가 없으므로, 동시에 시작된 console.log(4);가 먼저 출력되는 것이다!

반응형