Node.js 정리

Dayeon myeong·2021년 2월 16일
1

Node.js

목록 보기
1/1

node.js

  • 자바스크립트 어플리케이션이 서버로써 기능을 하기 위한 도구를 제공함으로써 서버 역할 수행할 수 있음
  • 표준 js를 실행시키기 위해 구글 v8 엔진 기반 동작
  • 이벤트 기반
  • non-blocking I/O 비동기 방식
  • 싱글 스레드 기반

node.js의 장점

  • 싱글 스레드,비동기 I/O처리에 기반한 빠른 속도
  • I/O 작업이 많은 서버로 적합
  • 멀티 스레드 방식에 비해 컴퓨터 자원 적게 사용
  • 웹 서버 내장
    - 아파치 등의 별도의 소프트웨어 없이 http 서버라이브러리를 포합하고 있어 웹서버 동작이 가능.
  • spring 등 기존 서버 사이드 프레임워크에 비해 생산성 높음
  • 진입장벽 낮음

node.js의 단점

  • 싱글스레드 -> 하나의 큰 작업이 들어오면 부하가 크게 걸림
  • 싱글스레드 -> cpu 코어 하나 사용 -> cpu 작업 많은 서버로 부적합
  • 비동기 방식의 싱글 스레드를 채용하기 때문에 콜백 지옥에 빠질 수 있다.
    - 이벤트 콜백 중심으로 코드가 중첩될 경우 코드 가독성이 떨어짐
  • 에러가 발생하면 프로세스 자체가 죽어버림

대규모 프로젝트, 게임서버보다는 Restful API, 채팅 PUSH 서버에 적합

이벤트 기반 시스템

  • 이벤트가 발생할 때 미리 지정해둔 작업을 수행하는 방식
  • 이벤트 리스너에 콜백 함수를 미리 등록
  • 발생할 이벤트가 없거나 발생했던 이벤트 다 처리하면 다음 이벤트 발생할 때까지 대기
  • 여러 이벤트 동시 발생 시 어떤 순서로 콜백 호출할지는 이벤트 루프가 판단
  • 이벤트 루프
    • 자바스크립트는 싱글 스레드 → 메인 스레드인 이벤트 루프가 싱글스레드이기 때문.
    • 이벤트 발생 시 호출할 콜백함수들을 관리하고 호출된 콜백 함수의 실행 순서를 결정
    • 호출 스택이 비어있을때만 태스크 큐에 있는 함수를 호출 스택으로 가져옴
    • 노드가 종료될때까지 이벤트 처리 작업 반복해서 루프라고 부름
  • 태스크 큐
    • 이벤트 발생 후 콜백함수들이 기다리는 공간
    • 이벤트 루프가 정한 순서대로 줄 서있음(콜백 큐라고도 부름)
    • 이벤트 발생 후, 백그라운드에서는 태스크 큐로 타이머나 이벤트 리스너의 콜백 함수를 보낸다.
  • 백그라운드
    • setTimeout과 같은 타이머나 I/O 작업 콜백 또는 이벤트 리스너들이 대기하는 곳
  • 콜백함수
    • 다른 함수의 인자로써 이용되는 함수
    • 어떤 이벤트가 발생했거나 특정 시점에 도달했을때 시스템에서 호출하는 함수
function run() {
  console.log('동작');
}
console.log('시작');
setTimeout(run, 3000);
console.log('끝');

위 코드의 출력 결과는
시작

동작


호출스택에서는 전역 컨텍스트인 main()이 쌓이고 console.log('시작')이 쌓입니다.
console.log('시작')함수가 호출되며 호출스택에서 빠집니다.('시작' 출력)
setTimeout이 호출되고 지워지면서 백그라운드로 run 함수와 함께 3초 타이머를 보냅니다. 백그라운드는 3초를 센 후 태스크 큐에 run 함수를 보냅니다.
그리고 console.log('끝')이 호출스택에 쌓엿다가 끝이 호출되면서 지워집니다.
그리고 마지막으로 main()이 사라지며 호출 스택이 비워지게 됩니다.

이벤트 루프는 항상 대기하고 있다가 호출 스택이 비워지면(전역 컨텍스트 main 실행이 종료되면) 태스크 큐에서 함수를 하나씩 호출 스택으로 밀어 올립니다.

이제 run 함수가 실행되고 호출 스택에서 지워지게 됩니다. 이벤트루프는 태스크 큐에 새로운 함수가 들어올 때까지 대기합니다.

출처 : (JavaScript) 호출 스택과 이벤트루프

동기,비동기 vs 블록킹,논블록킹

  • 동기 vs 비동기 : 호출되는 함수의 작업 완료 여부를 누가 신경쓰느냐

    • 호출되는 함수에게 callback을 전달해서 호출되는 함수의 작업이 완료되면 호출되는 함수가 전달받은 callback을 실행하고, 호출한 함수는 작업 완료 여부를 신경쓰지 않는다면 비동기이다. (호출되는 함수의 작업완료를 호출한 함수가 신경쓰지않는다면 async)
    • 호출하는 함수가 호출되는 함수의 작업 완료 후 return을 기다리거나 호출되는 함수로부터 바로 return 받더라도 작업 완료 여부를 호출한 함수 스스로 확인하며 신경 쓴다면 동기이다.(호출되는 함수의 작업완료를 호출한 함수가 신경쓴다면 sync)
  • 블로킹 vs 논블로킹 : 호출되는 함수가 바로 return하느냐 마느냐

    • 호출된 함수가 바로 return해서 호출한 함수에게 제어권을 넘겨주고 호출한 함수가 다른 일을 할 수 있는 기회를 줄 수 있으면 non-blocking이다. (바로 리턴하면 nonblocking)
    • 호출된 함수가 자신의 작업을 모두 마칠 때까지 호출한 함수에게 제어권을 넘겨주지 않고 대기하게 만든다면 blocking이다. (바로 리턴하지 않으면 blocking)

  • blocking(바로 return x) + synchronous(호출하는함수가 호출되는 함수의 작업 완료 여부 신경씀) : 결과가 처리되어 나올때까지 기다렸다가 return 값으로 결과를 전달한다
  • non-blocking(바로 return) + asynchronous(호출되는함수의 작업완료 여부 신경 쓰지않음) : 작업 요청을 받아서 별도의 프로레서에서 진행하고 바로 return(작업 끝)한다. 결과는 별도의 작업 후 간접적으로 전달(callback)한다.
    • 성능과 자원의 효율적 사용 관점에서 유리한 모델

  • nonblocking(바로 return) + synchronous(신경씀) : nonblocking 메서드 호출 후 바로 반환받아서 다른 작업을 할 수 있게 되지만, 메서드 호출에 의해 수행되는 작업이 완료된 것은 아니며, 호출하는 메서드가 호출되는 메서드 쪽에 작업 완료 여부를 계속 문의한다.

  • blocking(바로 return x) + async(신경쓰지않는다) : 호출되는 함수가 바로 리턴하지 않고, 호출하는 함수는 작업 완료 여부를 신경쓰지 않는다.

출처

Blocking / 동기

const fs = require('fs');
const data = fs.readFileSync('/file.md'); // 파일을 읽을 때까지 여기서 블로킹됩니다.
console.log(data);
// moreWork();는 console.log 이후 실행될 것입니다.
  • 백그라운드 작업 완료 여부 계속 확인
  • 호출한 함수가 바로 return되지 않고 백그라운드 작업이 끝나야 return

Non-blocking / 비동기

const fs = require('fs');
fs.readFile('/file.md', (err, data) => { //논블록킹
  if (err) throw err;
  console.log(data);
});
// moreWork();는 console.log 이전에 실행될 것입니다.

//fs.readFile()가 논블로킹이므로 
//계속 JavaScript를 실행하고 
//moreWork()가 먼저 호출될 것입니다. 
//파일 읽기가 완료되기를 기다리지 않고 moreWork()를 실행
  • 호출한 함수가 바로 return되어 다음 작업으로 넘어감
  • 백그라운드 작업 완료 여부는 신경 안쓰고 백그라운드가 알림을 줄 때 처리

출처

node js는 non-blocking 방식을 사용하는 이벤트 기가반의 싱글 스레드, 비동기 방식

  • non-blocking 방식으로 I/O 요청 즉시 바로 cpu 제어권을 다음 작업에게 반환
  • 요청한 I/O 작업이 완료되면 콜백 함수나 신호를 줌
  • 작업을 처리하는데에 순서가 없다.

콜백함수

  • 다른 함수의 인자로써 이용되는 함수
  • 어떤 이벤트가 발생했거나 특정 시점에 도달했을때 시스템에서 호출하는 함수


-결과를 돌릴 때마다 나오는 순서가 달라진다, 결과가 나오는 순서를 제어하지 못한다.

  • node.js는 비동기 방식 → 1,2,3이라는 함수가 있을 때 어떤 작업이 완료될지 예상할 수가 없다. → 하지만 이런 비동기에서 1,2,3이라는 순서를 주고 싶다면? 순차적인 작업을 진행하게 하고싶다면? → 콜백 사용

출처

콜백 지옥

  • 콜백 지옥은 비동기 처리 로직을 위해 콜백 함수를 연속해서 사용할 때 발생하는 문제
  • 비동기 프로그래밍을 주로 사용하는 node.js에서는 어떤 작업이 먼저 완료될 지 알 수 없다. 이러한 함수 흐름을 동기적으로 제어하고 순차적인 작업을 위해 연속적으로 콜백함수를 사용하려면 콜백 안에,콜백안에,콜백 안에,,,콜백 지옥에 빠지게 되는 문제가 발생

해결방법 : promise / async,await

promise

new Promise(function(resolve, reject){
  setTimeout(function() {
    resolve(1);
  }, 2000);
})
.then(function(result) {
  console.log(result); // 1
  return result + 10;//10+1을 다음 then의 result로 넘겨줌
})
.then(function(result) {
  console.log(result); // 11
  return result + 20;
})
.then(function(result) {
  console.log(result); // 31
});

위 코드는 프로미스 객체를 하나 생성하고 setTimeout()을 이용해 2초 후에 resolve()를 호출하는 예제입니다.

resolve()가 호출되면 프로미스가 대기 상태에서 이행 상태로 넘어가기 때문에 첫 번째 .then()의 로직으로 넘어갑니다. 첫 번째 .then()에서는 이행된 결과 값 1을 받아서 10을 더한 후 그다음 .then() 으로 넘겨줍니다. 두 번째 .then()에서도 마찬가지로 바로 이전 프로미스의 결과 값 11을 받아서 20을 더하고 다음 .then()으로 넘겨줍니다. 마지막 .then()에서 최종 결과 값 31을 출력합니다.

출처

async / await

출력결과
func1 hello
func1
func2 world
func2

출처 : sopt 동아리 ppt

동시성과 처리량 (Concurrency and Throughput)

Node.js에서 JavaScript 실행이 싱글 스레드(이벤트 루프와 같은 스레드)에서 동작한다.
따라서 Node.js의 동시성은 다른 작업이 완료된 후에 JavaScript 콜백 함수를 실행하는 이벤트 루프의 능력(수용량)을 의미한다. 즉, javascript 실행은 하나의 이벤트 루프에서만 동작하지만 이벤트 루프의 여러 콜백 함수를 실행하여 동시에 처리되도록 보이는 것을 의미한다.
따라서 동시성을 높이기 위해 동시에 실행시킬 코드는 I/O와 같은 non-Javascript 연산이 일어날 때, 이벤트 루프가 계속 실행되도록 해주는 것이 좋다.
예를 들어 웹 서버에 대한 각 요청을 수행하는데 50ms가 걸릴 때 database I/O가 45ms 걸리고, 비동기적으로 처리한다고 생각하보자. 이렇게 non-blocking 비동기 연산 방식이기 때문에 45ms 동안 다른 일을 할 수 있게 되고 따라서 그 동안에 다른 요청을 처리할 수 있다. 이 점 때문에 blocking 함수 대신 non-blocking 함수를 사용하게 되면 더 많은 요청을 처리할 수 있다.
출처

profile
부족함을 당당히 마주하는 용기

0개의 댓글