API 비용 최적화 (숨겨진 낭비, 캐싱 전략, 지속 관리)
API 시스템 운영에서 비용은 단순한 인프라 지출이 아니라, 아키텍처 설계와 직접적으로 연결된 핵심 지표입니다. 많은 조직이 트래픽 증가에 맞춰 인프라를 확장하지만, 그 과정에서 불필요한 비용이 지속적으로 누적되는 경우가 많습니다.
API 비용을 그냥 "쓴 만큼 내는 것"이라고 생각했습니다. 그러다 어느 달 청구서를 보고 멈칫했습니다. 분명 트래픽이 크게 늘지 않았는데 비용이 전달 대비 40% 가까이 올라 있었습니다. 그때부터 코드가 아니라 구조를 들여다보기 시작했고, 생각보다 많은 돈이 아무도 신경 쓰지 않는 곳에서 조용히 새고 있었습니다.
숨겨진 낭비는 어디서 오는가
처음에는 서버 사양 문제라고 생각했습니다. 인스턴스를 업그레이드하면 나아지겠거니 했는데, 제가 직접 로그를 뜯어보니 문제는 전혀 다른 곳에 있었습니다. 같은 데이터를 5초 간격으로 반복 호출하는 클라이언트 로직이 있었고, 그게 전체 요청의 30% 가까이를 차지하고 있었습니다.
API 비용은 크게 컴퓨트, 네트워크, 스토리지 세 축으로 구성됩니다. 컴퓨트는 요청을 처리하는 서버 자원의 비용이고, 네트워크는 데이터가 오가는 전송량 비용, 스토리지는 로그와 캐시 데이터를 보관하는 비용입니다. 문제는 이 세 가지가 독립적으로 움직이지 않는다는 점입니다. 불필요한 호출 하나가 컴퓨트 비용을 올리고, 동시에 응답 데이터가 오가며 네트워크 비용을 높이고, 그 요청 로그가 쌓이며 스토리지 비용까지 잡아먹습니다.
또 하나 간과하기 쉬운 부분이 응답 크기입니다. 당시 저희 API는 클라이언트가 실제로 쓰는 필드가 전체의 절반도 안 됐는데, 응답 전체를 그대로 내려주고 있었습니다. 이걸 Projection, 즉 클라이언트가 필요한 필드만 선택적으로 반환하는 방식으로 바꿨을 때 네트워크 전송량이 눈에 띄게 줄었습니다. Field Filtering이라고도 부르는 이 방식은 GraphQL을 쓰면 자연스럽게 구현되지만, REST 환경에서도 쿼리 파라미터로 응답 필드를 제한하는 설계를 적용할 수 있습니다.
로그 수집도 문제였습니다. 모든 요청과 응답을 풀 텍스트로 저장하고 있었는데, 대부분의 로그는 실제로 한 번도 조회되지 않았습니다. Sampling Rate, 즉 전체 요청 중 일정 비율만 기록하는 방식으로 전환했더니 스토리지 비용이 한 달 만에 20% 넘게 줄었습니다. 솔직히 이건 예상 밖이었습니다.
캐싱 전략, 생각보다 까다롭습니다
캐싱이 비용 절감의 정석이라는 말은 많이 들었습니다. 그런데 캐싱은 "걸어두면 끝"이 아니라는 걸 뼈저리게 느꼈습니다. TTL, 즉 캐시 만료 시간을 어떻게 설정하느냐에 따라 효과가 완전히 달라집니다. 너무 짧으면 캐시 히트율이 낮아서 의미가 없고, 너무 길면 데이터 정합성 문제가 생깁니다.
처음 캐싱을 적용했을 때는 TTL을 일괄 60초로 설정했습니다. 결과적으로 자주 바뀌지 않는 상품 목록 같은 데이터는 TTL이 너무 짧았고, 반대로 사용자 세션과 연결된 데이터는 60초도 길어서 문제가 생겼습니다. 결국 데이터 성격별로 TTL을 분리 설정하는 방향으로 바꿨고, 그때부터야 비로소 캐시가 제 역할을 하기 시작했습니다.
캐시 구조를 잡을 때 다음 기준으로 분류했습니다.
- 변경 빈도가 낮고 전사적으로 공유되는 데이터(공지사항, 카테고리 목록 등): TTL 10분 이상, CDN 레이어 캐싱 적용
- 사용자 단위로 달라지는 데이터(장바구니, 최근 조회 등): TTL 30초 이하, 서버 사이드 캐시는 최소화
- 실시간성이 중요한 데이터(재고, 가격 등): 캐싱 제외 또는 이벤트 기반 무효화 적용
이렇게 분류하고 나서 전체 DB 쿼리 수가 약 35% 감소했습니다. 수치보다 더 체감되는 건 응답 속도였습니다. 캐시 히트가 발생하면 응답이 수십 밀리초 단위로 나오다 보니, 사용자 경험도 같이 개선되는 부수 효과가 있었습니다. 비용과 성능이 함께 좋아진 드문 케이스였습니다. 출처: AWS Caching Overview에서도 캐싱이 응답 지연과 백엔드 부하를 동시에 줄이는 효과를 설명하고 있습니다.
API Aggregation도 이 시기에 함께 적용했습니다. API Aggregation이란 클라이언트가 여러 번 나눠서 보내던 요청을 서버 측에서 하나로 합쳐 처리하는 방식입니다. 예를 들어 화면 하나를 그리기 위해 상품 정보, 리뷰, 재고 API를 각각 호출하던 구조를 BFF(Backend For Frontend) 패턴으로 묶었더니, 클라이언트 요청 횟수 자체가 줄고 네트워크 왕복 비용이 떨어졌습니다.
지속 관리 없이는 금방 원점으로 돌아갑니다
최적화 작업을 마치고 나서 한 달쯤 지나자 비용이 다시 슬금슬금 오르기 시작했습니다. 새로운 기능이 배포되면서 캐싱이 적용되지 않은 엔드포인트가 늘어났고, 오토 스케일링 설정이 예상보다 공격적으로 작동하고 있었습니다. 오토 스케일링이란 트래픽에 따라 서버 인스턴스 수를 자동으로 늘리거나 줄이는 기능인데, 최솟값과 최댓값을 잘못 잡아두면 트래픽이 없는 새벽에도 불필요한 인스턴스가 계속 살아있는 상황이 생깁니다. 경험상 이건 설정 한 번 잘못 건드리면 한 달 비용이 바로 티가 납니다.
그래서 API별 비용을 태그로 구분해서 추적하는 Cost Allocation 체계를 만들었습니다. Cost Allocation이란 클라우드 리소스에 태그를 붙여 어떤 서비스나 기능이 비용을 얼마나 쓰고 있는지 구분하는 방식입니다. 이렇게 하면 특정 API가 갑자기 비용을 많이 쓰기 시작할 때 바로 감지할 수 있습니다. 출처: AWS 비용 할당 태그 공식 문서를 참고해서 체계를 잡았는데, 처음 세팅이 번거롭긴 해도 한번 만들어두면 이후가 훨씬 편합니다.
SLO 기반 접근도 중요하다고 느꼈습니다. SLO란 서비스 수준 목표(Service Level Objective)로, 응답 시간이나 가용성 같은 품질 지표의 목표치를 수치로 정의한 것입니다. 비용만 줄이려다 응답 속도가 나빠지면 아무 의미가 없으니, 허용 가능한 성능 범위 안에서만 최적화 작업을 진행하는 원칙을 세워뒀습니다. 그 기준이 없으면 "어디까지 줄일 수 있는가"에 대한 판단 근거가 없어서 팀 내 논의가 계속 겉돌게 됩니다.
비용 리뷰는 격주 단위로 짧게라도 하는 게 낫습니다. 월 단위로 보면 이미 문제가 커진 다음에야 알게 되는 경우가 많았습니다. 경험상 격주 리뷰만 해도 이상 패턴을 훨씬 빠르게 잡아낼 수 있었고, 그만큼 수습 비용도 줄었습니다.
API 비용 최적화는 한 번 잘 해두면 끝나는 작업이 아닙니다. 서비스가 자라면서 구조도 바뀌고, 낭비 지점도 함께 이동합니다. 중요한 건 비용을 무조건 줄이는 게 아니라 어디서 새는지 계속 들여다보는 습관입니다. 청구서가 이상하다는 느낌이 들 때는 인스턴스 사양보다 요청 패턴과 응답 크기부터 먼저 확인해 보시길 권합니다. 그게 훨씬 빠른 길이었습니다.
관련 글
댓글
댓글 쓰기