주요 컨텐츠로 이동
파트너

Consort를 소개합니다: 브랜칭 데이터베이스 기반의 테스트 주도 개발(TDD)

Databricks Field Engineering에서 개발한, 실제 브랜칭 데이터베이스에서의 테스트 주도 개발을 위한 오픈 소스 에이전틱 프레임워크입니다.

작성자: Kevin Hartman

  • 데이터베이스 브랜칭으로 무엇이 달라질까요
  • 데이터베이스 브랜칭을 활용한 에이전트 기반 개발 워크플로
  • Consort를 직접 시도해 보는 방법

지난 25년 동안 저는 제가 배우며 자란 관행들을 기반으로 소프트웨어를 구축해 왔습니다. 켄트 벡(Kent Beck)의 TDD, 마틴 파울러(Martin Fowler)의 리팩터링(Refactoring), 엉클 밥(Uncle Bob)의 클린 코드(Clean Code), 제즈 험블(Jez Humble)과 데이브 팔리(Dave Farley)의 지속적 인도(Continuous Delivery), 프라모드 사달라게(Pramod Sadalage)와 스콧 앰블러(Scott Ambler)의 진화적 데이터베이스 설계(Evolutionary Database Design)가 바로 그것입니다.

현대의 소프트웨어 개발 관행에서는 코드를 브랜치할 수 있고, 환경은 컨테이너화되며, 인프라는 코드가 됩니다. 하지만 스택 중 단 한 부분, 바로 데이터베이스는 결코 이에 동참하지 못했습니다. 데이터베이스는 경직된 모놀리스로 그 자리에 머물러 있었고, 이 때문에 우리는 실제 데이터베이스 대신 모의 객체(mock)를 사용하거나, 스테이징 환경을 공유하고, 스키마 변경을 조심스러운 의식처럼 치르는 등 많은 임시방편을 받아들여야 했습니다.

저는 한동안 소프트웨어 개발 팀들과 Lakebase Postgres에 대해, 그리고 이것이 우리가 기존에 하던 방식을 어떻게 바꾸는지에 대해 이야기해 왔습니다. 아마 제가 코드를 브랜치하는 것처럼 데이터베이스도 브랜치하세요.라고 말하는 것을 들으셨을지도 모릅니다. 하지만 이것이 실제로는 무엇을 의미하며, 어떻게 적용해야 할까요?

데이터베이스 브랜칭으로 무엇이 달라질까요

통합 테스트가 이너 루프(inner loop)로 돌아옵니다

실제 데이터베이스를 대상으로 테스트하는 것은 과거에 아우터 루프(outer-loop)의 영역이었습니다. 데이터베이스를 띄우고, 스키마와 데이터를 시딩하고, 스키마 버전을 관리하고, 이를 단위 테스트마다 수행하는 것은 비용이 너무 많이 들었기 때문에 저는 그렇게 하지 않았습니다. 아무도 하지 않았죠. 대신 우리는 모의 객체(mock)를 대상으로 단위 테스트를 작성했습니다. 모의 객체가 좋아서가 아니라, 이너 루프에서 실제 데이터베이스를 사용하는 것이 불가능했기 때문입니다. 그리고 우리는 모의 객체가 시간이 지남에 따라 실제와 달라진다는 것을 알고 있습니다. 모의 객체는 데이터베이스의 실제 동작 방식과 어긋나게 되며, 이를 유지보수할수록 검증하는 실제 동작은 줄어들게 됩니다.

Copy-on-write 브랜칭은 모의 객체가 존재해야 할 이유를 없애줍니다. 데이터 크기에 관계없이 거의 일정한 시간 내에 실제 데이터베이스의 격리된 브랜치를 생성할 수 있습니다. 제가 TDD 방식으로 작성하는 첫 번째 테스트는 가짜가 아닌 실제 데이터의 라이브 브랜치를 대상으로 실행됩니다. 파괴적인 테스트를 실행하고, 모든 것을 날려버리고, 스키마를 망가뜨려도 팀의 다른 구성원들이 작업 중인 부분에는 전혀 영향을 주지 않습니다. 저만의 브랜치이니까요. 작업이 끝나면 그냥 버리면 됩니다.

코드와 스키마가 하나로 배포됩니다

이제 스키마가 버전 관리되는 마이그레이션(스택에 따라 Alembic, Flyway 또는 Knex)으로 이동하기 때문에, 스키마 변경 사항과 이에 의존하는 코드가 하나의 단위로 함께 움직입니다. 데이터가 아닌 스키마를 병합하면 코드가 이에 맞춰 정렬됩니다. 제가 서로 다른 팀의 엔지니어 두 명에게 이것을 보여주었을 때, 두 사람 모두 똑같은 표현을 썼습니다. 바로 'Data CD'였습니다.

새벽 2시의 프로덕션 장애를 발생하기 전에 잡아냅니다

프로덕션에서 무언가 고장 나는 것은 대개 새벽 2시이며, 여러분은 무엇이 바뀌었는지 역공학(reverse-engineering)으로 찾아내야 합니다. 브랜칭은 이 타이밍을 뒤집습니다. 모든 풀 리퀘스트(pull request) 시점과 병합(merge) 시점에 대상 환경에서 신선한 데이터베이스 브랜치를 생성하고, 마이그레이션과 전체 테스트 제품군을 실행하여 배포되기 전에 회귀(regression)나 충돌을 찾아냅니다. 스키마 변경 사항은 마이그레이션으로서 풀 리퀘스트에 포함되므로, DBA는 다운스트림 대기열의 티켓이 아니라 코드 소유자로서 그곳에서 바로 검토할 수 있습니다.

스키마 변경 사항을 반영(promote)하는 것은 과거에는 매우 위험한 의식이었습니다. 이제는 운영(prod) 환경을 브랜치하고, 변경 사항을 적용하고, 격리된 상태에서 테스트한 후 반영하면 됩니다. 일반적인 클라우드 Postgres 설정에서 이와 동일한 작업을 수행하려면 취약한 DevOps 스크립팅이 잔뜩 필요하고 소중한 인적 시간을 낭비하게 됩니다.

이 중 어느 것도 그냥 켜기만 하면 되는 데이터베이스 기능이 아닙니다. 이는 까다로운 테스트가 수행되는 시점을 실제로 코드를 작성하는 루프의 맨 왼쪽으로 이동시키는 전환입니다.

데이터베이스 브랜칭을 활용한 에이전트 기반(agentic) 개발 프레임워크

이제 브랜칭을 위한 인프라가 갖춰졌으니, 논의에서 빠져 있었던 것은 이를 실제 개발 루프에서 작동하게 만드는 무언가였습니다. 그것이 제가 Consort를 만든 이유입니다.

Consort는 Lakebase 브랜칭을 테스트 주도 빌드의 기반으로 사용하는 오픈 소스 에이전트 기반 개발 프레임워크입니다. 스크럼(Scrum)을 아신다면 그 개념이 이름에 담겨 있음을 알 수 있습니다. 팀의 각 역할(제품 소유자, 사양 작성자, 아키텍트 검토자, DBA, 테스트 전략가, 내비게이터/드라이버 쌍)이 에이전트가 됩니다. 이들은 지휘자(conductor)의 인도에 따라 함께 연주하며, 궁극적으로 하나의 합주단(consort)으로서 협업합니다.

작업은 두 가지 경로로 진행됩니다. 의도가 합의되고 고정되는 사양 우선(spec-first) 설계 경로와, 실제 데이터베이스의 라이브 브랜치를 대상으로 전체 red/green/refactor 주기를 실행하는 빌드 경로입니다. 에이전트가 코드를 작성할 때 이 프로세스가 유기적으로 유지되도록 몇 가지 구성 요소가 이를 뒷받침합니다.

  • 에이전트가 부딪힐 수 있는 실제 경계(edge)를 얻게 됩니다. 에이전트에게 구체적인 것을 제공하면 코드가 작동하지 않을 때를 감지할 수 있기 때문에 문제를 스스로 해결합니다. 밀폐되고 모의 처리된(mocked) 테스트를 제공하면, 통과하는 것처럼 보이지만 실제 데이터베이스에서 실행하는 순간 무너지는 결과물을 기꺼이 내놓을 것입니다. 실제 데이터의 라이브 브랜치는 구체적인 경계가 되며, 에이전트는 실제로 존재하는 데이터베이스를 우회하여 임의로 가정할 수 없습니다.
  • 드라이버는 골대를 옮길 수 없습니다. 코드를 작성하는 역할은 테스트를 통과시키기 위해 테스트 자체를 변경할 수 없습니다. 유일한 예외는 이전 테스트를 정당하게 대체하는 후속 스토리와 같이 진정한 대체가 일어나는 경우뿐입니다. Green은 에이전트가 통과했다고 말했기 때문이 아니라, 실제 데이터베이스 브랜치를 대상으로 테스트가 실행되어 통과했음을 의미합니다.
  • 지휘자(conductor)는 에이전트가 아닌 코드입니다. 팀에서 스크럼 마스터라고 부르는 역할은 결정론적 상태 머신(deterministic state machine)이 됩니다. 작업을 라우팅하고, 사람의 승인 단계를 관리하며, 충돌이 발생하면 책임이 있는 역할로 작업을 다시 라우팅하므로, 주기를 구동하는 프로세스가 단계를 건너뛰거나 길을 잃지 않도록 보장합니다.
  • 목표를 잃지 않습니다. 다른 프레임워크에 대해 제가 가장 많이 듣는 불만은 며칠이 지나면 에이전트가 목표를 잃어버려 사용자가 좌절하고 결국 재시작하게 된다는 점입니다. Consort는 각 기능의 결과물을 에이전트가 역할 간의 계약으로 읽을 수 있는 전용 디렉터리 구조에 보관합니다. 이는 실제 팀이 수행하는 인수인계와 동일합니다. 각 전문가가 작업을 검토하고, 우려 사항을 추가한 다음, 이를 다음 사람에게 전달합니다.

최적화 전략으로서(에이전트가 매 차례 시작할 때마다 모든 것을 다시 스캔하느라 시간을 허비하지 않도록), Consort는 에이전트를 전체 코드베이스에 무작정 풀어놓는 대신 범위가 지정된 컨텍스트 패키지, 통과해야 할 정확한 테스트, 설계 요구사항 및 해당 테스트의 위치를 각 에이전트에 제공합니다. 범위가 지정되지 않은 접근 방식은 에이전트가 방향을 잃게 만들고, 무엇을 해야 하고 어디로 가야 하는지 파악하기 위해 코드 위에서 오랜 시간을 허비하게 만들며, 결국 DRY 원칙이 무엇인지조차 잊어버리게 만듭니다.

아키텍트는 코드가 작성되기 전에 사양을 검토하고 다기능 요구사항, 레이어링, DRY, SRP, SOLID와 같은 패턴을 포함하여 귀하가 중요하게 생각하는 설계를 레이어링합니다. 이러한 사전 설계 덕분에 모든 것이 하나의 파일에 담기는 불상사를 방지할 수 있습니다. 또한 새로운 시도를 해보고 싶을 때, Consort는 하나의 스토리에 대해 병렬 실험을 실행하며, 각 실험은 완전히 격리된 자체 데이터베이스 브랜치와 작업 트리(worktree)에서 진행되므로 여러 접근 방식을 시도해보고 더 나은 실험 결과를 유지할 수 있습니다. 제가 다른 엔지니어들에게 이것을 보여주었을 때, 그들은 대개 자신들이 직접 만들려고 노력했던 바로 그것임을 즉시 알아보았습니다.

이 모든 과정을 지켜보고 방향을 제어할 수 있습니다. VS Code 플러그인은 여러 계층에 걸쳐 페어링된 Git 및 Lakebase 브랜치를 보여주며, 코드와 스키마 변경 사항을 단일 diff 보기로 제공합니다. 관측 가능성(observability) 대시보드는 각 차례를 실시간으로 보여줍니다. 모든 역할, 프롬프트, 그리고 다음 역할이 사용할 결과물을 보여줍니다. 또한 각 단계에서 다음 단계로 승인하기 전에 자체 브랜치에서 실행 중인 작동하는 소프트웨어를 직접 검사할 수 있습니다.

Consort란 무엇이며, 무엇이 아닐까요

Consort는 오픈 소스이며 커뮤니티 지원을 받습니다. 이는 자체 수명 주기를 가지고 채택, 실행 및 추천할 수 있는 프로젝트입니다. SLA가 제공되는 번들 플랫폼 기능이 아닙니다.

이를 사용하기 위해 스택을 재구성할 필요는 없습니다. Lakebase는 Postgres이므로 완성된 앱은 브랜칭 워크플로 없이도 그 위에서 실행됩니다. 페어링된 Git 및 Lakebase 브랜칭은 개발 중에 수행하는 작업입니다.

25년 동안 데이터베이스는 브랜치할 수 없는 유일한 스택이었습니다. 이제는 가능합니다. 그것이 변화된 점이며, 제가 Consort를 만든 이유입니다.

직접 시도해 보세요

Lakebase만 있으면 됩니다. 무료 에디션으로 시작해 보세요.

나머지 스택은 Java, Python 또는 Node 등 오픈되어 있으며, Claude에서 실행되고 함께 사용할 수 있는 VS Code 플러그인이 제공됩니다.

준비가 되셨다면, 다음 세 가지 명령어로 시작할 수 있습니다.

/consort:start를 통해 프로젝트를 시작할 수 있습니다. StockFlow 예제로 시작해 보세요. 세 개의 파일로 시딩되며, 거기서부터 전체 코드베이스로 성장하는 과정을 지켜볼 수 있습니다.

소프트웨어 구축의 (혁)명적 진화

이 작업이 영감을 주었고 더 발전시키고 싶다면, 저는 더 많은 기여자(contributor)와 코드 소유자(codeowner)를 찾고 있습니다. 참여하고 싶거나 내부 작동 방식을 확인하고 싶다면, arXiv에 게시된 논문을 확인해 보세요: https://arxiv.org/abs/2609.09671 또는 리포지토리를 방문해 보세요: https://github.com/databricks-solutions/consort

(이 글은 AI의 도움을 받아 번역되었습니다. 원문이 궁금하시다면 여기를 클릭해 주세요)

최신 게시물을 이메일로 받아보세요

블로그를 구독하고 최신 게시물을 이메일로 받아보세요.