지루한 코드로 10억 웹 요청을 처리한 경험
(notes.billmill.org)- 미국 메디케어 건강보험 플랜 비교 시스템을 재구축하며, 검증된 기술(Postgres, golang, React 등) 로만 구성된 단순한 구조로 10억 건 이상의 웹 요청을 안정적으로 처리한 경험을 공유함
- 단순함과 안정성을 목표로 아키텍처를 설계해, 평균 10ms 이하 응답 속도와 아주 낮은 장애율을 달성함
- 혁신(innovation token) 은 핵심적인 구조 분리(3개 대형 모듈, gRPC 통신)에만 최소한으로 적용하고, 그 외에는 모두 지루하지만 신뢰할 수 있는 방법론을 선택함
- DB 스키마 관리, ETL 파이프라인, 테스트, 로깅, 문서화, CLI 도구까지 모든 운영 요소를 반복 가능하고 단순한 방식으로 구축해 팀 전체가 쉽게 이해·유지보수할 수 있는 시스템을 완성함
- 지속적 품질관리와 강한 팀워크가 대규모 정부 프로젝트에서도 통한다는 사례를 생생히 보여줌
Serving a billion web requests with boring code
High level
- 2년 반에 걸쳐 미국 정부의 메디케어 플랜 비교·구매 웹사이트를 리드 개발함
- 일 평균 500만 API 요청을 처리, 평균 응답 속도는 10ms 미만, 95% 요청은 100ms 이하로 유지
- 장애 발생률이 매우 낮아 실제 엔지니어가 새벽에 호출된 사례는 한 손에 꼽을 정도
- Postgres, golang, React 등 누구나 이해할 수 있는 검증된 기술로만 구성해, 꾸준히 안정적인 시스템을 구축함
Boring über alles
- 최우선 원칙은 '지루하고 검증된 기술' 만을 우선하는 것(** Choose Boring Technology**)
- 혁신 시도는 꼭 필요한 곳에서만 innovation token을 아껴서 사용
- 복잡하고 화려한 솔루션보다 안정적이고 명확한 기술과 프로세스를 선호
The boring bits
- Postgres: 데이터 저장의 핵심, 신뢰성과 확장성을 모두 만족. 복잡한 검색(페이시티드 검색 등)도 Postgres로 해결
- golang: 빌드와 배포가 빠르고, 바이너리 산출물이 명확함. 에러 핸들링이 직관적이고, 새로운 팀원도 쉽게 적응 가능
-
React: SPA 프레임워크 중 가장 검증되어 있고, 팀원들이 이미 익숙했음. 접근성과 다양한 기기 지원도 중요한 고려 요소였음
- 장기적으로는 번들 크기와 속도 저하 이슈가 발생했지만, 당시 상황에서는 시간 내에 결과를 내기 위한 최적의 선택이었음
The innovation tokens
-
Modular backend: 전체 백엔드를 마이크로서비스도, 모놀리식도 아닌 3개의 대형 모듈(druginfo, planinfo, beneinfo)로 구성
- 각 모듈은 별도의 Postgres DB를 사용하고, 데이터 공유는 오직 gRPC를 통해서만 이뤄짐
-
druginfo
: 약국, 보험, 포장 등 조합이 기하급수적으로 늘어나는 약가 정보를 매우 정교하게 인덱싱하고, 복잡한 사전처리와 성능 최적화가 필요 -
planinfo
: 매일 새로운 CMS 데이터를 수신해, DB 전체를 새로 만들어 사용함(불변성 유지) -
beneinfo
: 실제 가입자 정보를 보관하는 유일한 부분으로, 민감한 PII(개인정보)는 최소한만 저장. 데이터 유출 리스크 최소화를 위해 설계와 운영에 신경 씀
-
gRPC: 모듈간 통신 인터페이스를 코드로 명확히 정의할 수 있는 장점. 자동화 도구와 연동성이 뛰어남
- 단, 빌드·툴링·디버깅은 복잡하고, JSON API 대비 직관성이 떨어지는 단점도 경험
- grpc-gateway를 통해 웹클라이언트 지원 및 대량 트래픽을 무리 없이 처리함
Strict backwards compatibility
-
API 및 데이터베이스의 하위 호환성 유지를 엄격하게 지킴
- 공개 API의 필드는 절대 삭제하지 않고, 보안 문제가 있지 않는 한 평생 유지
- DB 컬럼도 추가는 자유롭지만 삭제는 여러 단계 검증(참조 제거→몇 주 대기→실제 삭제) 절차를 거침
- 이 규율이 높은 변화 속도와 안정적인 배포·운영의 핵심 기반이 됨
Faceted search
-
ElasticSearch 대신 Postgres만으로 페이시티드 검색 구현
- well-indexed plan 테이블에 조건을 조합하는 250줄 함수 하나로 모든 검색 로직을 처리
- 비즈니스 요구에 집중, 불필요한 복잡성 없이 단순하게 해결함
Database
-
creation
- DB 스키마를 숫자가 붙은 .sql 파일로 관리, 순서대로 로딩하여 신뢰성 보장
-
planinfo
/beneinfo
DB는 매일 재생성, 마이그레이션 필요 없음. 버전 불일치 등 설정 오류 시 앱 자체를 아예 시작하지 않도록 설계
-
ETL
- 데이터 소스별 셸 스크립트로 S3에 적재 → cron으로 EC2 인스턴스가 최신 ETL 코드/데이터 가져와 신규 RDS DB 생성
- Postgres의 COPY 구문을 적극 활용, INSERT 대신 대량 데이터 적재를 효율적으로 처리
- 매일 2~4시간이면 수억 행 데이터를 새 DB로 전환 가능
-
models
- xo 라이브러리로 DB 모델 자동 생성, 커스텀 템플릿으로 팀에 맞는 코드 생성
-
testing
- 가장 큰 실수는 sqlmock을 활용한 테스트를 과하게 만들어 데이터가 자주 바뀌는 상황에서 유지보수가 매우 번거로움
- 실제 불변 DB라면 실DB에 대한 테스트가 더 효율적이었을 것
-
Local database for development
- 각 테이블의 부분 데이터를 자동 생성하는 스크립트로, 개발자별로 작은 로컬 DB로 실제 데이터 기반 테스트와 개발 가능
- DB가 커지기 전에 이런 도구를 마련하면 전체 팀 개발 효율이 극대화됨
Miscellaneous tooling
- 각종 운영·관측 자동화를 위해 CLI 도구를 셸 스크립트로 구현, 모든 유틸리티 기능을 하나로 모아 관리
- splunk 로그를 Slack 명령어로 바로 그래프로 시각화하는 등 현장 중심의 도구를 적극 개발·활용
Logging
- 요청 진입 시점에 request id를 생성, 그 id가 모든 로그 컨텍스트에 붙어서 어디서든 추적 가능
- zerolog로 안전하고 체계적인 로깅 설계
- 시스템 입구·퇴출, 예외 상황 등 중요 시점마다 필수 로그 남기기
Documentation
- GitHub markdown 문서를 sphinx-book-theme으로 변환해 위키북으로 운영
- 팀원 모두가 적극적으로 문서화에 기여, 한 곳에서 모든 시스템 문서를 찾을 수 있도록 함
- 뛰어난 문서화 문화가 팀의 성장과 유지보수, 신입 온보딩 효율을 크게 높임
Runtime integrations
- 클라이언트의 성능 저하 요청(분석 스크립트 삽입 등)은 최대한 설득해 최소화
- 쿼리도 브라우저 런타임이 아닌 빌드타임 처리로 전환해, 서비스 성능을 유지
- 실제론 고객 요청을 모두 막지는 못해, 일부는 성능 저하로 이어졌음
And more
- 기술 이외에도 긍정적이고 협력적인 팀 분위기와 강한 동기 부여가 대규모 시스템 성공의 진짜 원동력임을 강조
- 사소하지만 중요한 실무적 선택과 꾸준한 품질 관리의 힘을 실감한 사례였음
외국에선 그게 지루한 스택인 것 같습니다.
실제로 go는 뭐 그냥 웹서버 만드는데 가장 쉬운 선택이고..
뭐 rust fp쪽 언어 이런걸로 개발해야 지루하지 않다고 하는 것 같네요.
golang, React 전부 새 시대의 지루한(boring) 엔터프라이즈 코딩 언어라고 생각합니다.
boring -> 지루한 으로 100% 맞게 번역되는 게 아니다보니 한국 독자들에게는 뉘앙스가 제대로 전달되지 않는 거 같아 보이네요.
이정도는 그렇게 지루해보이지만은 않은 스택으로 보이네요. 정말 지루하다면 java 1.8 이하버전이나 vb 정도는 나와야하지 않을까... 하는 불충한 생각이 듭니다
The nice thing about boringness (so constrained) is that the capabilities of these things are well understood. But more importantly, their failure modes are well understood.
원문에 boring 과 관련된 링크가 있는데, 내용을 보면 boring = 너무 익숙한이라는 의미인 것 같습니다.
experienced, verified, skillful 같은 더 적절한 단어가 있는데 굳이 boring 을 쓴건 어그로를 끌 저의가 있는것 같네요