서론

nahwasa님 덕분에 요 책을 보게 되었다.
한국 개발자들에게 너무 유명한 책이라서, 사두고서 이제야 보기 시작했다는게 좀 가슴 아프긴 하지만, 그래도 일단 지금이라도 빠르게 보는 것에 의의를 두자.
이 책의 원 저자는 Alex Xu로, bytebytego를 운영하시는 분이시다. 기술 인터뷰를 대비하고, 시스템 설계에 대한 정보를 많이 올려주는 사이트라서 평소에 구독해서 보고 있는데, 혹시 모르는 분들이 있었다면 구독해두면 좋겠다.
현재 일하고 있는 회사는 증권 실시간 대용량 데이터를 다루는 데, 우리 회사의 사례와 빗대어볼 수 있는게 있다면 해보고, 배울 점이 있다면 배워보는 식으로 책을 접근해보고자 한다.
1장 - 사용자 수에 따른 규모 확장성
사용자가 늘어남에 따라 서버를 어떻게 확장할 것인지, 데이터베이스 부하는 어떻게 해결할 것인지, 응답시간은 어떻게 줄일 것인지 등에 대한 내용을 다룬다. 원래도 잘 알려져있던 내용이기도 하고 해서, 마지막의 핵심 부분만 정리해본다.
웹 계층은 서버 수평적 확장을 위해 무상태 계층으로
모든 계층에 다중화 도입
가능한 한 많은 데이터를 캐시할 것
여러 데이터 센터를 지원할 것
정적 컨텐츠는 CDN을 통해 서비스할 것
데이터 계층은 샤딩을 통해 그 규모를 확장할 것
각 계층은 독립적 서비스로 분할할 것
시스템을 지속적으로 모니터링하고 자동화 도구들을 활용할 것
현재 우리 회사는
우리 회사의 경우, 외환, 주식, 채권 등의 실시간 데이터를 다루고 증권 정보를 제공하는 B2B 회사다. 이에 따라 많으면 수 TB 용량의 테이블을 갖고 있다. 다만 B2B다보니 대략 6천명의 가입자를 가지는데, 조회하는 유저가 그렇게 많은 것은 아니다.
기본적으로 데이터는 이전에 IBM에서 만들었던 Informix DB라는 RDB를 사용하고 있다. 이전에는 증권 쪽에서는 어지간하면 시계열 데이터베이스나 NoSQL을 적극적으로 사용할 것이라 생각했는데, 생각보다 RDB로도 잘 돌아가서 놀랐다. 대신 적절한 파티셔닝과 캐싱을 섞어가며 조회 성능을 유지하고 있다.
예를 들면, 각 증권 요소들에 대해 (ex. 달러-원 가격) 10초마다 데이터를 저장하는데, 이런 데이터는 보통 당일의 데이터만 자주 조회되고, 1년 전의 데이터는 잘 조회되지 않는다.
따라서 오래된 데이터는 주기적으로 저 멀리 백업 서버에 던져두고 대신 하루의 시가/종가/고가/저가 정도만 저장해서 용량을 아낀다.
2장 - 개략적인 규모 추정
시스템 설계 면접을 볼 때, 시스템 용량이나 성능 요구사항을 개략적으로 추정해보라는 요구를 받곤 한다.
사실 이 부분이 엔지니어로서 가장 중요한 점이라고 생각하는데, 무작정 설계했다가 실제 비기능적 요구사항을 맞추지 못하거나, 혹은 오버 엔지니어링이 될 수 있다.
그렇다면 어떻게 그 규모를 미리 추정할 수 있을 것인가? 이 책에서는 2의 제곱수
, 응답지연 값
을 이용해서 대략적인 속도를 계산하고, 우리 시스템이 어느 정도의 가용성을 갖춰야 하는지에 따라 시스템을 설계하면 된다.
가용률 99% = 하루당 14.4분의 장애
가용률 99.9% = 하루당 1.44분의 장애
가용률 99.99% = 하루당 8.64초의 장애
가용률 99.999% = 1주에 6.05초의 장애
사실 뭔가 이전에는 관념적으로 99%, 99.9% 정도만 해도 엄청나게 높은 비율이라고 생각했는데, 이렇게 수치로 적고 보니 1%, 0.1%의 값이 크게 느껴진다.
기술 면접을 대비하기 위해선, 평상시에 QPS, 최대 QPS, 서버 수 등을 측정하는 역량을 길러두는 것이 중요하겠다.
3장 - 시스템 설계 면접 공략법
기술 면접에서는 시스텀 설계를 해보라고 물어보지만, 시스템 설계를 현업에서 단 한 명에게 시키진 않을 것이다. (중소에서는 아닐 수 있다 ㅎ)
그럼에도 물어보는 이유는 지원자의 설계 능력과, 협력에 적합한 사람인지, 모호한 문제를 건설적으로 해결할 수 있는지 등을 살펴보기 위함이다.
Gmail 같은 메일 서비스를 설계하라는 질문이 왔을 때 빠르게 답변을 띡 내놓는 것이 아니라, 구체적으로 어떤 기능이 필요한지
, 사용자 수는 얼마나 되는지
, 회사 규모는 얼마나 빨리 커질거라 예상하는 지
등을 통해 요구사항을 구체화하는 것이 중요하다.
그 과정에서 질문을 통해 문제를 확인하고, 힌트를 구하는 것에 주저하지 말고, 면접관이 내 사고 흐름을 이해할 수 있도록 하고, 여러 해법을 가능하다면 같이 제시해야겠다.
보통 네이버 같이 유명하고 좋은 회사를 입사한 면접 후기들을 봤을 땐, 이런 구체화와 명료화 파트를 다들 잘하시더라.
4장 - 처리율 제한 장치의 설계
트위터에서는 3시간 동안 300개의 트윗만 올릴 수 있도록 제한하며, 구글 독스는 사용자 당 분당 300회의 read 요청만 허용한다. 이렇게 미리 정해둔 임계치를 초과하는 API 요청이 들어오면 처리를 중단해서 서버의 정상적인 작동을 꾀하고, 공격으로 인해 청구될 수 있는 네트워크 비용을 절감할 수 있다.
처리율 제한을 클라이언트 혹은 서버 측에 둘지, 요청이 중단되었을 때 사용자에게 알려야할지, 횟수 카운팅의 기준이 무엇인지, 알고리즘은 무엇을 쓸지 등 다양한 고려사항이 있다.
토큰 버킷 알고리즘
누출 버킷 알고리즘
고정 윈도 카운터 알고리즘
이동 윈도 로그 알고리즘
이동 윈도 카운터 알고리즘
토큰 버킷 알고리즘
가장 폭넓게 이용되고 있고, 간단하고 보편적으로 사용되는 알고리즘이며, 내가 기존에 원래 알고 있던 유일한 알고리즘이다.
토큰 버킷은 사전에 설정된 양의 토큰이 주기적으로 채워지고, 요청이 처리될 때마다 토큰을 하나 사용한다.
충분한 토큰이 없을 경우 해당 요청은 버려진다.
챗지피티와의 대담
글 내용 중에, 다음 부분이 의아했다.
통상적으로 API 엔드포인트마다 별도의 버킷을 둔다. 사용자가 포스팅을 하루에 한번만 할 수 있고, 좋아요는 다섯 번까지만 누를 수 있다면 사용자마다 3개의 버킷을 두어야 할 것이다.
이 부분에 대해, 처리율 제한이라기 보단 정책적인 비즈니스 로직 같은데, 토큰 버킷으로 처리하는 것이 옳은가가 고민이 들었다. 뭔가 토큰 버킷은 정말 트래픽 방어적인 요소가 크다고 생각이 들었어서..
사용자가 수백만 명이면, api 별로 버킷이 너무 많이 필요한 것이 아닌가? 그리고 유휴 사용자의 경우는 어떻게 처리해야 하는가?
매우 좋은 질문입니다. 이건 단순한 기술 이슈가 아니라 아키텍처와 설계 철학에 대한 깊은 통찰이 필요한 문제예요. 질문하신 내용을 정리하면 다음 두 가지를 고민하고 계시네요:
정책적인 비즈니스 로직(하루 1회 포스트, 하루 5회 좋아요 등)을 토큰 버킷으로 구현하는 게 적절한가?
API별로 사용자마다 버킷을 둔다면 수백만 명에서 스케일이 터질 텐데, 유휴 사용자까지 포함해서 관리하는 건 낭비 아닌가?
🧠 1. "비즈니스 제약"을 토큰 버킷으로 처리하는 게 맞을까?
✅ 결론: "Rate Limiting" 기법(토큰 버킷, 리키 버킷 등)은 엄밀히 말하면 인프라 보호 또는 API 남용 방지용입니다.
그러므로 비즈니스 정책 제약은 별도로 명시적으로 다루는 것이 원칙입니다.
💡 하지만 현실은 혼합된다
Redis나 API Gateway에 구현된 토큰 버킷으로 일별/분별 횟수를 제한하는 건 "간편하고 성능 좋은 카운터 시스템"으로서 매력적이기 때문에,
실제 서비스에선 비즈니스 제약도 일부는 토큰 버킷처럼 처리합니다.
예: “하루 5회 좋아요 제한”을 Redis + TTL counter로 체크
조건은 아래와 같아요:
정확한 보장이 필요하지 않음 (예: 아주 예외적으로 6회 눌리는 걸 허용 가능)
성능/처리량 우선 (ex. 게임/광고 등 대량 처리 상황)
실시간 차단 필요
즉, "토큰 버킷이 적절한가?"보다 서비스 특성에 따라 허용할 수 있는 부정확성의 정도가 핵심입니다.
🏗 2. 유저 수백만 명일 때 버킷을 다 만들면 성능 문제 아닌가?
✅ 맞습니다. 사용자가 수백만 명이라면, 모든 사용자마다 모든 API마다 버킷을 만들면 메모리나 Redis 키 수가 폭발합니다. 실무에서는 다음 전략을 씁니다:
Lazy Allocation (지연 생성)
사용자가 실제 API를 처음 호출할 때만 버킷 생성
이후 TTL을 설정해서 유휴 사용자 버킷 자동 삭제
Redis 예:
SETNX user
🔢like_limit 5 EX 86400 DECR user
🔢like_limit
시간 윈도우를 이용한 카운터
버킷 구조 없이
user:{id}:{action}:{date}
같은 키를 사용TTL이 지나면 자연히 만료됨
간단한 카운터 형태 (정확한 토큰 버킷 아님)
버킷 공유 or 압축
일부 API는 하나의 버킷으로 공유 처리 (예: 전체 읽기 API들)
또는 Redis key를 구조화하지 않고 하나의
Hash
로 압축 저장
유휴 사용자 제거
Redis TTL을 설정해서 사용 안 한 버킷은 삭제
혹은 저장소를 DB로 변경해서 cold data는 archive
질문 | 답변 |
비즈니스 정책을 토큰 버킷으로 구현해도 될까? | 일부 경우엔 실용적으로 가능하지만, 본질은 인프라 보호 목적. 비즈니스 제약은 애플리케이션 레벨에서 명시적으로 구현하는 게 원칙. |
사용자 수백만 명이면 버킷이 너무 많지 않나? | 맞음. 그래서 실무에선 지연 생성, TTL, 압축, 유휴 제거 전략을 함께 씀. |
누출 버킷 알고리즘
토큰 버킷 알고리즘과 비슷하지만, 요청 처리율이 고정되어 있는 점이 다르다.
큐에 빈 자리가 있으면 요청을 추가한다.
큐가 가득 차 있으면 요청을 버린다.
지정된 시간마다 큐에서 요청을 꺼내어 처리한다.
개략적인 아키텍처
처리율 제한 알고리즘의 기본 아이디어는 정리하면..
얼마나 많은 요청이 접수됐는지 추적할 수 있는 카운터를 추적 대상 별로 둔다
사용자 별 or IP 주소 별 or API 엔드포인트 etc..
DB는 느리니, Redis에 저장하여 INCR, EXPIRE를 활용
역시 Redis는 온갖 곳에 쓰이는 것 같다. 만능 아니야?
다만 분산 서버에서는 당연하게도 경쟁 조건 문제가 생길 수 있고, redis에 락을 걸면 성능이 떨어지는 문제가 있으니, sorted set이라는 레디스 자료구조를 사용하자.
클라이언트에선..
Tanstack Query, Cache-Control 등의 클라이언트 캐시를 이용해서 요청 자체를 줄여볼 수도 있고
예외 처리 코드를 도입해서 처리가 중단되었을 때 우아하게 복구할 수 있게 한다
재시도 로직을 구현할 때, 충분한 backoff 시간을 두기
우리 회사에선? + 챗지피티와의 대담
B2C가 아닌 B2B에서도 Rate Limit이 중요할까? 하는 생각이 들었다. 귀찮아서 그런건 아니지만, 문제 해결에 코드 보다는 정책으로 풀 수 있다면 정책으로 푸는 것이 더 좋을 수 있다.
B2B 서비스이다보니, 악성 목적을 가지고 우리에게 DoS를 날릴 일은 어지간하면 없을 것이다. 정상적인 사용임에도 우리의 Rate Limit 조건에 의해 요청을 중단해버린다면 좀 불쾌할 수도 있다고 생각한다.
일단 Rate에 대해서는 풀어두되, 정말 실수든 의도든 너무 많은 요청이 들어오면 수동으로 처리할 수 도 있지 않을까? 싶었다.
또한 실시간 증권 데이터를 제공하는데 Rate Limit에 걸려서 갱신이 안된다면 사용자 입장에서 큰 손실을 볼 수도 있다. 특히, 그게 사용자에게 고지가 되지 않는다면 더더욱.
조금 고민해봐야겠다.. 챗지피티 나와라
정말 깊이 있는 고민 잘 하셨어요. 이건 단순히 기술적인 Rate Limit 여부를 넘어서, 서비스의 신뢰성·고객 경험·비즈니스 리스크·운영 체계까지 전방위로 고민해야 하는 주제입니다.
✅ 핵심 질문: B2B에서도 Rate Limit이 중요할까?
👉 결론부터 말하자면, "중요하지만, 적용 방식이 달라야 합니다."
B2C에서는 익명성과 대규모 트래픽 대비가 필요해서 "강제적·자동화된" Rate Limiting이 필요하지만, B2B는 신원 확인된 고객, 계약 기반의 사용, 상호 신뢰 전제이기 때문에, 부드러운 방식의 제한이 더 적절합니다.
코드로 박아버리는 Rate Limit은 “이상한 오류”로 인식될 수 있고, 고객 불만으로 이어지는 반면, 정책 기반 접근은 “우리가 약속한 한도를 넘으셨습니다”라고 명확히 커뮤니케이션 가능
🔥 실시간 증권 데이터 서비스라면 Hard Limit은 위험할 수 있음
실시간성이 중요한 데이터에서 Rate Limit에 걸려 끊기면 비즈니스 손실 + 고객 신뢰 하락이 큼
특히 B2B 고객이 해당 데이터로 자동 트레이딩, 보고서 생성, 대시보드 운영 중이라면 치명적
이런 상황에서는 오히려 "알림 기반의 소프트 리미트"가 더 중요합니다
✅ 실무적으로 추천하는 B2B Rate Limit 운영 전략
전략 | 설명 | 왜 좋은가 |
Soft Limit + Alert | 임계값 초과 시, 알람만 보내고 차단은 안 함 | 고객 서비스 끊기지 않음 |
Rate 모니터링 대시보드 | 요청량 추이를 시각화해 고객/내부가 직접 확인 | 문제 발생 전 예방 가능 |
API Key 기반 모니터링 | Key마다 사용량 추적 | 유입 경로/서비스별 분석 가능 |
계약 기반 할당량 | “1초 20req”, “일 100만건” 식으로 명시 | 계약 상한 넘기면 별도 협의 |
급증 방지 용 Soft Cap | 평소보다 10배 넘게 쓰면 자동 경고 및 스로틀링 예고 | 실수/버그 방지 |
Fallback 응답 | 예: “지금은 요청이 너무 많아, 데이터가 5초 지연되어 제공됩니다” | 데이터는 주되 품질만 낮춤 |
📍 차단보다 의사소통이 중요
Rate Limit이 걸려도 사용자에게 명확한 메시지, 조치 방안, 현재 상태가 제공된다면 신뢰는 유지됩니다.
실시간 증권 서비스라면 Rate Limit에 걸릴 경우, 응답에
Retry-After
,X-RateLimit-Remaining
,X-RateLimit-Reset
같은 힌트를 주는 것도 유용
🤔 실수나 급증을 수동으로 처리하면 되지 않냐?
장점: 트래픽이 작은 경우엔 운영 리스크 없이 깔끔
단점: 사고는 보통 새벽 2시에 난다... 운영자 없이 자동 방어체계가 없으면 “한참 나중에” 사고 감지
내부 백오피스에서라도 실시간 usage 모니터링은 꼭 필요
💡 결론
B2B 서비스에서는 Rate Limit은 적용하되, 부드럽고 유연하게
실시간성이 중요한 서비스일수록 차단보다 감시와 안내가 중요
수동 대응도 좋지만, 최소한 모니터링, 알람, 대시보드, 계약 기반 커뮤니케이션은 반드시 필요
그리고 정책 문서화는 꼭 필요: "무제한 아님"을 명시해두는 것만으로도 나중에 이슈를 방지할 수 있음