모노레포의 기술적 요구사항 (5) - Sparse Checkout

2022. 8. 7. 17:10Frontend

 

Overview

모노레포의 문화적 의의를 다룬 이전 포스팅에 이어, 이번 포스팅 시리즈에서는 실제로 모노레포를 팀에 도입하기 위해 거쳐왔던 여러 기술적인 고려 사항들을 간단하게 이야기해보려고 합니다. "모노레포는 이렇게 운영하는 것이 좋다"라는 가이드라기보다는 프론트엔드 팀이 모노레포로 전환하는 과정에서 겪은 여러 문제들과, 이를 해결해 나가는 과정에 대한 기록에 가까울 것 같습니다. 글은 다음과 같은 순서로 작성되었습니다.

 

  1. Workspaces & Dependencies
  2. Global Lint & Prettier
  3. Deploy & Branch Management
  4. Plugin
  5. (Optional) Sparse Checkout

 

Large Files Problem

 

https://canvatechblog.com/we-put-half-a-million-files-in-one-git-repository-heres-what-we-learned-ec734a764181

 

10명정도 규모의 팀이 하나의 레포에서 개발을 하게 되면 모노레포 안에 상당히 많은 수의 서비스들이 존재하게 됩니다. 여기에 사용하는 각종 라이브러리들, 플러그인, 여러 설정 파일들을 더하면, 모노레포 안에 존재하는 파일들의 수는 단일 레포지토리에서 서비스를 개발할 때에 비해 수십 배에 달할 만큼 많아지게 됩니다. 개발을 하기 위해서 사용하는 코드에디터(콴다 프론트엔드 팀에서는 코드 에디터로 VSCode를 사용합니다.)는 에디터가 열리는 엔트리 포인트(기본적으로 레포지토리의 Root Directory)에서부터 node_modules를 포함한 모든 파일들을 디스크에서 읽어와 Type Inference, Move to Definition 등의 기능들을 수행하게 됩니다. 

 

 

단일 레포지토리의 경우 파일 자체가 대부분의 개발 환경에서 감당 가능할 정도의 수준의 크기를 가지고 있기 때문에 별 문제가 없지만, 모노레포의 경우 서비스 개수만큼의 파일들과 node_modules들이 존재하기 때문에 에디터가 빠르게 Type Inference, Move to Definition 등을 수행할 수 없게 됩니다. 이로 인해 실제 서비스를 로컬에서 실행하고 테스트하는 데는 큰 문제가 없더라도, 특정 함수에 커서를 올렸을 때 뜨는 Definition Popup, Type Inference 등의 기능들이 제대로 동작하지 않거나 버벅거려서 좋지 않은 개발 경험을 주게 됩니다. 이를 개선하기 위해서 git에서 제공하는 Sparse Checkout이라는 기능을 사용할 수 있습니다.

 

Sparse Checkout

Git 2.25.0 includes a new experimental git sparse-checkout command that makes the existing feature easier to use, along with some important performance benefits for large repositories.

 

 

모노레포에서 너무 많은 서비스들이 있을 때, 코드 에디터에서 제공하는 타입 추론과 함수 정의 이동 등의 기능들을 제대로 사용할 수 없는 이유는 "에디터가 알고 있어야 할 파일들이 너무 많다"는 이유 때문입니다. 따라서 모노레포를 사용해서 모든 팀원들이 하나의 레포지토리에서 개발을 하더라도, "지금 내가 개발하고 있는" 서비스의 파일들만 에디터가 알도록 할 수 있다면, 위와 같은 문제는 해결할 수 있을 것입니다. 그리고 이것이 git sparse-checkout이 해결할 수 있는 문제입니다.

 

 

Now, an improved and experimental sparse-checkout feature allows users to restrict their working directory to only the files they care about. Specifically, if you use the “microservices in a monorepo” pattern, you can ensure the developer workflow is as fast as possible while maintaining all the benefits of a monorepo.

 

 

.git 디렉토리의 구조

 

 

원격 레포지토리에서 레포지토리를 클론받게 되면, 레포지토리가 클론 된 디렉터리에는 .git이라는 hidden directory가 생기게 되고, 여기에 branch, object, tags 등 git과 관련된 모든 정보들이 위치하게 됩니다. 클론 후 개발을 진행하기 위해 로컬에서 "git checkout main / git checkout dev" 와 같은 커맨드를 사용해서 브랜치를 바꾸면 git은 해당 브랜치의 소스 코드를 Working Directory로 가져오게 되는데, 여기서 Working Directory는 실제 사용자의 작업공간으로, 파일이 수정 / 생성 / 삭제되는 공간입니다. 코드 에디터를 사용해서 개발할 때 우리가 보는 화면이 바로 working directory의 코드입니다. (아래의 설명에서 확인할 수 있듯, git clone 이라는 명령어는 git checkout을 포함하는 개념이며, git checkout은 Working Directory를 변경합니다.)

 

The git clone command is actually something of a wrapper around several other commands. It creates a new directory, goes into it and runs git init to make it an empty Git repository, adds a remote (git remote add) to the URL that you pass it (by default named origin), runs a git fetch from that remote repository and then checks out the latest commit into your working directory with git checkout.

 

 

 

 

실제로 .git 폴더는 오리진에서 fetch 한 모든 브랜치의 변경점과 스냅샷에 대한 정보를 가지고 있지만, 실제로 에디터는 .git에는 접근하지 않고 특정 브랜치로 checkout 된 Working Directory의 파일만 읽을 수 있습니다. sparse-checkout은 바로 이 Working Directory에 전체 레포지토리의 일정 부분만을 가져올 수 있는 기술입니다. github blog에서 소개하는 Example을 가지고 sparse-checkout이 어떻게 동작하는지를 살펴보도록 하겠습니다. (이번 포스팅에서는 sparse-checkout이 내부적으로 어떻게 동작하는지 등에 대해서는 자세히 설명하지 않겠습니다. 이에 대한 자세한 설명은 이를 다룬 Github Blog 글을 참고해주세요)

 

https://github.com/derrickstolee/sparse-checkout-example

 

GitHub - derrickstolee/sparse-checkout-example: An example repo for the git sparse-checkout feature

An example repo for the git sparse-checkout feature - GitHub - derrickstolee/sparse-checkout-example: An example repo for the git sparse-checkout feature

github.com

 

 

 

예제의 모노레포 구성은 다음과 같습니다. Root에 README, bootstrap.sh와 같은 문서 파일, 설정 파일들이 위치하고, client / web / service 등으로 디렉터리가 나누어져 있습니다. Client 개발자는 client 디렉터리만 관심이 있고, Web 개발자는 service와 web 디렉터리에만 관심이 있다고 할 때, sparse-checkout을 사용하면, 아래의 관심사와 같이 색깔로 칠해진 부분만 Working Directory에 올리게 됩니다. 특정 부분만 Working Directory에 올린다는 것은 앞에서 살펴보았듯, 개발자가 색깔로 칠해진 부분만을 수정하고, 코드 에디터가 색깔로 칠해진 부분만을 신경 쓴다는 의미가 되기 때문에 앞서 발생했던 "파일이 많아서 발생하는 문제"가 발생하는 것을 막을 수 있습니다

 

전체 디렉토리 구조

위의 예제에서 /web, /service directory만 Working Directory에 올리고 싶다고 할 때, 다음과 같이 bootstrap.sh를 실행할 수 있습니다. 실행하고 나면, 사용자의 Working Directory에서 /client 디렉토리가 사라진 것을 확인할 수 있습니다.

$ ./bootstrap.sh browser
Running ‘git sparse-checkout init --cone’
Running ‘git sparse-checkout set service web/browser’

$ ls
bootstrap.sh*  LICENSE.md  README.md  service/  web/

$ ls service/
common/  identity/  items/  list/

$ ls web
browser/

$ find . -type f | wc -l
641

 

# bootstrap.sh
#!/bin/bash -e

SCRIPTNAME=$0
die() {
	echo "$SCRIPTNAME: $1"
	exit 1
}

TEAM=$1

case $TEAM in
"android")
	FOLDERS="client/android"
	;;
"identity")
	FOLDERS="service/common service/identity"
	;;
"browser")
	FOLDERS="service web/browser"
	;;
*)
	die "please specify a valid team"
	;;
esac

echo "Running 'git sparse-checkout init --cone'"
git sparse-checkout init --cone

echo "Running 'git sparse-checkout set $FOLDERS'"
git sparse-checkout set $FOLDERS

 

콴다 프론트엔드 팀에서는 git sparse-checkout을 보다 효율적으로 사용하기 위해 다음과 같이 bootstrap.sh 스크립트를 조금 수정했습니다. 목적 조직 구조에 따라서 한 그룹의 개발자들이 같은 도메인의 여러 프로젝트들을 동시에 개발하고 있기 때문에, 그룹에 속한 여러 프로젝트들의 묶음으로 sparse-checkout을 할 수 있도록 하였습니다.

 

#!/bin/bash

PROJECT=$1

case $PROJECT in
"tutor")
  FOLDERS="
    services/tutor-web
    libraries/qanda-design-system-wide
    ...
    .yarn scripts .github .vscode
  ";;

"search")
  FOLDERS="
    services/qanda-ai-web
    services/qalculator-result-web
    ...
    .yarn scripts .github .vscode
  ";;
  
"core")
  FOLDERS="
    services/styleshare-event-webview
    ...
    .yarn scripts .github .vscode
  ";;

"adaptive-content")
  FOLDERS="
    services/coin-membership-webview 
    services/premium-membership-webview
    ...
    .yarn scripts .github .vscode
  ";;

*)
  FOLDERS="services/$PROJECT .yarn scripts .github .vscode";;


esac

# only "files(not directory)" on root
echo "Running 'git sparse-checkout init --cone"
git sparse-checkout init --cone

# add specific service directory
echo "Running 'git sparse-checkout set $FOLDERS"
git sparse-checkout set $FOLDERS

위의 bootstrap.sh와 관련된 커맨드들을 루트 package.json에 등록해서 다음과 같이 사용하고 있습니다.

 

 

# enable sparse-checkout
yarn sc tutor

# disable sparse-checkout
yarn sc:reset

 

Conclusion

위에서 설명한 코드 에디터와 관련된 문제를 해결하기 위해 sparse-checkout 이외에도 "특정 프로젝트를 root로 설정해서 코드에디터를 여는" 방법을 사용할 수도 있습니다. 실제로 콴다 프론트엔드 팀에서도 그때그때 상황에 따라 두 가지 방법을 각자가 편한 대로 섞어서 사용하고 있습니다. 웹서비스들이 마이크로 서비스 패턴을 따르는 경우가 많아지면서, 모노레포에 대한 니즈가 많이 생기고 있고, 이에 따라 git에서도 sparse-checkout / shallow-clone / sparse-index 등의 여러 기능들을 업데이트하고 있는 것으로 보입니다. Github Blog의 글들을 참고해서 모노레포를 더 잘 사용하기 위한 버전 관리 방법을 지속적으로 업데이트할 예정입니다

 

 

https://github.blog/?s=monorepo 

 

You searched for monorepo | The GitHub Blog

Prebuilding codespaces is now supported for multi-repository and monorepo projects Monorepo performance can suffer due to the sheer number of files in your working directory. Git’s new builtin file system monitor makes it easy to speed up monorepo perfor

github.blog

 

 

Reference

https://github.blog/2020-01-17-bring-your-monorepo-down-to-size-with-sparse-checkout/

https://git-scm.com/book/en/v2/Appendix-C:-Git-Commands-Getting-and-Creating-Projects

 

반응형