티스토리 뷰
모노리포 도입 배경
기존에 프론트엔드 팀은 40개가 넘는 repository를 각 도메인 및 기능 시스템 단위로 생성하여 멀티 리포(multi repo)방식으로 서비스를 운영하고 있었다.
멀티 리포 구조
레포(repository 의 발음이 리포지토리라서 리포라고 하겠습니다.)
멀티 리포 구조는 폴리리포(polyrepo)구조라고도 부른다. 분리된 각 모듈은 멀티 리포 구조에서 고유한 저장소가 있는 독자적 프로젝트가 된다. 각 프로젝트는 자율성이 높으며 독립적인 개발, 린트, 테스트, 빌드, 게시, 배포 파이프라인이 존재한다.
멀티 리포의 장점: 각 프로젝트가 고유의 저장소를 가지게 됨으로써, 다르 프로젝트와의 의존성을 가지고 있고 않아 독립적으로 빠르게 개발이 가능하며 비교적 크기가 가벼워 프로젝트 관리 면으로 수월하다.
현재 대부분의 어플리케이션을 개발하는 표준적인 방법이다. 업계는 팀의 자율성이라는 큰 이유 때문에 이 방식을 선호한다. 팀은 어플리케이션 개발의 라이프사이클을 스스로 결정하기를 원한다.
멀티 리포의 운영은 관리하는 프로젝트가 많아질수록 아래와 같은 문제점이 발생한다.
- 각 프로젝트의 코드 컨벤션이 통일하기가 어려워질 수 있다.
- 각 프로젝트 별로 사용하는 모듈 및 버전 스택이 달라질 수 있다.
- 오랫동안 건드리지 않은 프로젝트의 관리가 힘들어지며, 시간이 지날수록 해당 프로젝트의 Legacy 파악이 어려워질 수 있다.
- 팀원별 컨텍스트 공유가 서로 원활하지 않을 수 있다.
콴다의 경우, 각 팀원이 동일한 문제를 겪어도 서로 컨텍스트 공유가되기보다는, 각자 시간을 들여가며 서로 다른 방식으로 문제를 해결하는 비효율적인 팀워크 문제가 발생했다.
또한, 콴다 서비스에서 새로운 서비스 기능을 추가하기 위해, 새롭게 repo를 생성할 때마다 서비스의 환경과 Lint환경을 반복적으로 세팅해야 했으며 반복적인 작업이 매우 큰 불편함으로 다가왔다.
결론: 멀티 레포 방식은 지금 당장은 편리하고 빠른 퍼포먼스 but 프로젝트 규모가 커지고 참여하는 FE 개발자가 많아질수록 업무 퍼포먼스를 악화시킨다.
멀티 레포 방식을 개선하기 위한 노력
- 프로젝트 보일러플레이트 템플릿
- [개선을 통한 기대 결과] Lint 환경과 SSR 및 CSR 환경의 기본적인 세팅이 되어있는 template을 이용함으로써, 새로운 프로젝트에서 반복되는 환경 세팅을 더이상 진행할 필요없이 바로 코드 작업을 시작할 수 있도록하는 것을 기대
- [실제 결과 1] but 다른 멀티리포의 리포와 마찬가지로 별도의 관리가 필요하게 됐다. -> 시간이 지나면서 `보일러플레이트`의 환경 설정과 모듈 버전들이 최신화되지 않아 나중에는 Legacy로 남는 문제가 발생
- [실제 결과 2] 별도의 Owner가 지정되어 있지 않기 때문에 관리에 신경쓰기가 더욱 애매한 점도 있으며, 까먹고 활용하지 않는 경우도 많아 결국에는 적극적으로 활용x
- eslint-config 모듈
- 서로 독립적이고 각기 흩어져있는 repo의 코드 컨벤션을 최대한 통일시켜보고자, eslint 설정과 관련된 dependency 모듈들과 configuration 코드들을 하나의 패키지 모듈로 생성하여 각 프로젝트 별로 해당 모듈을 설치하도록 했다.
- 이를 통해 각 프로젝트에서 eslint관련 plugin과 별도의 rule에 대한 설정 필요없이 Lint환경을 바로 이용할 수 있도록 하였다.
- [실제 결과 1] 장기적으로 보았을 때 각 프로젝트 별로 서로 다르 코드 컨벤션을 가지게 될 요소가 있음
- [실제 결과 2] 해당 eslint-config 모듈이 업데이트된 사항이 있어도 서비스 프로젝트에서 업데이트 버전을 사용하지 않게 되면서, 각 프로젝트 별로 서로 다른 버전을 이용하게 되어 결국 다른 Lint Rule 컨벤션을 이용하게 됐다. -> 시간이 지날수록 서로 다른 Rule에 맞는 코드가 쌓이게 되면서, 같은 eslint-config 모듈을 이용하더라도 서로 다르 코드 컨벤션을 보게 될 것으로 예상
- 추가적으로 해당 모듈 역시 신규 프로젝트를 생성할 때마다 eslint-config 라는 모듈을 Dependency로 추가해주어야 하는 번거로움 존재. 빠르게 늘어나는 신규 프로젝트와 팀원들로 인해 eslint-config 모듈이 누락된 프로젝트가 발생할 가능성이 있었으므로, 수많은 프로젝트 모두가 이러한 하나의 dependency를 신경써야 한다는 점은 팀 차원에서 큰 부담
- 정기적인 컨텍스트 공유 미팅
- 모든 컨텍스트 문제를 해결하기에는 장기간의 미팅 시간이 필요하게 되었고 팀원 대부분이 업무에 많은 지장을 준다는 의견에 실패
결국 모든 서비스를 하나의 리포에서 관리할 수 있는 모노리포 시스템을 구축하기로 함
Yarn Workspace
yarn에서 여러 서비스를 관리할 수 있는 환경을 제공해주는 yarn workspace 라는 기능을 지원해준다. 이를 이용하여 모노리포 환경을 구축할 수 있다.
Yarn Workspace의 기능 동작
Dependency Module Hoisting
- 각 프로젝트의 중복되는 의존성 모듈을 최상단 node_modules에서 공유하여 함께 사용할 수 있다.
- 각각 프로젝트에 node_modlues 를 가지고 Dependency를 설치하지만, 여러 프로젝트에서 중복으로 사용되는 모듈은 중복 설치할 필요없이 최상단 node_modules로 Hoisting하게 된다. 이를 통해 중복되는 Dependency를 root node_modules로 끌어올려 하위의 프로젝트 간에 모듈을 공유할 수 있게 된다.
- 결과적으로 각 프로젝트에서 중복되는 모듈 설치를 줄이고 전체적인 dependency 사이즈를 줄일 수 있다.
[단점]
아직은 불안전한 yarn workspace Hoisting
yarn workspace에서 Hoisting을 이용한 dependency 관리는 아직 많이 불안정한 상태이다.
모노리포 내에서 각 프로젝트를 Build할 경우가 많은데, 그때마다 아래와 같은 에러 메세지를 자주 겪는다.
- can’t find module “B@2.0” from project root “monorepo” (not able to follow symlink)
- can’t find module “A@1.0” from “package-1” (unaware of the module tree above in “monorepo”)
yarn workspace 내 root node_modiels로 Hoisting된 모듈들은 하위 프로젝트에서 모두 엑세스할 수 있는 것처럼 보이지만 각 하위 프로젝트를 실제로 Build 할때에는 특정 모듈은 해당 로컬 node_modules만을 참조하려는 경우가 많다.
이때, root node_modules로 Hoisting된 모듈은 로컬 node_modules에는 실제로 존재하지 않기 때문에 not Found 에러가 발생하게 된다.
yarn 공식 문서에서도 모든 타사 라이브러리들이 아직 monorepo 환경에 최적화가 되어있는 상태가 아닌 만큼, 이러한 Cant Found 문제는 쉽게 해결할 수 있는 문제가 아니라고 설명한다. 따라서 yarn에서는 별도로 Hoisting이 되지 않도록 noHoist 옵션을 제공한다.
"workspaces": {
"nohoist": ["react-native", "react-native/**"]
}
noHoist를 이용하면, 각 프로젝트의 의존성 모듈을 프로젝트 내 각 로컬 node_moduels에 설치되기 때문에 이러한 문제를 해결할 수 있다.
모노리포 내 디렉토리 구조
yarn workspace에서 symbolic Link는 각 모듈의 dependency 관리에서도 강력하게 쓰이지만, 라이브러리 형식의 프로젝트를 서비스용 프로젝트에서 바로 사용할 때와 같이, 외부 프로젝트의 코드를 바로 import하여 사용할 경우에도 쓸 수 있다.
모노리포 내 타 프로젝트를 dependency로 설정하게 되면, 타 프로젝트를 심볼릭 링크로 참조하여 dependency 모듈처럼 사용할 수 있게 된다. 빌드 또한 심볼릭 링크로 참조되는 라이브러리 형식의 프로젝트로부터 빌드가 되며, 이후 서비스 프로젝트가 빌드하게 된다.
Lint Environment
모노리포 자체에 린트 환경을 설정함으로써, 더 이상 각 프로젝트에서 린트 관련 dependency 설치가 필요없고 린트 설정을 고려하지 않아도 된다.
...
├─ services
│ ....
│ ├─ service-a
│ │ ├─ .eslintrc
│ └─ services-b
│ └─ .eslintrc
...
└─ .eslintrc
만약 특정 프로젝트에만 eslint Rule의 변경할 사항이 있다면, 해당 프로젝트 내에서 별도로 `.esnlintc` 파일을 두고 관리할 수 있으므로, eslint설정 관련하여 유연하게 Rule을 가져갈 수 있다.
WorkFlow
기존(멀티 리포에서 각 리포)에는 Defalut Branch인 develop 과 master 가 있고, develop 하위로 feature브랜치를 따서 작업하는 git-flow 전략을 사용했다.
develop에 feature 브랜치가 merge되면 develop환경에서 CI/CD를 통한 배포가 되고, develop에서 master로 merge 시 production 환경으로 릴리즈되는 형식으로 사용 중이었다.
git-flow to Trunk based Development (WorkFlow -2)
그러나, 모노 리포로 이전되면서, 이러한 CI/CD 배포 방식을 그대로 이용하기가 어려워졌다.
여러 서비스를 함께 관리하는 만큼 develop 브랜치에 merge 가 되었을 시, 어떤 서비스가 CI가 돌고 배포가 되어야하는 지 기준을 잡을 수 없었다.
모노 리포에 더 알맞은 새로운 브랜치 전략이 필요하다고 느껴, Trunk based Development 전략을 도입
항상 릴리즈가 가능한 상태인 trunk(master)브랜치가 있고, 바로 하위로 Feature 브랜치를 따서 작업을 진행하는 브랜치 방식이다.
모든 개발자가 최상위 브랜치에 집중할 수 있게 되면서 많은 프로젝트가 최대한 align을 맞출 수 있는 형식이 된다.
단, master 브랜치의 바로 하위로 작업이 진행되는 만큼, 팀 내에서 지켜야할 팀 Rule이 있다.
- 작은 단위로 feature 작업을 진행할 것
- 주기적인 CI/CD로 빌드 문제가 발생하지 않도록 할 것
-> 콴다 팀 내에서는 더욱 세밀한 workflow 전략을 위해 Trunked Base Development 방식을 계속 연구하고 시도하고 있다.
all ref:
콴다 FE 팀의 궁극적인 목적 중 하나인 유저 경험에 집중할 수 있는 환경 만들기
FE 개발자가 갖추어야할 요소 중 가장 중요한 요소를 "좋은 유저 경험을 제공하는 것"
모호한 개념이 아닌, " 더 좋은 유저 경험을 주기 위해 페이지 로드 시간 500ms을 줄이하는 것이 필요하고, 이를 위해 JS 번들링 사이즈 최적화를 공부한다 " 와 같은 보다 구체적인 목표 ...(중략)
콴다 FE팀 Lint settings
콴다 FE 팀에서는 AirBnB Base의 꽤 엄격한 Lint Rule을 지키면서 코딩하고 있기 때문에 런타임에서 발생할 수 있는 의도치 않은 잠재적인 이슈들을 빌드하기 전에, 심지어 커밋하기 전에 상당 부분 제거한다. 또한, Pre-Commit Hook으로 Lint를 돌리고 있기 때문에 Lint 를 통과하지 못하는 코드는 커밋할 수도 없다. (충격)
+ 모던 프론트엔드 프로젝트 구성 기법 - 모노레포 개념편 NAVER D2
모노리포 반대파의 의견
https://www.itworld.co.kr/insight/214234
> 폴리리포(안정 추구파) vs 모노리포(파격 개혁파)
- Total
- Today
- Yesterday
- is()
- 원티드 프리온보딩 FE 챌린지
- 타입스크립트 장점
- aspect-ratio
- text input pattern
- && 셸 명령어
- getServerSideProps
- tilde caret
- 항해99추천비추천
- D 플래그
- grid flex
- nvm 설치순서
- 부트캠프항해
- reactAPI
- Prittier
- 프리렌더링확인법
- nvm경로 오류
- 타입스크립트 DT
- fs모듈 넥스트
- 원티드 프리온보딩 프론트엔드 챌린지 3일차
- getStaticPaths
- 틸드와 캐럿
- ~ ^
- 항해99프론트후기
- 항해99프론트
- 프리온보딩 프론트엔드 챌린지 3월
- 원티드 3월 프론트엔드 챌린지
- float 레이아웃
- 원티드 FE 프리온보딩 챌린지
- 형제 요소 선택자
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |