monorepo

4 개의 포스트

개발 속도 향상을 위한 모노레포 크기 줄이기 (새 탭에서 열림)

Dropbox는 87GB에 달하던 서버 모노레포 크기를 20GB로 약 77% 줄여 개발자 속도와 CI 효율성을 획기적으로 개선했습니다. 이 과정에서 Git의 기본 델타 압축 알고리즘이 특정 디렉토리 구조에서 비효율적으로 작동한다는 점을 발견했으며, GitHub 팀과 협력하여 최적화된 리팩(Repack) 설정을 적용해 저장소 용량 한계 문제를 해결했습니다. 결과적으로 1시간 이상 걸리던 클론 시간을 15분 미만으로 단축하며 운영상의 리스크를 제거했습니다. ### 대규모 모노레포 성장이 유발하는 운영 병목 - 저장소 크기가 87GB를 넘어서면서 초기 개발 환경 구축을 위한 클론 시간이 1시간을 초과했고, 이는 매번 신규 클론을 수행하는 CI(지속적 통합) 파이프라인의 성능 저하로 이어졌습니다. - 코드 데이터는 매일 20~60MB씩 증가하며 GitHub Enterprise Cloud의 하드 리밋인 100GB에 근접해 가고 있었으며, 이는 단순한 코드 양의 증가라기보다 저장 방식의 구조적 결함에 의한 현상이었습니다. - 내부 동기화 시스템의 타임아웃 발생 빈도가 높아지는 등 저장소 크기 자체가 엔지니어링 루프 전체를 느리게 만드는 핵심 원인이 되었습니다. ### Git 델타 압축 알고리즘과 디렉토리 구조의 충돌 - Git은 파일 간의 차이점(Delta)만 저장하여 용량을 줄이는데, 비교 대상 파일을 선정할 때 파일 경로의 '마지막 16자'만을 참조하는 휴리스틱 방식을 사용합니다. - Dropbox의 다국어(i18n) 파일 구조는 `i18n/[언어코드]/LC_MESSAGES/[파일명].po` 형태였는데, 언어 코드가 경로 중간에 있어 Git은 서로 다른 언어의 동일 파일명을 가진 파일들을 비교 대상으로 묶었습니다. - 내용이 전혀 다른 언어 간의 파일을 비교하다 보니 압축 효율이 극도로 낮아졌고, 아주 작은 번역 수정에도 불필요하게 큰 팩(Pack) 파일이 생성되는 결과로 이어졌습니다. ### GitHub 서버 측 리팩 최적화를 통한 문제 해결 - 실험적 플래그인 `--path-walk`를 사용하면 파일 경로 전체를 탐색해 압축 효율을 극대화할 수 있음을 로컬 테스트로 확인했으나, 이는 GitHub 서버의 비트맵 및 델타 아일랜드 최적화 기능과 호환되지 않았습니다. - 로컬에서 최적화하여 푸시하더라도 GitHub 서버가 전송 시 자체 설정으로 다시 팩을 구성하기 때문에, GitHub 지원팀과 협력하여 서버 측 리팩 설정을 조정하는 방식을 택했습니다. - Git이 더 넓고 깊게 유사성을 검색할 수 있도록 `window`와 `depth` 매개변수를 각각 250으로 상향 조정한 공격적인 리팩을 수행하여, 데이터 손실 없이 저장소 크기를 20GB 수준으로 압축하는 데 성공했습니다. ### 대규모 저장소 관리를 위한 제언 - 모노레포의 크기가 비정상적으로 급증한다면 단순한 바이너리 파일 유입뿐만 아니라, Git의 델타 압축 메커니즘과 현재의 디렉토리 구조가 상충하고 있지는 않은지 점검해야 합니다. - 저장소 최적화는 클라이언트 단의 노력만으로는 한계가 있으며, 호스팅 서비스(GitHub 등)의 서버 측 리팩 설정과 인프라 호환성을 반드시 고려하여 전략을 수립해야 합니다.

AI 및 엔지니어링 생산성에 (새 탭에서 열림)

Dropbox는 AI 도구를 단순한 실험을 넘어 비즈니스 가치 창출을 위한 핵심 전략으로 채택하고 있으며, 이를 통해 엔지니어링 생산성의 비약적인 향상을 꾀하고 있습니다. 최근 개최된 경영진 라운드테이블을 통해 AI 도입이 코드 리뷰와 디버깅 등 개발 전반의 효율을 높이는 동시에, 품질 유지와 비즈니스 성과 연결이라는 새로운 도전 과제를 제시하고 있음을 확인했습니다. 결과적으로 성공적인 AI 전환을 위해서는 기술적 도입뿐만 아니라 리더십의 조율과 조직적 프레임워크의 변화가 반드시 병행되어야 한다는 결론을 도출했습니다. ### AI를 통한 Dropbox의 생산성 가속화 전략 * **전사적 우선순위 설정:** AI 도입을 단순한 풀뿌리 수준의 실험이 아닌 회사 차원의 핵심 과제로 격상하여 리더십의 지지를 확보하고, 새로운 도구 도입을 위한 계약 및 승인 절차를 대폭 간소화했습니다. * **자체 AI 플랫폼 구축:** 대규모 다국어 모노레포(Monorepo)라는 특수한 환경에 맞추기 위해 기성 AI 도구에만 의존하지 않고, 풀 리퀘스트(PR) 빌드 실패 시 AI가 자동으로 수정안을 제안하는 자체 도구를 개발하여 운영 중입니다. * **데이터 기반의 성과 추적:** 엔지니어당 월간 PR 처리량(Throughput)을 핵심 지표로 설정하여 AI 도구 활용도가 높은 그룹의 생산성이 월등히 높음을 확인했으며, 내부 설문을 통해 개발자들의 긍정적인 감성 지표 변화를 모니터링하고 있습니다. * **개발자 자율성 부여:** 팀별로 최적의 도구를 선택할 수 있는 유연성을 제공하여 도입 과정에서의 마찰을 줄이고, 소프트웨어 개발 생애 주기(SDLC) 전반에서 AI가 자연스럽게 스며들 수 있도록 지원합니다. ### AI 시대의 엔지니어링 리더십과 조직 운영 * **균형 잡힌 생산성 관리:** AI로 인한 속도 향상이 코드 품질 저하나 장기적인 유지보수 비용 상승으로 이어지지 않도록 생산성과 품질 사이의 엄격한 균형 감각이 요구됩니다. * **리더십 정렬과 규범화:** 기술 리더십은 효과적인 AI 사용 규범을 설정하고 집행하는 중추적인 역할을 수행해야 하며, AI 배포 속도에 대해 경영진과 명확한 공감대를 형성해야 합니다. * **인적 역량의 공식적 평가:** AI 활용 능력을 엔지니어의 경력 개발 프레임워크(Career Framework)에 공식적으로 포함시켜 조직의 전략적 방향성을 명확히 하고, 비개발 직군의 생산성 향상으로도 그 범위를 확장하고 있습니다. ### 향후 과제 및 실무적 제언 * **유휴 용량의 전략적 재투자:** AI가 확보해 준 엔지니어링 여력을 기술 부채 해결, 시스템 마이그레이션, 서비스 신뢰성 강화 등 고부가가치 영역에 우선적으로 투입해야 합니다. * **비즈니스 성과와의 직접 연결:** 단순히 "코딩 속도가 빨라졌다"는 지표를 넘어, 향후에는 생산성 향상이 실제 비즈니스 결과물과 제품 출시 속도(Velocity)에 어떻게 기여하는지 직접적으로 매핑하는 운영 모델을 구축하는 것이 핵심입니다.

당근페이 백엔드 아키텍처가 걸어온 여정. Money라는 하나의 작은 프로젝트부터 수십 개의 서비스를 하나의… (새 탭에서 열림)

당근페이 백엔드 아키텍처는 서비스의 급격한 성장과 조직의 확장에 발맞춰 계층형, 헥사고날, 그리고 클린 아키텍처 기반의 모노레포 형태로 끊임없이 진화해 왔습니다. 초기에는 빠른 기능 출시를 위해 단순한 구조를 채택했으나, 비즈니스 복잡도가 증가함에 따라 의존성 관리와 코드 응집도를 높이기 위해 구조적 제약을 강화하는 방향으로 발전했습니다. 결과적으로 아키텍처는 기술적 부채를 해결하는 수단을 넘어, 대규모 팀이 협업하며 지속 가능한 성장을 이뤄낼 수 있는 기반이 되었습니다. ### 초기 성장을 견인한 계층형 아키텍처 (Layered Architecture) * **빠른 실행력 중심:** 2021년 당근페이 출시 초기, 송금 서비스의 신속한 시장 진입을 위해 `Controller-Service-Repository`로 이어지는 직관적인 3계층 구조를 사용했습니다. * **성장통의 발생:** 서비스가 커지면서 송금, 프로모션, FDS 등 다양한 기능이 하나의 계층에 뒤섞였고, 서비스 간 순환 참조와 강한 결합이 발생해 코드 변경의 영향 범위를 예측하기 어려워졌습니다. * **기술 부채의 축적:** 모든 비즈니스 로직에 프레임워크 기술(Spring)이 깊숙이 침투하면서 테스트 작성이 까다로워지고, 순수 도메인 로직만 분리해 관리하기 어려운 구조적 한계에 직면했습니다. ### 구조적 제약을 통한 응집도 향상 (Hexagonal Architecture) * **외부 구현과의 분리:** 도메인 규칙을 중심에 두고 UI, DB, 외부 API 등 인프라 영역을 포트와 어댑터를 통해 분리하여 프레임워크에 의존하지 않는 POJO 중심의 설계를 지향했습니다. * **모듈 역할의 세분화:** 프로젝트를 핵심 규칙을 담은 `domain`, 사용자 시나리오 단위의 `usecase`, 실제 입출력을 담당하는 `adapter` 모듈로 재구성하여 의존성 방향을 한곳으로 모았습니다. * **재사용성과 테스트 용이성:** 유스케이스 단위로 로직이 응집되면서 REST API뿐만 아니라 이벤트 컨슈머, 배치 잡 등 다양한 진입점에서 동일한 비즈니스 로직을 안전하게 재사용할 수 있게 되었습니다. ### 규모 확장에 대응하는 클린 아키텍처와 모노레포 * **모노레포 도입의 배경:** 머니, 포인트, 빌링 등 도메인이 늘어남에 따라 여러 저장소를 관리하는 비용이 증가했고, 이를 효율적으로 통합 관리하기 위해 하나의 저장소에서 여러 서비스를 운영하는 모노레포 구조를 채택했습니다. * **계약 기반의 모듈 분리:** 각 도메인을 `contract(인터페이스)`와 `impl(구현체)` 모듈로 쪼개어 의존성 규칙을 강제했습니다. 다른 모듈은 `contract`만 참조하게 하여 불필요한 내부 구현 노출을 차단했습니다. * **빌드 성능 및 생산성 최적화:** Gradle의 `api`와 `implementation` 구성을 활용해 컴파일 시점의 의존성을 제어함으로써, 대규모 프로젝트임에도 불구하고 빌드 시간을 단축하고 변경 영향도를 최소화했습니다. 아키텍처에는 정답이 없으며, 조직의 규모와 비즈니스의 현재 단계에 가장 적합한 형태를 선택하는 것이 중요합니다. 당근페이의 사례처럼 초기에 과도한 설계를 지양하되, 서비스 성장 속도에 맞춰 구조적 제약을 단계적으로 도입함으로써 기술 부채를 통제하고 개발 생산성을 유지하는 전략을 권장합니다.

에어비앤비 (새 탭에서 열림)

에어비앤비는 4.5년에 걸쳐 수천만 라인의 Java, Kotlin, Scala 코드로 구성된 대규모 JVM 모노레포를 Gradle에서 Bazel로 성공적으로 이전했습니다. 이번 마이그레이션을 통해 빌드 속도는 3~5배, IDE 동기화 및 배포 속도는 2~3배 향상되었으며, 개발자 만족도(CSAT)가 38%에서 68%로 크게 올랐습니다. Bazel의 밀폐성(Hermeticity)과 원격 실행 기능을 활용하여 대규모 코드베이스에서도 안정적이고 확장 가능한 빌드 시스템을 구축한 것이 핵심 성과입니다. **Gradle에서 Bazel로 전환한 이유** * **빌드 속도의 혁신:** Bazel의 원격 빌드 실행(RBE)을 통해 수천 개의 작업을 병렬로 처리하며, 'Build without the Bytes' 기능을 도입하여 필요한 아티팩트만 다운로드함으로써 대역폭 소모를 줄였습니다. * **빌드 안정성 및 밀폐성:** Gradle과 달리 샌드박스 환경을 제공하여 빌드 작업이 지정된 입력 외의 파일 시스템(예: /tmp 디렉토리)에 접근하는 것을 차단하고, 환경 차이로 인한 빌드 실패를 방지했습니다. * **통일된 인프라 구축:** 에어비앤비 내의 웹, iOS, Python, Go 등 다양한 언어의 레포지토리를 Bazel로 단일화하여 원격 캐싱, 로깅, 변경된 타겟 계산 로직을 공유할 수 있게 되었습니다. **단계적 마이그레이션과 개념 증명(PoC)** * **Viaduct 플랫폼 선정:** 에어비앤비에서 가장 크고 복잡한 서비스 중 하나인 GraphQL 모놀리스 'Viaduct'를 첫 타겟으로 선정하여, 가장 까다로운 케이스에서 성능 이점을 증명했습니다. * **공존 전략:** 초기에는 개발자가 Gradle과 Bazel 중 선택해서 사용할 수 있도록 두 시스템을 병렬로 운영하여 서비스 중단 위험을 최소화했습니다. * **개발자 설득:** 단순한 성능 향상을 넘어, 초기 단계에서 발생한 버그와 통합 문제를 해결하여 개발자들이 자발적으로 Bazel을 선택하도록 유도했습니다. **자동 빌드 파일 생성 및 유지보수** * **커스텀 생성기 개발:** Bazel 빌드 파일(BUILD)을 수동으로 관리하는 불편을 줄이기 위해 소스 코드의 패키지와 임포트 구문을 분석하여 의존성 그래프를 그리는 자동 생성기를 구축했습니다. * **Gazelle의 영감:** Go 언어의 Gazelle 도구에서 아이디어를 얻었으나, JVM 언어의 특성과 성능 요구사항을 맞추기 위해 캐싱 기능을 포함한 자체 도구로 발전시켰습니다. * **CI 통합:** 모든 커밋 전에 자동 생성기를 실행하여 Gradle과 Bazel의 빌드 그래프가 항상 일치하도록 유지했습니다. **IDE 사용자 경험 개선** * **IntelliJ 동기화 최적화:** 대규모 모노레포에서 Gradle 동기화가 최대 40분까지 소요되던 문제를 Bazel의 병렬 분석과 'Query Sync(실험적 기능)' 도입을 통해 3~10분 수준으로 단축했습니다. * **IntelliJ Aspect 활용:** Bazel의 Aspect 기능을 사용하여 프로젝트 구조 정보를 추출함으로써 IDE가 소스 코드와 의존성을 더 효율적으로 이해하도록 돕습니다. **성공적인 전환을 위한 교훈** 대규모 마이그레이션에서 가장 중요한 것은 **성능에 대한 집착**과 **개발자 경험(DevEx)에 대한 투자**입니다. 빌드 속도가 빨라지면 개발자들은 자연스럽게 새로운 도구를 수용하게 되며, 특히 IntelliJ와 같은 IDE와의 매끄러운 통합이 프로젝트의 성패를 좌우합니다. 또한 빌드 파일 생성과 같은 반복적인 작업을 자동화하여 개발자가 시스템 환경 설정이 아닌 코드 작성에만 집중할 수 있는 환경을 조성하는 것이 필수적입니다.