서버 부하 시뮬레이터 (TPS·병목 계산)

서버 대수·CPU·메모리, 런타임(스레드/비동기/이벤트 루프), DB 구조, 캐시 히트율, 외부 API 지연을 조합해 시스템의 최대 TPS와 첫 병목 지점을 시뮬레이션해요.

사용 방법

  1. 런타임·규모별 프리셋을 고르거나 빈 화면에서 시작해요.
  2. 서버 스펙, 스레드 풀, DB 구조, 캐시, 외부 API 지연을 조절해요.
  3. 목표 TPS를 주입하며 최대 처리량과 병목, 메모리(OOM) 위험을 확인해요.

계산은 Little's Law(동시 연결 = TPS × 응답시간) 기반 추정이에요. 스레드 블로킹 런타임은 스레드 풀이, 비동기 런타임은 CPU·소켓이 먼저 한계에 닿는 차이를 체험할 수 있어요. 실제 성능은 워크로드에 따라 다르니 부하 테스트로 검증하세요.

자세히 알아보기

최대 TPS 는 리틀의 법칙에서 나와요

이 시뮬레이터의 뼈대는 리틀의 법칙(Little's Law) 이에요 — «동시에 처리 중인 요청 수 = TPS × 응답시간». 응답시간이 100ms 인 요청을 초당 1,000건 받으면 어느 순간에나 100건이 처리 중이라는 뜻이에요.

응답시간은 세 조각의 합으로 봐요 — 순수 CPU 연산 시간 + DB 쿼리 지연 + 외부 API 지연. 이 합이 길어질수록 같은 TPS 에서 점유되는 스레드·소켓·커넥션이 늘어나요.

거꾸로 뒤집으면 자원마다 한계 TPS 가 나와요. 예를 들어 스레드 200개가 있고 요청 하나가 스레드를 100ms 점유하면, 그 풀이 감당하는 최대치는 200 ÷ 0.1초 = 2,000 TPS 예요.

자원마다 한계가 따로 있고, 최솟값이 병목이에요

계산은 자원별 한계 TPS 목록을 만들어요 — CPU(코어 수 ÷ 연산시간 × 서버 대수), 소켓(FD 한도 ÷ 전체 응답시간 × 대수), 스레드 런타임이면 스레드 풀, 그리고 DB 커넥션 풀(단일이면 전체 부하, Master/Slave 면 쓰기·읽기를 나눠서).

그중 가장 낮은 값이 시스템 전체의 최대 처리량이에요. 다른 자원이 아무리 여유 있어도 소용없어요 — 물이 가장 좁은 관을 넘지 못하는 것과 같아요.

그래서 증설도 병목을 봐야 해요. DB 커넥션 풀이 병목인데 앱 서버를 늘리면 최대 TPS 는 그대로고 DB 앞의 줄만 길어져요.

스레드 모델과 비동기 모델은 막히는 곳이 달라요

스레드-퍼-리퀘스트(전통적 Spring MVC 등)는 외부 I/O 를 기다리는 동안에도 스레드를 붙잡고 있어요. DB 50ms + 외부 API 300ms 를 기다리면 그 350ms 내내 스레드 하나가 잠겨요 — 그래서 느린 외부 호출이 섞이면 스레드 풀이 가장 먼저 말라요.

비동기 런타임(Virtual Thread·Go·Node.js)은 I/O 대기에 스레드를 잡지 않아서 같은 스펙으로 훨씬 높은 TPS 가 나와요. 대신 한계가 CPU 나 소켓 쪽으로 옮겨가요.

Node.js 는 예외 조항이 하나 있어요 — 이벤트 루프가 하나라서 CPU 연산이 20ms 를 넘으면 루프가 막혀 다른 요청까지 전부 기다려요. 시뮬레이터가 이 경우 경고 플래그를 세워요.

캐시 히트율이 처리량을 끌어올리는 원리

캐시에 맞은 읽기 요청은 DB 까지 가지 않아요. 시뮬레이터는 읽기 비율 × (1 − 히트율)만 DB 부하로 계산해요 — 읽기 70%·히트율 80% 면 실제 DB 에 닿는 읽기는 전체의 14%뿐이에요.

그래서 읽기 비중이 높은 서비스일수록 캐시가 커넥션 풀 병목을 크게 밀어 올려요. 반대로 쓰기 위주 서비스는 히트율을 올려도 별 효과가 없어요 — 쓰기는 캐시를 지나치지 않으니까요.

Master/Slave 구성이면 읽기는 Slave 복제본으로 분산되고 쓰기만 Master 로 가요. 읽기 병목은 Slave 를 늘려서, 쓰기 병목은 캐시로도 복제본으로도 못 풀어요.

MQ(Kafka 등) 위임 토글이 바꾸는 것

느린 외부 API 호출을 큐에 넣고 바로 응답한다고 가정하는 토글이에요. 응답시간에서 외부 지연이 빠지고 대신 큐 적재 비용 5ms 가 들어가요.

외부 API 가 300ms 면 스레드 런타임에서 효과가 극적이에요 — 스레드 점유 시간이 300ms 넘게 줄어드니 같은 풀로 몇 배의 TPS 를 감당해요.

덤으로 과부하 흡수 효과도 모델링돼요. 처리 한계를 넘는 트래픽이 들어와도 MQ 를 쓰면 잉여분이 큐에 보관돼 서버가 안전하고, 없으면 대기 커넥션이 메모리에 쌓여요.

공짜는 아니에요 — 응답이 «접수됨» 으로 바뀌는 거라 결과를 즉시 돌려줘야 하는 작업에는 못 써요. 이 트레이드오프는 아키텍처 결정이지 토글 하나가 아니에요.

메모리(OOM)는 TPS 와 다른 축이에요

메모리 사용량은 런타임 기본 점유 + 처리 중 연결 수 × 1MB 로 추정해요. 스레드 런타임은 기본 512MB 에 스레드당 1MB, 비동기 런타임은 코어당 100MB 로 잡아요.

처리 중 연결 수가 리틀의 법칙 그대로예요 — TPS × 응답시간. 응답이 느려질수록 같은 트래픽에서 메모리에 잠긴 요청이 늘어요.

처리 한계를 넘긴 채 MQ 없이 버티면 초과분이 대기 큐로 메모리에 쌓여요. 추정 사용량이 RAM 을 넘으면 병목 판정 자체가 OOM 으로 바뀌어요 — 처리량 한계보다 먼저 프로세스가 죽는 상황이에요.

추정의 한계 — 실측 부하 테스트는 여전히 필요해요

이 모델은 평균값 기반 정적 추정이에요. 트래픽의 순간 요동, GC 멈춤, 락 경합, 커넥션 수립 비용, 네트워크 대역폭은 반영하지 않아요.

실제 시스템은 병목에 가까워질수록 응답시간이 비선형으로 늘어나요 — 여기서는 자원이 남는 한 응답시간을 상수로 봐요. 그래서 추정치는 상한에 가깝고, 실측 최대치는 대개 이보다 낮아요.

쓰임새는 방향 감각이에요: 어느 자원이 먼저 막히는지, 캐시·MQ·복제본 중 무엇이 효과 있는지를 스펙 정하기 전에 가늠하는 것. 최종 수치는 k6·JMeter 같은 도구로 실제 환경에서 확인하세요.

자주 묻는 질문

최대 TPS는 어떻게 계산되나요?

CPU(코어÷연산시간), 소켓, 스레드 풀, DB 커넥션 풀 각각의 한계 TPS를 구해 가장 낮은 값이 시스템 전체의 최대 처리량이 돼요. 그 지점이 곧 병목이에요.

스레드 모델과 비동기 모델은 뭐가 다른가요?

스레드-퍼-리퀘스트(Spring MVC 등)는 외부 I/O 대기 중에도 스레드를 점유해 풀이 고갈되지만, Virtual Thread·Go·Node.js는 I/O 대기에 스레드를 잡지 않아 같은 스펙에서 더 높은 TPS가 나와요.

캐시 히트율을 올리면 왜 처리량이 늘어나요?

캐시에 맞은 읽기 요청은 DB까지 가지 않아 DB 커넥션 풀 부담이 줄어요. 읽기 비중이 높은 서비스일수록 효과가 커요.

MQ(Kafka 등) 토글은 뭘 바꾸나요?

느린 외부 API 호출을 큐에 위임한다고 가정해, 요청 처리 시간에서 외부 지연을 제거해요. 스레드 블로킹 런타임에서 특히 효과가 크게 보여요.

관련 도구

  • 개발자 도구 — JSON·Base64·URL·정규식·타임스탬프·JWT·Cron·CIDR·Diff.
  • 이미지 압축 — 업로드 없이 브라우저에서 이미지를 압축·리사이즈해요.
  • 글자수 세기 — 글자·단어·줄·바이트 수를 실시간으로 세요.
  • QR 코드 생성기 — 링크·텍스트를 QR 코드로 만들어 PNG로 저장해요.