끝이 없는 monorepo...
비대해지는 API와 늘어나는 프로덕트... 그에 따른 코드 파편화를 개선하기 위해 프로젝트들을 관심사별로 묶어 package개발을 위해 monorepo를 구성했습니다.
API federation + monorepo 구축기 (...?)
https://brunch.co.kr/@fifthsage/19
구성은 완료했지만 당연히 CI/CD도 monorepo에 맞게 수정을 해 자동화를 유지를 해야 궁극적인 결과를 얻을 수 있겠습니다. 웹, 앱 각각 monorepo에 맞게 CI/CD구성을 어떻게 변경했는지 이야기해 보도록 하겠습니다.
monorepo는 module들이 호이스팅이 되어 루트로 올라가고 package들은 루트 모듈의 심볼릭 링크를 사용할 뿐입니다. 따라서 배포의 기준이 되는 폴더가 각각의 프로젝트이었을 때 루트였던 것들이 각 package의 루트가 되면 되는 것이죠. CI/CD는 대부분 비슷합니다. 주로 yaml을 이용하게 됩니다. 따라서 빌드 스텝의 경로를 변경해주고 lerna 커맨드를 추가해주면 간단하게 끝이 납니다. 조금 학습이 필요한 부분은 CI/CD서비스마다 트리거 전략이 다르다는 부분 정도...?
웹 서비스는 GKE와 Cloud Run을 기반으로 운영하고 있기 때문에 따로 인증이 필요 없는 이득을 위해 Cloud build를 사용하고 있습니다. 먼저 트리거 전략을 살펴보겠습니다.
Google Cloud build의 경우 브랜치나 태그 기준(정규식)으로 트리깅이 가능하며 트리거 옵션 중 glob pattern이라는 옵션이 있습니다. 리눅스의 그것과 비슷한 옵션인데, 특정 파일이 변경되었을 때의 조건을 추가해 줍니다. lerna의 publish 자체가 npm version을 기준으로 하기 때문에 package.json의 변경점을 glob pattern으로 추가해 주면 lerna publish나 npm verion, yarn version 등의 명령어로 쉽게 트리거 조건을 만드는 게 가능합니다.
그다음은 package 루트 지정입니다. Cloud build에는 dir이라는 프로퍼티를 지정해 주면 됩니다. 트리거 옵션 중 환경변수를 이용하면 됩니다.
정리해 보자면 각 package별로 glob pattern을 이용해 트리거를 만들고 package이름을 환경변수로 지정해주면 쉽게 monorepo 배포로 변경이 가능합니다.
앱의 경우에는 iOS빌드를 위해 Circle CI를 사용하고 있습니다.
react native 통합 배포 자동화하기
https://brunch.co.kr/@fifthsage/7
웹과 마찬가지로 트리깅 조건과 package 이름을 어떻게 주입할지 살펴봅니다 트리거 조건을 먼저 살펴보면 Circle CI는 저장소 변경 전체를 감시하고 config.yaml파일을 읽어 branch와 filter라는 조건으로 트리깅을 합니다.
웹과 같이 glob pattern이나 환경변수로의 접근은 어려워 보입니다. (제가 방법을 찾지 못한 것일 가능성도 있습니다) 방법을 찾다 보니 공식문서에 conditional이라는 단어가 보입니다. 그리고 Circle CI의 config.yaml파일은 각각 job으로 구성하고 workflow의 job filter를 이용해 트리거 조건을 결정하므로 workflow를 잘 구성하면 가능할 것 같은 아이디어를 얻습니다.
workflow job filter를 이용해 분기가 가능할 것 같긴 하지만 package의 이름을 전달해야 프로젝트 루트를 특정할 수 있을 텐데... 공식문서를 뒤적여보면 parameters라는 항목이 보입니다. job에 parameter를 전달할 수 있게끔 해줍니다. 트리거 조건, package 이름 두 가지 조건이 만족되었으니 구성이 가능해 보입니다! 이제는 android job, iOS job에 package-name 파라미터를 받을 수 있게 해 두고 package-name 파라미터를 이용해서 job 내 분기 처리를 해줍니다. (뭔가 더 좋은 방법이 있을 듯싶어 보이는 구성이나...) 그리고 workflow를 배포할 앱 package별로 구성을 해주고 filter조건은 지정한 prefix (앱을 특정할 수 있는) + version으로 조건을 걸어줍니다. 각 앱을 배포할 때는 미리 지정된 태그로 저장소에 푸시를 해주면 트리거가 동작하게 되고 트리거 된 workflow에 따라 package가 배포됩니다. 조마조마하게 시작했으나 monorepo의 특성을 이해하면 많이 어렵지는 않게 구성할 수 있었습니다.
이렇게 monorepo적용을 마무리 (여기가 끝일까...?) 지었습니다. 이후에는 구성해 놓은 환경들을 이용해서 API endpoint를 분기 및 API의 A/B테스트를 해볼 수 있는 조건이 만족되었습니다. 작년까지만 해도 해야지 해야지 하며 높은 산처럼 느끼던 숙원사업을 2021년 1분기에 마무리할 수 있어 기쁠 뿐입니다.