개발

nodejs 서버는 언제 사용할까?

yjs3819 2023. 9. 23. 20:19
728x90

글을 쓴 배경

저는 제가 사용하는 기술을 왜 사용하는지 스스로에게 되묻는 편입니다.
저는 주도적으로 개발하기 위해선 왜 why가 중요하다고 생각하기 때문이에요.
단순히 기술을 좇아가는 맹목적인 개발자가 되기를 지양합니다. (자바 스프링이 유명하다더라~, 남들이 다쓰닌까 써볼까? 등등.)
남들이 한다고 해서 하는것도 좋아하지 않습니다.

그래서 전 제가 사용하는 기술을 왜 사용하는지 고민한답니다!

저는 주로 nodejs 런타임에서 서버개발을 했어요!

그래서 저 나름, nodejs 환경에서 서버개발을 하며 nodejs는 어떤 상황에서 언제 사용하는게 좋은지 고민을 해보았습니다. 부족하지만 하나하나 적어보겠습니다.

그 전에, nodejs의 특징을 간단하게 적어볼게요!

nodejs 특정

  • javascript는 싱글스레드 기반으로 동작하기에, 한번에 한 작업밖에 실행이 되지 않습니다.
  • nodejs는 내부적(libuv)으로 이벤트 루프를 통해 비동기 작업에 대해선 non blocking이 가능합니다!
    • 비동기 작업이라하면 network io, file io등이 있겠네요! 큰수에 대한 계산이라던지 복잡한 알고리즘같은 cpu intensive한 작업을 비동기 작업이라고 말한게 아닙니다~!

network io 비동기 작업과, cpu intensive한 작업을 nodejs 서버에 구현한 뒤, 동시에 두번 요청했을 때 nodejs 서버가 어떻게 처리하는지 확인하면서 nodejs 특징을 알아봐요!

network io 비동기 작업 예시

nodejs 서버에서 요청이 들어오면 유저의 정보를 Database에서 조회하는 api가 있다고 가정할게요.
코드는 아마 아래와 같을거에요

async function getUser(id: number){
      const user = await this.userRepository.findOneById(id);
      console.log(user);
      return user;
}

해당 api가 처리되는 데 걸리는 시간은 1000ms라고 합시다!

해당 api에 동시에 두번 요청하면 시간은 얼마나 걸리고 어떻게 처리될까요?

  1. 싱글 스레드라고 했고 await때문에 블라킹 되니, 2000ms 걸릴거같아요.
  2. 싱글 스레드지만, 비동기 작업에 대해선 넌블러킹 되기 때문에 1000ms 걸릴거같아요.

1이 맞을까요 2가 맞을까여?

가장 정확한 방법은 실제로 해보는 거겠져~?

그러기 위해선 위 nodejs로 위 api서버 하나 만들고, shell script를 하나 만들게요~!

#!/bin/bash

# 첫 번째 인수로 횟수를 전달받음
concurrency="$1"

# 횟수가 전달되지 않은 경우 기본값으로 2 설정
if [ -z "$concurrency" ]; then
  concurrency=2
fi

# URL 정의
url="http://localhost:3000/io/network"

# 시작 시간 기록 (마이크로초까지 포함)
start_time=$(gdate +%s.%N)

# 백그라운드에서 지정된 횟수만큼 curl 요청 보내기
for i in $(seq 1 $concurrency); do
    curl -s -o /dev/null $url &
done

# 모든 백그라운드 작업이 완료될 때까지 대기
wait

# 끝 시간 기록 (마이크로초까지 포함)
end_time=$(gdate +%s.%N)

# 총 걸린 시간 계산 (부동 소수점 형식)
elapsed_time=$(echo "$end_time - $start_time" | bc)

# 총 걸린 시간을 출력
echo "총 걸린 시간: $elapsed_time 초"

./script.sh 2로 쉘스크립트를 이용해 curl로 동시에 두번 요청해 보았습니다.! 결과는!?!


콘솔에 동시에 두번 출력되고

1.03초(1000ms보다 살짝더) 걸렸네요?

즉 2가 맞았습니다!!

await때문에 첫 요청을 처리하여 응답할 때까지 블라킹 되어 두번째 요청이 기다릴거같은데 그렇게 동작하지 않네요!
왜 그럴까요~?

async, awaitthen메서드로 promise 객체를 사용하는 형태로 바꾸면 확 이해가 될거에요 ㅎㅎㅎ. 아니면 콜백함수를 이용하는 형태로 바꾸면 확 이해가 될겁니다!

function getUser(id) {
  return this.userRepository.findOneById(id)
    .then(function (user) {
      console.log(user);
      return user;
    })
    .catch(function (error) {
      // 오류 처리
      console.error(error);
    });
}

약간 감이 잡히시나요?
넵 !

두번 요청이 동시에 오면 먼저 온 요청이 싱글스레드(단일 콜스택)에서 blocking하고 있는게 아니라, 비동기 작업인걸 보고 이벤트루프에게 위임하고 이벤트 루프에선 위임받은 비동기 작업을 백그라운드에도 실행합니다. 그럼 nodejs 서버는 바로 다음 두번째 요청을 싱글스레드(단일 콜스택)가 첫번째 요청과 동일한 방식으로 처리하기 때문에 1000ms 정도가 걸린겁니다! (완전히 1000ms가 아닌 이유는 네트워크를 타는 작업이고, 완전히 병렬적으로 처리 되는게 아니기 때문이에요~)

cpu intensive 작업 예시

이번엔, cpu 집약적인 작업을 동시에 두번 요청했을 때 nodejs는 어떻게 처리하는지 알아봐요

예시 코드는 아래와 같아요

function processCpuIntensiveJob() {
    const result = this.appService.performCPUIntensiveTask();
    console.log(result);
    return result;
}

해당 api도 처리되는 데 걸리는 시간은 1000ms라고 합시다!

./script.sh 2로 동시에 두번 요청해볼게요!

결과는!!?!?


콘솔에 하나씩 출력되고

2.03초(2000ms 보다 조금 더) 걸렸어요.

아하 cpu intensive한 작업은 어찌 됐건 싱글스레드인 단일 콜스택에서 하나씩 실행되기에 동시에 요청이 와도 먼저 요청이 온 녀석부터 처리되닌까 blocking될수 밖에 없군요~ (사실 요건 다 예상하셨쬬!?)

nodejs 특징 정리

  • 더 다양한 특징들이 많겠지만 제가 생각하는 nodejs 서버의 가장 큰 특징은 싱글스레드이지만 비동기작업을 이렇게 블라킹하지 않고 효율적으로 처리한다는 거에요!!!
  • 물론 위에서 알아본것처럼 cpu 집약적인 작업은 블라킹 되겠죠~? (요건 멀티쓰레드 환경인 java spring이 더 낫겠네요!)
  • 그치만 nodejs 서버는 멀티쓰레드 환경의 java - spring과 비교해보면 race condition에 대해서도 고민할 필요가 없으면서 비동기 작업을 효율적으로 처리하죠~? 전 이게 참 nodejs 서버의 큰 매력이라고 생각합니다.

고럼 이 특징을 토대로 어떤 상황에서 nodejs 서버를 사용하면 좋을지 고민해볼까욥!?

nodejs 서버는 언제 사용할까?

  • 의견이 다양하겠지만 저는 제 경험을 토대로 말씀드리는 것이기에 틀릴수도, 더 다양한 의견이 있을수도 있습니다! 추가적인 의견이 있으면 알려주세요 :)

nodejs는 싱글스레드라서, cpu intensive한 작업들은 저어어얼대로 병렬적으로 처리될수없어요!!!! (Worker thread로 가능하다곤 합니다. ㅎㅎ 그렇다면 저어어얼대는 아니겠네요)

그래서 cpu 집약적인 작업이 많이 필요로 하는 서버로는 적합하지 않고 network io라던지 file io가 자주 발생하는 서버로는 적합하다고 생각합니다.

예시로, MSA(도메인에 따라 서버와 Database를 하나로 묶어 network로 서로 소통하는 환경)환경에서 각 도메인 서버가 cpu 집약적인 작업을 주로 처리(배달 서버로 A주소에 B주소 까지 최단거리 알고리즘에 따라 계산하여 응답하는 둥)한다면 nodejs보단 멀티쓰레드인 java spring이 더 낫다고 생각합니다.
그치만, 여러 MSA 서버들과 네트워크를 통해 통신하여 데이터를 간단하게 가공하거나 처리(cpu 집약적인 작업이 크지 않아야함)하는 서버로는 nodejs가 적합하다고 생각해요!!!

nodejs가 N개의 MSA서버와 통신을 한다면 싱글 스레드로 동시성 이슈(race condition)를 고민할 필요가 없으면서 비동기 작업을 넌 블러킹(non blocking)하여 처리할수있으니 효율적이겠죠????~

인사

여기까지 입니다!!!
제가 소개한 내용이 100%정답이 아닐수 있어요. 왜냐면 제가 개발하며 고민하고 고민한 결과 나온 저의 의견(생각)이기 때문이죠 ㅎㅎ

제 말을 100%맹신하지 말고 그냥 저렇게 생각할수도 있겠구나 라고 봐주시면 좋을거 같습니다~!

읽어주셔서 감사합니다 🙇‍♂️

728x90