[인프콘 2023] Turborepo, Next.js, TypeScript를 이용한 프론트엔드 모노레포 적용기

Jeongho·2024년 1월 13일
2

인프콘 2023

목록 보기
3/3

모노레포란?

하나의 레포지토리에서 독립적인 여러 프로젝트를 관리하는 방법

장점

  • 빠른 코드 수정
    • utilA, utilB를 변경 하더라도 App에 바로 반영
    • 멀티 레포의 경우 버전을 올리고 의존성을 다시 설치해야만 사용할 수 있었음
  • 각 레포마다 사용했던 같은 코드들의 중복 제거
    • 레포 마다 매번 작성던 util, component를 한곳에 관리하여 코드의 중복을 줄이고 생산성을 높일 수 있음
  • 수월한 코드 리팩토링
    • 코드를 한번에 관리하기 때문에 대규모 리팩토링이 쉬워짐
  • 코드 컨벤션 통일
    • 멀티 레포에서는 각 레포마다 다른 컨벤션을 가졌다면 모노레포에서는 한곳에서 관리하여 통일 하기 수월함
    • eslint 패키지를 만들고 각 페이지에 주입하면 컨벤션 관리에 용이
  • 통합 CI, test 관리
    • 한꺼번에 ci를 돌릴 수 있고, test를 돌릴 수 있음
    • 멀티 레포의 경우 수정 사항이 생기면 각 레포마다 테스트를 돌렸다면, 'yarn test' 명령어 한 번으로 전체 테스트 가능

단점

  • 의존성 관리 복잡
    • 서로 의존성 연결이 쉽기 때문에 과도한 의존 관계가 생길 수 있음
    • A 패키지에 B 패키지를 의존하게 하면 C 패키지에 B 패키지를 명시하지 않아도 B 패키지를 사용할 수 있음 이는 배포할 때 문제를 야기함
    • 실제로 이번 프로젝트에서 editor라는 패키지는 design-system을 패키지를 주입할 수 있지만 design-system은 editor를 주입할 수 없음
  • 무거운 프로젝트 (CI 속도 저하)
    • 하지만 멀티 레포에서는 하나의 변경 사항이 다른 레포의 어떤 영향을 주었을지 모르고, 전부 테스트를 돌려야 했음 모노레포로 옮기면서 오히려 장점같은 단점
  • Code ownership 위배
    • 작은 팀이 사용한다면 상관 없겠지만 많은 팀이 하나의 레포를 관리한다면 코드 오너쉽을 위배하여 관리 체계가 혼동될 수 있음

모노레포를 선택하게 된 계기

Hops: 개발자를 위한 백오피스, 어드민을 쉽게 만들어내는 솔루션

Hops라는 제품에는 여러 시점이 존재

Hops의 개발자 시점 / Hops로 백오피스를 만드는 개발자 시점 / Hops로 백오피스를 운영하는 운영팀 시점

Hops의 개발자 시점

  • Hops 솔루션을 개발하기 위한 UI를 개발해야 함 (App)
  • Hops로 개발하는 개발자가 쓸 수 있는 UI를 제공 해주어야 함 (@hops/ui)
    • UI는 추후 라이브러리로 배포 될 수 있어야 함
  • UI와 App에 공통적으로 디자인 시스템을 적용해야 함 (@hops/design-system)
  • 컴파일을 통해 리액트로 코드를 추출 할 수 있어야 함 (@hops/compile)

Hops로 백오피스를 만드는 개발자 시점

  • 제공된 UI로 백엔드 로직을 연결해야 함
  • 추후 원하면 리액트로 커스텀 개발 해야 함
  • 코드를 다운 받아 로컬 개발을 할 수 있어야 함

Hops로 백오피스를 운영하는 운영팀 시점

  • Hops를 통해 회사 제품을 운영 해야함
  • 백오피스를 수정하지는 못해야 함

결론

  • 개발한 UI를 Hops의 개발자 뿐만 아니라 Hops로 백오피스를 만들 고객사의 개발자들도 쓸 수 있어야 한다!
    • React로 컴파일을 해서 로컬에서 개발이 가능 하도록 하기 때문.
  • 물론 UI만 라이브러리 레포로 만들어서 사용해도 됨
  • 하지만 모노레포를 적용함으로써 관리 코스트의 이점을 생각
  • 또한 앞으로 공통으로 쓰일 많은 패키지들을 위해 모노레포를 적용하기로 결정

왜 Turborepo인가?

실제로 Turborepo, Nx, Yarn workspace 만들어 봄

  • 세팅은 역시 Turborepo, Nx가 빠르다는 인상
  • Turborepo가 Next.js Vercel, TypeScript에 친화적이라 쓰고 싶었지만 yarn Plug'n'Play (a.k.a "PnP") 기능을 지원하지 않음
  • Nx는 다 해주지만 너무 커다란 프레임워크 느낌
  • Yarn workspace 세팅 해봤는데 단독으로 쓰기에는 다른 툴체인에 비해 부족함

결론

아무래도 원래 쓰는 스택을 생각하면 최대한 활용도가 높아보이는 Turborepo 채택!

Turborepo 적용하기

  • Next.js / TypeScript / Yarn 사용 (공식에서는 pnpm 추천)
  • 설치해보기 yarn dlx create-turbo@latest (yarn version 3.x.0)

Turborepo 구성 살펴보기

  • apps
    • apps/docs
      • 문서 폴더
      • 기본 next 프로젝트
    • apps/web
      • 실제 App 프로덕트 폴더
      • 기본 next 프로젝트
  • packages
    • packages/eslint-config-custom
      • eslint 커스텀하여 공통으로 쓸 수 있는 폴더
    • packages/tsconfig
      • Config 기본으로 설정하는 폴더
      • next 설정도 할 수 있음
    • packages/ui
      • UI 패키지 폴더
      • 공통으로 사용되는 UI 컴포넌트를 만들 수 있음

실제 Hops 프로덕트 구조

  • 원래는 스토리북이 패키지 별로 있었음
    • vercel에 띄우는게 각 패키지 별로 스토리북을 띄워야해서 리소스 낭비로 하나로 통합
  • 주요 패키지
    • compiler: Hops에서 만든 페이지/쿼리 리소스를 리액트 코드로 컴파일하는 패키지
    • core: Hops의 코어 로직을 담당하는 패키지 (직접 만든 StateMachine)
    • design-system: 디자인 시스템 패키지
    • domain: vo model을 위한 도메인 패키지
    • editor: Hops의 코드 editor를 담당하는 패키지 (SQL parser, JS parser 등)
    • lexical: Hops의 page editor를 담당하는 패키지 (실제 UI를 불러서 그리는 곳)
    • ui: UI 패키지 (라이브러리가 될 곳)

만든 패키지 연동하기

apps/web/package.json

  • 디펜던시에 사용하고자 하는 패키지 추가

새 패키지 만들어보기

수동 설치

mkdir packages/lib-a
yarn init

Code gen 사용

yarn turbo gen workspace --copy

의존성 그래프 보기

graphviz 설치

해당 명령어는 빌드와 린트에 대한 의존성을 보는 것이기 때문에 빌드만 보고 싶다면 린트는 빼도 상관 없다.

Vercel로 배포하기

  • 만약 vercel env를 사용한다면 turbo.json에 추가해주어야 함

  • Root 디렉토리: apps/web으로 설정

  • 빌드 세팅은 안해도 됨..인줄 알았으나..

  • 이번 프로젝트의 경우 pdkit 의존성 설치를 따로 해주어야 해서 따로 설정을 해주어야 했음

  • 이 경우 vercel에서 command override를 제공

Tip 1. filter 사용하기

Root 상황에서 yarn dev, yarn test와 같은 명령어 실행 시 모든 app과 package가 실행 됨.

하나의 모듈만 실행 시키고 싶다면?

yarn dev --filter=web
  • 프로젝트를 실행 시킬때 해당 프로젝트 경로로 이동 후 yarn dev해도 되지만 Root 경로에서 yarn dev --filter=web으로 하면 web만 실행 시킬 수 있음

Tip 2. path alias 하기

apps/web에서 import Button from '@hops/ui/Button'을 하고 싶다면?

compilerOptions path 설정하기

"compilerOptions": {
	"baseUrl": ".",
    "paths: {
		"@hops/ui/*": [
        	"./*"
        ]
	}	
}

적용 후 장 / 단점

장점

  • 패키지 분리로 인해 공통 로직 관리가 쉬워졌다.
    • @hops/core에서 fixture, test에 필요한 util 함수를 만들어서 재사용을 잘 활용하고 있음
  • 코드 구조가 한눈에 잘 보인다
    • 패키지의 코드 구조를 확인 하고 싶을 때 바로바로 확인이 가능하여 디버깅에 용이함.
  • Next, TypeScript와 찰떡콩떡

단점

  • 의존성 관리가 힘들다
    • 최근에는 root에 모든 패키지를 설치하기로 합의 함
    • yarn 1에서는 A패키지의 Z의존성의 버전이 1이었다면 B패키지의 z의존성의 버전이 2인 경우 제대로 나뉘어져서 주입되지 않는 이슈가 있음
    • Yarn berry로 올리면서 이슈는 해결 됨
  • 패키지 간의 이존성 관계 설계 복잡
    • CI나 컴파일러가 막아주는 부분은 없기 때문에 사람이 생각 해야함
    • 의존 관계로 만들기 전 구조를 먼저 생각하고 규칙을 정하기로 함
      • editor는 design-system을 중비할 수 있지만 design-system은 editor를 주입할 수 없음
  • 모든 패키지를 테스트 하기 때문에 CI는 역시 오래 걸린다
    • 최근에는 모든 테스트를 돌리지 않고 변동 사항이 있는 부분만 테스트 돌리게 변경함

결론

모노레포는 하나의 제품에 필요한 패키지를 여러개 만들어야 할 때 적절한 대안

이러한 특성 덕분에 이번 프로젝트에서의 모노레포는 아주 적절했던 방안이라 생각

마무리

이번 영상에서는 Turborepo, Next.js, TypeScript 조합으로 모노레포를 적용하는 방법에 대한 내용을 다뤘습니다. 저번 스터디 때 모노레포를 적용해보고 싶다는 분들이 많아서 모노레포가 뭐지? 하고 잠시 찾아본적이 있어서 더 관심있게 들었던 거 같습니다. 확실히 회사의 입장에서 관리해야 하는 repo가 많다면 고려해볼만한 방법인 거 같습니다.

Reference

profile
주도적으로 문제를 정의하고 코드를 통해 해결합니다.

0개의 댓글