supervisor

우리가 자바스크립트 코드를 수정하면 node가 이것을 알아서 감지하지 못합니다. 그래서 우리는 코드를 수정하고 서버를 껐다 켜야 하는 번거로운 작업을 수행하고 변경된 내용이 반영되는 것을 확인할 수 있습니다.

이러한 작업은 이미 실행된 프로그램이 루프를 계속 돌고 있기 때문에 우리가 변경한 내용이 반영되지 않는 것입니다.

  • watch : 변경된 사항을 보고 있다가 변경이 일어났을 때 자동으로 node를 내부적으로 껐다가 키는 것

먼저, npm install supervisor -g 를 통해서 전역적으로 설치한 후

supervisor app.js를 통해 코드를 수행하면 app.js가 실행이 되고 app.js의 코드를 수정하고 변경된 사항을 저장하면 아래와 같이 서버가 자동으로 꺼졌다가 켜지는 것을 확인할 수 있습니다.

Comment and share

Get vs Post


서버와 웹 브라우져가 상호작용하는 과정에서 크게 보면 두 가지 방법이 있습니다.

  • GET : 우리가 지금까지 웹 브라우져에서 웹 서버로 정보를 요청할 때 사용한 방식이며, 기본 방법입니다.

    • 기본적으로 웹 브라우저에 주소를 입력해서 정보를 가져오는 것은 get 방식입니다.
    • 쿼리스트링을 사용하는 방법 역시 정보를 가져오는 것이기 때문에 get 방식입니다.
    • 즉, 우리가 서버에게 어떠한 정보를 요청해서 가져오는 방식을 말합니다.
  • POST : 사용자의 정보를 서버로 전송할 때는 POST 방식을 사용합니다.

form


1
2
3
4
5
6
7
8
9
10
11
12
doctype html
html
head
meta(charset='utf-8' method="get")
body
form(action="/form_receiver")
p
input(type ="text", name='title')
p
textarea(name='description')
p
input(type="submit")

form을 이용해서 '/form_receiver’에 해당되는 url을 생성해주었고 사용자가 입력한 title과 description은 제출 버튼을 누르면 해당 url을 통해서 서버로 값을 보내줍니다.

1
2
3
4
5
app.get('/form_receiver', function(req, res){
var title = req.query.title;
var description = req.query.description;
res.send(title+", "+description);
})

지금까지 한 것은 get 방식입니다. post 방식은 url을 통해서 데이터 즉 값을 보내지 않습니다.

Post


1
2
3
4
5
6
7
app.get('/form_receiver',function(req,res){
res.send("Hello, GET");
})

app.post('/form_receiver',function(req,res){
res.send("Hello, POST");
})

사용자가 Post 방식으로 데이터를 전송하면 우리는 Post라는 메소드에 Controller를 연결시켜서 실행시킬 수 있습니다. form.jade 파일의 form 태그에서 method를 get인지 post인지 지정함에 따라서 사용자의 요청은 get과 post로 나뉘어 질 수 있고, 이러한 요청이 서버로 들어오게 되면 각각에 맞는 라우터에 걸려서 해당 익명 함수 즉, Controller의 동작을 수행할 수 있습니다.

그러면 post 방식으로 전송했을 때, post 방식으로 전송한 데이터를 우리 애플리케이션에서는 어떻게 받을 수 있을까요??

1
2
3
4
5
app.post('/form_recevier', function(req, res){
var title = req.body.title;
var description = req.body.description;
res.send(title+", "+description+"in Post");
});

Post 방식에서는 get 방식과는 다르게 body라는 객체를 통해서 사용자가 전송한 값을 받을 수 있습니다. 하지만, 코드를 실행하고 서버를 킨 다음에 테스트를 해보면 에러를 만날 수 있습니다.

기본적으로 post 방식으로 전달된 데이터는 정의되어 있지 않습니다. 이 데이터를 사용하려면 body-parsermulter와 같은 mmiddleware를 설치해야 합니다.

  • body-parser : post 방식으로 전송한 데이터를 우리의 애플리케이션에서 사용할 수 있도록 해주는 일종의 plug-in 또는 확장 기능이라고 생각하면 됩니다. body-parser를 우리의 애플리케이션에 포함시켜서 작동하도록 하면 우리는 req 객체 안의 body 객체를 사용할 수 있습니다.
1
2
3
4
5
6
7
8
9
var bodyParser = require('body-parser');

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

app.post('/form_recevier', function(req, res){
var title = req.body.title;
var description = req.body.description;
res.send(title+", "+description+"in Post");
});
  • 위의 코드를 통해서 npm을 통해서 우리 프로젝트에 포함시킨 body-parser란 모듈을 가져올 수 있습니다.
  • 우리가 가져온 body-parser란 모듈을 애플리케이션 객체에 use를 하면 이 모듈을 붙이는 것입니다. 즉, application 객체에 body-parser라는 모듈을 붙이는 것입니다.
  • 그리고 앞으로 이 애플리케이션으로 들어오는 모든 요청들은 body-parser라는 미들웨어를 먼저 통과한 다음에 라우터가 동작하게 됩니다.
  • 제일 앞쪽에서 body-parser가 항상 대기하고 있다가 사용자 요청이 들어오면 body-parser가 동작하면서 사용자가 포스트 방식으로 전송한 데이터를 우리가 사용할 수 있도록 하는 역할을 합니다.

정리하자면, body-parser라는 모듈을 추가했고, 이 body-parser라는 모듈을 우리의 애플리케이션에 user라는 메소드를 통해서 연결시켰습니다. 그러면 사용자에게서 들어오는 모든 요청들이 있을 때 body-parser가 제일 먼저 실행됩니다. 그러면 body-parser가 실행이 될 때, body-parser는 사용자가 post 방식으로 전송한 데이터가 있다면 애플리케이션 안에서 req 객체가 원래 가지고 있지 않았던 body라는 객체를 body-parser가 추가합니다. 그리고 사용자가 전송한 데이터의 이름이 title이라면 body라는 객체에 title이라는 프로퍼티에 그 값을 넣어서 사용자에게 제공합니다.

Get과 Post의 용도


  • get
    : 어떤 정보에 대한 주소를 나타낼 때는 url 상에 모든 정보를 포함시켜야 합니다.

  • post
    : 사용자가 id와 password를 입력해서 서버에서 전송해야 하는 경우라면 get 방식을 이용하게 된다면 url 상에 정보가 나타나게 됩니다. 하지만, 이 경우 우리의 id와 password가 노출될 위험이 있기 때문에 이러한 정보는 url 상에 정보가 표시되지 않는 방식인 post 방식을 사용해야 합니다.

하지만, 본질적으로 get 방식이건 post 방식이건 어떤 데이터가 전송되는가라는 것을 얻어내는 노력의 난이도가 조금 다를 뿐이지 본질적으로 다 알아낼 수 있기 때문에 post 방식이 get 방식보다 충분히 안전하다고 할 수 없고 둘 다 불안전합니다. 중간에 누군가가 데이터를 가로채지 못하게 하는 것은 다른 방법이 필요합니다.
ex. https(ssl)

또 하나는 url을 통해서(쿼리스트링) 우리가 길이가 굉장히 긴 정보를 전송할 때, url을 통해서 쿼리스트링에 포함시켜서 get 방식으로 전달한다면 그 정보가 굉장히 크면 url 규격상 일정한 길이보다 더 길어지면 서버가 정보를 버립니다. 또는 브라우저가 전송을 하지 않을수도 있습니다.
따라서 이런 경우에는 데이터가 중간에 끊기지 않고 온전히 전체 데이터가 대규모로 전송되려면 전송 방법을 post로 지정해야 합니다.

요약


  • get 방식
    • get 방식을 통해서 전송된 쿼리 스트링의 데이터에 따라서 다른 결과를 보여줄 수 있습니다.
    • 쿼리 스트링으로 데이터를 전송했을 때의 중요한 장점
    • express가 기본적으로 제공함
  • post 방식
    • url에 데이터가 포함되지 않고 데이터가 조용히 암시적으로 동작하기 때문에 불필요하게 정보가 노출되지않음
    • 용량이 큰 데이터를 전송하는데 제한이 없음
    • express가 기본적으로 제공하지 않기 때문에 body-parser라는 middleware를 로드하고 application의 use를 통해서 붙여서 사용자로부터 들어오는 요청을 중간에 가로채서 post 방식이라면 req 객체의 body라는 객체를 추가시켜주는 역할을 한다. 그리고 body라는 객체에는 form으로 전송될 때 name의 값으로 전달된 데이터의 이름이 body 객체에 프로퍼티로 들어오기 때문에 form의 이름을 통해서 사용자가 전송한 데이터를 받을 수 있습니다.

Comment and share

쿼리 스트링


어떠한 사용자의 입력이 있을 때, 그 입력에 따라서 적당한 다른 결과를 보여주는 것이 의미있는 즉, 기능성이 있는 애플리케이션이라고 할 수 있습니다.

지금까지는 사용자가 어떤 주소로 접근하느냐에 따라서 사용자에게 다른 결과를 보여주었습니다.

만약 우리의 웹사이트가 a.com이라고 한다면 사용자가 path에 /login을 하게 되면 login에 해당되는 Route가 그 라우트와 연결되어 있는 Controller(익명 함수)를 호출해서 적당한 결과를 만들어 내는 것이 애플리케이션의 구조입니다.

그림과 같이 paht만 놓고 본다면 서로 다른 결과를 사용자에게 보여주지만, http://a.com/topic이라는 path 한 개는 사용자에게 언제나 똑같은 결과만을 보여줍니다. 사고를 좀 더 확장해보면 이 topic으로 사용자가 접근했을 때도 경우에 따라서 다른 결과를 보여줄 수 있다면 더 좋을 것입니다.

이것을 하기 위해서 쿼리 스트링을 배울 것입니다.

  • topic이라는 라우터에 id가 1이라고 하는 값을 전달하고 있습니다.
    • 1이라는 숫자에 해당되는 결과를 처리해서 화면에 보여줍니다.
  • 결과적으로 topic이라는 단 하나의 path로, 다른 말로 단 하나의 라우터로 id 값을 다르게 주는 것에 따라서 다른 결과를 만들어 낼 수 있습니다.
  • ? 뒤에 나타나는 정보를 쿼리 스트링이라고 합니다.

query 객체 사용법


1
2
3
4
5
6
var express = require('express');
var app = express();
app.get('/topic',function(req, res){
res.send(req.query.id);
res.send(req.query.id+', '+req.query.name)
})
  • topic이라는 path가 가리키는 Controller가 쿼리 스트링을 어떻게 알 수 있을까??

    • 사용자는 주소를 통해서 웹 페이지에 접근하고 있고, 사용자는 우리에게 요청을 하고 있습니다. 이러한 요청과 관련된 정보는 req라는 첫 번째 매개변수에 값으로 request 정보가 들어오게 됩니다.
    • 쿼리 스트링으로 전달된 값이 이 함수의(Controller의) 첫 번째 매개변수의 값인 req에 query라고 하는 객체에 id라고 하는 프로퍼티 값으로 들어옵니다.
  • express는 req라는 값을 전달하며, req는 query라고 하는 객체를 갖습니다. 그리고 query라는 객체는 사용자가 전달한 쿼리스트링을 프로퍼티로 갖습니다.

  • topic?id=1&name=“이승우”

    • 쿼리스트링을 통해서 우리가 애플리케이션에 전달할 수 있는 값은 하나가 아니라 여러 개 일수도 있습니다. 값을 구분하는 구분자로 쿼리스트링은 &를 사용합니다.

query 객체의 활용


1
2
3
4
5
6
7
8
9
10
11
12
13
14
app.get('/topic',function(req, res){
var topics = [
'Javascript is ...',
'Nodejs is ...',
'Express is ...'
];
var output = `
<a href="/topic?id=0">JavaScript</a><br>
<a href="/topic?id=1">Nodejs</a><br>
<a href="/topic?id=2">Express</a><br><br>
${topics[req.query.id]}
`
res.send(output);
});
  • 쿼리스트링 : 어떤 애플리케이션에서 정보를 전달할 때 사용하는 URL의 약속되어 있는 국제적인 표준입니다.
  • 그리고 쿼리스트링으로 전달된 값은 request 영역이기 때문에 첫 번째 매개변수에 담겨 있는 값인 req 객체, 또 그 객체가 가지고 있는 query라는 객체 또 그 객체 안에 있는 id 값을 통해서 가져올 수 있습니다.

시멘틱 URl


topic/?id=2가 아니라 쿼리스트링이 없이 topic/2와 같은 깔끔한 URL을 통해서 애플리케이션을 제어할 수 있습니다. 이런 방식의 URL 체계를 시멘틱 URL이라고 합니다.

하지만, 기존의 쿼리스트링 방식의 URL을 사용하면 topic/2와 같은 URL을 잡아내지 못하고, 주소 뒤에 오는 숫자 값을 사용하기 위해서는 시멘틱 URL에서는 params를 이용합니다.
따라서 아래와 같은 수정이 필요합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
app.get('/topic/:id', function(req, res){
var topics = [
'Javascript is ...',
'Nodejs is ...',
'Express is ...'
];
var output = `
<a href="/topic?id=0">JavaScript</a><br>
<a href="/topic?id=1">Nodejs</a><br>
<a href="/topic?id=2">Express</a><br><br>
${topics[req.params.id]}
`
res.send(output);
})
  • 쿼리 스트링을 사용해 접근하는 경우
    : req.query.id로 접근하면 됩니다.
  • 시멘틱 URL(path 방식)을 사용해 접근하는 경우
    : req.params.id로 접근하면 됩니다.

Comment and share

템플릿 엔진이란 무엇인가?


  • 정적인 방법과 동적인 방법의 장/단점을 가지고 있습니다.

    • 정적인 파일만 서비스한다면 필요없지만, 동적인 결과를 정적인 파일에 담기 위해 사용합니다.
    • 풀어 설명하자면, 자바스크립트 코드로 연산된 결과를 변수에 넣고 변수를 뷰 파일에서도 사용할 수 있게끔 합니다.
  • 템플릿 엔진을 사용하는 이유

    • 클라이언트 요청에 따라 웹 페이지에 들어가는 내용(결과)이 달라질 수 있어서 정적인 부분과 동적인 부분을 따로 하기위해 사용합니다.
  • app.js 내 html 코드를 쓰지 않아도 됩니다.

  • 뷰 파일과 자바스크립트 코드를 한 파일에 정의하지 않고 따로 따로 사용할 수 있습니다.

  • 자바스크립트로 연산된 결과를 뷰 파일에 쉽게 넣을 수 있습니다.

  • 템플릿 엔진 중 Jade를 사용합니다.

  • 템플릿 엔진을 이용하면 짧은 코드로 장황한 HTML 코드를 만들어 낼 수 있는 기능을 제공합니다.

  • 템플릿 엔진 안에서 변수도 사용할 수 있다.

Express와 함께 템플릿 엔진 사용


Express는 템플릿 엔진 기능이 없기 때문에 사용하기 위해서 템플릿 엔진을 설치해서 express와 연결한 다음에 사용하면 됩니다.

'npm install jade --save’를 통해 모듈을 설치합니다.

이를 통해 node_modules 폴더에 Jade가 추가되고, package.json 파일에 Jade가 dependencies 항목으로 추가됩니다.

1
2
3
4
5
6
7
8
9
var express = require('express');
var app = express();
app.set('view engine','jade');
app.set('views', './views');
// 위의 한 줄을 생략해도 express 기본적으로 views라는 디렉토리를 찾도록 설정되어 있습니다.

app.get('/template',function(req, res){
res.render('temp');
})
  • view engine : 약속되어 있는 이름
  • jade : 템플릿 엔진

express의 application에게 jade라는 템플릿 엔진을 set합니다.

우리가 설치한 jade라고 하는 템플릿 엔진과 우리가 지금 만들고 있는 애플리케이션 프레임워크인 express를 연결합니다.

그리고 views라는 폴더를 생성해줍니다. 앞으로의 jade 파일은 이 디렉토리에 저장합니다. (템플릿 엔진들의 템플릿 파일을 views라는 파일에 넣습니다.)

  • ‘/template/’ 경로를 통해서 들어온 사용자에게 함수가 실행되면서 temp라는 템플릿 파일을 웹 페이지로 렌더링해서 전송한다는 뜻입니다.
  • 템플릿 엔진의 코드에 따라서 만들어진 템플릿 파일을 읽어옵니다.
  • temp라는 파일은 views 폴더에 위치합니다.

response 즉, 응답으로 temp를 render하게 되면 express는 내부적으로 위쪽에 정의해놓은 views라고 하는 디렉토리에서 우리가 템플리세 엔진으로 jade를 정해놓았기 때문에 jade의 확장자인 temp.jade라는 파일을 찾아서 그 파일에 있는 내용을 jade의 문법에 따라서 해석한 후에 결과를 가져오고 그것을 사용자에게 response하는 코드입니다.

Jade 문법


1
2
3
4
5
6
7
8
9
html
head
body
h1 Hello Jade
ul
- for(var i=0;i<5;i++)
li coding

div= time // jade의 변수

들여쓰기를 통해서 head 태그와 body 태그를 html 태그 안에 넣을 수 있습니다. 하지만, 웹 페이지에서 소스보기를 통해서 확인하면 코드의 가독성이 떨어지고 이쁘지 않은 모습을 볼 수 있습니다. 이럴 때는 app.locals.pretty = true;라는 코드를 추가해주면 이쁘게 바뀐 모습을 확인할 수 있습니다.

반복적인 작업을 할 때는 for문을 이용하면 되는데, 이는 화면에 출력하는 코드가 아니고 프로그래밍적으로 제어하기 위한 코드입니다. 이러한 코드는 앞에 -라는 Jade 안에서 약속되어 있는 특수 기호를 붙이면 jade는 -를 보고 for가 화면에 출력되는 것이 아니라는 것을 알게 됩니다. for는 Jade 안에서 반복문으로 사용됩니다.

Html을 작성하는 것이 훨씬 더 간결하게 가능해집니다. 그리고 동시에 프로그래머블하다는 특성을 가지게 됩니다.

time이라는 변수로 현재 시간을 넣기 위해서는 jade 안에서 가능한 것이 아니고 jade를 사용하는 express 쪽에서 변수 값을 주입해주어야 합니다.

1
2
3
app.get('/template',function(req, res){
res.render('temp', {time: Date()});
})

render 함수의 두 번째 인자로 객체를 전달합니다. time이라는 객체가 render에 의해서 temp.jade라고 하는 template에 흘러들어가게 됩니다. 그러면 temp가 가리키고 있는 temp.jade는 내부적으로 time이라는 변수를 사용할 수 있게 됩니다. 우리가 넘긴 변수 값이 jade 파일 안에서 사용될 수 있습니다. 변수의 값은 여러 개가 전달될 수 있으며, Jade 파일에서 명시된 것과 동일한 이름을 사용해야 합니다.

정리


먼저, 'npm install jade --save’를 통해서 사용할 템플릿 엔진인 Jade를 설치합니다.

1
2
3
4
5
6
7
8
var express = require('express');
var app = express();

app.set('view engine', 'jade');
app.set('views', './views');
app.get('/template', function(req, res){
res.render('temp', {time : Date(), _title : 'Jade'});
})
  • 설치된 jade와 express를 연결하기 위해서 위의 코드를 통해서 환결 설정을 해줍니다.
  • 그리고 우리가 어떤 특정한 템플릿을 사용하고 싶다면,
    • temp.jade를 사용하고 싶다면 res가 가지고 있는 메소드 중에서 render라는 메소드를 호출하면서 첫 번째 인자로 그 템플릿의 이름을 전달해주면 render가 내부적으로 템플릿을 읽어서 그 템플릿 엔진의 문법에 따라서 해석한 다음에 사용자에게 response 해줍니다.
    • 만약에 템플릿에 데이터를 주입하고 싶다면, 객체를 정의해서 그 객체의 프로퍼티 값으로 원하는 값을 전달하면 그 템플릿 엔진에서는 = time과 같이 문법을 통해서 변수의 값을 화면에 출력할 수 있습니다.

Comment and share

Express


  • IP : 어떠한 컴퓨터를 식별하는 식별자
  • Port : 그 컴퓨터 안에 설치되어 있는 서버들 중에서 어떤 서버를 사용할 것인가에 대한 식별자

우리가 웹 서버를 만든 다음에 그 웹서버를 어떤 포트가 실행시킬 것이지를, 바라보게 할 것인지를 결정해야 합니다. 컴퓨터에는 0~65535개의 포트가 있고, 우리는 이 포트 중에서 어떤 특정한 웹서버를 만든 다음에 그 웹 서버가 특정한 포트를 바라보게 하면 사용자의 요청은 그 포트를 향해서 들어오게 됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const http = require('http');
const hostname = '127.0.0.1';
const port = 1339;

var server = http.createServer(function(req, res){
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello World\n');
});


server.listen(port, hostname, function(){
console.log(`Server running at http://${hostname}:${port}/`);
// 만들어진 서버를 가지고 실제로 그 포트로 사용자가 들어왔을 때, 어떤 내용을 출력할 것인가는 위에서 작성할 수 있습니다.
});
  • http라는 변수에 http라는 객체가 담겨있는데, 그 객체가 가지고 있는 createServer라는 함수를 호출해서 서버를 만듭니다.
  • createServer에 의해서 서버가 만들어지고 만들어진 서버를 우리가 제어할 수 있도록 객체를 리턴합니다.
  • 실제로 그 포트로 사용자가 들어왔을 때 어떤 내용을 출력할 것인가는 이 함수에서 정의할 수 있습니다.
  • 역시나 이 함수도 익명함수를 통해 작성합니다.
  • 이 함수는 두 개의 인자를 받는데, req(요청과 관련된 것), res(응답과 관련된 것)
  • 서버가 특정 포트를 바라보게 하기 위해서 Port를 매개변수로 넘겨줍니다.
  • 사용자가 우리 서버로 접속할 때 여러가지 방법으로 접속할 수 있습니다. 그래서 어떤 IP를 타고 들어온 사용자를 수용할 것인가를 확인하기 위해서 hostname을 적습니다.
  • 시간이 좀 걸릴 수 있는 작업이기 때문에 listen이라는 메소드는 Callback으로 비동기적으로 작동합니다. 그래서 listen이 완료되었을 때 이 callback이 실행되도록 약속이 되어있는 것입니다.

이것도 편한지만, 이런 것들을 우리가 훨씬 더 적은 코드로 이런 역할을 하면서도 더 많은 일을 할 수 있도록 해주는 도구들 (프레임워크)이 존재합니다. Node를 직접 사용해서 웹 애플리케이션을 구현하는 것도 좋겠지만, 손이 많이 가기 때문에 Node를 이용해서 만들어진 프레임워크를 통해서 웹 개발을 해볼 것입니다. 그것이 바로 Express라는 것입니다.

Express 설치


  1. 먼저 설치를 하기 전에 해당 디렉토리를 Npm이 사용할 수 있는 프로젝트로 만들기 위해서 ‘npm init()’ 명령어를 수행합니다.
  2. 그 다음 ‘npm install --save express’ 명령어를 통해 express를 설치합니다.

이제 해당 프로젝트 폴더에 설치해서 Express라는 프레임워크를 사용할 수 있는 기본적인 준비는 끝났습니다.

Express - 간단한 웹애플리케이션 만들기


  • 지금 app.js 라는 파일을 만들 것입니다. 이 파일을 메인 파일이라고 합니다. 또는 메인 애플리케이션(엔트리 애플리케이션)이라고 부릅니다. 최초의 진입점이 되는 파일입니다.
  • app.js라는 이름은 express에서 권장하는 메인 애플리케이션의 이름입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var express = require('express');
// require를 통해 express 모듈을 리턴받고 express라는 변수를 통해서 모듈을 제어할 수 있습니다.
// 다음은 express라는 모듈을 통해서 application 객체를 만듭니다.
var app = express();
// express라는 모듈은 함수입니다. 그래서 실행하게 되면 함수는 application라는 객체를 리턴합니다.

app.get('/', function(req, res){
res.send("Hello Homepage");

})
app.get('/login', function(req, res){
res.send("Login please");
})

app.listen(3000, function(){
console.log("Connected 3000 Port!");
})
  • Home 화면에 접속했을 때 우리가 실행시킨 get이라는 메소드를 통해서
  • 사용자가 홈으로 접속하면 두 번째 인자로 전달한 callback 함수가 실행되도록 약속되어 있습니다.
  • 함수의 인자로 첫 번째 매개변수는 req, 두 번째 매개변수는 res가 들어온다고 약속되어 있습니다.
  • 함수를 이렇게 정의하면, get이라는 함수는 사용자가 Home으로 들어오고 이것이 완료되었을 때, 인자로 전달된 함수를 실행시키면서 첫 번째 매개변수의 값으로 사용자가 요청한 것과 관련된 request 객체를 전달하는 것이고 두 번째로 사용자가 요청한 정보에 대해서 응답을 할 수 있는 여러가지 방법을 담고 있는 응답에 대한 객체를 전달하도록 약속이 되어 있습니다.

정리하자면, get이라는 메소드는 사용자가 어떠한 경로로 들어왔을 때, 어떠한 것이 실행이 될 것인가를 결정하는 역할을 합니다. 이러한 역할을 하는 것을 router라고 부르며 router가 하는 일을 routing이라고 합니다. 우리가 배우는 메소드 중 get, post, put, deleterouter의 역할을 하는 메소드입니다.

Routing


  • 사용자 : 웹 브라우저를 사용해서 우리의 애플리케이션으로 접속하는 사람
  • 우리의 애플리케이션은 RouterController라는 것으로 이루어져 있습니다.

사용자가 '/'로 들어오게 되면 get('/')가 받고 이것을 두 번째 인자로 전달되었던 익명 함수를 실행시키고 "Welcome to Home Page"라는 정보를 사용자에게 응답해주고 있습니다.

사용자가 '/login’으로 접속하면 저 get('/login')가 받고 두 번째 인자인 익명 함수 안에 있는 코드를 실행시켜서 사용자에게 "login please"라고 하는 정보를 사용자에게 응답해주고 있습니다.

그래서 get이라고 하는 함수의 역할은 사용자의 요청과 그 요청에 대한 처리인 Controller를 중개해주는 역할을 하는 것이 라우터의 역할입니다. 이 라우터라고 하는 것은 웹 애플리케이션을 만든다라고 했을 때 가장 중요한 것 중 하나입니다.

연결성


Javascript와 Nodejs의 관계

연결이라는 측면에서 보면 기본적으로 Node.js는 다음과 같은 기능을 제공합니다.

  • FS : File을 읽고 쓰는 시스템
  • HTTP : 네트워크를 통해 어떠한 일을 할 수 있는 기능
  • OS : 운영체제를 제어할 수 있는 여러가지 기능

Nodejs가 제공하는 기본적인 기능들을 Javascript라고 하는 프로그래밍 언어의 문법에 맞게 결합해서 우리가 의도하는 바에 해당되는 프로그램을 만들어 낼 수 있습니다. 일단 한 번 프로그램을 만들면 그 프로그램은 언제든지 똑같이 실행되는, 재사용할 수 있는 애플리케이션이 됩니다.

이것이 가능한 이유는 Nodejs가 기초적인 명령들을 제공하고 Javascript이라는 프로그래밍 언어가 노드가 제공하는 명령들을 조합해서 새로운 명령을 만들어 낼 수 있습니다. 그렇게 해서 만들어 낼 수 있는 명령어의 개수는 정말 많고 다양한 가능성을 제공합니다.

이것과 비슷한 관계에 있는 또 다른 하나는 ModuleNPM의 관계입니다.

  • Module : 우리의 프로그램 안에서 부품으로 사용될 작은 프로그램들을 말합니다. 이 세상에는 여러가지 모듈들이 만들어져 있고 그 중에는 우리가 만든 것, 우리가 만들지 않은 것이 있습니다.

  • NPM : 다양한 모듈들을 NPM을 통해서 우리의 애플리케이션에 담아서 다양한 형태의 애플리케이션을 만들어 낼 수 있습니다. (ex. Express, Underscore, Jade)

  • Router : 사용자의 접속을, 사용자의 요청을 어떤 Controller에 전달해줄 것인가라고 하는 중개자의 역할을 합니다.

  • Controller : 예를 들면, 회원을 가입할 때 사용하는 Controller, 홈페이지의 표현을 위한 Controller, 에러 화면을 만들어주는 Controller가 있습니다.

JavaScript, NPM, Router 이러한 연결하는 녀석들에게 연결할 대상을 제공해주는 것이
Node.js, Module, Controller 입니다.

정적파일을 서비스하는 법


  • 정적인 정보 : 사람이 한 번 작성한 것이 즉, 만들어진 것이 언제나 똑같이 보이면 이것을 정적인 정보라고 합니다.
  • 동적인 정보 : 프로그래밍적으로 만들어진 정보를 동적인 정보라고 합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var express = require('express');

var app = express();

app.use(express.static('public'));
app.get('/', function(req, res){
res.send("Hello Homepage");

})
app.get('/login', function(req, res){
res.send("Login please");
})

app.listen(3000, function(){
console.log("Connected 3000 Port!");
})

정적인 파일을 서비스하고 싶다면
app.use(express.static('public')); 이 한줄을 추가합니다.

public이라는 디렉토리에 우리가 정적인 파일을 갖다 놓으면 그 정적인 파일을 사용자에게 서비스 할 수 있습니다.

웹 페이지를 표현하는 방법


웹 페이지를 사용자에게 서비스 하는 방법은 두 가지가 있다.

  1. 정적인 파일 전달하는 법
    • 한 번 만들어지면 언제나 똑같은 모습인 Html
    • public이라는 디렉토리에 static.html 파일을 놓습니다.
    • 서버를 동작시키고, localhost:3000/static.html로 접근하면 작성된 웹 페이지를 확인할 수 있습니다.
    • 내용을 수정하면 서버를 껐다 킬 필요 없이 내용을 바로 반영시킬 수 있습니다.
    • 하지만, 웹 페이지 상에서 반복적인 일들이나 추가적인 사항이 많이 생긴다면 반영하기 어렵습니다.
    • 그 하나의 예로는 현재 시간을 보여주는 것이라고 할 수 있습니다.

자바스크립트에서는 여러 줄의 코드를 삽입하고 싶다면 \를 붙이면 가능하지만, 코드의 가독성이 많이 떨어집니다. 자바스크립트의 새로운 표준에는 formatted text라는 기능이 추가되었습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
app.get('/dynamic', function(req, res){
var time = Date();
var lis = ' ';
for(var i = 0;i<5;i++){
lis = lis + '<li>Coding</li>';
}
var output = `
<html>
<head>
<meta charset="utf-8">
</head>
<body>
Hello, Dynamic~~~~~
<ul>
${lis}
</ul>
${time}
</body>
</html>`;

res.send(output);
})
  1. 동적인 파일 전달하는 법
    • 우리가 작성한 코드가 다시 실행되어야 하기 때문에 서버를 껐다가 다시 node app.js를 실행해주어야 합니다.
    • 내부적으로 dynamic하게 웹 코드를 작성하면 코드의 짧은 수정을 통해서 반복적인 작업의 횟수가 프로그래밍적으로 달라지기 때문에, 자바스크립트 코드에서 일부분만 수정하면 되므로 정확하게 원하는만큼 반복시킬 수 있습니다.

하지만, 정적인 파일의 경우에는 요청이 들어올 때마다 node가 그것을 잡아서 던져주기 때문에, 우리가 굳이 껐다 킬 필요가 없습니다. 이로 인해서 정적인 파일을 서비스하는 것이 좀 더 코드를 짜는데는 편리합니다. 물론 동적인 파일도 자동화 시키는 방법이 있지만, 아직은 배우지 않고 다음에 배울 것 입니다.

또는 현재 시간을 웹 페이지에 표시하고 싶다면 Date()라는 API를 사용하여 동적인 파일을 서비스하는 방법을 통해서 웹 페이지에 보여줄 수 있습니다.

변수의 값을 Html 코드에 삽입하면 문자열로 인식하기 때문에 ${time}과 같은 방법을 써서 변수를 포함시킬 수 있도록 합니다.

Comment and share

동기와 비동기 프로그래밍


  • 동기(Synchronous)
    • 줄여서 sync라고도 부릅니다. 예를 들어서, 빨래와 설거지 청소의 3가지 일을 해야 한다면 빨래를 1시간 동안하고 끝낸 다음에 설거지를 1시간 동안하고 그 다음에 청소를 1시간 동안하고 끝냅니다. 총 3시간에 걸쳐서 일을 끝냅니다.
    • 즉, 순차적으로 일을 스스로 끝내 나가는 방식입니다.
  • 비동기(Asynchronous)
    • 일단은 빨래하는 업체 A가 있고, 설거지 하는 업체 B, 청소하는 업체 C가 있다고 가정하면 빨래를 하기 위해서 A 업체에 전화를 하고 빨래가 끝나면 알려 달라고 한 다음에 전화를 끊고 B 업체에 전화해서 청소를 부탁하고 끝나면 알려 달라하고 전화를 끊고, C 업체에 전화해서 청소를 부탁하고 끝나면 알려 달라고 합니다.
    • 전화를 하는데 각각 1분씩 걸렸다면 총 3분만에 일처리를 시작했다는 걸 끝낼 수 있습니다. 시간이 지난 후에 각각의 업체로부터 업무가 끝났다는 알림을 받을 수 있습니다.
    • 어떤 업무가 먼저 끝날 지 알 수가 없다는 단점이 있습니다.

예시

Node.js 사이트에서 document(문서)를 참고해서 file system 모듈을 참고합니다. file system 모듈은 Node.js를 이용해서 file을 제어하는 것과 관련된 기능을 합니다.

Node.js는 기본적으로 시간이 필요한 작업들(I/O가 필요한 작업)을 비동기적으로 처리합니다.

우리가 특별히 원할 경우에는 동기적으로 일을 처리할 수 있도록 Sync라는 키워드가 붙은 메소드들이 존재합니다.

동기식 코드

1
2
3
4
5
6
7
8
9
var fs = require('fs');

console.log(1); // 실행 순서 1
var data = fs.readFileSync('textdata.txt',{encoding:'utf8'});
console.log(data); // 실행 순서 2

// 결과
1
Hello Sync And Async

만약 readFileSync를 통해서 파일을 읽는데 시간이 오래 걸린다면 동기적 처리 방식에서는 그 시간 동안 다른 작업을 수행할 수 없습니다.

비동기식 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var fs = require('fs');

console.log(2); // 실행 순서 1
var data = fs.readFile('textdata.txt',{encoding:'utf8'}, function(err, data){
console.log(3); // 실행 순서 3
console.log(data); // 실행 순서 4
});
console.log(4); // 실행 순서 2

// 결과
2
4
3
Hello Sync And Async

readFile을 통해서 파일을 읽는 동안 다른 작업을 먼저 수행합니다. 그리고 readFile이 파일을 다 읽고 에러가 없다고 판단하면 readFile 함수의 매개변수로 전달된 익명 함수(즉, Callback 함수)의 data라는 매개변수 값으로 textdata.txt 파일의 내용을 전달하고 이를 호출합니다. 그리고 이후 나머지 동작들을 수행하게 됩니다.

Comment and share

Callback 함수


Callback 함수를 배웠습니다.
우선, Terminal에서 javascript로 작성된 파일을 node로 실행시킬 때는 node hello.js를 통해서 실행시키면 됩니다.
하지만, 지금 배우는 환경에서는 node라고 입력하고 인터프리터 환경에서 실습을 진행할 예정입니다.

1
2
3
4
5
6
7
a = [3,1,2];
function b(){ // 여러번 사용될 함수라면 이름을 붙여줍니다.
return v2-v1;
}
a.sort(); // [1,2,3]
a.sort(b); // [3,2,1]
console.log(a);
1
2
3
4
5
6
a = [3,1,2];
a.sort(function(v1, v2){
// 이 경우 정렬할 때 한번만 사용할 목적이라면 익명 함수를 이용해 callback함수로 사용
return v2-v1;
});
console.log(a);

위의 코드를 보면 sort라는 함수의 인자로 다른 함수를 전달했고, 저렇게 전달된 함수를 callback 함수라고 부릅니다.

callback 함수를 우리는 정의했습니다. 하지만, 우리가 callback 함수를 호출하지 않았고, sort라는 함수가 필요할 때마다 내부적으로 b를 호출하고 있습니다. 다시 말해서, b라는 함수는 우리가 호출할 함수가 아니라 누군가에 의해서 나중에 호출 당할 함수입니다.

그리고 sort라는 함수가 가지는 기본적인 방법을 확장할 수 있는 가능성을 우리가 어떠한 logic을 주입함으로써 할 수 있었습니다. 이것이 Callback을 우리가 사용하는 이유입니다.

Callback 함수를 사용하여 프로그램의 흐름을 끊지 않음으로서, Non-Blocking 코드를 사용하는 서버는 Blocking 코드를 사용하는 서버보다 더 많은 양의 요청을 빠르게 처리할 수 있습니다.

Comment and share

인터넷의 동작 방법


클라이언트 : 웹 브라우저가 설치된 컴퓨터, 요청
서버 : Client가 요청한 정보를 응답
도메인 : 사람이 쉽게 이해할 수 있도록 만들어진 서버의 주소(이름)
IP : 실제 웹 브라우저가 연결되는 주소
Port : 웹 서버를 실행시킬 때 6만개의 포트 중에 80번 포트에 웹 서버를 실행시킵니다. 즉, 웹 서버를 실행시켜놓고 80번에 해당되는 문에다가 웹 서버를 연결시켜 놓고, 웹서버가 80번 포트를 바라보게 합니다. 즉, 80번 포트를 리스닝하게 하는 것입니다.

사용자가 이 서버에 접속할 때, 예를 들어서 http://a.com:80 이렇게 접속하면 웹 브라우저는 a.com에 해당되는 컴퓨터를 찾고 a.com에 해당되는 컴퓨터한테 80번 포트와 연결하고 싶다라고 얘기를 합니다. 그럼 그 컴퓨터가 80번 포트를 연결해주기 때문에 웹 브라우저를 통해서 들어온 접속은 80번 포트에서 리스닝하고 있는 즉, 듣고 있는 웹 서버를 호출하고 그 웹서버가 요청을 받아서 웹 서버가 응답할 수 있게 됩니다.
이러한 과정이 인터넷의 동작 방법이라고 볼 수 있습니다.

http로 접속하는 경우에는 80번 포트를 쓰자라는 약속이 되어 있기 때문에 끝에 붙이는 :80을 생략할 수 있습니다.

모듈과 NPM

모듈은 다른 말로는 부품이라고 할 수 있으며, 우리가 필요로 하는 모듈을 가져와서 쓸 수 있습니다. Node.js에서는 필요한 모듈을 가져와서 쓰기 위해서는 require라는 함수를 사용합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const http = require('http');
// http라는 변수에 http라는 서버를 구동시키기 위해서 필요한 모듈을 담은 것이다.
// 이 http를 통해서 모듈을 제어할 수 있습니다.
// 값이 한 번 할당이 되면 그 이후에는 값을 바꿀 수 없음.
const o = require('os');
console.log(o.platform());
const hostname = '127.0.0.1';
const port = 1338;

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}/`);
});

// 이 코드가 웹브라우저를 통해서 요청한 내용을 받아서 우리에게 hello world라는 텍스트를
// 전송한 것입니다.


// createServer를 통해서 서버 한개를 만들고
// listen을 통해서 그 서버가 컴퓨터 한 대를 리스닝하게 만든다.
// 모듈은 부품과 같은 것이다. 그래서 가져다 쓸 수 있다.
// 우리가 필요한 모듈을 가져다 쓸 수 있고 그러한 모듈을 가져다 쓰기 위해서는
// require라는 함수를 호출해서 사용한다.
  • NPM : Node Package Manager
  • NPM -> Node계의 앱스토어라고 부르기도 합니다.
    • 어떠한 모듈을 우리의 프로젝트에 사용하기 쉽게 그리고 간편하게 설치하고 필요없으면 삭제할 수도 있고, 우리가 쓰고 있는 모듈이 기능 개선이 되었으면 업그레이드를 편리하게 해주고, 의존성 관리도 해줍니다.
  • Node.js는 다양한 모듈을 제공합니다.
    • HTTP, OS -> Node.js가 제공하는 모듈
    • Date, String, Array -> JavaScript가 제공하는 모듈
  • NPM은 다른 사람의 S/W를 가져와서 연결시켜주는 연결자의 역할을 하고 있습니다. 따라서 Node.js의 생태계의 중심에는 NPM이 있다고 할 수 있습니다.

이번에는 타인의 모듈을 사용하는 방법을 배워볼 것이고 그 방법으로서 NPM을 사용해볼 것입니다.

  • NPM 소프트웨어 패키지의 종류
    • 독립적으로 동작하는 소프트웨어
      : npm install underscore -g
      g는 global의 약자이며, 전역적으로 실행할 수 있는 독립적인 소프트웨어의 설치

    • 부품 모듈
      : 모듈을 설치하기 전에 지금 현재 진행하고 있는 프로젝트 폴더를 npm의 패키지로 초기화하는 작업을 거쳐야 합니다.
      -> npm init() 사용하고 정보들을 등록합니다. 이 정보들을 명령어 수행 후 package.json이라는 파일에 저장되어 생성됩니다.
      npm install underscore
      해당 폴더, 즉 패키지에서 부품으로 사용할 수 있는 모듈을 설치하는 것입니다.
      npm install underscore --save
      --save 옵션을 주게 되면 underscore라는 모듈이 package.json 파일 안에 dependencies 항목으로 추가되기 때문에 이 프로젝트의 의존성을 명시적으로 표시해서 다른 디렉토리에서 이 프로젝트를 사용할 때 의존성을 갖고 있는 프로젝트를 쉽게 가져올 수 있습니다.
      어떠한 모듈을 항상 프로젝트에 포함시킬 때는 --save 옵션을 사용하는게 좋습니다.
      일시적으로 사용하는 모듈은 --save 옵션이 필요없습니다.

npm install underscore
이렇게 옵션을 주면 해당 폴더, 즉 패키지에서 부품으로 사용할 수 있는 모듈을 설치하는 것입니다.

npm install underscore --save
이렇게 --save 옵션을 주게 되면 underscore라는 모듈이 package.json 파일안에 dependencies 항목으로 추가되기 때문에 이 프로젝트의 의존성을 명시적으로 표시해서 다른 디렉토리에서 이 프로젝트를 사용할 때 의존성을 갖고 있는 프로젝트를 쉽게 가져올 수 있습니다.
그리고 npm을 이용해서 모듈을 사용하기 전에는 지금 현재 프로젝트 폴더를 npm의 패키지로 초기화해야 합니다.
npm init()

Comment and share

SOPT라는 동아리를 통해서 Client 즉, Android에 대해서 공부하면서 프로젝트를 진행해보고 App 개발에 대한 실력을 얻었고 관련 지식도 얻을 수 있었습니다. 저는 Android에 대해 더 공부하기 위한 발판으로 서버 공부를 하기 위해서 JavaScript와 Node.js를 공부할 계획입니다. 공부하는 내용은 이곳에 포스팅할 예정입니다.

JavaScript


  • 웹 브라우저에서 동작하는 스크립트 언어로 시작
  • 다양한 분야에서 활용 가능, 풀스택개발까지 가능
  • 가볍고 손쉽게 작성이 가능한 프로그래밍 언어
  • HTML 내에 코드를 삽입해서 사용 가능
  • 거의 모든 브라우저에서 실행 가능
  • 이벤트 중심 프로그래밍
  • Node.js의 등장으로 서버 사이드 개발 가능
  • 클래스는 지원하지 않지만 객체 지향 프로그래밍 가능
  • 대부분의 개념은 객체(기본 데이터타입, null, undefined)
  • 함수조차 객체로 취급! -> 일급 객체로 다뤄짐
  • 실행 컨텍스트가 독특하다. -> 스코프(객체의 유효범위)가 일반적이지 않다.
  • 모든 객체는 프로토타입을 가짐
  • 일급객체클로저의 특성으로 함수 지향 프로그래밍까지 가능
  • Javascript의 표준이 ECMAScript이고 현재는 ECMAScript6(줄여서 ES6)가 표준

Javascript 문법


Javascript는 변수 타입을 표시하지 않고, 값이 할당되는 과정에서 자동으로 자료형이 결정됩니다. => 그래서 같은 변수에 여러 자료형의 값을 대입할 수 있습니다.
자료형은 var, let, const로 표시 -> Scope의 차이

  • String, Number, Boolean, undefined, null, Object의 자료형을 갖고 있습니다.
  • ES6에서 Symbol이 추가됨 : primitive type이기 때문에 new로 생성 X
  • 기본 타입 : Number, String, Boolean, undefined, null + Symbol
  • 참조 타입 : Array, Function(Object)
    • Object에는 배열, 함수, 정규표현식 등이 포함됩니다.

자료형

  1. Number
    다른 언어들처럼 여러 타입(short,int,long,byte) 등이 있지 않고, 정수값과 실수값을 구분하지 않습니다. 모든 숫자를 실수로 표현합니다.(64bit의 floating point type으로 저장) 그리고 비트연산도 가능하자 속도가 느립니다.
    53bit의 정확도로 정수 표기 가능합니다. 즉, int형도 완벽히 표기 가능!
  1. String
    2byte의 값들이 연속적으로 나열되어 있습니다. 0기반의 인덱싱 사용(다른 언어와 동일)합니다.
    그리고 문자 하나를 표현하는 문자형은 제공하지 않습니다.(길이가 1인 문자열)
    또한, ‘(작은 따옴표)’,"(큰 따옴표)" 둘 다 가능하며, 여러 문자열을 '+'를 이용해 연결할 수 있습니다. (+가 additionconcatenation으로 사용되기 때문에 주의하여 사용해야 합니다.)
    문자열을 수정하는 모든 메소드는 새로운 문자열을 반환합니다.
  1. Boolean
    true, false 중 하나의 값을 가지며, 비교의 결과로 생성됩니다.
    false : 0, “”, undefined, null, NAN(Not A Number)
  1. null, undefined
    null객체가 아님을 뜻하는 특수한 값입니다.
    undefined값이 없음을 나타내는 값으로 값 자체가 없거나 초기화 되어 있지 않거나 존재하지 않는 값에 접근할 때 사용합니다.
    시스템 레벨에서는 undefined, 일반적인 프로그램 레벨에서는 null을 사용
  1. Object
    속성(property) : 키(key) - 값(value)의 쌍으로 이루어지며, 속성끼리는 쉼표(,)로 구분합니다.
    키는 문자열만 가능하고 따옴표가 있어도 없어도 가능합니다.
  1. Array
    []로 감싸며, 값들이 순서대로 나열되어 있습니다.
    배열의 원소에는 다른 데이터 타입들이 들어갈 수 있습니다.
  • 기본 자료형
    • Number
    • String : " ",’ ’ 둘 다 가능
    • Boolean : true/false
    • Array : 0 이상의 임의의 종류의 값으로 이루어진 순서가 있는 리스트. 대괄호 [] 사용
    • Object : 순서가 없는 이름 - 값 의 쌍으로 이루어진 집합이며, 이름(키)은 문자열입니다. 중괄호 {} 사용
    • null

함수(Function)

  • 일급객체
    javascript는 함수형 프로그래밍 언어이며, 객체가 일급 객체(First Class Object)입니다. => 함수도 객체! => 함수가 일급 객체!
    익명 함수(Anonymous Function) : 함수의 이름이 없는 함수
    고차 함수(Higher-Order Function) : 함수를 인자로 받거나 반환할 수 있는 함수

일급 객체의 조건

(1). 변수나 데이터 구조 안에 담을 수 있습니다.
(2). 다른 함수의 파라미터로 전달할 수 있습니다.
(3). 반환값(return value)로 사용할 수 있습니다.
(4). 할당에 사용된 이름과 관계없이 고유한 구별이 가능합니다.
(5). 동적으로 프로퍼티 할당이 가능합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// 변수나 데이터 구조 안에 담을 수 있습니다.
var func1 = function(){
console.log(1);
}

func1();
// (1)

// 다른 함수의 파라미터로 전달할 수 있습니다.
var func2_1 = function(){
return 2;
}
//
var func2_2 = function(value){
console.log(value);
}

func2_2(func2_1());
// (2)

// 반환값(return value)로 사용할 수 있습니다.
var func3 = function(){
return function(){
console.log(3);
}
}

func3()();
// (3)

// 할당에 사용된 이름과 관계없이 고유한 구별이 가능합니다.
var func4 = function func44(){
console.log(4);
}

func4();
// (4)

// 동적으로 프로퍼티 할당이 가능합니다.
var func5 = function(){
console.log(5);
}
func5.property = '55';
console.log(func5.property);
// (5)

함수의 생성 방법


  • 함수 선언문을 사용한 생성
    • 코드 블록 자체는 실행 가능한 코드는 아님
    • 함수명이 반드시 정의되어야 함
    • 일반적인 언어의 함수 정의와 같음
    • 매개변수의 타입을 표시하지 않음
1
2
3
function func1(n){
console.log('func1 : '+n);
}
  • 함수 표현식을 사용한 생성
    • 함수 리터럴(표현식)로 특정 변수에 할당되거나 즉시 실행 가능한 코드 블록
    • 일급 객체이므로 변수에 할당이 가능
    • 함수의 이름은 선택 사항이고 함수 표현식에서 사용된 함수 이름은 외부에서 접근할 수 없음 -> 그래서 보통 익명함수로 생성 후 변수명으로 사용합니다.
1
2
3
4
5
6
7
var func2 = function(n){
console.log('func2 : '+n);
}

var func4 = function func44(n){
console.log('func4 : '+n);
}
  • 생성자 함수를 사용한 생성
    • 함수가 일급 객체이기 때문에 객체 생성 방식과 비슷
    • new 키워드로 객체를 생성할 수 있는 함수
    • prototype을 사용하여 한 번만 메소드를 생성할 수 있음
    • 자주 사용하지 않습니다.

Json(JavaScript Object Notation)


  • 속성 - 값의 쌍으로 이루어진 데이터 Object를 전달하기 위해 인간이 읽을 수 있는 텍스트를 사용하는 개방형 표준 포맷을 말합니다.
  • 일반적으로 서버에서 클라이언트로 데이터를 보낼 때 사용하는 포맷입니다.
  • 또한, 자바스크립트에서 파생되어 자바스크립트의 구문 형식을 따르지만 언어 독립형 데이터 포맷입니다.-> 자바스크립트에서만 사용하는 것이 아님!
  • 공식 인터넷 미디어 타입 : application/json
  • 프로퍼티 사이의 구분은 쉼표(,)로 하고 마지막 프로퍼티 끝에는 붙이지 않습니다.
  • 아래와 같이 둘 다 가능합니다.
1
2
3
4
5
6
7
8
9
10
11
{
"name" : "이승우",
"age" : 25,
"married" : false
}

{
name : "이승우",
age : 25,
marrie" : false
}

1. Object() 생성자 함수 이용

1
2
3
4
var server = new Object();
server.name = "이승우";
server.age = 26;
server.married = false;

2. 객체 리터럴(표현식) 방식 이용

1
2
3
4
5
6
var server = {
name : "이승우",
age : 25,
married : false,
gender : "남자"
}

3. 프로퍼티 접근

1
2
3
4
server["name"];
server["age"];
server.married;
server.gender;

이처럼 대괄호 표기법을 이용하면 프로퍼티 이름을 문자열 형식으로 적어줘야 접근할 수 있습니다. 만약 server[name]으로 접근시 undefined

마침표 표기법을 이용해서 간단한게 .을 통해서 객체의 프로퍼티에 접근할 수 있습니다.

4. 객체 프로퍼티 순회

for in문을 사용해서 객체의 프로퍼티에 접근할 수 있습니다. 객체에 포함된 모든 프로퍼티에 대해 루프를 수행합니다.

1
2
3
for(i in server){
console.log(server[i]);
}

5. 객체 프로퍼티 삭제

delete연산자를 통해서 가능합니다. delete 연산자가 객체의 프로퍼티는 삭제할 수 있지만 객체 자체를 삭제할 수는 없습니다.

1
delete(server.name);

배열


1
var nameOfArray = [1, 2.5, "sopt",true, {"part" : "server", "name" : "이승우"}]

위의 코드처럼 배열의 원소에는 다른 데이터 타입들이 들어갈 수 있습니다. 또한, 함수도 객체이므로 들어갈 수 있습니다.

1. 배열 요소 추가
동적으로 배열 원소를 추가할 수 있습니다. 특히나, 값을 순차적으로 넣을 필요 없이 아무데나 넣을 수 있습니다.
array.push(넣을값) -> 가장 끝에 있는 인덱스 뒤에 넣습니다.
array.splice(index,0,item) -> 원하는 index에 item 값을 넣을 수 있습니다.

2. 배열 요소 삭제
delete를 통해 배열 요소를 삭제할 수 있습니다. 삭제를 하고 나서는 해당 인덱스의 값이 undefined로 남아 있습니다. 즉, 비어있는 상태로 남아있습니다.

1
delete(nameOfArray[0]);

3. 배열 요소 접근
array[음이 아닌 정수 or 변수]로 배열 요소에 접근이 가능합니다. 그리고 현재 배열 내 없는 인덱스로 접근하더라도 out of bound 가 나지 않습니다.

4. 배열 요소 순회

1
2
3
4
5
6
7
console.log("** 배열 요소 순회 예제 **");
for (var i = 0; i < nameOfArray.length; i++) {
console.log(nameOfArray[i]);
} // 배열 내 없는 원소까지 undefined로 모두 출력
for(var j in nameOfArray){
console.log(nameOfArray[j]);
} // 배열 내 없는 원소는 출력하지 않음

5. 배열도 객체다!
배열이름.프로퍼티이름 = 값으로 프로퍼티를 추가하는 것이 가능합니다. 또한, length도 메소드가 아니라 객체의 프로퍼티입니다. 그래서 실제 배열의 길이와 관계없이 변경이 가능합니다. 인덱스에만 관계가 있기 때문에 배열 객체의 프로퍼티는 length에 포함되지 않습니다.

실행 컨텍스트


아직까지 무슨 이야기인지 잘 모르겠습니다…ㅜ_ㅜ

자바스크립트가 실행될 때 생성되는 하나의 실행 단위를 말합니다. C,C++,Java 등의 콜스택에 들어가는 하나의 실행정보와 비슷한 개념입니다. 실행 가능한 자바스크립트 코드 블록이 실행되는 환경입니다.

현재 실행되는 컨텍스트에서 이 컨텍스트와 관련 없는 실행 코드가 실행되면 새로운 컨텍스트가 생성되어 스택에 들어가고 제어권이 그 컨텍스트로 이동합니다.

실행 컨텍스트 생성 과정

  1. 활성 객체 생성
  2. arguments 객체 생성
  3. 스코프 정보 생성
  4. 변수 생성 -> 여기서 선언이 이루어짐
  5. this 바인딩
  6. 코드 실행 -> 여기서 할당이 이루어짐
  • 변수 타입별 스코프(영역)
    • var : 함수 단위 스코프, 재선언 가능, 재할당 가능
    • let, const : 블록 단위 스코프 (일반적인 C,C++,Java)
    • let : 재선언 불가능, 재할당 가능
    • const : 재선언 불가능, 재할당 불가능

호이스팅이란?? - 사실, 이 친구도 어려워요…ㅜ

변수, 함수의 선언부가 스코프 가장 위로 끌어올려지는 것을 말합니다. 블록 내부에 정의된 변수는 블록에 포함된 함수 전체에 선언되는 것과 같으므로 유효범위가 함수 전체로 확대됩니다. 반복문, 조건문 내부에 사용된 변수를 같은 함수 내라면 바깥에서 접근이 가능합니다.
또한 함수 표현식으로 정의되어 있으면 호이스팅이 발생하지 않습니다.

연산자의 종류


보통의 연산자는 다른 언어와 유사합니다. 그래서 일반적이지 않은 javascript의 연산자에 대해서 공부해보았습니다.

  • 관계 연산자

    • 두 피연산자의 관계를 검사하여 관계가 성립하면 true, 아니면 false를 반환합니다.
    • 항상 Boolean 값을 반환합니다.
  • 동등 연산자 vs 일치 연산자

    • ==, != (동등 연산자, Equality) : 타입이 다를 경우 형변환(묵시적 형변환)을 한 후에 값을 비교합니다. 즉, 타입이 달라도 동등할 수 있습니다.
    • ===, !== (일치 연산자, Identity) : 형변환을 하지 않고 현재 상태로 값을 비교합니다. 즉, 타입이 다르면 일치하지 않습니다.
  • + 연산자

    • Number + Number => 더하기 연산 수행
    • String + String, Number + String, String + Number => 문자열 연결 연산을 수행
    • 여러 숫자, 문자열을 결합 시 연산자가 실행된 순서에 따라 연산 결과가 바뀌게 됩니다.
  • / 연산자

    • 자료형만 확실히 이해하고 있다면 어렵지 않습니다. console.log(5/2)의 결과는 => 2.5
  • typeof 연산자
    • 피연산자의 타입을 String 형태로 반환하는 연산자

변수의 범위(Variable Scope)


변수가 존재하는 컨텍스트(함수가 실행되거나 변수를 참조할 때의 환경). 어디에서 변수가 접근할 수 있는지, 그 컨텍스트에서 변수에 접근할 수 있는지를 명시적으로 나타냅니다.

  • 지역 변수(함수 수준 범위) : 함수 내에 정의된 변수는 지역 범위를 가지며, 해당 함수와 내부 함수에서만 접근이 가능합니다. 지역 변수는 함수 내에서 전역 변수보다 높은 우선순위를 갖습니다.
  • 전역 변수 : 함수의 외부에서 선언된 모든 변수는 전역 범위를 가집니다. 전역 컨텍스트(scope)는 window를 가리킵니다.

지금까지 Javascript에서 변수를 선언하는 방법은 var를 이용하는 것 뿐이었다고 합니다. 물론 Javascript 특성상 var 없이 변수를 선언하는 것도 가능하지만 부작용이 심각하기 때문에 실무에서는 적용하지 않는 것이 보통입니다. 일이 아니더라도 var 선언 없이 변수를 사용하는 것은 정신건강에 좋지 않기 때문에 절대로 사용하지 말 것을 권장한다고 합니다.

본론으로 넘어가서, ES6로 넘어오면서 letconst라는 새로운 선언 방법이 생겼습니다.

  • var : function-scoped. 변수 재선언 가능
1
2
3
console.log(foo);

var foo;

위의 코드는 에러가 발생하지 않습니다. 변수 foo는 값이 정의되지 않은 형인 undefined가 되어 있을 뿐입니다. 선언보다 호출이 먼저 있었음에도 불구하고 이 코드는 정상적으로 작동합니다. 이유는 호이스팅이라는 키워드를 검색해보면 알 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
function ab(){
var foo = 'bar1';
console.log(foo); // bar1

if (true) {
var foo = 'bar2';
console.log(foo); // bar2
}

console.log(foo); // bar2
}

ab();

위의 코드가 ab()라는 함수 안에 존재한다고 했을 때, if문 밖의 변수 foo와 if문 안의 변수 foo는 동일한 변수가 됩니다. 중복 선언을 했지만 앞서 말한바와 같이 별다른 에러를 발생시키지 않고, 값마저 'bar2’로 변경해버립니다.

하지만, letconstBlock-scoped라고 합니다. 유효 범위가 블록 즉, {}로 감싸지는 범위라는 뜻입니다.

  • let : block-scoped. 변수 재선언 불가능. 변수 재할당 가능
1
2
3
let c = "kim";
let c = "lee"; // 재선언 불가능. 에러 발생
c = "lee"; // 재할당 가능. 에러 없이 정상적으로 작동

위의 코드를 통해서 let이 잘 작동하는지 확인해보았습니다. 그리고 여러 예제를 통해서 let의 의미를 더 알아보았습니다.

1
2
3
4
5
6
7
8
9
let foo = 'bar1';
console.log(foo); // bar1

if (true) {
let foo = 'bar2';
console.log(foo) // bar2
}

console.log(foo); // bar1

위 코드에서는 var를 사용한 경우와는 달리 if문 밖의 foo와 if문 안의 foo는 서로 다른 변수입니다. 따라서 중복 선언으로 인한 에러도 발생하지 않으며, if문 안쪽에서 선언한 foo의 경우 if문이 닫히는 시점에서 유효 범위가 끝납니다.

여기서 의문이 조금 생깁니다. if문 안에서 foo를 먼저 호출한 다음 let으로 foo에 값을 할당하게 된다면??

1
2
3
4
5
6
7
8
9
10
let foo = 'bar1';
console.log(foo); // bar1

if (true) {
console.log(foo) // bar1
foo = 'bar2';
console.log(foo) // bar2
}

console.log(foo); // bar2

걱정했던 것과는 다르게 정상적으로 호출도 되고 값의 변경에도 아무 문제가 없습니다. 그럼 foo 호출 이후에 let으로 foo를 선언해보겠습니다.

1
2
3
4
5
6
7
8
9
10
11
let foo = 'bar1';
console.log(foo); // bar1

if (true) {
console.log(foo);
// Uncaught ReferenceError: foo is not defined

let foo = 'bar2';
}

console.log(foo);

foo는 정의되지 않았다는 에러가 발생합니다. 앞에서 말한 임시적 사각지대(TDZ)의 정체가 이것입니다. 어떤 변수가 호출되었을 때 블록 안에 같은 이름의 변수가 없으면 상위 블록에서 선언된 같은 이름의 변수를 호출합니다. 하지만 블록 안에서 let이나 const로 변수 선언이 있었다면 그 이름의 변수는 변수가 선언되기 이전까지 그 블록 안에서는 정의되지 않은 변수로 간주되는 것입니다.

  • const : block-scope. 변수 재선언 불가능. 변수 재할당 불가능
1
2
3
console.log(foo);

let foo;

호출한 시점에서 변수가 선언되어 있지 않음을 알리는 에러가 발생합니다;. 일시적 사각지대(Temporal Dead Zone : TDZ)라는 개념인데, 특정 개념을 설명하지 않더라도 let과 const의 동작 방식이 직관적이고 자연스럽다고 생각합니다.

실제로 원시형(primitives type : String, Number, Boolean, null, undefined)에서 const는 상수로 동작합니다. 따라서 const로 선언되면 값을 재할당할 경우 에러가 발생합니다. 또한, 당연하게도 초기값을 설정하지 않으면 에러가 발생합니다.

따라서 단순형의 경우 값의 변경이 있는 경우에는 let으로, 상수로 사용하는 경우에는 const로 선언하는 것이 바람직합니다.

하지만, 참조형(Complex tyupe : Array, Object, Function)의 경우 결론부터 말하자면 const로 선언하는 것이 바람직합니다. 참조형은 const로 선언하더라도 멤버값을 조작하는 것이 가능합니다.

1
2
3
4
5
6
7
const foo3 = [0,1];
const bar3 = foo3;

foo3.push(2);
console.log(foo3, bar3);
bar3[0] = 10;
console.log(foo3, bar3);

위의 결과를 보면 const bar = foo;의 선언으로 bar는 foo를 참조합니다. 참조가 아니라 값을 복사(copy)하는 경우에는 array는 … 연산자를 사용하고, object는 assign() 함수를 사용합니다.

  • 호이스팅 : 변수의 정의가 그 범위에 따라 선언과 할당이 분리되는 것을 의미
    • 함수 내에서 정의되었을 경우 : 선언이 함수의 최상위
    • 함수 바깥에서 정의되었을 경우 : 전역 컨텍스트의 최상위로 변경
    • 변수의 선언이 초기화나 할당시에 발생하는 것이 아니라, 최상위로 호이스트 됨
    • 함수 선언문 방식만 호이스팅이 제대로 이루어짐

함수 선언은 변수 선언을 덮어씁니다. 하지만, 변수에 값이 할당될 경우 반대로 변수가 함수 선언을 덮어쓰게 됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var test; 

function test(){
console.log('test');
}

console.log(typeof test); // function

var test = 'test';

function test(){
console.log('test');
}

console.log(typeof test); // string
  • 변수는 camelCase로 작성합니다. [ ex. var mainTest; ]
  • 세미콜론은 항상 붙여줍니다.
  • 변수 선언은 스코프 상단에 선언합니다.
  • 동치 연산자(==,!=)보다는 일치 연산자(===,!==)를 사용합니다.
  • 중괄호 ‘{’ 위치는 선언문과 같은 줄에
  • 결론
    • ES6에서는 var는 지양하고 가급적 let과 const를 사용
    • 원시형 변수는 let, 상수는 const로 선언
    • 참조형은 const로 선언

Node.js?


NodejsChrome V8 JavaScript 엔진으로 빌드된 JavaScript 런타임 환경으로 주로 서버 사이드 애플리케이션 개발에 사용되는 소프트웨어 플랫폼입니다.
Javascript가 브라우저 밖에 존재하지 않았던 한계를 극복하여 브라우저 외부 환경에서 Javascript 애플리케이션 개발에 사용되며 이에 필요한 모듈, 파일 시스템, HTTP 등 Built-in API를 제공합니다. 또한, Node.js는 이벤트 기반, 논 블로킹 I/O 모델을 사용해 가볍고 효율적입니다.

런타임(RunTime)이란??

단순하게 말해서 프로그래밍 언어가 구동되는 환경이라고 이해하면 됩니다. Javascript라면 Web Browser에서 작동하는 Javascript 측면이 있고, Node.js라는 환경에서 구동되는 측면이 존재합니다. 여기에서의 BrowserNode.js를 런타임이라고 볼 수 있습니다.

특징


기존의 웹에서 서버는 스레드를 기반으로 하는 동기 방식(Multi-Thread)으로 네트워크 입출력을 처리합니다. 여기서 동기 방식이란 작업 요청이 들어올 때마다 스레드를 여러 개 만들어 동시에 일을 처리하는 것을 말합니다. 좋은 해결법이지만, 일이 많아질수록 스레드를 더 많이 만들어야 하므로 메모리 사용량이 많아질 수 있습니다. 그러나 서버의 자원이 제한되어 있기 때문에 Thread를 무한히 생성할 수는 없습니다.

Multi-Thread 방식은 이러한 문제를 서버의 성능을 높이거나 Load-Balancing 등으로 분산 처리하여 해결합니다. Multi-Thread 방식은 Thread 간의 공유 자원 접근 시에도 신중해야 합니다. 각 Thred는 독립적인 시점에서 동작하기 때문에 공유 자원에 대한 동기화도 반드시 필요합니다.

Node.js의 경우, 모든 API는 이벤트 기반 비동기 방식으로 동작하여 Non-blocking I/O가 가능(제어권을 넘김)하여 요청을 처리하면서 다음 요청을 받을 수 있습니다. 또한, single thread를 사용하여 Multi Thread의 문제로부터 자유롭습니다.

Node.js 동작원리


Node.jsEvent-Callback 방식을 이용하여 비동기식 처리를 진행합니다. 클라이언트가 Event를 요청하면 Message 형태로 Event Queue에 저장됩니다. Event Loop는 Node.js에서 Single thread에서 돌아가며 I/O Bound 작업들을 비동기적으로 처리하기 위하여 필요합니다. Event Loop가 Queue에 있는 Task를 Pop하여 kernel에 요청하게 되는데 처리하는 동안 제어권은 다음 요청으로 넘어가게 됩니다. (Non-Blocking:또 다른 요청을 처리할 수 있게 됩니다.) 요청이 Blocking I/O 혹은 처리에 많은 시간을 요구하는 복잡한 성격의 요청(파일 시스템 I/O, 데이터베이스, 외부 서비스 처리 등)이라면 내부 쓰레드 풀에서 대기 중인 쓰레드에게 요청을 위임합니다. 요청이 Non-Blocking I/O 또는 복잡하지 않은 작업이라면 Event Loop Thread는 요청을 즉각 처리합니다.

요청 처리가 완료되면 Callback을 호출하여 처리 완료를 클라이언트에게 전달합니다. 내부적으로 비동기식 방식(Non-Blocking)을 지원하지 않는 task는 Multi Thread Pool로 처리하는데 이는 내부 처리를 위한 목적이며 요청 처리 자체를 Thread로 하지 않습니다.

그림을 통해 설명하자면, Client A가 요청을 하면 CPU 작업이 수행되고 I/O 요청을 보냅니다. Non-Blocking 처리 방식을 사용하기 때문에 I/O 작업을 수행하며 또 다른 요청을 처리할 수 있습니다. Client B의 요청이 오면 CPU 작업을 수행하고 I/O 요청을 보냅니다. I/O 작업 시 기다리지 않기 때문에 single thread가 다른 요청을 받아서 작업을 처리할 수 있는 구조입니다.

Event Loop는 Single Thread로 이루어져 있습니다. 따라서, 하나의 request 처리 작업이 CPU를 많이 사용(CPU intensive한 처리)하게 된다면 전체 서버 처리에 영향을 줄 수 있는 단점이 있습니다. (다른 요청이 CPU 작업을 수행하기 힘들다.) 이에 따라, 처리해야할 작업이 CPU를 많이 소모한다거나 대용량 파일을 처리해야 한다면 Node.js가 적합하지 않을 수 있습니다.

장단점


이벤트 기반 비동기 방식으로 많은 클라이언트 요청에 대하여 이벤트 루프를 사용하기 때문에 많은 스레드를 사용하지 않습니다. 따라서, 멀티 스레드 방식보다 Thread 수가 적기 때문에 메모리 또는 자원 소모가 작습니다. 오버헤드 또한 적습니다.

추가적인 장점으로 JavaScript를 사용하여. 웹 개발자가 쉽게 접근할 수 있습니다. 또한, Google이 만든 자바스크립트 엔진을 사용하고 있습니다. 계속해서 여러 큰 기업들이 경쟁하며 자바스크립트 엔진의 속도를 높이고 있어 Google이 무너지지 않는 이상 속도는 계속 빨라질 것입니다.

마지막 장점으로, C++로 개발된 V8 JavaScript 엔진을 사용하여 C++을 사용해 기능을 확장할 수 있습니다.
JavaScript를 사용한 것이 단점이 될 수도 있습니다. C/C++로 개발된 서버 애플리케이션보다는 느리기 때문입니다. 그러나, 또 다른 스크립트 언어를 사용해서도 웹 개발이 많이 이루어지고 있어 큰 문제라고는 생각할 수 없습니다.

Single Thread기반의 비동기 I/O처리, 이벤트 처리 방식으로 인해 성능이 매우 빠릅니다. 또한, 시스템 리소스의 부하가 적습니다.

또한, Single Thread를 사용하기 때문에 CPU intensive한 영역에서는 약한 모습을 보입니다. 그러나 상대적으로 CPU intensive한 작업이 없고, 많은 Connection을 동시에 처리해야 하는 경우에는 node.js의 성능이 압도적으로 높은 것을 알 수 있습니다.

단점으로는 일을 처리하는 한 명(즉,Single Thread)이 쓰러지는 순간 프로그램 전체에 문제가 발생하게 됩니다.
또한, 하나의 작업 자체가 시간이 많이 걸리면, 전체 시스템의 성능이 급격하게 떨어집니다. 그리고 코드의 가독성이 떨어져 유지보수가 어렵습니다.
마지막으로 에러가 날 경우 프로세스 자체가 죽어버립니다.

요약하자면, 개발 관점에서는 빠르고 쉬운 장점이 있지만, 반대로 운영관점에서는 테스트, 디버깅 등에 어려움이 있을 수 있습니다. 대규모 프로젝트나 게임 서버보다는 RESTful API 서버, 채팅 서버 등에 적절합니다.

Comment and share

  • page 1 of 1
Author's picture

VictoryWoo

기록을 통해 사람들과 공유하는 것을 좋아합니다.


Android Developer