본문 바로가기

프로그래밍/frontend.

Turborepo를 사용하여 모노레포 구성하기: Storybook 포함

안녕하세요. 이번 글에서는 프론트엔드 개발 환경에 모노레포를 도입하는 방법을 소개 하도록 하겠습니다

 

모노레포는 하나의 코드베이스에서 여러 애플리케이션과 패키지를 효율적으로 관리할 수 있는 구조입니다. 특히 여러 앱과 패키지를 한 곳에서 관리할 때 Turborepo는 빠른 빌드, 의존성 관리, 최적화된 캐싱을 통해 생산성을 높여줍니다. 이 글에서는 app과 package 폴더로 구성된 모노레포 프로젝트를 단계별로 구성하고, 각 폴더의 역할을 정의하는 과정을 안내합니다.

프로젝트 구성도

프로젝트는 다음과 같이 구성됩니다.

 
project-root/
├── apps/
│   ├── app1/
│   ├── app2/
│   └── storybook/
└── packages/
    ├── ui/
    ├── apiclient/
    └── utils/

1. 프로젝트 초기 설정

1-1. Turborepo 설치

먼저, Turborepo를 설치하여 초기화합니다. 터미널에서 다음 명령어를 입력합니다.

# 프로젝트 생성 및 Turborepo 설치
npx create-turbo@latest turbo-monorepo
cd turbo-monorepo

 

위 명령을 실행하면 기본적인 Turborepo 폴더 구조가 생성됩니다.


1-2. 폴더 구조 및 각 역할 설명

  • apps/: 개별 애플리케이션들이 포함되는 폴더입니다. 여기서는 app1, app2, 그리고 UI 개발을 위한 storybook이 들어갑니다.
  • packages/: 앱에서 공통으로 사용되는 패키지들이 포함됩니다. ui는 재사용 가능한 UI 컴포넌트를 모아두며, apiclient는 API 호출과 통신을 담당하고, utils는 공통 유틸리티 함수를 담습니다.

2. 앱과 패키지 설정

2-1. app1, app2, storybook 생성

각 애플리케이션을 apps 폴더에 설정합니다. 여기서는 app1, app2, 그리고 Storybook 설정을 위한 storybook을 생성합니다.

mkdir -p apps/app1 apps/app2 apps/storybook
 

app1 및 app2의 초기화

app1과 app2는 Next.js를 기반으로 설정해 보겠습니다. (React로 설정해도 무방합니다)

cd apps/app1
npx create-next-app@latest .
cd ../app2
npx create-next-app@latest .
 

각 애플리케이션의 package.json에서 이름과 의존성을 Turborepo에 맞춰 조정할 수 있습니다.

2-2. Storybook 설정

storybook 폴더에 들어가 Storybook을 설정합니다.

# Storybook 설치
cd ../storybook
npx storybook init

설치 후, .storybook/main.js 파일을 열고, 모노레포 내 다른 패키지와 연동할 수 있도록 다음 설정을 추가합니다.

// .storybook/main.js
module.exports = {
  stories: ["../packages/ui/**/*.stories.@(js|jsx|ts|tsx)"],
  addons: [
    "@storybook/addon-links",
    "@storybook/addon-essentials",
    "@storybook/addon-interactions",
  ],
  framework: "@storybook/react",
};

3. 공통 패키지 구성하기 (packages 폴더)

공통으로 사용될 패키지들은 packages 폴더에 추가합니다. 여기서는 ui, apiclient, utils 세 가지 패키지를 설정하겠습니다.

3-1. UI 패키지

packages/ui 폴더를 생성하고 기본적인 컴포넌트를 추가합니다.

# UI 패키지 초기화
mkdir -p packages/ui/src
cd packages/ui
npm init -y
 

기본 버튼 컴포넌트를 만들어 봅니다.

// packages/ui/src/Button.js
import React from 'react';

export const Button = ({ children, onClick }) => (
  <button onClick={onClick}>{children}</button>
);
그리고 package.json 파일에 다음과 같은 설정을 추가해 컴포넌트가 외부에서도 사용할 수 있도록 합니다.
 
// packages/ui/package.json
{
  "name": "@turbo-monorepo/ui",
  "version": "1.0.0",
  "main": "src/index.js",
  "peerDependencies": {
    "react": ">=17.0.0"
  }
}

UI 패키지를 Storybook에서 사용하기

apps/storybook에서 @turbo-monorepo/ui 패키지를 가져와 사용할 수 있도록 합니다.

3-2. API Client 패키지

apiclient는 API 통신을 담당합니다.

mkdir -p packages/apiclient/src
cd packages/apiclient
npm init -y

기본적인 API 호출 함수를 src 폴더에 작성합니다.

// packages/apiclient/src/index.js
export const fetchData = async (url) => {
  const response = await fetch(url);
  return response.json();
};
 

3-3. Utils 패키지

마지막으로 공통 유틸리티 함수를 모아두는 utils 패키지를 설정합니다.

 
# Utils 패키지 초기화
mkdir -p packages/utils/src
cd packages/utils
npm init -y

 

 

// packages/utils/src/dateFormatter.js
export const formatDate = (date) => {
  return new Intl.DateTimeFormat("en-US").format(new Date(date));
};

package.json에 설정을 추가해줍니다.


4. 터보 레포 루트 설정

프로젝트 루트에 있는 turbo.json 파일을 열어 설정을 추가합니다. 각 패키지가 다른 패키지를 사용할 때 캐시를 효율적으로 활용하도록 설정할 수 있습니다.

 

// turbo.json
{
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**"]
    },
    "lint": {
      "outputs": []
    },
    "test": {
      "outputs": []
    }
  }
}

이 설정으로, build 작업이 다른 패키지에 의존하도록 설정하고, lint와 test는 캐시를 생성하지 않도록 합니다.


5. 실행 및 테스트

설정을 완료했으면 각 앱과 패키지가 잘 동작하는지 확인합니다.

 
# 루트 폴더에서 터보레포 빌드 실행
turbo run build

# 스토리북 실행
cd apps/storybook
npm run storybook

이제 app1과 app2는 ui, apiclient, utils 등의 패키지를 가져다 쓸 수 있으며, storybook에서 UI 컴포넌트를 미리보기로 확인할 수 있습니다.