공정하고 격리된 방식으로 Web Frameworks 벤치마크하는 방법 | Mahdi Shamlou

발행: (2026년 2월 22일 오전 05:39 GMT+9)
6 분 소요
원문: Dev.to

Source: Dev.to

안녕하세요 여러분! Mahdi Shamlou입니다 👋

온라인에서 웹 프레임워크를 비교하는 글을 많이 보았지만, 대부분 편향되었거나 오래됐거나 재현하기 어렵습니다. 그래서 저는 웹 프레임워크를 공정하고 격리된 방식으로 벤치마킹하는 실용적인 방법을 공유하고자 합니다.

우리는 격리를 위해 Docker를 사용하고, 부하 테스트를 위해 k6를 사용하며, 예시로 Python 프레임워크인 FastAPI와 Flask를 사용할 것입니다. 이 접근 방식은 Node.js, Go, Java, Rust 등 다른 언어에도 적용할 수 있습니다.

개요

웹 프레임워크 벤치마킹은 까다로울 수 있습니다. 결과에 영향을 주는 요인은 다음과 같습니다:

  • CPU 및 메모리 가용성
  • 워커 / 스레드 수
  • 백그라운드 프로세스
  • 라우팅, 로깅, 데이터베이스, I/O

공정한 비교를 위해서는 다음이 필요합니다:

  • 고정된 CPU 및 메모리 제한을 가진 Docker 컨테이너
  • 각 프레임워크에서 동일한 라우트 또는 엔드포인트
  • k6(또는 유사 도구)를 사용한 제어된 부하 테스트
  • 이후 분석을 위한 결과 저장

프로젝트 구조

mkdir web_framework_benchmarks
cd web_framework_benchmarks
mkdir framework1 framework2 k6-tests results

framework1framework2를 비교하고 싶은 다른 프레임워크로 교체할 수 있습니다. 예시로 간단한 /hello 엔드포인트를 사용합니다.

FastAPI (Python)

# app.py
from fastapi import FastAPI

app = FastAPI()

@app.get("/hello")
def hello():
    return {"message": "hello world"}

Flask (Python)

# app.py
from flask import Flask, jsonify

app = Flask(__name__)

@app.route("/hello")
def hello():
    return jsonify({"message": "hello world"})

Node.js, Go, Java 등에서도 동일한 엔드포인트를 구현할 수 있으며, 기능은 동일하게 유지합니다. 필요에 따라 I/O‑무거운 작업을 시뮬레이션하기 위해 슬립 라우트를 추가할 수 있습니다.

Dockerfiles (공정한 비교)

FastAPI Dockerfile

# Dockerfile (FastAPI)
FROM python:3.11-slim
WORKDIR /app
COPY . .
RUN pip install fastapi uvicorn gunicorn
CMD ["gunicorn", "-k", "uvicorn.workers.UvicornWorker", "-w", "1", "-b", "0.0.0.0:8000", "app:app"]

Flask Dockerfile

# Dockerfile (Flask)
FROM python:3.11-slim
WORKDIR /app
COPY . .
RUN pip install flask gunicorn
CMD ["gunicorn", "-w", "1", "-b", "0.0.0.0:8000", "app:app"]

✅ 두 컨테이너 모두 동일한 워커 수와 동일한 CPU/메모리 제한을 가지고 있어 공정한 기준을 보장합니다.

k6 로 부하 테스트

k6 스크립트 (JavaScript)

// k6-tests/framework1.js (or framework2.js)
import http from "k6/http";
import { sleep } from "k6";

export const options = {
  stages: [
    { duration: "30s", target: 50 },   // ramp‑up
    { duration: "1m", target: 200 },  // hold load
    { duration: "30s", target: 0 },    // ramp‑down
  ],
  thresholds: {
    "http_req_duration": ["p(95)<200"], // 95% of requests should be <200 ms
  },
};

export default function () {
  http.get("http://localhost:8001/hello");
  sleep(1);
}

테스트 실행

mkdir -p results
k6 run --out json=results/framework1.json k6-tests/framework1.js
k6 run --out json=results/framework2.json k6-tests/framework2.js

JSON 출력은 어떤 언어나 시각화 도구로도 처리할 수 있습니다.

결과 분석

Below is a minimal Python script that extracts average latency, 95th‑percentile latency, request count, and failure rate, then plots the average response time.

# analyze.py
import json
import numpy as np
import matplotlib.pyplot as plt

files = {
    "Framework1": "results/framework1.json",
    "Framework2": "results/framework2.json"
}
summary = {}

for name, file in files.items():
    durations, fails, total = [], 0, 0
    with open(file) as f:
        for line in f:
            obj = json.loads(line)
            if obj.get("type") == "Point":
                metric = obj.get("metric")
                if metric == "http_req_duration":
                    durations.append(obj["data"]["value"])
                if metric == "http_req_failed":
                    fails += obj["data"]["value"]
                    total += 1
    if durations:
        summary[name] = {
            "avg": np.mean(durations),
            "p95": np.percentile(durations, 95),
            "requests": len(durations),
            "fail_rate": fails / total if total else 0,
        }

print(summary)

plt.bar(summary.keys(), [summary[n]["avg"] for n in summary])
plt.title("Average Response Time (ms)")
plt.ylabel("Milliseconds")
plt.show()

주요 시사점

  • Docker 격리는 벤치마크를 재현 가능하게 합니다.
  • 워커 수 및 CPU 제한은 컨테이너 간에 일치해야 합니다.
  • 단순 라우트는 Flask가 더 빠르게 보이게 할 수 있습니다; 속지 마세요.
  • Async/I/O‑무거운 라우트는 FastAPI(또는 다른 async 프레임워크)의 강점을 보여줍니다.
  • 실제 워크로드를 항상 벤치마크하세요, 작은 예제만이 아니라.

저장소

Dockerfile, k6 스크립트 및 분석 코드를 포함한 전체 예제는 다음에서 확인할 수 있습니다:

https://github.com/mahdi-shamlou/web_framework_benchmarks
0 조회
Back to Blog

관련 글

더 보기 »

Spring CRUD Generator v1.3.0 출시 🚀

MariaDB 지원 + REST 응답에서 선택적 Null 제외 방금 Spring CRUD Generator v1.3.0을 출시했습니다 🎉 Spring CRUD Generator는 오픈 소스 Maven 플러그인입니다.