drrr은 백오피스와 실제 서비스를 관리하는 두 개의 프로젝트를 운영해야 했습니다. 이 과정에서 프로젝트 관리의 복잡성과 중복 코드 및 컴포넌트 관리의 비효율성이 예상되었습니다.

이 문제를 해결하기 위해 토스 기술 블로그에서 소개된 Monorepo 구조에 주목하게 되었고, 이를 drrr 프로젝트에 도입하기로 결정했습니다. Monorepo를 적용함으로써 두 프로젝트가 공통으로 사용하는 컴포넌트를 하나의 저장소에서 통합적으로 관리할 수 있게 되었으며, 이를 통해 중복 작업을 줄이고 효율적인 개발 환경을 구축할 수 있었습니다.

Multi-repo란

각 프로젝트나 서비스가 별도의 저장소에 관리되며, 프로젝트마다 독립적인 버전 관리와 개발 환경을 가집니다.

Multi-repo의 장단점

장점

  • 독립성과 자율성: 각 프로젝트가 독립적으로 운영되므로 개발 주기가 유연합니다.
  • 복잡성 관리: 개별 프로젝트의 규모와 복잡성을 효율적으로 관리할 수 있습니다.
  • 세밀한 접근 제어: 프로젝트별로 권한을 별도로 설정할 수 있어 보안 관리가 용이합니다.

단점

  • 코드 중복: 프로젝트 간 코드 공유가 어렵고, 중복 코드가 늘어날 수 있습니다.
  • 일관성 부족: 여러 프로젝트 간에 동일한 기준을 유지하기 어렵습니다.
  • 관리 부담: 여러 저장소를 관리하면서 복잡성이 증가할 수 있습니다.

Monorepo란?

여러 개의 프로젝트 또는 패키지를 단일 저장소에서 관리하는 방식을 의미합니다. 이 방식은 하나의 저장소에서 서로 관련된 프로젝트들을 효율적으로 관리하고 통합할 수 있도록 설계되었습니다.

Monorepo의 장단점

장점
1. 코드 공유와 재사용성 향상
- 모든 패키지가 동일한 저장소에 있으므로, 코드 공유와 모듈화가 용이합니다.
- 공통 코드 업데이트가 바로 다른 패키지에 반영됩니다.
2. 통합된 버전 관리
- 단일 버전 관리 시스템으로 모든 프로젝트를 관리할 수 있어 변경 사항 추적이 간편합니다.
3. 일관된 개발 환경
- 저장소 전체에서 동일한 개발 환경과 도구를 설정할 수 있습니다.
4. CI/CD 관리 단순화
- 단일 파이프라인에서 여러 프로젝트를 관리할 수 있습니다.

단점
1. 복잡성 증가
- 저장소 크기가 커지고, 빌드 및 테스트 시간이 길어질 수 있습니다.
2. 권한 관리의 어려움
- 저장소 전체에 대해 접근을 부여하거나 제한해야 하므로, 특정 프로젝트에만 접근을 허용하기 어렵습니다.
3. 초기 설정 부담
- 효율적인 관리를 위해 추가 도구 및 설정이 필요할 수 있습니다.

Monorepo를 도와주는 도구

  1. lerna

lerna는 Monorepo 관리의 초기 도구 중 하나로, 다수의 Javascript/Typescrirpt 패키지를 효율적으로 관리할 수 있도록 설계되었습니다.

주요 특징

  • 패키지 버전 관리: 독립적 버전 관리와 통합적 버전 관리 지원
  • Dependency Hoistring: 공통 의존성을 끌어올려 중복 제거.
  • 빌드 및 배포: 패키지 간 의존성 순서를 고려해 빌드하거나, 필요한 패키지만 선택적으로 배포 가능
  1. Turborepo
    Turborepo는 고성능 Monorepo 관리 도구로, 캐싱과 병렬 처리를 통해 대규모 프로젝트에서도 뛰어난 성능을 제공

주요 특징

  • Remote Caching: 빌드 결과를 원격 서버에 캐싱하여 팀원 간 재사용 가능
  • Incremental Builds: 변경된 파일만 다시 빌드.
  • Zero-Config Integration: 초기 설정이 간단하며, 기존 프로젝트에 쉽게 통합 가능.
  1. Nx
    Nx는 Monorepo 관리 및 생산성 향상에 초첨을 맞춘 도구로, 대규모 프로젝트에서 효율적으로 작업할 수 있도록 설계되었습니다.

주요 특징

  • Graph Analysis: 프로젝트 간 의존성을 시각화하고, 필요한 부분만 빌드/테스트.

  • Plugin Ecosystem: Angular, React, Node.js 등을 위한 다양한 플러그인 제공.

  • Custom Executors: 사용자 정의 빌드/테스트 워크플로 설정 가능.

  1. Yarn Workspaces
    Yarn Workspaces는 Yarn의 내장 기능으로, Monorepo를 구성하고 패키지 간 의존성을 효율적으로 관리합니다.

주요 특징

  • Dependency Hoisting: 공통 의존성을 루트로 끌어올려 설치 시간 단축.
  • Native Support: Yarn 자체에서 제공되므로, 추가 설치 불필요.
  • Symlink 관리: 로컬 패키지를 연결하여 의존성을 효율적으로 관리.

모노레포를 쉽게 도입할 수 있게 해주는 도구들이 존재하지만, 별도의 설치가 필요 없고 러닝러브가 비교 적으며, 기본적으로 제공되는 yarn의 내장 기능인 yarn workspaces를 사용하여 Monorepo를 구축하기로 하였습니다.

Yarn Workpsaces로 Monorepo 도입하기

package.json

{
"private": true,
    "workspaces": [
        "service",
        "admin",
        "component"
    ],
}

private:true는 필수라는 점을 참고하세요. 작업 영역을 게시하기 위한 것이 아니므로 실수로 작업 영역이 노출되지 않도록 안전 조치를 추가했습니다.

폴더 및 파일 생성

최상단 루트에 workspaces에 해당하는 폴더를 생성합니다.

생성한 해당 폴더 안에 package.json 파일 생성하여 코드를 작성해줍니다.

package.json

{
    "name": "admin",
    "version": "1.0.0",
    "dependencies": {
        "cross-env": "5.0.5",
		"service": "1.0.0",
        "@monorepo/component": "*",
        "@monorepo/drrr-front": "*"
    }
}

❗ 여기서 admin 폴더에서 service 폴더를 의존할려고 한다면 dependencies에 추가하여 주면 됩니다.

확인 하기

yarn install

yarn install 명령어를 사용하면 각 폴더마다 node_modules 파일이 생성됩니다.

여기서 똑같은 package를 추가할 경우 최상단 node_modules에 폴더가 생성되며 호이스팅이 일어나게 됩니다.

Monorepo script 작성하기

"scripts": {
    "@monorepo/component": "yarn workspace @monorepo/component",
        "@monorepo/service": "yarn workspace @monorepo/service",
        "@monorepo/admin": "yarn workspace @monorepo/admin"
    },
  • script

root package.json에 스크립트를 설정해주지 않으면 매번 프로젝트를 실행할 때 마다, cd ..을 입력해야하는 번거로움이 생깁니다.

각 프로젝트의 package.json의 script를 root에서 경로 이동 없이 바로 사용할 수 있습니다. scripts의 key 값인 client와 server는 그 때 사용하는 용도입니다.

❗ 주의 사항

최상단 root package.jsonmonorepo 내 전체적인 Dependencyworkspace에 대한 정보를 가지고 있으며, 하위 디렉토리 내 각 프로젝트의 pakcage.json에서 해당 프로젝트의 Dependency를 관리하게 됩니다.

root package.json에서 관리하는 DendencyMonorepo 환경 자체에 적용되는 것이므로, 모든 프로젝트에 필요한 Dependecy만 설정하는 것이 좋습니다.

Dependency Module Hoisting

1. Hoisting 방식 이해하기

yarn worskpaces에서는 각 프로젝트의 중복되는 의존성 모듈을 최상단 node_modules에서 공유하여 함께 사용할 수 있습니다.

yarn workspaces 이전에, 우선 하나의 프로젝트가 Hoisting 되는 방식에 대해 살펴봅시다.

package-1Dependency 정보를 보면, A 모듈을 직접 의존하고 있고, A를 의존성으로 가지고 있는 C 모듈 역시 의존성 모듈로 이용하고 있습니다. 여러 곳에서 동일한 의존성을 가지는 모듈은 중복으로 설치될 필요가 없으므로, 함께 참조되어 사용할 수 있도록 node_modules 최상단 계층으로 Hoisting 됩니다.

2. 이러한 Hositing 방식이 yarn workspaces에서는 어떻게 동작할까?

Yarn Workspaces에서는 위에서 설명한 방식처럼 중복되는 의존성 모듈이 최상단 node_modules로 호이스팅되어 여러 프로젝트 간에 공유될 수 있습니다.

기본적으로 Monorepo 내 각 프로젝트는 자신만의 node_modules 폴더를 가지고 있습니다. 그러나 여러 프로젝트에서 중복되는 모듈은 최상단 node_modules로 호이스팅되어 중복 설치를 방지하고, 하위 프로젝트 간에 모듈을 공유할 수 있습니다.

3. 아직은 불안전한 yarn workspace Hoisting

그러나 아쉽게도 현재 yarn workspace에서 hoisting을 이용한 Dependency 관리는 아직 많이 불안정한 상태라고 합니다.

yarn 공식문서에도 모든 타사 라이브러리들이 아직 monorepo 환경에 최적화가 되어 있는 상태가 아닌 만큼, 이러한 Can’t Found 문제는 쉽게 해결할 수 있는 문제가 아니라고 설명하고 있습니다. 따라서 yarn에서는 별도로 Hoisting이 되지 않도록 noHoist 옵션을 제공합니다

"workspaces": {
  "nohoist": ["react-native", "react-native/**"]
}

noHoist 옵션을 사용하면 해당 모듈들이 각 하위 프로젝트의 로컬 node_modules에 설치되므로, Build 시 발생할 수 있는 not Found 오류를 방지할 수 있습니다.

후기

Monorepo를 처음 사용하면서 초기 설정에 많은 시간이 소요되었습니다. 블로그에서 제시한 대로 진행했지만, 여러 문제들이 발생해 어려움을 겪었습니다. 특히, workspace 간 의존성 문제나 hoisting 관련 이슈들이 빈번하게 발생했습니다.이 과정을 통해 monorepo의 장점과 한계를 알게 되었고, 초기 설정에서 주의할 점을 배웠습니다. 여러 프로젝트를 효율적으로 관리할 수 있다는 장점이 있지만, 의존성 관리와 같은 부분에서 신중함이 필요하다는 점을 깨달았습니다. 이번 경험은 Monorepo를 활용할 때 더 나은 설정과 관리 방법을 찾는 데 큰 도움이 되었고, 향후 비슷한 상황에서 더 잘 대응할 수 있을 것 같습니다.

📚 참고한 블로그

Monorepo로 대규모 React 프로젝트 관리하기

팀워크 향상을 위한 모노레포(Monorepo) 시스템 구축

모노레포부터 yarn berry 적용까지

profile
풋살을 좋아하는 프론트엔드 개발자

0개의 댓글

Powered by GraphCDN, the GraphQL CDN