당신의 서버는 느린 것이 아닙니다. 시스템 설계가 느립니다.

발행: (2025년 12월 21일 오후 09:44 GMT+9)
7 min read
원문: Dev.to

Source: Dev.to

Your server isn’t slow. Your system design is.

Your CPU is fine.
Memory looks stable.
Disk isn’t saturated.

Yet users complain the app feels slow — especially under load. So you scale: more instances, bigger machines, extra cache layers. And somehow… it gets worse.

위안을 주는 거짓말: “우리는 더 많은 자원만 필요해”

When performance degrades, most teams instinctively look for a single broken thing:

  • 느린 쿼리
  • 과부하된 CPU
  • 메모리 부족
  • 캐시 누락

That mental model assumes performance problems are local.
In reality, production systems fail systemically. Latency emerges from interactions — not isolated components.

왜 지표는 괜찮아 보이지만(사용자는 고통을 느낀다)

  • Average CPU: 30–40%
  • Memory: 충분한 여유
  • Error rate: 낮음
  • Alerts: 알림 없음

하지만:

  • p95 / p99 지연 시간이 계속 증가함
  • 처리량이 정체됨
  • 트래픽 급증 시 꼬리 요청이 쌓임

이 불일치는 리소스 활용이 성능이 아니기 때문입니다. 실제로 문제를 일으키는 부분은 대부분 대시보드가 강조하지 않는 영역에 있습니다:

  • 대기열 깊이
  • 락 경쟁
  • 요청 직렬화
  • 의존성 팬‑아웃
  • 불균형 워크로드 분배

시스템이 과부하된 것은 아닙니다. 현재 처리하고 있는 워크로드에 형태가 맞지 않게 설계되었습니다.

Performance problems rarely have a single cause

팀들은 종종 묻습니다: “병목 현상이 무엇인가요?”
불편한 답은 보통: “하나가 없습니다. 체인이 있습니다.”

Example

  1. 하나의 엔드포인트가 5개의 서비스로 퍼집니다.
  2. 그 서비스 중 하나가 데이터베이스를 동기식으로 호출합니다.
  3. 데이터베이스는 행‑레벨 잠금을 사용합니다.

폭증하는 트래픽 하에서는 잠금 대기 시간이 급증하고, 요청이 상위에서 대기열에 쌓이며, 지연 시간이 체인 전체에 걸쳐 곱해집니다. 개별 구성 요소가 “느리다”는 것이 아니라, 함께 작동할 때 취약해집니다.

트래픽 확장은 처리량 확장과 동일하지 않다

위험한 가정:

“인스턴스를 더 추가하면 더 많은 사용자를 처리할 수 있다.”

이는 시스템이 선형적으로 확장될 때만 성립한다—대부분은 그렇지 않다. 확장이 역효과를 내는 일반적인 이유:

  • 공유 상태(데이터베이스, 캐시, 메시지 브로커)
  • 경쟁이 심한 코드 경로
  • 동기식 의존성
  • 고르지 않은 트래픽 분산
  • 캐시 스탬프

동시성을 높이지만 시스템이 이를 흡수하지 못해 처리량이 아니라 지연 시간이 증가한다. 이 때문에 팀은 인프라 비용은 늘리면서 성능은 악화되는 상황에 처한다.

“그냥 Redis만 추가하면” 실망스러운 이유

Caching은 유용하지만 자주 잘못 적용됩니다. If:

  • cache invalidation이 비용이 많이 들 때
  • cache keys가 너무 세분화되어 있을 때
  • cache misses가 synchronous 재계산을 일으킬 때
  • cache hit rate가 burst traffic 아래에서 급락할 때

then Redis는 부하를 줄여주지 못하고 다른 failure mode를 추가합니다. Caching은 트래픽이 문제를 드러낼 때까지 설계 문제를 가립니다.

성능 감사가 답해야 할 진짜 질문

적절한 성능 감사는 문제를 나열하는 것이 아닙니다. 하나의 명확한 질문에 답해야 합니다:

오늘 시스템이 근본적으로 제약받는 요소는 무엇인가?

아니라:

  • “무엇을 최적화할 수 있을까?”
  • “무엇이 비효율적으로 보이는가?”
  • “어떤 모범 사례가 누락되었는가?”

하지만:

  • 이 시스템이 허용 가능한 지연 시간으로 더 많은 작업을 처리하지 못하게 하는 원인은 무엇인가?

그것을 알기 전까지는 모든 최적화가 추측에 불과합니다.

경험이 풍부한 팀이 이를 다르게 접근하는 방법

  • 지연 기준을 설정합니다 (특히 p95/p99)
  • 요청 경로를 끝까지 매핑합니다
  • 요청이 대기하는 위치를 식별합니다, 실행되는 위치만이 아니라
  • 워크로드 형태를 분석합니다, 평균만이 아니라
  • 변경 사항을 전후 데이터로 검증합니다

그들은 성능을 시스템 속성으로 여기며, 튜닝 작업이 아니라는 점을 강조합니다.

불편한 진실

대부분의 성능 문제는 나쁜 코드에서 오는 것이 아니다. 구축된 가정들을 조용히 초과하는 시스템에서 비롯된다:

  • 트래픽 패턴이 변화한다
  • 사용이 몇몇 엔드포인트에 집중된다
  • 기능이 아키텍처가 진화하는 속도보다 빠르게 쌓인다

외부에서는 모든 것이 여전히 “작동”한다. 내부에서는 압력이 쌓여—사용자가 이를 느낄 때까지.

최종 생각

시스템이 느리게 느껴지지만 서버는 정상으로 보인다면, 묻지 마세요:

“어떤 자원이 더 필요합니까?”

묻는 대신:

“부하, 동시성 및 조정에 대한 어떤 가정이 더 이상 유효하지 않습니까?”

그곳이 실제 성능 작업이 시작되는 지점입니다.

Back to Blog

관련 글

더 보기 »