우리 팀 FE 파트에서는 멀티레포가 왜 벅찰까
저는 멀티 클라우드 플랫폼, 스페이스원(SpaceONE)의 웹 Console FE(프론트엔드) 개발을 하고 있습니다.
이 글은 SpaceONE Console을 개발하는 우리 팀 프론트엔드 개발자들의 개발 경험(DX)를 높이기 위해, 멀티레포 방식을 두고 무엇을 고민했는지 그 흔적을 남겨두기 위한 포스트입니다.
스페이스원 프론트엔드의 멀티레포
SpaceONE Console 프로젝트는 멀티레포로 관리되고 있습니다.
아… 물론 이전에는 모놀리틱하게 관리되고 있었습니다. 네, 태초에는요.
그런데 그것들이 점점 커지면서 아래와 같은 문제들이 불거지기 시작하더군요.
- 서로간 의존성이 커지면서 관심 분리가 어려워지고
- 뭐 하나 리팩토링을 하거나, 설계를 할 때에 그 범위 자체가 거대해서 비효율적이고
- 전혀 다른 프로젝트를 PoC로 진행해보려고 해도, 공통적으로 사용되는 코어 모듈들을 재사용할 수 없어 copy and paste를 해야 하는
대략 이런 문제들이요.
그래서 나름 머리를 써서 해결했던 방법이 멀티레포였어요.
그렇게 해서 저희가 지금 관리하는 멀티레포는 아래와 같아요.
(참고로 스페이스원은 모두 오픈소스로 개발되고 있어요. 아래 링크를 통해 확인할 수 있습니다.)
- spaceone console (main application)
- spaceone design-system (하위 package)
- spaceone console-core-lib (하위 package)
그러나 이 방식은 개발인력이 4명 남짓인 상황에서, 빠르게 개발하기에 벅차다는 생각이 점점 커지기 시작합니다.
멀티레포, 이상과 현실
서비스가 빠르게 발전하고 있기 때문에, 3 개의 프로젝트에 모두 기능의 추가 및 변경이 일어나야 하는 상황은 저희 팀에게 매우 빈번하게 일어납니다.
물론 이럴 때 이상적인 것은, 하위의 패키지들이 변경되고 충분히 테스트된 후 application 에 반영되는 것이겠죠.
그리고 아래의 패키지들이 배포되고 나면, 그것을 바탕으로 application 에서도 필요한 기능 변경 및 추가가 일어나는 것입니다.
그러나 현실은 녹록치가 않습니다. 애자일이라는 명목 아래에, 개발기간 내에도 기획자나 디자이너와 많은 소통을 하면서 기획 및 디자인 자체의 변경까지도 흔하게 일어나기 때문입니다.
개발하다보면 따로 분리되어 있을 때는 분명히 보이지 않고 디자인만으로는 확인되지 않는, “구현 단계에서만 보이는 어색한 기능이나 UX”가 분명히 존재합니다.
기업들이 더 나은 UI/UX로 개선하기 위해 끊임없이 노력하는 프론트엔드 개발자를 원하는 이유겠죠.
이러한 숨겨진 불편함과 어색함을 개선하는 것은 제품의 품질향상을 위해서는 꼭 필요한 과정입니다.
그리고 더욱 유연하고 빠르게 이를 찾아내는 것은, 각 레포에 분리되어 있는 코드를 함께 변경하고 동작시킬 때 가능해집니다.
우리 팀 FE 파트에서 개발 프로세스에 멀티레포가 걸림돌이 되는 이유
각 레포에 변경사항이 있을 때 빠른 대응을 해주기 위해, 우리는 로컬에서 패키지들을 간단하게 npm link 걸어놓고 개발합니다.
그리고 최소한으로만 구현을 해놓고, 기획자와 디자이너에게 중간 결과물에 대한 피드백을 받아 수정해나갑니다.
여기서 포인트는
- 분리되어있는 레포를 개발할 때에는 함께 개발해야 효율적이고
- 하위 패키지의 배포는 따로 이뤄져야 하며
- 어플리케이션이 동작할 때에는 패키지 의존성을 통하여 또다시 함께 동작해야 한다
는 점입니다.
이렇게 개발하기 위해서는 우리는 아래의 프로세스를 거칩니다.
npm link 로 로컬에서 개발한 후 → 하위 패키지로 사용하는 레포(design-system or console-core-lib)가 배포된 후 → link 를 끊고 → 배포된 패키지를 버전 업데이트한 다음 → 로컬에서 잘 돌아가는지 확인하고 → 푸시
물론 멀티레포로 사용하더라도 이 방식이 best practice 는 아니겠죠.
하위 패키지의 버전 관리 전략을 잘 세워두고 CI 과정과 적절히 믹스할 수 있을테지만, 더 쉽고 빠르게 해결할만한 다른 전략과 방식에 대한 고민이 생기기 시작했습니다.
"우리 팀의 규모와 프로세스에 fit한 다른 방식은 없을까? 🤔"
서브모듈은 어떨까
가장 빠르게 달성할 수 있는 방식은 서브모듈 방식이라는 대안이 있습니다.
우선 종속성으로 설치되어 사용되는 core-lib을 서브모듈 방식으로 관리한다면 어떻게 할까를 두고 고민해봤습니다.
메인 어플리케이션인 console 레포에 core-lib 레포를 서브모듈로 두어 사용하는 것이죠.
어떤 경우에는 이러한 방식이 편할 수도 있겠지만, 우리 팀의 브랜치 전략 및 개발 프로세스를 생각해보면 조금 불편한 부분이 있습니다. 아래의 특징 때문입니다.
- 우리 팀은 핫픽스가 필요한 경우, 마스터에서 체리픽하여 릴리즈 브랜치에 반영합니다.
- 메인 어플리케이션 레포를 제외하고는 마스터 브랜치만 관리합니다.
이러한 특징으로 인해 생길 수 있는 문제를 한번 예를 들어 추적해봅시다.
console(v1.1.0) 에서 core-lib의 헤드 aaa에 대하여 최종적으로 배포되었다고 가정해봅시다.
⇒ console(v1.1.0) > core-lib.aaa
그리고 새로운 스프린트(v1.2.0)가 시작됩니다. core-lib에 기능의 변경이 생겼고, 이것을 console에 반영하기 위해, console 의 마스터 브랜치에는 core-lib의 헤드가 bbb로 바뀌어 있습니다.
⇒ console(master) > core-lib.bbb
그런데 스프린트 중간에, 이미 배포된 console(v1.1.0) 버전에 대하여 core-lib 에 크리티컬한 문제가 있는 것을 발견하여 핫픽스 이슈가 생깁니다.
변경사항을 반영하여 core-lib 마스터 브랜치에 새로운 커밋이 올라가고, 마스터의 최신 브랜치는 ccc가 됩니다. 그리고 console v.1.1.1 은 바로 core-lib 의 ccc 헤드를 바라보죠.
⇒ console(v1.1.1) > core-lib.ccc
여기서 문제가 뭘까요.
문제는 core-lib 의 ccc 헤드는 console의 v1.2.0 버전에 변경되어야 하는 core-lib의 스펙이 bbb에 이미 들어가있습니다.
이 말은, 핫픽스인 console(v1.1.1)에 v1.2.0 버전에 추가되어야 할 core-lib 의 스펙이 포함되어 심각한 문제를 초래할 수 있다는 것이죠.
이에 따라 core-lib도 메인 어플리케이션인 console과 동일한 버저닝 및 브랜치 전략이 필요할 수 있습니다.
그리고 서브모듈로 관리해야 할 레포가 늘어나면 늘어날수록, 우리는 각 레포의 관리 포인트가 늘어나 걷잡을 수 없을 것이라는 생각이 들었습니다.
그래서 좀 더 편리하게 관리할 수 있는 대안을 모색해봅니다.
모노레포는 어떨까
개발환경을 비슷하게 유지하고, 함께 동작하는 프로젝트들을 함께 개발하여 개발 효율을 높이자는 취지에서 합당한 결론이라는 생각이 들었습니다.
모노레포에 대한 장점은 정말 많은 글들을 통해서 확인할 수 있죠.
하지만 뭐든지 이론적으로는 다 최고입니다. 거기에는 또다른 단점도 따라오죠.
그래서 정말 이 대안이 훌륭한 대안인가 검증을 해보려고 npm workspaces 를 이용하여 스페이스원 console 프로젝트에 적용해보려고 합니다.
후속 포스트들을 통해 모노레포로의 첫 경험과 Trouble Shooting 한 내용 등을 다루도록 하겠습니다.