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

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

API Sorting 전략 (성능 부하, 유연성, 설계 원칙)

정렬 기능 하나 잘못 설계했다가 쿼리 응답 시간이 3초를 넘어버린 경험이 있습니다. 그때 처음으로 API Sorting이 단순한 편의 기능이 아니라는 걸 뼈저리게 깨달았습니다. 최신순, 인기순, 가격순처럼 사용자 입장에서는 당연한 기능이지만, 서버 입장에서는 꽤 까다로운 처리가 뒤따릅니다. 이 글은 그 경험에서 출발해, 실제 설계에서 부딪힌 문제와 해결 방향을 솔직하게 풀어봅니다.

정렬 요청 하나가 서버를 흔든 날

처음 API에 Sorting 기능을 붙였을 때는 꽤 뿌듯했습니다. 쿼리 끝에 ORDER BY 한 줄만 추가하면 됐고, 기능도 바로 동작했습니다. 그런데 데이터가 수십만 건으로 불어나면서 상황이 달라졌습니다. 특정 정렬 요청이 들어올 때마다 응답이 눈에 띄게 느려졌고, 결국 모니터링 도구에서 슬로우 쿼리 경고가 떴습니다.

문제는 풀 테이블 스캔(Full Table Scan)이었습니다. 풀 테이블 스캔이란 데이터베이스가 정렬 기준이 되는 컬럼에 인덱스가 없을 때, 테이블의 모든 행을 처음부터 끝까지 읽어야 하는 상황을 말합니다. 인덱스(Index)란 책의 목차처럼 특정 컬럼에 대한 탐색 경로를 미리 만들어둔 구조인데, 이게 없으면 데이터베이스는 전체 데이터를 뒤져야 합니다. 제가 직접 실행 계획(EXPLAIN)을 돌려봤을 때, 정렬 대상 컬럼에 인덱스가 아예 없다는 걸 그 순간에야 확인했습니다. 부끄럽지만 처음 설계할 때 인덱스를 전혀 고려하지 않았던 겁니다.

더 곤란했던 건 다중 필드 정렬이었습니다. 사용자가 "가격 낮은 순 + 최신 등록순" 같이 두 가지 조건을 동시에 요청하면 쿼리 복잡도가 크게 올라갑니다. 실행 계획이 예측과 다르게 흘러가면서 인덱스를 타지 않는 경우도 생겼습니다. 솔직히 이건 예상 밖이었습니다. 단순히 정렬 조건 두 개를 묶었을 뿐인데 성능이 이렇게까지 달라질 줄은 몰랐습니다.

이 사건 이후로 저는 정렬 기능을 추가할 때 반드시 인덱스 설계부터 함께 논의하는 습관이 생겼습니다. Use The Index, Luke라는 사이트가 이때 많이 도움이 됐습니다. 인덱스 동작 원리를 쿼리 흐름과 연결해서 설명해주는 곳인데, 제 경험상 이 수준의 이해가 없으면 Sorting 설계에서 반드시 한 번은 넘어지게 됩니다.

유연성과 통제, 둘 다 포기할 수 없는 이유

API Sorting의 매력은 유연성입니다. 클라이언트가 원하는 기준으로 데이터를 요청하면 서버가 이미 정렬된 결과를 돌려줍니다. 클라이언트 측 정렬(Client-side Sorting)이란 서버에서 전체 데이터를 받은 뒤 브라우저나 앱에서 직접 순서를 바꾸는 방식인데, 이 경우 불필요한 데이터 전송이 발생하고 대용량 환경에서는 성능 저하로 이어집니다. 서버에서 정렬을 처리하면 이 문제를 깔끔하게 해결할 수 있습니다.

그런데 문제는 허용 범위를 어디까지 열어두느냐입니다. 이론적으로는 모든 컬럼에 대해 정렬을 허용할 수 있지만, 제 경험상 이건 꽤 위험한 선택입니다. 어떤 클라이언트가 인덱스가 없는 컬럼에 대해 정렬을 요청하면 그 순간 전체 시스템 응답이 느려질 수 있습니다. 실제로 한 프로젝트에서 정렬 필드를 무제한으로 열어뒀다가, 특정 요청이 DB 커넥션 풀(Connection Pool)을 잠식해버린 일이 있었습니다. 커넥션 풀이란 데이터베이스 연결 자원을 미리 확보해두는 공간으로, 이게 꽉 차면 다른 요청들이 줄줄이 대기 상태로 빠집니다.

그래서 저는 허용 가능한 정렬 필드를 명시적으로 화이트리스트 방식으로 관리하는 것이 맞다고 봅니다. 화이트리스트(Whitelist)란 허용할 항목을 미리 목록으로 정해두고, 그 외의 요청은 차단하는 방식입니다. 클라이언트에게 유연성을 주되, 서버가 감당할 수 있는 범위 안에서만 허용하는 것입니다. "모든 걸 다 지원한다"는 건 API 설계에서 꽤 무서운 말이라는 걸 직접 겪어보니 알게 됐습니다.

참고로 Microsoft Azure API 설계 모범 사례에서도 정렬 파라미터를 허용할 때 서버가 지원하는 필드 범위를 명확히 문서화하고 제한하도록 권고하고 있습니다. 이건 저도 실제로 공감하는 방향입니다.

실전에서 통하는 Sorting 설계 원칙

여러 번 시행착오를 겪고 나서 지금은 몇 가지 원칙을 갖고 있습니다. 이걸 처음부터 알았더라면 꽤 많은 시간을 아낄 수 있었을 텐데, 하는 아쉬움이 있어서 가능한 구체적으로 적어보겠습니다.

  1. 정렬 허용 필드를 사전에 확정하고 인덱스를 함께 설계한다. 기능 구현보다 인덱스 설계가 먼저입니다.
  2. 페이지네이션(Pagination)과 반드시 결합한다. 페이지네이션이란 전체 데이터를 한 번에 반환하지 않고 일정 단위로 나눠 제공하는 방식입니다. Sorting과 Pagination을 함께 쓰면 정렬 대상 데이터 양 자체를 줄일 수 있어 성능 부담이 크게 낮아집니다.
  3. 정렬 기준 조합이 복잡해질수록 실행 계획(Execution Plan)을 직접 확인한다. 실행 계획이란 데이터베이스가 쿼리를 처리하는 방법을 미리 보여주는 분석 도구로, 인덱스가 제대로 활용되고 있는지 눈으로 확인할 수 있습니다.
  4. 기본 정렬 값(Default Sort)을 반드시 설정해둔다. 클라이언트가 정렬 파라미터를 넘기지 않았을 때 서버가 임의의 순서로 데이터를 반환하면 결과 일관성이 깨집니다.
  5. 복잡한 맞춤 정렬은 클라이언트에 위임하는 것도 선택지다. 서버에서 표준 정렬을 지원하고, 그 이상의 조건은 클라이언트가 받아서 처리하게 하면 API 복잡도를 낮출 수 있습니다.

이 중에서 제 경험상 가장 자주 놓치는 게 네 번째, 기본 정렬 값 설정입니다. 작은 것처럼 보이지만 페이지네이션과 맞물리면 데이터 중복이나 누락이 생기는 원인이 됩니다. 실제로 한 번 이 문제로 클라이언트 팀과 꽤 오래 원인을 추적한 적이 있습니다.

API Sorting은 결국 사용자 경험과 시스템 안정성 사이에서 줄을 타는 설계입니다. 너무 닫아두면 클라이언트가 불편하고, 너무 열어두면 서버가 흔들립니다. 제 결론은 "자주 쓰이는 것만 잘 지원하자"입니다. 모든 경우를 커버하려다 아무것도 제대로 못 하는 것보다, 실제 사용 패턴 기반으로 핵심 정렬 기준을 골라 안정적으로 제공하는 편이 훨씬 낫습니다. 처음 설계할 때 사용 로그나 기획 단계의 사용자 시나리오를 꼼꼼히 보는 습관, 저는 이게 API Sorting 설계의 출발점이라고 생각합니다.


관련 글

댓글

이 블로그의 인기 게시물

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

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

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