TIL/TIL

Node.js와 Express 그리고 Nest.js

JoJobum 2022. 10. 3.

Node.js 란?

Node.js는 서버 사이드 도구들과 어플리케이션을 JavaScript로 만들 수 있도록 해주는 런타임 환경

 

런타임 환경이란 컴퓨터가 실행되는 동안 프로세스나 프로그램을 위한 소프트웨어 서비스를 제공하는 가상 머신의 상태

 

Node.js 의 장단점

장점

싱글 스레드 기반 Non Blocking, 비동기 IO처리로 매우 빠른 고성능 서버

CPU 파워를 많이 필요로 하지 않는, 연결을 동시에 처리해야 하는 상황에서 좋음

프론트엔드와 벡엔드 기술의 통합으로 언어들 사이의 context shift를 다루는 시간을 줄일 수 있음

단점

싱글 스레드 모델이기에 하나의 작업이 오래 걸리면 전체 시스템 성능에 영향 많이 줌 ⇒ CPU 파워를 많이 필요로 하는 작업에 부적절

스크립트 언어의 특성 상 런타임에 에러를 확인할 수 있고, 그러한 경우 프로세스가 종료되기에 잘 죽는다

 

JS 구동 방식(feat. 이벤트 루프)

Node.js 는 구글의 V8 엔진으로 시작

  • 메모리 힙: 메모리 할당이 이루어 지는 곳
  • 콜 스택: 코드가 실행되면서 스택 프레임이 쌓이는 곳

JS는 싱글 쓰레드 프로그래밍 언어 => 콜 스택이 1개라는 뜻, 한 번에 하나의 일만 할 수 있음

=> 콜 스택 내에 수행시간이 긴 함수가 들어가면 모두 수행되고 나올 때까지 다른 작업이 불가능 == 블록킹 된 상황

 

JS 엔진은 단일 콜 스택을 사용, 동시성에 대한 처리는 JS를 구동하는 환경인 브라우저나 Node.js(libuv 라이브러리)가 처리한다

 

 

Node js가 우리가 알고 있는 싱글 쓰레드의 개념대로 동작하면 서로 다른 클라이언트 10개가 처리하는데 3초 짜리 요청을 보내면 마지막 요청은 30초 후에야 응답을 받는다.

 

반면 자바에서는 Tomcat 서버를 사용하는데, 멀티쓰레드를 사용하기에 각각의 쓰레드가 요청을 나누어가져 같은 상황에서 모두 3초만에 응답을 받을 수 있다. 다만 최대 쓰레드를 넘는 동시 요청이 들어오면 딜레이가 생길 수 있음

 

앞서 말한 Node.js의 단점이 너무 치명적이기에 Node.js는 싱글 쓰레드로 동작하면서 동시에 여러 요청을 처리하기 위해 libuv 라이브러리를 사용한다.

non-blocking I/O : 요청 받으면 쓰레드에게 토스함

asynchronous : 토스했던 요청의 응답 받으면 콜백으로 응답을 클라이언트로 보내줌

 

동작 흐름

  1. node.js 요청이 들어오면 event queue에 추가한다
  2. node.js의 이벤트 루프가 event queue를 조회해 FIFO로 요청을 Libuv의 internal C++ Thread Pool 로 보낸다.
  3. Thread Pool에서 작업 처리하고 callback 함수 실행시켜 이벤트 루프로 응답 전달
  4. 이벤트 루프가 응답을 클라이언트에게 보냄

사실 Node.js는 싱글 쓰레드인척하는 멀티쓰레드였던 것…!

 

 

싱글 쓰레드 vs 멀티 쓰레드

싱글 쓰레드는 하나의 쓰레드를 갖는 프로세스

멀티 쓰레드는 하나 이상의 쓰레드를 갖는 프로세스

 

싱글 쓰레드는 하나의 작업이 끝나야 다음 작업 시작

멀티 쓰레드는 2개 이상의 작업을 짧은 시간 동안 번갈아 가며 수행한다.

마치 동시에 이뤄지는 것처럼 보임, 실제론 작업 전환을 수시로 하면서 작업을 수행 이러한 작업 전환을 Context Switching 이라고 함.

컨텍스트 스위칭이 과도하거나 오래 걸리는 경우 멀티 쓰레드가 싱글 쓰레드에 비해 느릴수도 있음

쓰레드 간의 컨텍스트 스위칭 보다 프로세스간의 스위칭이 더 많은 정보를 저장해야 하기에 시간이 오래 걸림

 

Express란?

Express는 Node 웹 프레임워크로, 미들웨어 구조를 통해 가볍고 유연한 웹 프레임웍을 구성할 수 있음

여러 툴, 기술, 미들웨어 선택 가능한 자유도가 있는 반면 이로 인한통일성이 저하됨, 또한 미들웨어 대한 추가적인 학습과 검증이 필요함

근본적인 부분부터 설계를 해야하기에 난이도가 오히려 상승 (DI/IOC 없고 여러 라이브러리 비교 분석 후 사용해야 함)

 

미들웨어란 요청과 응답 사이에서 어떠한 동작을 해주는 프로그램

 

Nest.js란?

Nest.js 는 Node 웹 프레임 워크이자, Express를 기반으로 만들어졌음

TypeScript 를 디폴트로 사용하면서 서버 개발시 발생할 수 있는 오류를 사전에 방지할 수 있게 함.

모듈로 감싸는 형태로 개발하기에 모듈별 테스트 코드 쉽게 작성할 수 있음

모듈을 통해 확장이 용이하게 설계되어 있음

Express의 자유도가 발생시키는 문제점을 보완하기 위해 아키텍처를 통일 ⇒ 서로가 작성한 코드 구조 쉽게 파악 가능

OOP (객체 지향 프로그래밍), FP (기능 프로그래밍), FRP (기능 반응성 프로그래밍) 요소를 결합

 

DI/IOC 란?

DI(Dependancy Injection)은 의존 관계 주입 기능, 객체를 직접 생성하는 것이 아닌 외부에서 생성한 후 주입시켜 주는 방식

⇒ 클래스들간 의존 관계를 최소화, 프로젝트 유지보수 용이, 객체의 생성 소멸 등 클래스간 의존관계를 프레임 워크가 제어

IoC(Inversion of Control) 은 제어 역전, 나 대신 프레임워크가 제어한다.

const sword = new Sword();

Warrior 클래스에 Sword 클래스 인스턴스화 하여 사용했는데, 갑자기 활도 쓰자 라고 바뀌면 수정하기가 어려움, 즉 Warror 와 Sword 사이의 의존성이 생긴 것

⇒ 이를 인터페이스로 해결

interface Weaponable {
  swing(): void;
}

interface Playable {
  attack(): void;
}

class Warrior implements Playable {
  private Weaponable weapon;

  constructor Warrior(private readonly Weaponable _weapon) {
    weapon = _weapon;
  }

  public void attack() {
    weapon.swing();
  }
}

class Bow implements Weaponable {
  public void swing() {
    console.log('Bow Swing!');
  }
}
Warrior warrior = new Warrior(new Bow());

이런 식으로 warrior 와 Bow 사이의 의존성을 해결했지만, 복잡한 프로그램에서 직접 객체를 넘기거나 여러 대상에게 같은 객체를 넘기는 등의 상황에서 좋지 않음 ⇒ 제어 역전

⇒프레임워크가 우리가 필요한 클래스의 인스턴스를 생성하여 넘겨줌

 

반응형

댓글