🎥 AI 시대 옵저버빌리티 전략 웨비나 | 무료 다시보기 (~4/9)
Top
도입문의
테크
2026-08-24

API 응답 시간이 느린 이유 7가지와 진단 순서

"API가 느려요." 이 한마디로 시작된 조사가 반나절을 쓰고도 원인에 닿지 못할 때가 있습니다. 응답이 느려지는 지점은 애플리케이션 코드일 수도, 데이터베이스일 수도, 외부 API나 네트워크일 수도 있습니다. 어디부터 볼지 정하지 못한 채 짐작 가는 곳을 하나씩 열어 보면 시간만 흘러갑니다. 짐작을 앞세우기 전에 요청이 지나가는 경로를 순서대로 따라가는 편이 빠릅니다.

API 응답 시간은 클라이언트가 요청을 보낸 시점부터 응답을 모두 받은 시점까지 걸린 시간입니다. 이 시간에는 애플리케이션 처리, 데이터베이스 조회, 외부 호출, 네트워크 구간이 쓴 시간이 모두 더해져 있어서, 느려졌다는 사실만으로는 어느 구간이 범인인지 알 수 없습니다. 응답이 느려지면 사용자 이탈로 이어질 수 있는데, 원인이 여러 계층에 흩어져 있다 보니 한 계층만 붙잡고 있으면 조사가 길어집니다. 원인 7가지를 요청이 지나가는 순서대로 놓고 보면, 지금 내 경우가 어느 구간인지 빠르게 찾을 수 있습니다.

API 응답 시간이 느릴 때 먼저 확인할 것

응답 지연 조사에서 가장 먼저 할 일은 원인을 추측하는 것이 아니라, 느려진 지점을 특정하는 것입니다. 요청 하나가 실제로 어디를 지나며 시간을 쓰는지 봐야 방향이 잡힙니다.

이때 쓰는 것이 분산 추적(distributed tracing, 요청 하나가 서비스와 데이터베이스, 외부 호출을 거치는 전 구간을 이어 붙여 각 구간에 걸린 시간을 보여 주는 방법)입니다. 요청 하나를 구간별로 펼쳐 보면 애플리케이션 내부에서 오래 걸렸는지, 데이터베이스 응답을 기다렸는지, 외부 API에서 시간이 샜는지가 한눈에 드러납니다. 어느 구간이 느린지 잡히면 그다음에 볼 곳이 정해집니다.

추적 도구를 붙이기 전이라도 명령 한 줄로 첫 구분은 할 수 있습니다. curl-w 옵션은 요청 하나가 단계별로 쓴 시간을 나눠서 보여 줍니다.

curl -o /dev/null -s -w \
  "dns:%{time_namelookup} connect:%{time_connect} ttfb:%{time_starttransfer} total:%{time_total}\n" \
  https://api.example.com/orders
# dns:0.004 connect:0.021 ttfb:1.842 total:1.851   (값의 단위는 초)
#                          ↑ 연결은 빠른데 첫 바이트가 늦으면 서버 처리 구간이 원인

ttfb(첫 바이트를 받기까지 걸린 시간)만 크다면 서버가 요청을 처리하는 동안 시간을 쓴 것입니다. connect가 크면 연결 수립 단계부터 막힌 것이라, 네트워크 경로와 연결 수락 상태를 봅니다.

느린 구간을 특정했다면, 그 지점을 요청 경로의 위쪽(진입)에서 아래쪽(인프라)으로 순서대로 좁혀 갑니다. 아래 7가지가 그 순서입니다.

요청 경로를 따라가며 보는 7가지 원인

애플리케이션·데이터베이스·외부 호출·인프라·네트워크 다섯 계층에 API 응답 지연 원인 7가지를 배치한 요청 경로 도식
느린 구간을 트레이스로 먼저 잡고, 그 구간이 속한 계층부터 후보를 좁혀 갑니다.

원인은 경로 위 어디에 있느냐로 나눠 보면 겹치지 않게 정리됩니다. 각 원인은 남기는 단서가 다르고, 주로 확인하는 관측 지표도 다릅니다.

계층 원인 결정적 단서 주로 보는 지표
애플리케이션 GC 정지 주기적으로 응답이 함께 튐, 정지 시간 급증 GC 로그, 힙 메트릭
애플리케이션 스레드 풀 고갈·블로킹 락 대기 액티브 스레드가 최대치에 붙고 큐만 늘어남 스레드 상태, 액티브 스레드 수
데이터베이스 느린 SQL·N+1 쿼리 특정 요청만 느림, 쿼리 수가 비정상적으로 많음 쿼리 실행 시간, 호출 횟수
데이터베이스 커넥션 풀 대기 DB는 한가한데 요청이 커넥션을 기다림 풀 사용률, 대기 시간
외부 호출 다운스트림 API 지연·타임아웃 특정 외부 호출 구간만 길게 잡힘 외부 호출 응답 시간, 타임아웃 수
인프라 CPU·메모리 포화 부하가 오를 때만 전반적으로 느려짐 CPU 사용률, 메모리, 스로틀
네트워크 DNS 지연·구간 지연 연결 수립 단계부터 오래 걸림 연결 시간, 구간 RTT

계층 순서대로 핵심만 정리했습니다.

애플리케이션 계층부터 봅니다. GC(가비지 컬렉션, 사용하지 않는 메모리를 정리하는 작업)가 도는 동안 애플리케이션이 잠시 멈추는 stop-the-world 구간이 길어지면, 그 순간을 지나던 요청이 함께 느려집니다. 응답 지연이 주기적으로 몰려 나타나면 GC부터 의심합니다. 스레드 풀 고갈과 락 대기도 여기에 속합니다. 처리할 스레드가 모자라거나, 여러 요청이 같은 자원을 두고 순서를 기다리는 동안 스레드가 블로킹되면, 정작 CPU는 한가한데 요청만 줄 서서 느려집니다. 다만 락 경합이라고 늘 CPU가 낮은 것은 아닙니다. 잠금을 기다리며 짧게 계속 확인하는 스핀 방식에서는 반대로 CPU가 올라가므로, CPU가 낮다는 것만으로 락 경합을 후보에서 빼서는 안 됩니다.

데이터베이스 계층은 응답 지연에서 자주 지목되는 구간입니다. 인덱스를 타지 못하는 느린 SQL 하나, 또는 한 화면을 그리려고 쿼리를 수백 번 던지는 N+1 문제(목록을 한 번 조회한 뒤 각 항목마다 추가 쿼리를 반복해 호출 수가 폭증하는 패턴)가 대표적입니다. 느린 쿼리를 골라내 실행 계획까지 파고드는 방법은 슬로우 쿼리 원인 분석과 튜닝에서 다룹니다. 커넥션 풀 대기도 자주 놓칩니다. 커넥션 풀은 미리 열어 두고 돌려쓰는 연결의 묶음인데, 데이터베이스 연결뿐 아니라 외부 API를 호출하는 HTTP 클라이언트에도 따로 있습니다. 데이터베이스가 한가한데도 풀이 동나면 요청은 쿼리를 던지기도 전에 커넥션을 얻는 단계에서 기다립니다. 커넥션이 모자랄 때 무엇을 확인하는지는 Too many connections 원인과 해결 방법, 커넥션 풀부터 확인하세요에 정리돼 있습니다.

외부 호출 계층은 직접 통제할 수 없는 구간이라 특히 조심해야 합니다. 다운스트림 API(내 서버가 다시 호출하는 바깥 서비스)의 응답 시간이 길어지거나 타임아웃이 나면 내 응답까지 함께 늘어집니다. 이 구간에 잡히는 시간에는 네트워크 왕복뿐 아니라 상대 서버가 처리한 시간이 함께 들어 있습니다. 상대가 끝까지 응답하지 못하면 앞단 게이트웨이가 기다리다 포기해 504로 끊기기도 합니다.

인프라 계층은 부하가 오를 때만 전반적으로 느려지는 특징이 있습니다. CPU가 포화되거나 컨테이너가 스로틀링에 걸리면 그 시간대의 요청이 전반적으로 밀립니다. CPU 사용률이 높게 잡혔다면 리눅스 CPU 사용률이 높은 이유와 확인 방법에서 원인을 좁히는 순서를 다룹니다. 게이트웨이가 먼저 연결을 끊어 504가 함께 났다면 504 Gateway Timeout 원인과 해결 방법을 같이 봅니다.

마지막으로 네트워크 계층은 DNS 조회나 구간 지연처럼 연결을 수립하는 단계부터 시간이 걸리는 경우입니다. 요청이 애플리케이션에 닿기도 전에 느리다면 이쪽을 봅니다.

API가 느릴 때 자주 하는 오진 3가지

CPU 사용률·외부 API·평균 응답 시간을 두고 자주 나오는 오진 세 가지와 먼저 확인할 곳을 짝지은 도식
증상이 먼저 보이는 곳과 원인이 있는 곳은 다를 수 있습니다.

경로를 순서대로 보는 게 왜 중요한지는, 자주 나오는 오판을 보면 분명해집니다.

CPU 사용률이 높다고 코드 최적화부터 파고드는 경우가 많습니다. 하지만 CPU가 튀는 진짜 이유가 GC 폭주이거나 스핀으로 대기하는 락 경합일 때가 있어서, 코드 로직을 아무리 들여다봐도 답이 안 나옵니다. CPU는 증상이고 원인은 위쪽 계층에 있을 수 있습니다.

외부 API가 느리다고 단정하는 것도 마찬가지입니다. 트레이스를 펼쳐 보면 외부 호출 자체에 걸린 시간과, 그 호출을 시작하기 전 HTTP 클라이언트 커넥션 풀에서 커넥션을 기다린 시간이 나뉘어 보입니다. 커넥션을 기다린 시간이 대부분이라면 원인은 상대 서비스가 아니라 이쪽 커넥션 풀입니다. "기다린 시간"이 어느 구간에서 발생했는지 구분하지 않으면 엉뚱한 팀에 원인을 넘기게 됩니다.

평균 응답 시간만 보고 정상이라 판단하는 것도 위험합니다. 평균은 멀쩡한데 p95(전체 요청의 95%가 이 시간 안에 들어오는 값)나 p99 같은 상위 백분위만 튀는 경우, 특정 조건에서만 걸리는 지연이 숨어 있습니다. 분포를 봐야 꼬리에 숨은 문제가 보입니다. 평균과 TPS, 상위 백분위 가운데 언제 무엇을 보는지는 응답시간 지표 비교, 평균·TPS·p95·p99 중 뭘 봐야 하나에서 비교했습니다.

자주 묻는 질문

API가 갑자기 느려졌는데 CPU와 메모리는 정상입니다. 어디를 봐야 하나요?

인프라가 정상이면 그 위 계층을 의심합니다. 스레드 풀 고갈, 락 대기, 커넥션 풀 대기처럼 자원을 기다리는 상황은 CPU를 거의 쓰지 않으면서 응답만 늘립니다. 요청이 어느 구간에서 대기했는지 트레이스로 확인하는 것이 먼저입니다.

느려진 원인이 배포 때문인지 트래픽 때문인지 어떻게 구분하나요?

지연이 시작된 시각을 배포 시각, 트래픽 곡선과 나란히 놓고 봅니다. 배포 시각을 경계로 응답 시간이 계단처럼 올라갔는데 트래픽은 평소와 같다면 새로 들어간 코드나 설정을 봅니다. 반대로 트래픽이 오르는 구간에서만 응답 시간이 함께 늘고 트래픽이 빠지면 회복된다면 자원 포화 쪽입니다. 둘 다 아니라면 외부 의존 서비스의 변화나 데이터 증가처럼 배포와 무관한 변화를 찾습니다.

재현되지 않는 간헐적 지연은 어떻게 잡나요?

재현을 기다리는 대신, 느렸던 요청의 기록이 남게 만들어 두는 편이 빠릅니다. 느린 요청만 골라 트레이스를 보관하고 그 시점의 스레드 상태와 자원 지표를 함께 남기면, 사건이 지난 뒤에도 그 순간을 다시 볼 수 있습니다. 간헐적 지연은 특정 사용자, 특정 파라미터, 캐시 미스처럼 조건이 붙어 있는 경우가 많아 느린 요청 몇 건의 공통 조건을 찾는 것이 지름길입니다.

API 응답 시간이 느리다고 보는 기준은 몇 초인가요?

절대 기준은 없습니다. 같은 500ms라도 내부 배치 API에서는 문제가 아니고 사용자가 화면에서 기다리는 조회 API에서는 이탈로 이어집니다. 그래서 기준은 그 API의 평소 값에서 얼마나 벗어났는지로 잡습니다. 평소 p95가 200ms이던 API가 800ms가 됐다면 절대값이 작아도 조사 대상입니다.

특정 API 하나만 느릴 때는 어디부터 봐야 하나요?

하나만 느리다면 그 API가 하는 일을 먼저 봅니다. 같은 서버의 다른 API가 멀쩡하다면 CPU나 메모리 같은 공용 자원이 원인일 가능성은 낮습니다. 그 API가 던지는 쿼리, 호출하는 외부 서비스, 그 API에서만 잡히는 락을 순서대로 확인합니다. 여러 API가 같이 느려졌다면 공용 자원이나 인프라를 먼저 봅니다.

마치며

API 응답이 느려졌을 때는 요청 하나를 경로대로 펼쳐 어느 구간에서 시간이 새는지부터 특정합니다. 그다음 애플리케이션, 데이터베이스, 외부 호출, 인프라, 네트워크 순서로 좁혀 가면 7가지 원인 중 어디인지 빠르게 잡힙니다.

느렸던 그 요청이 어느 구간에서 얼마나 기다렸는지는 대개 지나가고 나면 남지 않습니다. 기록이 없으면 다시 느려질 때를 기다려 그 순간을 붙잡아야 합니다. 와탭 APM은 요청 하나를 애플리케이션부터 데이터베이스, 외부 호출 구간까지 트레이스로 펼쳐 시간이 어디서 쌓였는지 되짚을 수 있습니다. 트랜잭션을 구간별로 분석하는 방법은 Java 애플리케이션 모니터링 소개 (와탭 공식 문서)에서 확인할 수 있습니다.

더 읽을거리

와탭 모니터링을 무료로 체험해보세요!