Node 개발자라면 알아야 할 기본 지식 (JavaScript )

Node 개발자라면 알아야 할 기본 지식을 한번 짚고 넘어가자는 의미에서 글을 쓰겠습니다.

Node와 JavaScript

Node는 구글에서 개발한 C++ 기반의 V8 엔진으로 빌드된 이벤트 기반 자바스크립트 런타임(실행 환경)입니다. 런타임 환경은 call stack, web apis, callback queue, event loop로 이루어져 있습니다.

Node는 기본 언어가 자바스크립트이기 때문에 이 언어를 알아야 합니다. 자바스크립트 엔진은 메모리 힙(Memory Heep)과 호출 스택(call stack)로 구성되어 있습니다. 메모리 힙은 우리가 선언한 변수, 함수 등이 담겨져 있고 호출 스택에는 코드가 실행될 때 쌓이는 곳입니다.

JavaScript

자바스크립트는 웹 브러우저에서 동작하는 스크립트 언어입니다. 즉, 한 줄씩 코드를 읽고 번역하고 실행합니다.

스크립트 언어의 장점으로는 다음과 같습니다.

 

  • 실행 속도는 컴파일 언어보다 느리지만 코드 수정시 바뀐 부분만 번역하고 실행하기 때문에 바로 실행합니다.
  • 이벤트에 따라 다른 페이지를 띄우지 않고도 바뀐 내용을 실시간으로 보여줄 수 있게 해줍니다.

싱글 스레드와 멀티 스레드

싱글 스레드와 멀티 스레드

스레드는 프로세스 내 실행 단위로 하나의 경우 싱글 스레드, 2개 이상일 경우 멀티 스레드라고 부릅니다.

자바스크립트는 싱글 스레드입니다. 한 가지의 스레드를 가지고 있기 때문에 하나의 일을 처리할 수 있다는 뜻에서 call stack이라고 부릅니다.

아래 예시를 통해 자바스크립트 실행 순서 및 call stack에 대해 알아보겠습니다.

console.log('1');
setTimeout(console.log, 1000, '2');
console.log('3');

위 코드를 실행하면 1,3,2의 순서대로 출력될 것입니다. 자바스크립트가 싱글 스레드라면 setTimeout 함수를 끝내고 다음 코드를 실행할 것입니다. 하지만 출력해보면 1,2,3과는 다른 결과가 나타났습니다. 이런 현상을 이해하기 위해서는 이벤트 루프를 알아야 합니다.

 

 

JavaScript 동작 원리

위 코드는 어떻게 처리되는지 살펴보겠습니다.

console.log('1')

  1. call stack에서 console.log('1')을 추가합니다.
  2. console.log('1')을 실행하면 console창에 1이 출력됩니다.
  3. 실행 완료 후 call stack에서 console.log('1')이 제거됩니다.

 

 

 

 

 

 

 

 

setTimeout(console.log, 1000, '2');

  1. call stack에서 setTimeout(console.log, 1000, '2')를 추가합니다.
  2. setTimeout(...)을 실행 후 web apis의 timer 스레드에 작업을 넘깁니다. 이렇게 넘기면 1초 간 기다리는 작업을 main 스레드가 아닌 timer 스레드가 수행합니다. 마치 멀티 스레드처럼 말이죠. 하지만, 이렇게 싱글 스레드임에도 여러 가지 일을 동시에 처리하는 것을 동시성이라고 부릅니다.
  3. 실행 완료된 setTimeout()는 이미 web apis에 넘겼으므로 call stack에서 사라집니다.

 

 

 

console.log('3');

  1. call stack에서 console.log('3')을 추가합니다.
  2. console.log('3')이 실행하면 console창에 3이 출력됩니다.
  3. 실행 완료 후 call stack에서 console.log('3')이 제거됩니다.

 

 

 

 

 

 

1초 뒤 web apis에 있는 timer 스레드는 console.log('2')를 callback queue로 이동합니다.

 

 

 

 

 

 

 

 

 

  1. call stack에 비어진 것을 확인한 event loop가 callback queue에 있던 console.log('2')를 call stack로 이동합니다.
  2. console.log('2')가 실행하면 console창에 2가 출력됩니다.
  3. 실행 완료 후 call stack에서 console.log('2')를 제거합니다.

 

 

 

 

 

 

 

이처럼 자바스크립트는 싱글 스레드로 동작하지만 런타임환경이 멀티 스레드를 제공하기 때문에 비동기 콜백 작업이 가능합니다.

 

정리하자면, event loop는 callback queue와 call stack을 지켜보다가 call stack에 아무것도 없으면 콜백을 실행시켜 주는 역할을 합니다. callback queue는 비동기적으로 실행된 콜백함수가 보관되는 영역입니다. 예를 들어 위에서 동작 원리를 설명한 것과 같이 setTimeout에서 타이머 완료 후 실행되는 함수, addEventListener에서 click 이벤트가 발생했을 때 실행되는 함수 등을 보관합니다. web apis은 브라우저에서 제공하는 API로 대표적으로 DOM, AJAX, Timeout 등이 있습니다. call stack은 위에서 보시다시피 함수를 실행하면 해당 함수의 기록을 call stack에 추가합니다. 그리고 결과값이 출력되면 제거 됩니다.

 

이제 JavaScript에 대해 알았다면 Node에 대해 알아보겠습니다.

NodeJS

 

 

Node는 구글에서 개발한 C++ 기반의 V8 엔진으로 빌드된 이벤트 기반 자바스크립트 런타임입니다. 앞서 자바스크립트 런타임은 call stack, web apis, callback queue, event loop로 이루어져 있다고 했습니다. Node는 이러한 자바스크립트 환경뿐만 아니라 노드의 패키지 매니저인 npm을 활용하면 다른 개발자의 코드를 사용함으로써 개발 시간을 단축할 수 있는 장점이 있습니다.

Express

많은 npm 모듈 중 프레임워크인 Express에 알아보겠습니다. Express의 장점은 서버를 쉽게 실행 및 운영할 수 있고 내장된 라우터로 코드를 쉽게 재사용이 가능합니다. 

라우팅

라우팅은 URL 및 HTTP 요청 메소드인 특정 엔드포인트에 대한 클라이언트 요청에 애플리케이션이 응답하는 방법을 결정하는 것을 말합니다.

HTTP 요청 메소드는 대표적으로 GET, POST, PUT, DELETE 등이 있습니다.

  • GET: HTTP 서버에 데이터 검색을 요청합니다. 즉, 조회의 역할을 할 때 사용합니다.
  • POST: 데이터를 생성할 때 사용합니다. 대표적으로 회원가입, 게시글 작성 등이 있습니다.
  • PUT: 데이터를 수정할 때 사용합니다. 대표적으로 회원정보변경, 게시글 수정 등이 있습니다.
  • DELETE: 데이터 삭제할 때 사용합니다. 대표적으로 회원탈퇴, 게시글 삭제 등이 있습니다.
const express = require('express');
const app = express();

app.get('/test', function(req,res,next){
	res.send('Hello')
});

위 코드는 /test로 요청이 들어오면 Hello라는 페이지를 응답하는 코드입니다.

미들웨어

구조 내에서 중간 처리를 위한 함수입니다.

미들웨어의 특징으로는 다음과 같습니다.

  • 다음 미들웨어 호출
  • 요청, 응답 객체 변경 가능
  • 모든 코드를 실행
  • 요청-응답 주기를 종료

다음 미들웨어 호출의 경우는 다음과 같습니다.

var express = require('express');
var router = express.Router();

const test = function (req, res, next) {
  console.log('test');
  next();
}

router.use(test)

/* GET home page. */
router.get('/', function (req, res, next) {
  console.log('get');
  res.render('index', {
    title: 'Express'
  });
});

module.exports = router;

루트 경로('/')로 요청을 보냈을 때 test 미들웨어를 거치고 라우팅 부분을 실행하는 코드입니다. 즉, 콘솔창에 test다음으로 get이 출력됩니다.

 

요청, 응답 객체 변경 가능은 다음과 같습니다.

var express = require('express');
var router = express.Router();

const time = function (req, res, next) {
  req.time = Date.now();
  console.log(req.time);
  next();
}

router.use(time)

/* GET home page. */
router.get('/', function (req, res) {
  let changeTime = req.time + '12';
  console.log(changeTime);
  res.render('index', {
    title: 'Express'
  });
});

module.exports = router;

req.time을 미들웨어에서 사용했는데 라우팅에서 가져와서 변경할 수 있습니다.

 

모든 코드를 실행은 다음과 같습니다.

var logger = require('morgan');
var express = require('express');
var cookieParser = require('cookie-parser');

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

이런식으로 클라이언트로부터 요청이 들어올 때마다 거치게 되는 미들웨어입니다.

이 글을 공유하기

댓글

Designed by JB FACTORY