67번의 스프린트를 돌아보며
67번째 스프린트를 마치고
멀티에이전트 시스템에 대한 호기심이 출발점이었습니다. "AI 에이전트 여러 명이 협업하면 혼자서도 서비스를 만들 수 있지 않을까?" 그 궁금증 하나로 시작한 프로젝트가 67번의 스프린트를 지나며 하나의 플랫폼이 됐습니다.
- 67개 스프린트
- 2,432개 테스트 케이스, branches 커버리지 99.51%
- 6개 마이크로서비스 + 1개 프론트엔드
- 34개 Identity API 엔드포인트
- 12개 AI 에이전트
숫자만 보면 깔끔해 보이지만, 이 뒤에는 결정과 실수, 그리고 수정의 반복이 있었습니다.
해보니까 보이는 것들
일찍 한 결정들
DB 분리 — Sprint 12에서 결정, Sprint 51에서 빛남. 서비스가 3개밖에 없던 시점에 데이터베이스를 3개(identity_db, problem_db, submission_db)로 나눴습니다. 솔직히 과하다 싶었죠. 그런데 Sprint 51에서 Gateway가 직접 접근하던 Identity DB를 완전히 분리하는 대규모 리팩토링을 할 때, 이미 DB가 나뉘어 있어서 마이그레이션이 훨씬 수월했습니다. 40스프린트 전의 "과한 설계"가 미래의 저를 구해줬습니다.
httpOnly Cookie — Sprint 8에서 결정, Sprint 65에서 빛남. OAuth를 붙이면서 JWT를 localStorage 대신 httpOnly 쿠키에 저장하기로 했습니다. XSS 공격 표면을 줄이기 위한 선택이었는데, Sprint 65에서 localStorage 관련 함수 11개를 제거할 때(+42줄 / -440줄) 진가를 발휘했습니다. 초기에 조금 더 손이 갔지만, 나중에 정리해야 할 빚이 없었습니다.
테스트 집착 — 2,432개, 99.51%. ADR-001(Sprint 51)처럼 34개 API를 새로 만들고 19개 파일을 전면 교체하는 작업을 할 수 있었던 건, 테스트가 안전망 역할을 해줬기 때문입니다. "이거 고치면 저쪽이 깨지지 않을까?"라는 불안 없이 과감하게 리팩토링할 수 있었습니다.
미룬 결정들
타입 동기화 자동화를 만들지 않은 것.
MSA에서 enum 하나를 추가하면 Identity 엔티티 → Gateway 타입 → Frontend api.ts → 컴포넌트까지 최소 5개 파일을 수동으로 바꿔야 했습니다. Sprint 64에서 FEEDBACK_RESOLVED enum을 Identity에만 추가하고 나머지를 전파하지 않아 Sprint 66까지 끌고 간 사고가 있었습니다. 코드 생성기나 공유 패키지를 일찍 도입했으면 이 삽질은 없었을 거예요.
모니터링 알림 채널을 늦게 연결한 것. Prometheus와 alertmanager는 진작 설정해놨으면서, 실제로 사람에게 알림이 가는 Discord 채널은 Sprint 63까지 연결하지 않았습니다. 도구가 있다는 것과 도구가 동작한다는 것은 다른 문제였습니다.
디테일에서 배운 것들
Sprint 62 — 당연한 버그를 못 본 이유. 관리자 페이지의 피드백 필터를 클라이언트에서 처리하고 있었습니다. 서버 페이지네이션(20건/페이지)과 조합하면 어떻게 될까요? 현재 페이지의 20건 안에서만 필터링되고, 다른 페이지의 데이터는 보이지 않습니다. 당연한 버그인데, 만들 때는 인지하지 못했어요. 서버 사이드 필터로 전환하고 전체 counts를 응답에 포함시켜야 했습니다. 8커밋, 17파일 변경.
Sprint 63~64 — Discord 알림의 함정. 코드는 완성했고, Secret도 만들었고, 소스 레포 매니페스트도 작성했습니다. 그런데 알림이 오지 않았습니다. 원인은 aether-gitops 레포의 env 블록을 반영하지 않은 것. ArgoCD가 동기화하는 건 GitOps 레포의 매니페스트인데, 소스 레포만 수정하고 GitOps 레포를 잊어버린 겁니다. 이걸 수정하면서 enum 전파 누락까지 겹쳐, 결국 3-tier(Identity → Gateway → Frontend) 전체를 Sprint 66에서 재동기화해야 했습니다.
두 에피소드의 공통점은 같습니다. 부분만 보면 맞는데, 전체를 보면 빠진 게 있었다는 것.
숫자로 보는 AlgoSu
코더에서 빌더로
돌아보면, 매 스프린트가 새로운 기술과의 첫 만남이었습니다. Saga 패턴을 처음 접했을 때의 막막함, RabbitMQ 메시지가 소비되지 않아서 새벽까지 로그를 뒤졌던 밤, ArgoCD 동기화가 처음 성공했을 때의 안도감. 이런 순간들이 67번 반복되면서 저도 모르게 변해 있었습니다.
처음에는 "이 기능을 어떻게 구현하지?"가 전부였습니다. 코드를 짜는 사람, 코더였죠. 스프린트가 쌓이면서 질문이 달라졌습니다. "이 결정이 20스프린트 뒤에도 괜찮을까?", "이 구조에서 장애가 나면 어디서 잡지?", "이 자동화가 정말 동작하는지 어떻게 증명하지?" 코드를 짜는 게 아니라 시스템을 만드는 사람, 빌더가 되어가고 있었습니다.
토큰과 시간을 갈아넣으며 생긴 것들이 있습니다.
Sprint 68은 아직 비어 있습니다. 하지만 스프린트는 계속될 겁니다. 호기심으로 시작한 프로젝트가 플랫폼이 됐고, 코더가 빌더가 됐으니까요. 다음 스프린트에서도 새로운 기술 앞에서 막막해지겠지만, 67번의 경험이 말해줍니다 — 해보면 됩니다.