박종훈 기술블로그
close menu

K-DEVCON 시스템 디자인 스터디 시즌 2 기록 - 『대규모 시스템 설계 기초 2』 1~6장

시즌 1을 마치고 잠시 쉬었다가, 2025년 12월 4일부터 시즌 2를 시작했다. 교재는 『가상 면접 사례로 배우는 대규모 시스템 설계 기초 2』의 1~6장이었다.

1권이 “이런 시스템은 이렇게 설계한다”에 가까웠다면, 2권은 다루는 주제 하나하나가 더 깊고 좁아졌다. 기초 설계안 자체는 오히려 단순해서, 그 안에서 무엇을 더 고민해야 하는지에 시간을 많이 썼다.

1·2회차 후기는 K-DEVCON 운영자 계정으로 올라가 있지만 모두 내가 작성한 글이다.

회차별 기록

1회차 — 근접성 서비스

내 주변의 식당·시설·부동산을 찾는 위치 기반 서비스다. 기초 설계안이 워낙 단순해서, 이 장의 핵심은 지리 정보를 어떻게 효율적으로 색인하느냐였다.

  • 책은 GeoHash, Quadtree, 구글 S2를 소개한다. 구현은 달라도 아이디어는 “지도를 작은 영역으로 분할해 고속 검색이 가능하도록 색인한다”로 동일하다.
  • Lyft가 Redis 내장 GeoHash를 안 쓴 이유. Geospatial Indexing: The 10 Million QPS Redis Architecture Powering Lyft 발표를 정리해서 공유했다. Lyft는 초기에 MongoDB로 행정 구역 기반 서비스를 만들었는데, 경계에 걸치면 1km 떨어진 가까운 가게 대신 같은 구역 안의 5km 떨어진 가게를 반환하는 문제가 있었다. 그래서 Redis + GeoHash로 옮겼는데, 재미있는 건 Redis가 제공하는 GeoHash를 쓰지 않고 직접 구현했다는 점이다. 만료 기능이 없었기 때문이다. ZSet의 score에 timestamp를 넣고 특정 score 이하를 제거하는 방식으로 만료를 구현했다. 여기에 envoy를 활용한 redis proxy로 약 50개 클러스터, 750개 인스턴스에 데이터를 분산한다.
  • 구글 S2가 힐베르트 곡선으로 “1차원 색인화”를 한다는 표현이 이해하기 어려웠는데, 공간 채움 곡선으로 공간을 훑으며 셀마다 번호를 붙이면 2차원 위치를 1차원 인덱스로 바꿀 수 있고, 2차원에서 가까운 두 지점은 1차원 인덱스에서도 가까운 번호를 갖는다는 게 핵심이었다. Uber H3의 육각형 방식, R-Tree의 MBR 개념도 함께 훑었다.

K-DEVCON 원문 후기

2회차 — 주변 친구

지난 회차가 정적인 위치 정보였다면, 이번엔 동적인 위치 정보다. 높은 쓰기 부하와 실시간성이 과제가 된다.

  • 그런데 이 서비스, 요즘은 대부분 사라졌다. 텔레그램은 2024년, 페이스북은 2022년, 트위터(X)는 2019년에 종료했다. 텔레그램은 실제 사용 유저가 0.1% 미만이었고 봇과 사기꾼 이슈가 있었다고 밝혔다. 그럼에도 설계 연습으로는 의미가 있다고 봤다.
  • 책의 기초 설계안에 아쉬움이 남아 바꿔봤다. 모바일 OS 특성상 앱이 백그라운드로 가면 WebSocket 같은 지속 연결을 유지하기 어렵고, 위치 업데이트가 완전한 실시간일 필요도 없다. 그래서 WebSocket은 수신(Read) 전용으로 쓰고 내 위치 전송(Write)은 REST API로 분리하는 설계를 제안했다. 시즌 1 채팅 회차에서 본 Slack 사례와 같은 방향이다.

    주변 친구 서비스 변경된 설계안

  • SSE를 실제로 써본 사람을 드디어 만났다. 1권 스터디 때부터 SSE 이야기가 나왔지만 실사용자가 없어 아쉬웠는데, 이번엔 쓰고 계신 분이 있었다. ChatGPT·Gemini·Claude 같은 LLM 챗봇들이 답변을 스트리밍하는 데 SSE를 쓴다. 유실 걱정에 대해서는, 생각보다 유실이 잘 나지는 않지만 최종 생성 데이터를 한 번에 불러오는 API를 따로 제공해서 연결이 끊겨도 복구할 수 있게 한다고 했다.
  • Redis Pub/Sub은 왜 앞에 Proxy를 두지 않을까. 지난 회차에서 본 envoy redis proxy처럼 하면 될 것 같았는데, Redis Pub/Sub은 클러스터링을 해도 샤딩이 아니라 브로드캐스트 방식이라 Proxy가 의미가 없었다. Redis 7의 Sharded Pub/Sub은 이미 내부에 라우팅이 있어 역시 Proxy가 오버헤드다. 책은 서비스 디스커버리와 안정 해시로 확장한다.

K-DEVCON 원문 후기 → 관련 글: 안정 해시 설계하기

3회차 — 구글 맵

이 회차는 후기 글을 남기지 못했다.

4회차 — 분산 메시지 큐

  • 메시지 큐와 이벤트 스트리밍 플랫폼은 다르다. 흔히 카프카를 메시지 큐라고 부르지만 엄밀히는 이벤트 스트리밍 플랫폼이다. 메시지 큐는 배달에 집중해서 소비 순서를 보증하지 않고 소비된 메시지를 지운다(RabbitMQ). 이벤트 스트리밍 플랫폼은 저장 후 공유에 집중해서 순서를 보장하고 메시지를 보관하며 다른 소비자도 읽을 수 있다(Kafka).
  • 회전식 디스크가 느리다는 건 편견이다. 메시지 큐의 접근 패턴은 읽기·쓰기 모두 순차적이라 WAL(Write-Ahead Log)이 잘 맞는다. 디스크가 정말 느려지는 건 접근 패턴이 무작위일 때다. 랜덤 seek에 2ms가 드는 반면, 순차적으로 1MB를 읽는 데는 1ms도 걸리지 않는다.
  • 운영 관점 키워드들을 짚었다. 타임아웃(너무 짧아도 장애로 이어진다), 리밸런싱 중 소비 중단·중복, ISR, 전송 보장 수준 3가지. 그리고 카프카 파티션은 늘릴 수는 있어도 줄일 수 없다는 점, Consumer Lag이 장애의 전조라는 점, 병목이 CPU·메모리가 아니라 Disk I/O나 네트워크 대역폭일 수 있다는 점.
  • 메시지 지연 전송을 어떻게 구현할까. 책은 지연 전송 전용 큐와 계층적 타이밍 휠 두 가지를 소개한다. RocketMQ는 이 기능을 자체 지원한다고 한다.

이 회차는 서로 겪었던 실제 이슈를 가장 많이 나눈 시간이었다.

K-DEVCON 원문 후기

5~6회차 — 지표 모니터링, 광고 클릭 이벤트 집계 (完)

두 주제 모두 “데이터를 어떻게 수집할지”를 공통으로 다룬다. 기억에 남은 두 가지를 정리했다.

  • Gorilla 논문의 압축 기법. Gorilla: A Fast, Scalable, In-Memory Time Series Database(Facebook)에서 제시한 압축 컨셉은 이후 많은 시계열 DB에 영향을 줬다. 타임스탬프는 delta가 아니라 delta-of-delta로 저장한다. 모니터링 데이터는 보통 규칙적으로 들어오므로(예: 10초마다) delta-of-delta가 0이면 비트 하나만 저장하면 된다. 값은 XOR 압축을 쓴다. 이전 값과 XOR해서 0이면 비트 하나, 다르면 유효 비트 범위가 이전과 비슷한지에 따라 위치 정보를 재사용하거나 갱신한다. XOR 연산 개념은 알아도 실제 쓰이는 곳을 본 건 처음이라 재미있었다.
  • 분산 환경에서의 exactly-once, 세 가지 사례. Yelp는 집계 데이터에 카프카 오프셋으로 버전을 부여해서, 버전보다 높은 오프셋을 가진 데이터만 처리하는 방식으로 중복을 막았다. Flink는 2PC를 쓴다. JobManager가 체크포인트를 주도하며 배리어를 전달하고, 사전 커밋 중 하나라도 실패하면 전체를 롤백하며, 최종 커밋은 성공할 때까지 재시도한다. Uber 사례도 책에 소개되어 있다. 발표 중 “분산 시스템 설계의 주된 관심사는 실패(Failures)에 대비하는 것”이라는 표현이 인상 깊었다.

K-DEVCON 원문 후기

시즌 2를 마치며

서울로 올라와서 운영한 두 번째 스터디였다. 어떻게 해야 좋은 스터디가 될까 고민하면서, 내가 더 많이 학습해서 스터디원분들께 공유해드리려고 노력했다.

마무리하며 받은 후기 중 이런 이야기가 있었다.

같은 글을 읽고도 각자가 주목하는 지점이 달랐고, 그 과정에서 ‘이 글에서는 이런 고민까지 확장해볼 수 있겠구나’라는 생각을 자주 했습니다. 특히 제가 당연하게 넘겼던 부분에 대해 다른 스터디원들이 던지는 질문을 통해, 스스로의 사고가 얼마나 제한적이었는지를 돌아볼 수 있었습니다.

감사하게도 대부분의 분들이 다음 스터디에도 참여하고 싶다는 의사를 남겨주셔서, 시즌 3는 같은 멤버로 이어가게 됐다.

시리즈