Skip to content

ADR-05: Polyrepo 리포지토리 전략

English Version

날짜작성자대상 리포
2024-12-17@KubrickCodeAll

Context

문제 상황

다중 서비스 플랫폼은 코드 구성 전략에 대한 근본적인 결정이 필요함. 이 결정은 다음에 영향을 미침:

  1. 개발 워크플로우: 서비스 경계를 넘나드는 개발 방식
  2. 릴리스 관리: 서비스 버저닝 및 배포 방식
  3. 팀 협업: 소유권과 책임 정의 방식
  4. 기술 진화: 기술 선택의 독립적 발전 가능성

리포지토리 전략 옵션

전략설명일반적 사용 사례
Monorepo모든 서비스를 단일 리포지토리에 구성긴밀하게 결합된 서비스, 동일 스택
Polyrepo각 서비스를 독립 리포지토리로 분리독립적 서비스, 다양한 스택
Hybrid핵심은 Monorepo, 특정 컴포넌트만 분리점진적 마이그레이션, 혼합 요구사항

핵심 결정 요소

  • 기술 스택 다양성: 서비스별 다른 언어/프레임워크 사용
  • 릴리스 독립성: 독립적인 배포 주기 필요성
  • 팀 구조: 소유권 경계 및 접근 제어 요구사항
  • 코드 공유 패턴: 공유 코드 변경 빈도
  • 오픈소스 전략: 특정 컴포넌트 오픈소스화 계획
  • 플랫폼 배포 요구사항: 마켓플레이스 및 레지스트리 제약

플랫폼 배포 제약

특정 배포 플랫폼은 독립 리포지토리를 요구하거나 강하게 선호함:

배포 대상리포지토리 요구사항Monorepo 제약
GitHub Actionaction.yml이 리포지토리 루트에 위치 필수불가능 - Marketplace 등록 안됨
VSCode Extensionpackage.json이 루트, vsce 패키징복잡한 워크스페이스 설정 필요
Go Modulego.mod 경로가 import path가 됨서브모듈 경로가 불편 (repo/pkg)
npm Package독립 패키지 관리가능하나 워크스페이스 도구 필요
Docker HubDockerfile 컨텍스트가 루트 선호멀티 컨텍스트 빌드로 복잡성 증가

이 제약은 생태계 확장(IDE 확장, CI 연동, CLI 도구) 계획 시 특히 중요함.

Decision

배포 단위와 기술 경계에 따라 리포지토리를 분리하는 Polyrepo 전략을 채택한다.

리포지토리 분리 기준:

  • 배포 단위: 독립적으로 배포되는 서비스는 별도 리포지토리
  • 기술 경계: 다른 주요 언어/프레임워크는 분리
  • 소유권 경계: 명확한 팀 소유권은 리포지토리 경계와 일치

Options Considered

Option A: Polyrepo 전략 (선택됨)

각 서비스가 독립적인 라이프사이클을 가진 자체 리포지토리를 유지함.

장점:

  • 독립적 릴리스 주기: 다른 서비스와 조율 없이 배포 가능
  • 명확한 소유권: 리포지토리 경계 = 소유권 경계
  • 기술 자유도: 각 서비스가 제약 없이 최적 스택 사용 가능
  • 세분화된 접근 제어: 민감한 코드에 대한 리포지토리 수준 권한
  • 오픈소스 활성화: 전체 코드베이스 노출 없이 특정 컴포넌트만 오픈소스화
  • 집중된 CI/CD: 빠른 빌드, 영향받는 서비스만 테스트
  • 단순화된 의존성: 크로스 언어 지원을 위한 빌드 시스템 복잡성 없음

단점:

  • 크로스 리포지토리 변경: 여러 리포 PR은 조율 필요
  • 의존성 동기화: 라이브러리 버전을 리포 간 추적 필요
  • 코드 중복 위험: 공통 유틸리티가 중복될 수 있음
  • 개발 환경 설정: 로컬 환경에 여러 리포지토리 클론 필요

Option B: Monorepo 전략

모든 서비스를 공유 도구와 함께 단일 리포지토리에 구성함.

장점:

  • 원자적 변경: 단일 PR로 여러 서비스 수정 가능
  • 코드 재사용: 공통 코드와 유틸리티 공유 용이
  • 일관된 도구: 통합된 빌드 시스템과 CI/CD
  • 단순화된 리팩토링: 대규모 변경 실행 용이
  • 단일 진실의 원천: 모든 코드가 한 곳에

단점:

  • 빌드 시스템 복잡성: 다중 언어 지원에 정교한 도구 필요 (Bazel, Nx)
  • 스케일링 문제: 리포지토리 크기가 클론/체크아웃 시간에 영향
  • 결합된 릴리스: 변경이 불필요한 재빌드를 유발할 수 있음
  • 접근 제어 한계: 세분화된 권한 설정이 복잡
  • 기술 고정: 단일 스택으로 표준화 압력
  • 플랫폼 배포 제약: GitHub Actions Marketplace 등록 불가; VSCode 확장 복잡한 설정 필요

Option C: 하이브리드 접근

핵심 서비스는 Monorepo에, 특정 컴포넌트(라이브러리, 인프라)는 별도 리포에 구성함.

장점:

  • 양쪽의 장점: 관련 서비스에 대해 Monorepo 이점 결합
  • 선택적 분리: 특별한 요구사항이 있는 컴포넌트 격리

단점:

  • 이중 복잡성: 두 패턴과 도구를 모두 유지 필요
  • 경계 결정: 어디에 무엇이 속하는지 기준 불명확
  • 마이그레이션 마찰: mono와 poly 섹션 간 코드 이동이 복잡

Consequences

Positive

  1. 독립적 릴리스 주기

    • 서비스가 자체 일정에 따라 배포
    • 릴리스 조율 오버헤드 없음
    • 롤백 범위가 단일 서비스로 제한
  2. 명확한 소유권 경계

    • 리포지토리 소유권 = 서비스 소유권
    • 코드 리뷰 범위 명확 정의
    • 책임 소재 명확
  3. 기술 유연성

    • 각 서비스가 최적의 기술 사용
    • 프레임워크/라이브러리 업그레이드가 연쇄되지 않음
    • 언어별로 최적화된 빌드 시스템
  4. 접근 제어

    • 민감한 코드(인프라, 시크릿) 격리
    • 리포지토리별 기여자 권한
    • 감사 추적 분리
  5. 오픈소스 가능성

    • 라이브러리 컴포넌트를 독립적으로 배포 가능
    • 별도 라이선스 가능
    • 커뮤니티 기여 범위 제한
  6. 생태계 배포

    • GitHub Actions를 Marketplace에 등록 가능 (루트 action.yml 필요)
    • VSCode 확장을 복잡한 워크스페이스 설정 없이 배포 가능
    • 각 도구/확장이 깔끔한 리포지토리 정체성 보유

Negative

  1. 크로스 리포지토리 변경

    • 다중 서비스 기능에 조율된 PR 필요
    • API 계약 변경에 신중한 순서 지정 필요
    • 완화책: 안정적인 API 계약, 버저닝 전략, 피처 플래그
  2. 의존성 동기화

    • 리포지토리 간 공유 라이브러리 업데이트
    • Breaking change 전달 필요
    • 완화책: 자동화된 의존성 업데이트 (Renovate/Dependabot), 시맨틱 버저닝
  3. 코드 중복

    • 유틸리티 코드가 중복될 수 있음
    • 공통 패턴 재구현
    • 완화책: 공유 코드를 전용 라이브러리 리포지토리로 추출
  4. 개발 환경

    • 클론 및 구성할 리포지토리 다수
    • E2E 테스트에 서비스 오케스트레이션 필요
    • 완화책: 로컬 개발용 Docker Compose, 문서화

Technical Implications

측면영향
의존성 관리Go 서비스는 Go modules, TypeScript는 npm
버전 전략라이브러리는 SemVer, 서비스는 독립 버저닝
CI/CD리포지토리별 파이프라인, 전용 환경에서 통합 테스트
코드 공유공유 라이브러리를 패키지로 배포 (Go modules, npm packages)
로컬 개발다중 서비스 설정을 위한 Docker Compose 또는 개발 스크립트

재검토 시점

  • 크로스 리포지토리 변경이 개발 작업의 대부분이 될 때
  • 팀 통합으로 공유 소유권이 더 실용적이 될 때
  • 기술 스택이 단일 언어/프레임워크로 수렴할 때
  • Monorepo 도구(Bazel, Nx)가 현재 규모에서 매력적이 될 때

Open-source test coverage insights