K-DEVCON 시스템 디자인 스터디 시즌 1 기록 - 『대규모 시스템 설계 기초』 8주
2025년 9월부터 11월까지 K-DEVCON에서 시스템 디자인 스터디를 운영했다. 교재는 『가상 면접 사례로 배우는 대규모 시스템 설계 기초』(1권)였고, 총 8회차로 진행했다.
매주 후기를 K-DEVCON 커뮤니티에 남겼는데, 커뮤니티 글은 커뮤니티대로 두고 블로그에는 각 회차에서 무엇을 다뤘는지 정리해두려고 한다. 전체 내용은 링크된 원문에 있다.
시즌 1 후기 글들은 K-DEVCON
운영자계정으로 올라가 있지만 모두 내가 작성한 글이다.
스터디를 어떻게 운영했나
이 스터디에서 가장 신경 쓴 건 책을 요약하는 자리로 만들지 않는 것이었다. 첫 시간에 참여자분들께 “책에 없는 내용이라도 주제와 관련 있다고 생각되면 자유롭게 공유해달라, 다른 의견이 있으면 서로 피드백을 달라”고 요청드렸다.
그래서 매주 나는 책의 참고문헌을 따라가 원 논문이나 발표 자료를 읽고 정리해서 공유했고, 참여자분들은 각자 회사에서 겪은 실제 사례를 가져왔다. 아래 정리한 내용 중 상당수는 책에 없는 이야기들이다.
회차별 기록
1회차 — 시스템 디자인의 기초
책은 시스템 디자인 면접을 “두 명의 동료가 모호한 문제를 풀기 위해 협력하여 해결책을 찾아내는 과정에 대한 시뮬레이션”이라고 설명한다. 첫 시간이라 트레이드오프를 어떻게 판단할 것인가에 대한 이야기를 많이 나눴다.
기억에 남는 세 가지:
- 정족수 합의 프로토콜을 Amazon Aurora로 검증해보기. 책은
W + R > N이면 강한 일관성이 보장된다고만 설명하고 넘어간다. Aurora는 3개 AZ × 2개 노드 = 6개 스토리지 노드를W=4, R=3으로 운영하는데, 이게 정말 강한 일관성을 만족하는지 노드가 다운되고 복구되는 시나리오를 그려가며 확인해봤다.W=3, R=3이면 왜 깨지는지도 함께 짚었다. - 블룸필터는 왜 해시 함수를 여러 개 쓰는가. 스터디원 한 분의 질문이었다. 답은 해시 충돌로 인한 거짓 양성을 낮추기 위해서인데, 해시를 늘릴수록 공간이 빨리 포화되므로 균형이 필요하다.
- 처리율 제한 장치의 실무 적용 사례. 각자 회사에서 어떻게 쓰고 있는지 들어봤다. 배치로 토큰을 채우는 방식과 마지막 리필 시각을 기준으로 계산하는 방식이 나뉘었다. OPEN API 악용 방지 사례, 그리고 GraphQL은 사용자가 필요한 필드를 직접 명시하기 때문에 요청을 엔티티 단위로 쪼개어 계산한다는 이야기가 흥미로웠다.
→ K-DEVCON 원문 후기 → 관련 글: 안정 해시 설계하기, API 제한 설정하기 - 처리율 제한 장치의 설계, 키-값 저장소 설계
2회차 — 유일 ID 생성기, URL 단축기
- Flickr의 티켓 서버. 중앙화한 전용 DB의 Auto Increment로 전역 고유 ID를 발급하되, SPoF를 피하려고 두 대를 운영하며 각각 홀수/짝수 ID를 담당하게 했다. 두 서버의 데이터가 몇십만 개씩 차이 나도 운영상 문제가 없었다는 대목이 실제로 굴려본 사람만 할 수 있는 이야기라 재미있었다.
- “UUID는 성능이 나쁘다”는 통념 다시 보기. 지금 쓰는 UUID가 몇 버전인지부터 확인해볼 만하다. v1·v4는 순차적이지 않아 PK로 쓰면 인덱스 효율이 떨어지지만, v6·v7·ULID는 그 문제를 보완했다. 그렇다고 v4가 나쁜 것도 아닌 게, 강한 랜덤성은 악의적 접근을 막는 데 유리하다. 내부 ID는 순차, 외부 공개 ID는 랜덤으로 가져가는 하이브리드 사례도 나왔다.
- Snowflake ID에 남는 1비트의 정체. 다들 궁금해했던 부분이다. 스터디 때는 reserved가 아닐까 추측했는데, 찾아보니 JVM이 unsigned를 지원하지 않기 때문이었다. 그 밖에 시계가 뒤로 돌아갈 수 있다는 것(그리고 NTP의 slew 보정), 서버 내 순서는 알아도 글로벌 순서는 모른다는 한계도 짚었다. Twitter·Discord·Instagram·Mastodon처럼 엄밀한 순서가 필요 없는 SNS 계열에서 주로 쓰인다.
- URL 단축기는 책 내용이 충분해서, 짧은 URL의 실익(SMS 비용 절감)과 순차 URL의 예측 가능성 문제, 만료 정책 같은 운영·보안 관점만 다뤘다.
→ K-DEVCON 원문 후기 → 관련 글: 분산 시스템을 위한 유일 ID 생성기 설계, URL 단축기 설계
3회차 — 웹 크롤러, 알림 시스템
- 크롤러에 왜 별도의 DNS 변환기를 두는가. OS 기본 Resolver는 기본적으로 동기적이라 블로킹이 발생하고, 한 VM에서 여러 크롤러가 돌면 DNS가 병목이 된다. 크롤러 특성에 맞는 TTL 정책을 직접 설계할 수 있다는 점, 조회 결과를 여러 크롤러가 공유할 수 있다는 점도 이유가 된다.
- 예의(politeness)와 robots.txt. 마침 AI 학습 데이터 수집 과정에서 robots.txt를 우회한 사례가 논란이던 시기여서, Cloudflare가 Perplexity 크롤러를 지적한 글을 함께 봤다. 기술적 구현만큼 윤리적 고려도 설계의 일부라는 이야기.
- 알림 시스템의 무서운 점은 회수가 안 된다는 것. 한 스터디원은 20~30만 유저에게 이벤트 시작 알림을 보내야 했는데, 서드파티 Rate Limit 때문에 나눠 보내다가 일부 고객에게는 이벤트가 끝난 뒤에 알림이 도착했다고 한다. 토큰 단위가 어렵다면 토픽 단위로 보낼 수 있는지 확인해보는 것도 전략이다.
- Kafka의 “정확히 한 번”은 어디까지인가. exactly-once는 Kafka 트랜잭션이 보장하는 것이고, 그 범위는 어디까지나 “카프카 내부에서 한 번”이다. 카프카 이벤트가 한 번 실행되더라도 그 안에서 외부 시스템과의 통신은 여러 번 일어날 수 있다. 멱등성, 아웃박스 패턴도 함께 다뤘다.
- 법적으로 광고 불가 시간대가 있고 어기면 과태료로 이어질 수 있다는, 개발자가 놓치기 쉬운 이야기도 나왔다.
→ K-DEVCON 원문 후기 → 관련 글: 웹 크롤러 설계, 알림 시스템 설계
4회차 — 뉴스피드
책의 설계를 따라간 뒤 팬아웃(fan-out)과 팬인(fan-in)을 중심으로 확장했다.
- 책의
<포스팅 id, 사용자 id>매핑 테이블은 실제로 뭘까. Twitter의 Timelines at Scale 발표를 보면 각 row가 사용자 한 명의 타임라인이고 그 뒤로 트윗이 붙는 구조다. 책이 이걸 테이블처럼 그린 건 한 유저의 리스트를 세로로 펼쳐 설명한 것으로 보인다. 각 아이템에user_id까지 넣어둔 이유는, 타임라인을 받은 직후 작성자 정보를 병렬로 조회하기 위해서다.tweet_id만 있으면 트윗 조회 → 작성자 조회로 이어져 읽기가 느려진다. - 쓰기 시점 팬아웃의 한계. 비활성 사용자에게도 캐시를 쌓아둬야 하고, 팔로워가 많은 사람이 글을 쓰면 쓰기 부하가 폭증한다. 그래서 읽기 시점 팬아웃과 결합하게 된다.
- 그리고 Timeline Mixer로. 결합 방식으로도 광고·친구 추천·인기 급상승 같은 새 요구가 들어올 때마다 구조가 복잡해진다. Twitter는 How We Learned to Stop Worrying and Love Fan-In at Twitter에서 여러 마이크로서비스의 결과를 우선순위에 따라 합치는 Timeline Mixer를 소개한다. 이 발표는 따로 볼 만하다.
→ K-DEVCON 원문 후기 → 관련 글: 뉴스 피드 시스템 설계
5회차 — 채팅 시스템
이번 회차는 Slack 사례를 조사해서 공유했다. 서비스가 점진적으로 개선되어 온 과정이 그대로 드러나서 재미있었다.
- Slack은 원래 게임 회사였다. 그래서 초기 설계도 게임처럼 시작할 때 대부분의 데이터를 한 번에 로드하는 구조였다. 워크스페이스 사용자가 200명을 넘어가면서 초기 로딩 데이터가 감당이 안 되기 시작했고, 필수 데이터만 먼저 받고 나머지는 API로 가져오는 구조로 바꿨다.
- WebSocket은 유지 비용이 크다. 특히 모바일은 네트워크가 계속 바뀐다. Slack은 메시지 발송을 HTTP API로 분리하고, broadcast에 가깝던 구조를 pub/sub으로 바꿨다. 조사하면서 받은 인상은 WebSocket은 최소한의 실시간 이벤트 수신에만 쓰고 실제 처리는 HTTP API로 분리하려 한다는 것이었다.
- Redis 큐가 꽉 찼던 사고. 초기에는 Redis를 메시지 큐로 썼는데 속도를 조절할 방법이 없었다. 한 번은 과도한 produce로 병목이 생겨 Redis 메모리가 꽉 찼고, 데이터를 지우는 데도 여유 공간이 필요해서 쉽게 풀리지 않았다. 이후 Redis를 Kafka로 교체하는 대신 Redis 앞에 Kafka를 추가했다. 레이어가 늘어 복잡해 보이지만 기존 enqueue/dequeue 인터페이스를 그대로 유지할 수 있었다는 게 컸을 것이다.
- Geo-distributed edge 서버 도입, 카카오의 풀 메시 릴레이 서버(서버당 약 50만 세션), WebRTC로 프로젝트를 해본 분의 경험(표준 스펙만으로는 상용 수준이 안 나온다) 이야기도 나왔다.
→ K-DEVCON 원문 후기 → 관련 글: 채팅 시스템 설계
6회차 — 검색어 자동완성
면접 자료들은 보통 트라이(Trie)로 설계하고 끝나지만, 실제로 만든다면 ElasticSearch를 쓸 것이다. 그래서 그 기반인 Lucene 쪽으로 파고들었다. 배경지식이 많이 필요해서 쉽지만은 않았던 회차다.
- Lucene은 Trie 대신 FST(finite-state transducer)를 쓴다. Trie 기반 구조를 압축하고 상태 전이를 공유해서 대규모 단어 사전을 효율적으로 표현한다. Trie와 비슷하지만 prefix를 공유한다는 차이가 있다.
- Fuzzy search에서 사전의 모든 단어와 거리를 재야 할까. Fast approximate search in large dictionaries(Mihov & Schulz) 논문을 조금 읽어봤다. 입력값과 허용 오차
k로 비결정적 오토마타를 만들고, 이 Levenshtein 오토마타와 단어 사전(FST)을 함께 탐색해 나가면서 FST에서 허용되지 않는 경로는 시작 단계에서 잘라낸다. Lucene이k를 2까지로 제한하는 것도 그 이상은 성능 영향이 크기 때문이다.
→ K-DEVCON 원문 후기 → 관련 글: 검색어 자동완성 시스템
7회차 — 유튜브
- Facebook의 SVE 논문. 초기 아키텍처는 monolithic 인코더라 영상 전체가 처리될 때까지 기다려야 했고 확장성에 한계가 있었다. 이를 DAG 기반 파이프라인으로 바꾸면서 업로드 시점부터 파일을 쪼개어 처리하게 됐다. 재시도 전략이 인상적인데, 로컬에서 최대 2회 재시도하고 지속 실패 시 다른 워커로 최대 6회 재스케줄링해서 최대 21회 실행, 성공률 99.995%를 달성했다고 한다.
- GOP와 세그먼트. 영상 압축의 기본 단위인 GOP(I·P·B 프레임 조합)는 매우 작은 단위라, 책 설명대로 GOP 자체를 업로드하면 업로드 횟수가 지나치게 많아진다. 논문에서는 클라이언트에서 GOP로 구성된 세그먼트로 분할해 업로드한다. 세그먼트는 독립적인 작은 동영상이라 병렬 처리가 가능하다.
- 넷플릭스 캡처가 검게 나오는 이유. 브라우저 기능인 줄 알았는데 HDCP라는 DRM 기술이고, 그래픽 카드·모니터 같은 하드웨어 수준에서 지원되는 것이었다.
8회차 — 구글 드라이브 (完)

책은 ‘클라우드 저장소 → 로드밸런서’로 표현하는데, ‘블록 저장소 서버 → 로드밸런서’가 더 적절한 연결로 보여 위 그림에서는 수정했다.
- Dropbox의 블록 크기 4MB. 왜 4MB냐는 질문에 대한 답이 “그냥 정해봤는데 꽤 잘 동작해서 바꿀 생각을 안 했다”였다. 서로 다른 사용자가 같은 파일을 올리면 같은 블록에 매핑해서 실제 저장량을 줄인다는 점도 재미있었다.
- 롱폴링의 실용적인 용도. 롱폴링을 직접 써본 적이 없어서 스터디원들께 여쭤봤는데, Amazon SQS가 요청당 과금이라 롱폴링으로 요청 횟수를 줄여 비용을 아끼고 있다는 사례를 들을 수 있었다.
- 차분 동기화(Differential Synchronization). 책은 충돌을 first write win으로 간단히 처리하고 넘어간다(참고로 DynamoDB는 반대로 last write win). Google Docs 같은 실시간 서비스는 어떻게 할까 싶어 논문을 읽어봤다. 락 → edit-based → three-way merge로 범위를 넓혀가며 설명하는데, 최종적으로 제안하는 차분 동기화는 클라이언트/서버가 각각 shadow를 들고 diff와 patch를 주고받는 대칭적 구조다. 여기에 네트워크 장애로 ack를 못 받는 상황까지 대비하려고 backup shadow를 추가한다.
- 그리고 오버엔지니어링. How We’ve Scaled Dropbox에서 가장 기억에 남은 대목. Dropbox의 최초 설계는 단일 서버였고, 한계에 부딪힐 때마다 순차적으로 개선해 나갔다. 이건 창업자들이 더 나은 구조를 몰라서가 아니라 가장 중요한 것(서비스의 존재 증명)에 집중하기 위한 의도된 선택이었다고 한다.
시즌 1을 마치며
계획했던 8회를 모두 마쳤다.
운영자 입장에서 가장 좋았던 건, 매주 책의 참고문헌을 따라 원문을 읽게 됐다는 점이다. 혼자 읽었다면 “그렇구나” 하고 넘어갔을 정족수 프로토콜이나 GOP 같은 개념을, 공유할 자료를 만들어야 한다는 이유로 한 단계 더 파고들 수 있었다.
참여자분들이 각자 도메인에서 겪은 사례를 가져와주신 것도 컸다. 최저가 보상 서비스가 실제로 크롤링으로 구현된다는 이야기, 이벤트가 끝난 뒤 알림이 도착한 사고, SQS 비용 때문에 롱폴링을 쓰는 이야기 같은 건 책에서는 절대 나올 수 없는 내용이었다.
여담으로, 이 책은 2023년에 혼자 읽으며 블로그에 챕터별로 정리한 적이 있다. 같은 책을 2년 만에 여러 사람과 다시 읽으니 완전히 다른 책이었다.
시즌 2는 『대규모 시스템 설계 기초 2』로 이어진다.
시리즈
- 시즌 1 — 『대규모 시스템 설계 기초』 8주 (이 글)
- 시즌 2 — 『대규모 시스템 설계 기초 2』 1~6장
- 시즌 3 — 『대규모 시스템 설계 기초 2』 7~13장