API 응답 속도 (서버 점검, DB 최적화, 캐싱 전략)

API 응답 속도 저하는 사용자 경험을 직접적으로 악화시키는 핵심 문제 중 하나입니다. 응답 시간이 길어지면 사용자는 서비스가 느리다고 인식하게 되며, 이는 이탈률 증가와 서비스 신뢰도 하락으로 이어질 수 있습니다. API 응답이 느려졌을 때, 그냥 서버를 재시작하면 해결될 거라고 생각합니다. 그러나 재시작 후 10분도 안 돼서 똑같이 느려졌고, 원인을 찾는 데 반나절이 걸렸습니다. API 응답 속도 문제는 하나의 원인이 아니라 서버, DB, 네트워크가 뒤엉켜서 발생하는 경우가 대부분입니다. 이 글은 그 삽질을 줄이기 위한 점검 순서를 정리한 것입니다. 서버 점검, 어디서부터 봐야 할까요 API가 느려졌다는 신고를 받으면 가장 먼저 뭘 확인하시나요? 초반에 무조건 로그부터 뒤졌는데, 사실 그보다 먼저 봐야 할 게 있습니다. 바로 서버의 CPU 사용률과 메모리 점유율입니다. CPU 사용률이 80% 이상을 지속적으로 유지하고 있다면, 요청 하나하나를 처리하는 데 이미 자원이 부족한 상태입니다. 메모리도 마찬가지입니다. 가용 메모리가 거의 없으면 운영체제가 디스크 스왑(swap, 부족한 메모리를 디스크로 대신 사용하는 방식)을 시작하는데, 이 순간부터 응답 속도는 눈에 띄게 떨어집니다. 스왑이 발생하는 서버에서는 평균 응답 시간이 평소의 3배 이상 늘어났습니다. 그 다음은 스레드 풀(Thread Pool) 상태입니다. 스레드 풀이란 서버가 동시에 처리할 수 있는 요청 작업자의 수를 미리 정해둔 것인데, 들어오는 요청 수가 이 한도를 넘으면 나머지 요청은 줄을 서서 기다리게 됩니다. 이 대기 시간이 응답 지연으로 직결됩니다. 이런 구조에서는 스레드 수를 늘리거나, 비동기 처리 방식으로 전환하는 것이 현실적인 해결책입니다. 애플리케이션 내부 로직도 빠뜨리면 안 됩니다. 특히 반복문 안에서 외부 API를 호출하거나 DB 쿼리를 실행하는 구조가 있다면, 요청 1건에 수십 번의 외부 호출이 발생할 수 있습니다. 이건 코드 리뷰에서도 쉽게 놓치는 부분이라 따로 ...

API Lazy Loading 전략 (요청 흐름, 비용과 복잡도, 설계)

API Lazy Loading 전략은 필요한 데이터만 우선적으로 로드하고, 추가 데이터는 실제로 요청이 발생했을 때 지연 로딩하는 방식입니다. 

 API 응답 속도를 개선하겠다고 Lazy Loading을 도입했다가, 오히려 전체 흐름이 느려진 경험이 있으신가요? 저도 같은 실수를 한 적 있습니다. 초기 응답은 분명 빨라졌는데, 실제 화면이 완성되기까지 체감 속도는 이전보다 오히려 답답해졌습니다. 이 글은 그 경험에서 출발해, API Lazy Loading이 정말 성능 개선인지 성능 저하인지를 요청 흐름과 비용 관점에서 따져봅니다.

Lazy Loading이 요청 흐름을 어떻게 바꾸는가

일반적인 API 설계에서는 필요한 데이터를 단일 호출로 모두 가져오는 Eager Loading 방식을 사용합니다. Eager Loading이란 요청 시점에 연관 데이터를 한 번에 조회하는 방식으로, 클라이언트 입장에서는 한 번 기다리면 모든 데이터가 준비되는 구조입니다. 반면 Lazy Loading은 기본 데이터만 먼저 반환하고, 추가 정보는 실제 필요한 시점에 별도 요청으로 불러옵니다.

이 구조 자체는 나쁘지 않습니다. 목록 화면에서는 제목과 썸네일만 먼저 보여주고, 상세 화면으로 넘어갈 때 본문과 댓글을 로드하는 방식이 대표적인 예입니다. 사용자가 실제로 열어보지 않는 항목에 대해서는 데이터를 아예 전송하지 않으니, 불필요한 네트워크 낭비를 줄이는 효과가 있습니다.

그런데 제가 직접 써봤는데, 문제는 요청이 분산되는 순간부터 시작됩니다. 단일 요청이 아니라 여러 단계의 API 호출이 순차적으로 발생하면, 각 요청마다 네트워크 왕복 지연(RTT, Round-Trip Time)이 누적됩니다. RTT란 요청을 보내고 응답을 받을 때까지 걸리는 시간으로, 이것이 3~4번 쌓이면 초기 응답 속도 개선 효과가 순식간에 상쇄됩니다. 초기 응답은 분명 빨라졌는데, 전체 흐름은 오히려 길어지는 아이러니가 여기서 나옵니다.

네트워크 비용과 클라이언트 복잡도, 실제로 어느 정도인가

Lazy Loading에서 가장 간과되는 부분이 API 호출 비용입니다. 내부 API라면 서버 부하 증가 정도로 끝나지만, AWS API Gateway나 Stripe 같은 외부 API를 사용하는 경우 호출 횟수가 곧 비용과 직결됩니다. 제 경험상 이건 좀 다릅니다. 설계 단계에서 "어차피 필요할 때만 호출하니까 더 절약된다"고 생각했는데, 실제 사용 패턴을 뜯어보니 대부분의 사용자가 목록에서 거의 모든 항목을 열어보고 있었습니다. 결국 Eager Loading 한 번보다 API 호출이 훨씬 많았습니다.

모바일 환경에서는 이 문제가 더 두드러집니다. 출처: web.dev에 따르면, HTTP/1.1 환경에서 다수의 순차 요청은 헤드오브라인 블로킹(Head-of-Line Blocking) 문제를 일으킬 수 있습니다. 헤드오브라인 블로킹이란 앞선 요청이 완료되기 전까지 다음 요청이 대기 상태에 묶이는 현상으로, LTE나 5G 환경이라도 신호가 불안정한 지하철이나 건물 내부에서는 체감 속도가 눈에 띄게 느려집니다.

클라이언트 로직 복잡도도 무시하기 어렵습니다. 단순히 API를 나눠 호출하는 게 아니라, 각 응답의 상태 관리, 에러 처리, 데이터 병합 로직이 모두 클라이언트에 생깁니다. 솔직히 이건 예상 밖이었습니다. 처음에는 "API만 나누면 되겠지"라고 봤는데, 실제 구현 단계에서 로딩 상태, 부분 실패 처리, 재시도 로직까지 추가하다 보니 코드 복잡도가 상당히 올라갔습니다. 유지보수 비용이 늘어난다는 것을 몸으로 느낀 순간이었습니다.

Lazy Loading이 진짜 문제인지 판단하는 데 도움이 된 체크포인트를 정리하면 이렇습니다.

  1. 사용자가 실제로 추가 데이터를 열어보는 비율이 30% 이하인가
  2. API 호출 한 건당 추가 비용이 발생하는 외부 서비스를 사용하는가
  3. 서비스 주요 접속 환경이 모바일 또는 고지연 네트워크인가
  4. 클라이언트 팀의 상태 관리 역량이 충분한가

이 중 두 개 이상 해당하면, Lazy Loading을 그대로 적용하는 것은 한 번 더 검토할 필요가 있습니다.

그래서 어떻게 설계해야 하는가, 실전 기준

일반적으로 Lazy Loading이 적합한지는 데이터 접근 패턴을 분석하면 알 수 있다고 알려져 있지만, 경험상 이것만으로는 부족합니다. 접근 패턴 분석과 함께 요청 수를 줄이는 구조 설계가 반드시 병행되어야 합니다.

가장 현실적인 접근은 API 응답 필드를 선택적으로 지정할 수 있는 Sparse Fieldsets 구조를 설계하는 것입니다. Sparse Fieldsets란 클라이언트가 필요한 필드만 쿼리 파라미터로 지정해서 받아오는 방식으로, 하나의 API 엔드포인트에서 화면별로 필요한 데이터만 골라 받을 수 있습니다. Lazy Loading처럼 요청을 분산하지 않으면서도, Eager Loading처럼 불필요한 데이터를 전부 전송하는 낭비도 줄일 수 있습니다. 출처: JSON:API 공식 스펙에서 이 방식의 표준 구현을 확인할 수 있습니다.

캐싱 전략과 결합하는 것도 효과가 컸습니다. 이미 한 번 조회한 데이터는 클라이언트 캐시나 CDN 레이어에 보관해 두면, 같은 항목을 다시 열 때 API 호출 자체가 발생하지 않습니다. 저는 React Query의 staleTime 설정으로 중복 요청을 상당히 줄인 적이 있는데, 이것만으로도 서버 호출 횟수가 눈에 띄게 줄었습니다.

결국 API Lazy Loading을 적용하기 전에 가장 먼저 물어봐야 할 질문은 "이 데이터를 사용자가 실제로 얼마나 자주, 어디까지 조회하는가"입니다. 사용 패턴이 분명하고, 추가 데이터 조회율이 낮으며, HTTP/2 이상 환경이 보장된다면 Lazy Loading은 분명히 효과적인 선택입니다. 그러나 그 반대라면, 구조를 다시 설계하거나 Sparse Fieldsets처럼 절충안을 찾는 것이 더 현명합니다.

API Lazy Loading은 성능 개선 도구이기도 하고, 잘못 쓰면 성능 저하 원인이 되기도 합니다. 어느 쪽이 될지는 기술 자체보다 설계 시점의 판단에 달려 있습니다. 데이터 접근 패턴을 먼저 측정하고, 요청 수와 비용, 클라이언트 복잡도를 함께 따져보는 것이 출발점입니다. API Caching 전략과 함께 검토하면 더 균형 잡힌 설계가 나옵니다. 다음 시간에는 "API Caching 전략"을 확인하세요


관련 글

댓글

이 블로그의 인기 게시물

HTTP 메서드의 필요성 (GET과 POST, PUT과 DELETE, API 보안)

API 없는 세상의 불편함 (로그인 연동, 서비스 구조, 디지털 인프라)

API 이해하기 (서비스 연결, 시스템 협력, 디지털 구조)