Node.js vs FastAPI 이벤트 루프: 비동기 동시성 심층 탐구

발행: (2026년 3월 5일 오전 06:34 GMT+9)
12 분 소요
원문: Dev.to

Source: Dev.to

프로덕션에서의 API 스택

현대 프로덕션 시스템에서 반복적으로 등장하는 두 가지 스택:

  • Node.js
  • FastAPI

두 스택 모두 고동시성 워크로드를 처리하는 것으로 널리 알려져 있으며 이벤트 기반 아키텍처에 크게 의존합니다.

간단 비교

기능Node.jsFastAPI
동시성 모델단일 스레드 이벤트 루프 + 논블로킹 I/O비동기 코루틴 + 다중 워커 프로세스
일반적인 런타임JavaScript (V8)Python (asyncio)
코어 간 확장cluster 모드 필요다중 프로세스를 통한 자연스러운 확장
최적 활용 분야I/O 중심 API, 실시간 애플리케이션, WebSocketsAPI, 머신러닝 추론 백엔드, 스트리밍, 마이크로서비스
잠재적 병목 현상CPU 집약 작업이 이벤트 루프를 차단별도 프로세스를 사용해 GIL을 회피

Node.js

핵심 철학

  • 단일 스레드에서 JavaScript를 실행합니다(브라우저 모델을 그대로 반영).
  • I/O 작업은 libuv라는 고성능 C 라이브러리에게 위임됩니다.
  • I/O가 완료되면 콜백이 이벤트 루프로 다시 푸시됩니다.

이 설계는 대부분의 웹 서버가 수행하는 I/O‑중심 워크로드에 매우 적합합니다.
트레이드‑오프: CPU‑집중 작업은 JavaScript 스레드를 차단하여 전체 서버를 정지시킵니다.

중요한 특성

  • 모든 JavaScript 코드는 하나의 스레드에서 실행됩니다.
  • I/O → libuv에 위임(네트워크, 파일 시스템, 타이머, 스레드‑풀 스케줄링).
  • 이벤트 루프 단계:
PhaseWhat Happens
TimerssetTimeout() / setInterval() 로 예약된 콜백을 실행합니다.
Pending Callbacks시스템 수준 콜백을 처리합니다(예: TCP 오류).
Poll루프의 핵심 – I/O 이벤트를 기다리고, 완료된 작업을 가져와 콜백을 실행합니다.
ChecksetImmediate() 콜백을 실행합니다.
Close Callbacks정리 이벤트를 처리합니다(예: socket.destroy()).

스레드 풀 (libuv)

  • 파일 시스템 접근, DNS 조회, 압축, 암호화 등을 처리합니다.
  • 기본 크기: 4 스레드( UV_THREADPOOL_SIZE 로 설정 가능).
  • JavaScript 실행은 여전히 단일 스레드이며, 무거운 CPU 작업은 여전히 루프를 차단합니다.

간단한 예시 (Node.js)

// Callback style (old)
db.query(sql, (err, result) => {
  if (err) throw err;
  console.log(result);
});

// Promise / async‑await (modern)
const result = await db.query(sql);
console.log(result);

async/await을 사용하더라도 Node는 여전히 콜백 큐를 통해 작업을 스케줄합니다; 이 문법은 프로미스와 콜백 위에 얹힌 문법 설탕에 불과합니다.

Source:

FastAPI

핵심 철학

  • FastAPI는 단순히 프레임워크일 뿐이며, 런타임은 ASGI 서버에서 제공됩니다. 예:
    • uvicorn
    • hypercorn
    • gunicorn + uvicorn workers
  • 각 워커는:
    • 별도의 OS 프로세스
    • 자체 Python 인터프리터
    • 자체 이벤트 루프

따라서 동시성은 두 층에서 발생합니다:

  1. 각 워커 내부의 비동기 코루틴.
  2. CPU 코어마다 다중 워커 프로세스.

일반적인 엔드포인트

from fastapi import FastAPI

app = FastAPI()

@app.get("/users")
async def get_users():
    result = await db.fetch_all()
    return result
  • Python은 코루틴 객체(일시 중단 가능한 계산)를 생성합니다.
  • 이벤트 루프는 필요에 따라 코루틴을 스케줄링하고 재개합니다.
  • 이는 콜백 지옥에 비해 선형적이고 가독성 높은 프로그래밍 모델을 제공합니다.

콜백 vs 코루틴 모델

측면Node.js (콜백)FastAPI (코루틴)
문법db.query(sql, (err, result) => {...})result = await db.query()
흐름중첩된 콜백 → “콜백 지옥”(프라미스/async‑await 로 완화)일시 중단 가능한 함수 → 자연스러운 흐름
스케줄링콜백 큐(이벤트 루프)코루틴의 협력적 스케줄링

프로세스 기반 병렬성

  • FastAPI 배포는 일반적으로 다중 워커 프로세스를 실행합니다:
    • 각 워커 → 1개의 OS 프로세스1개의 Python 인터프리터1개의 이벤트 루프
  • 이를 통해 CPU 코어 전반에 걸친 진정한 병렬성을 구현할 수 있으며, 각 프로세스가 자체 인터프리터를 가지므로 Python의 전역 인터프리터 락(GIL)을 우회합니다.
Worker 1 → CPU core 1
Worker 2 → CPU core 2
...

동시성 전략 요약

Node.js 동시성 모델

  • 싱글 스레드 이벤트 루프 + 논블로킹 I/O.
  • 뛰어난 분야:
    • API
    • 스트리밍 서비스
    • 실시간 애플리케이션 (WebSockets, 채팅 서버, 라이브 대시보드)
  • 강점:
    • 높은 I/O 처리량
    • 실시간 시스템
    • 방대한 생태계 및 성숙한 툴링
  • 약점:
    • CPU 집약적인 작업이 루프를 차단함 (예: 대용량 JSON 파싱, 이미지 처리, 암호화, ML 추론)
    • 무거운 CPU 작업을 위해 워커 스레드 또는 별도 마이크로서비스가 필요함.

FastAPI 동시성 모델

  • 비동기 코루틴 + 다중 워커 프로세스.
  • 워커가 CPU 작업으로 블록되더라도 다른 워커가 계속 서비스를 제공하여 응답성을 유지함.
  • 강점:
    • 머신러닝 프레임워크와의 원활한 통합
    • Pydantic을 통한 강력한 타입 힌트 및 자동 검증
    • 뛰어난 개발자 경험
    • I/O‑바운드와 CPU‑바운드 워크로드 모두에 대한 우수한 성능 (프로세스 스케일링을 통해)
  • 흔한 오해: FastAPI는 모든 작업을 자동으로 비동기로 실행하지 않는다; async def를 명시적으로 사용하고 I/O‑바운드 호출을 await해야 함.

Takeaways

  • Node.js는 작업 부하가 I/O‑bound이고 JavaScript 실행을 가볍게 유지할 수 있을 때 빛을 발합니다.
  • FastAPI는 Python의 풍부한 생태계와 프로세스‑레벨 병렬성을 활용하여 mixed workloads에 더 유연한 모델을 제공합니다.
  • performance profile, team expertise, operational constraints에 맞는 스택을 선택하세요.

FastAPI에서 비동기 엔드포인트 명확히 하기

주장: “그것은 사실이 아닙니다.”

실제 상황

def endpoint():
    ...
  • 이것은 비동기가 아닙니다.
  • FastAPI는 이를 스레드 풀 실행기 내부에서 실행합니다.

결과:

Blocking function → Executed in thread pool → Event loop remains free

이벤트 루프를 직접 사용하려면, 엔드포인트를 다음과 같이 선언해야 합니다:

async def endpoint():
    ...

전체 스택 레이어 이해

Node.js

V8 Engine → Node.js Runtime → libuv → Operating System
  • V8은 JavaScript를 실행합니다.
  • libuv는 비동기 I/O를 처리합니다.

FastAPI

Python Interpreter → FastAPI Framework → ASGI → Uvicorn Server → asyncio / uvloop → Operating System
  • 많은 FastAPI 배포에서는 C로 작성된 이벤트 루프 구현인 uvloop를 사용합니다.
  • uvloop 자체는 libuv 위에 구축되어 있습니다 — Node.js에서 사용되는 동일한 라이브러리입니다.

결과적인 동등성

FastAPI + uvloop  =  Python + libuv

다른 언어이지만, 동일한 기본 비동기 엔진을 사용합니다.

동시성 철학

Node.js와 FastAPI는 동일한 기본 철학을 따릅니다:

  • 이벤트 기반, 논블로킹 I/O

하지만 두 프레임워크는 동시성을 서로 다른 방식으로 확장합니다.

기능Node.jsFastAPI
실행 모델단일 JavaScript 스레드asyncio 코루틴
I/O 처리libuv 스레드 풀asyncio / uvloop
병렬성클러스터 모드 또는 워커 스레드다중 워커 프로세스
멀티코어 확장성명시적 클러스터링 필요자연스러운 멀티코어 지원

실용적인 가이드

  • Node.js 사용: 실시간 시스템을 구축하고 JavaScript 생태계 내에 머무를 때.
  • FastAPI 사용: Python, 데이터 과학, 머신러닝 또는 AI 워크로드와 관련된 서비스를 구축할 때.

핵심 요점: 각 동시성 모델이 부하 하에서 어떻게 동작하는지 이해하는 것은 매우 중요합니다. 초기 아키텍처 결정이 나중에 시스템이 확장되는 방식을 좌우하기 때문입니다.

당신의 생각

고성능 동시성 API를 위해 어떤 런타임을 선호하시나요? 그리고 그 이유는 무엇인가요?


추가 읽기

이 글이 마음에 드셨다면, 여기에서 제 블로그를 더 읽어보실 수 있습니다:

https://writings.dipchakraborty.com

0 조회
Back to Blog

관련 글

더 보기 »