현재 개발 중인 프로젝트는 사용자가 모바일 청첩장을 만들어 공유 가능하도록 하는 서비스이고, 사용자가 청첩장을 생성하는 페이지와 완성본이 공유되는 청첩장 페이지, 그리고 Admin 페이지 이렇게 세가지 도메인으로 이루어져 있다.
www - static 간의 공유되는 디자인 컴포넌트들이 많기도 했고, prettier와 ESLint 와 같은 설정들을 공유하면 관리 및 유지/보수하기 좋을 것 같아 모노레포로 구성하기로 했다.
한 개의 Repository 안에서 다수의 프로젝트를 관리하는 소프트웨어 개발전략.
✔️ 다수의 프로젝트 컨벤션의 일관성 유지가 용이.
✔️ 프로젝트 간 중복되는 코드를 감소시킴으로써 코드 재사용성 향상.
✔️ 다수의 프로젝트 간의 의존성을 쉽게 관리할 수 있고 유지 보수가 용이.
✔️ Repository 가 지나치게 무거워질 수 있다.
✔️ 빌드 및 테스트 시간이 길어질 수 있다.
✔️ 저장소 관리가 복잡해질 우려가 있다.
따라서 빌드와 테스트 시간을 단축하기 위해 CI/CD 파이프라인을 최적화시킬 필요가 있다.
복잡한 의존성을 가진 대규모 프로젝트에 유리.
러닝커브가 높으며 설정이 비교적 복잡한 편이다.
패키지들의 버전 동기화 및 독립적으로 관리 가능.
빌드 최적화 기능이 부족 & 상대적으로 오래된 도구.
yarn 과 조합이 좋음.
Next.js의 Vercel 배포가 Yarn Berry에서는 잘 동작하지 않는다.
빌드 최적화에 중점. 병렬 빌드 가능. 러닝커브가 비교적 낮음.
로컬 컴퓨팅 캐싱 및 로컬 작업 오케스트레이션 제공.
영향을 받는 패키지를 감지하는 기능 제공.
vercel에서 개발하고 있다.

Javascript / Typescript 기반의 monorepo 를 위한 고성능 빌드 시스템.

✔️ 로컬 캐싱을 통해 한번 빌드된 내용은 다시 빌드 X.
✔️ 병렬적으로 작업을 수행하여 멀티태스킹 능력을 극대화.
pnpm dlx create-turbo@latest --example with-tailwind
✔️ tailwind css 설정을 포함한 monorepo 생성.
📦.turbo
📦node_modules
📦apps
┣ 📂admin
┣ 📂www
┗ 📂invitation
📦packages
┣ 📂config-tailwind
┣ 📂config-eslint
┣ 📂config-typescript
┗ 📂shared
📜.prettierrc
📜.gitignore
📜 package.json
📜.pnpm-lock.yaml
📜.pnpm-workspace.yaml
📜turbo.json
📜.npmrc
TurboRepo로 생성 시, apps 폴더 내부에 web docs 존재.
✔️ web > admin docs > www 로 폴더명 변경.
✔️ 폴더를 복사해서 1개 더 생성 후에 invitation 으로 변경.
✔️ 각 폴더 내에 존재하는 package.json 의 name 을 폴더명으로 설정.
✔️ port 번호는 3000 으로 통일.
✔️ root에 존재하는 package.json 에서 dev 설정 변경.
// filter 된 파일만 실행.
"scripts": {
"dev": "turbo dev --filter www",
},
TurboRepo로 생성 시, packages 폴더 내에 ui 폴더 존재.
✔️ ui > shared 로 폴더명 변경.
→ UI 컴포넌트 외에도 types constants 파일을 공유.
✔️ shared 내부 src 폴더에 components types constants 폴더 생성.
✔️ 폴더명이 변경됨에 따라, 전체 레포 내에 있는 모든 파일들의 ui 폴더명을 shared 로 변경해주었다 😇
// page.tsx
import { Card } from '@repo/shared/components';
// layout.tsx
import '@repo/shared/styles.css';
✔️ 각 폴더 내에 있는 page.tsx layout.tsx 에서 import 에러 발생.
✔️ Card 컴포넌트 command + I 를 통해 import 불가능.
→ 경로를 찾지 못하는 듯 했다.. 🤦🏻♀️
// packages/shared/package.json
"exports": {
"./styles.css": "./src/styles.css",
"./components": "./src/components/index.ts"
},
처음에는 해당 exports 문을 삭제 후, 에러가 발생했던 import 문에 /src 를 추가.
더 이상 에러는 발생하지 않았지만,,
문제 1 ❓︎ command + I 를 통한 자동 import 불가.
문제 2❓︎ 모든 경로에 **** /src 를 추가해야했다 🤯
// shared/src/components/index.ts
export { Card } from './card';
✔️ shared/src/components 폴더 내에 index.ts 파일 생성.
✔️ index.ts 파일에 생성한 component 파일 export 문을 추가.
// packages/shared/package.json
"main": "./src/components/index.ts",
✔️ package.json 파일에서 해당 파일을 main으로 내보내기 !
// page.tsx
import { Card } from '@repo/shared';
✔️ command + I 를 통한 자동 import 가능 + 아주 깔끔해졌다 ✨✨
모노레포에 storybook을 도입해보기로 하면서 storybook 설치 위치에 대해서 많은 고민을 했다..
각 레포마다 storybook을 설치하는 방안도 있었지만, turborepo 공식문서에 나와있는대로 storybook 레포를 따로 구성해서 진행하기로 했다.
📦apps
┣ 📂admin
┣ 📂www
┣ 📂invitation
┗ 📂storybook
이런 방식으로 구성하면, storybook을 독립적으로 운영할 수 있고 문서화된 페이지를 배포해서 볼 수 있다는 장점이 있다.
// apps/storybook
pnpm dlx storybook@latest init
✔️ apps 내부에 storybook 폴더를 생성한 후, storybook 폴더 내에서 설치.
// apps/storybook/package.json
"dependencies": {
"@repo/shared": "workspace:*",
},
✔️ 설치한 폴더 내 package.json 파일에 코드 추가.
pnpm install @repo/shared --filter=storybook
✔️ 경험담인데 의존성 추가안하면 설치 안됩니다..😉
// turbo.json
{
"tasks": {
"build": {
"outputs": [
+ "storybook-static/**"
]
}
}
}
✔️ turbo.json 파일에 build 내 outputs 에 storybook-static 을 추가.
→ 빌드를 실행할 때 파일 출력이 캐시되도록 하기 위해
// .gitignore
+ storybook-static
✔️ .gitignore 파일에 storybook-static 을 추가.
→ storybook에 대한 build 의 output을 소스 제어에 커밋되지 않게 하기 위해
Storybook 설정 관련 내용은 모두 turborepo 공식문서 를 참조했습니다.