NEW 기획 디자인 개발 프로덕트 아웃소싱 프리랜싱

개발

Airbridge API 개발팀의 Monorepo 도입기

국내 유명 IT 기업은 한국을 넘어 세계를 무대로 할 정도로 뛰어난 기술과 아이디어를 자랑합니다. 이들은 기업 블로그를 통해 이러한 정보를 공개하고 있습니다. 요즘IT는 각 기업의 특색 있고 유익한 콘텐츠를 소개하는 시리즈를 준비했습니다. 이들은 어떻게 사고하고, 어떤 방식으로 일하는 걸까요?

 

이번 글은 ‘성과 극대화를 위한 머신러닝. 모바일 마케팅 성과분석 솔루션’ 에어브릿지(airbridge.io)를 만드는 AB180 Airbridge API 팀의 이야기입니다. 다양한 컴포넌트를 다루면서 생긴 문제점을 Monorepo로 해결한 경험을 공유합니다.

 

Monorepo 도입 배경

SaaS를 만들어 고객에게 제공하는 과정에서 한 개로 시작한 컴포넌트가 필요에 따라 여러 개로 나누어져 관리하는 경우를 흔히 볼 수 있습니다. 이렇게 나누어진 컴포넌트 사이에는 연관관계가 생기고, 때로는 하나의 컴포넌트가 다른 컴포넌트에 의존성이 생기기도 합니다. AB180의 Airbridge API 개발팀에서도 다양한 컴포넌트를 다루고 있고, 이 과정에서 겪었던 몇 가지 문제점을 Monorepo를 통해 해결한 경험을 소개하고자 합니다.

 

Monorepo란?

Monorepo는 하나의 저장소에서 여러 프로젝트를 관리하는 것을 의미합니다. 각각의 프로젝트들은 기존에 사용하던 환경처럼 각각 독립된 개발 환경을 가질 수 있습니다. 다시 말해, 같은 저장소에 속한 프로젝트들이 동일한 개발환경을 구성하고 있어야 하는 것은 아닙니다.

 

도입 배경

앞서 말씀드린 것처럼 Airbridge API 개발팀은 다양한 컴포넌트를 관리하고 있으며, 각 컴포넌트는 독립된 저장소를 가지고 있습니다. 이 중 api, dashboard, utils 컴포넌트를 예시로 들어보겠습니다.

 

도입 배경

 

utils 컴포넌트는 다른 여러 컴포넌트에서 사용되는 모델, 로직 등의 코드를 공통으로 사용하고자 만들어둔 패키지 형태의 컴포넌트입니다. 해당 컴포넌트는 api, dashboard 컴포넌트에서 사용되고 있습니다.

 

utils 컴포넌트

 

utils 컴포넌트에 있는 코드를 업데이트하고 api, dashboard에 반영하기 위해서는 다음과 같은 과정을 밟아야 했습니다. 먼저 utils 컴포넌트에서 발생한 변경점을 새 브랜치에 commit하고 utils 저장소에 push하고 pr을 만든 뒤 master에 merge하고, github에서 새로운 버전을 release합니다.

 

api와 dashboard 컴포넌트에서 새로운 버전의 utils를 반영하고, 발생한 변경점을 새 브랜치에 commit하고 각 저장소에 push하고 pr을 만든 뒤 master에 merge합니다. 만약 추가로 utils에 변경이 필요하다면 보기만 해도 복잡한 이 과정을 다시 한번 진행해야 합니다.

 

 

작업 목표

기존 개발 패턴을 해치지 않으면서 위에서 보신 것처럼 복잡한 업데이트 과정을 단순화하여 생산성을 높이고, Airbridge API 팀에서 사용하는 여러 컴포넌트를 한 번에 관리할 수 있게 가시성을 개선하고자 했습니다. 그리고 utils처럼 다른 컴포넌트에 영향을 줄 수 있는 경우, 연관된 컴포넌트까지 일관성을 유지할 수 있게 테스트와 배포까지 잘 수행되는 것을 목표로 삼았습니다.

 

저장소 합치기

Airbridge API 팀에서 사용하는 컴포넌트를 api 저장소로 합치는 작업을 진행했습니다. 루트 디렉터리를 기준으로 프로젝트를 구분하는 형태로 Monorepo를 구성했습니다. 루트 디렉터리에 api, dashboard, utils 폴더를 만들고 각 컴포넌트의 소스를 옮겼습니다. 패키지 형태로 사용하던 utils 컴포넌트의 경우에는 api, dashboard에서 로컬 패키지 형태로 사용하도록 수정했습니다.

 

airbridge github

 

CI/CD 연결

각 컴포넌트는 Github Webhook을 이용하여 Codebuild와 연동되어 있고, PR CI, Merge 생성 시 CD가 이루어지는 자동화된 CI/CD가 구성되어 있고, PR에서 CI의 결과를 직접 확인할 수 있습니다.

 

자동화 ci/cd

 

Monorepo가 적용된 환경에서도 각 컴포넌트의 CI/CD는 기존처럼 별개로 동작하는 것을 목표로 했기에, 각 저장소에 연결되어 있던 Github Webhook들을 Monorepo인 api 저장소로 이동시켰습니다.

 

github webhook
api 저장소에 연결된 컴포넌트 CI/CD용 Webhook

 

git diff 정보를 이용하여 해당 컴포넌트의 변경점이 있거나 연관된 컴포넌트의 변경점이 발생한 경우에만 CI/CD를 수행할 수 있도록 각 컴포넌트의 Codebuild 스크립트를 변경합니다. 이렇게 하면 api 저장소에 PR이 생성될 경우 CI/CD가 실행되지만, 변경점이 컴포넌트에 없으면 실제 CI/CD 과정을 밟진 않습니다.

 

api 컴포넌트
api 컴포넌트의 경우 api, utils의 변경점이 있을 때 CI/CD를 수행한다.

 

Monorepo를 적용한 상태에서 utils 컴포넌트를 업데이트하는 과정을 살펴보겠습니다. 로컬 패키지를 사용하고 있기에 utils 컴포넌트의 코드를 변경하고 새 브랜치를 만들고 api 저장소에 utils, api 변경점을 한 번에 commit, push, pr, merge 하는 과정을 밟으면 업데이트 과정이 완료됩니다.

 

Multirepo를 사용할 때 보다 훨씬 간단해진 것을 체감할 수 있습니다.

 

 

작업 완료 이후 발견한 문제

각 컴포넌트의 CI/CD 수행 여부를 CI/CD 과정에서 직접 판단하도록 설계하면서 발생한 몇 가지 문제를 먼저 살펴보겠습니다.

 

1) 복잡해진 CI/CD 스크립트

CI/CD의 필요 여부를 판단하고 이에 따라 분기하는 로직이 추가되어 Codebuild 스크립트가 복잡해졌고, 본연의 역할인 CI/CD를 넘어 더 많은 역할을 책임지게 되었습니다. 그리고 컴포넌트의 연관관계를 변경될 때도 Codebuild 스크립트를 변경해야 합니다.

 

2) CI/CD 성공 처리 문제

생략된 CI/CD의 경우에도 성공으로 처리되어 종료됩니다. 정상적으로 수행되어 성공으로 처리된 것인지, 생략하여 성공으로 처리된 것인지를 판단할 수 없었습니다.

 

3) 같은 코드 재배포 불가

모종의 이유로 특정 컴포넌트의 재배포가 필요한 경우가 있습니다. 하지만 같은 코드를 배포할 경우 이미 동일한 코드가 배포된 상태이므로 변경점이 없다고 판단하여 CD를 수행할 수 없습니다.

 

4) 관리 포인트의 증가

api 저장소에 새로운 컴포넌트를 추가로 합치거나 기존 컴포넌트를 제거할 때 CI/CD용 Github Webhook을 추가하는 것도 신경 써야 했습니다.

 

 

해결하기 위해서

앞서 말씀드린 문제점을 근본적으로 해결하기 위해서는 CI/CD Codebuild가 실행되기 전에 각 컴포넌트의 CI/CD 수행 여부를 판단할 수 있는 무언가가 필요했습니다.

 

컴포넌트 ci/cd 수행 여부
Code Deployer의 탄생

 

Code Deployer라고 이름을 지은 새로운 컴포넌트를 만들어서 CI/CD가 수행될 수 있도록 과정을 개선했습니다. Code Deployer는 PR이 생성되거나 PR의 상태가 변화될 때 Github Webhook을 통해 Trigger 됩니다. PR의 브랜치와 master 브랜치 사이의 코드 변경사항을 추적하여 각 컴포넌트의 CI/CD가 필요한지 판단하고, 필요 시 CI/CD를 실행하는 역할을 합니다. Code Deployer의 도입으로 앞서 언급했던 문제를 해결할 수 있었습니다.

 

1) 복잡해진 CI/CD 스크립트 해결

ci/cd 스크립트

 

각 컴포넌트의 Codebuild 스크립트에서 CI/CD 수행 여부를 판단하기 위해 추가했던 로직을 제거했습니다. 또한 컴포넌트의 연관관계를 지정하는 project_diff.txt 파일을 각 컴포넌트에 추가하여 관리할 수 있게 함으로써, 연관관계 설정을 위해 스크립트를 변경해야 하던 것을 해결했습니다.

 

2) CI/CD 성공 처리 문제 해결

Code Deployer가 각 컴포넌트의 CI/CD 필요 여부를 확인하고, 불필요한 CI/CD는 수행하지 않게 만들어 실제로 성공한 CI/CD 결과만 기록될 수 있게 되었습니다.

 

3) 같은 코드 재배포 불가 해결

CI/CD 과정에서 배포가 필요한지 판단하는 로직을 제거함으로써 자연스럽게 이 문제도 같이 해결되었습니다. 만약 재배포가 필요하다면 CD용 Codebuild를 Trigger하면 같은 코드라고 하더라도 정상적으로 배포가 이루어집니다.

 

4) 관리 포인트의 증가 해결

관리 포인트 증가

 

Codebuild와 직접 연결되어 있던 Github Webhook을 모두 제거하고, Code Deployer와 연결되는 Github Webhook을 추가했습니다. Code Deployer가 저장소 내의 모든 컴포넌트에 대한 CI/CD 실행을 담당하게 되면서 컴포넌트의 추가/제거 시 Github Webhook에 대해 신경 썼던 부분도 해결됐습니다.

 

 

조금 더 잘하기 위해서

PR에서 CI 상태 확인

ci 상태 확인

 

Code Deployer가 Codebuild를 실행하도록 변경되더라도 기존처럼 각 컴포넌트의 CI 결과를 PR에서 확인할 수 있도록 Github Checks API를 활용하여 구현했습니다.

 

생략된 CI도 확인 가능

변경점이 없는 컴포넌트의 경우 CI/CD를 진행할 필요가 없으므로 Skip 했고, 이에 대한 가시성을 확보했습니다.

 

Slack에서 CI/CD 상태 확인

슬랙 ci/cd 확인

 

기존 Codebuild 알림에서는 PR이나 CI 진행 상태가 한정적이었기에 이를 의미 있게 활용하기 어려웠습니다.

 

codebuild 알림

 

누가, 무엇을 배포한 것인지 PR 정보를 알 수 있게 추가하고, 작업자가 CI 완료 알림을 받을 수 있게 해 알림의 가시성을 챙겼습니다.

 

알림 가시성

 

또한 작업이 프로덕션에 반영된 경우 따로 알림을 보내주도록 했습니다.

 

이러한 과정을 거치면서 개선하고자 했던 목표를 달성했고, 생산성 향상을 위한 추가 기능 구현까지 완성하며 Monorepo 도입 작업을 완료할 수 있었습니다.

 

 

Monorepo 도입 이후 장단점

좋았던 점

Airbridge API 팀에서 관리하는 여러 컴포넌트를 하나의 저장소에서 한눈에 확인할 수 있게 되었습니다. 여러 컴포넌트에 해당하는 티켓을 작업할 때 관련된 변경점을 하나의 PR에서 확인할 수 있게 되었습니다. utils와 같은 패키지 형태의 컴포넌트도 하나의 저장소에서 관리할 수 있게 되어 생산성을 향상시킬 수 있게 되었습니다.

 

아쉬웠던 점

컴포넌트별로 코드 접근 권한을 제어할 수 없다는 것과 로컬 패키지로 사용하는 컴포넌트들을 각각 다른 버전으로 사용할 수 없는 것이 아쉬웠습니다.

 

 

마치며

Airbridge API 팀은 Monorepo 도입과 같이 팀원들의 생산성 향상과 보다 나은 개발 환경을 끊임없이 고민하고 있습니다. 다양한 컴포넌트를 다루는 다른 회사의 개발팀에서 Monorepo 도입을 고려할 때 이 글이 조금이나마 도움되기를 기대합니다.

 

<원문>

Airbridge API 개발팀의 Monorepo 도입기

댓글 0

AB180 엔지니어링 베이스

초당 10만 건의 트래픽 처리, 월 2,000만명의 활성 유저, 일 10억 이벤트 수집 및 분석, 실시간 수집되는 대규모 데이터, 성과 극대화를 위한 머신러닝. 모바일 마케팅 성과분석 솔루션 에어브릿지(airbridge.io)를 만드는 AB180 엔지니어들의 경험을 공유합니다.

같은 분야를 다룬 글들을 권해드려요.

요즘 인기있는 이야기들을 권해드려요.

일주일에 한 번!
전문가들의 IT 이야기를 전달해드려요.

[구독하기] 버튼을 누르면 개인정보 처리방침에 동의됩니다.

일주일에 한 번! 전문가들의 요즘IT 이야기를 전달해드려요.

[구독하기] 버튼을 누르면 개인정보 처리방침에 동의됩니다.