xtring.dev

[모노레포] PNPM과 Turborepo로 말아올려 Vite 처럼 빠르게 Application과 UI 개발하기 본문

Front-End/Mono Repo

[모노레포] PNPM과 Turborepo로 말아올려 Vite 처럼 빠르게 Application과 UI 개발하기

xtring 2024. 11. 24. 20:29

 

 

  배경

이커머스 프로덕트는 상품을 전시하고 전시된 상품을 사용자들에게 자세한 설명을 전달하는 '상품 상세 페이지'가 있습니다. 상품의 콘텐츠를 만들기 위해서는 상품의 메타 정보 뿐만 아니라 상품에 대한 자세한 설명, 판매자의 의도가 담긴 글과 이미지를 잘 정리해서 설명해야합니다. 상품상세 페이지는 국내외 이커머스 서비스에서 10년 이상 대부분 비슷한 형상을 유지하고 있습니다. 상품 판매를 위한 설명 콘텐츠를 좀 더 쉽고 효율적으로 만들어내고 일관성있게 제공하는 것을 위해 업계는 많은 시도를 하고 있습니다.

 

저는 현재 회사에서 입사부터 지금까지 상품상세를 개발하고 고도화하는 작업을 해왔습니다. 수 많은 작업을 해왔지만 그 중 쉽게 고치거나 대체하기 어려운 부분 중 타사 에디터 기반의 상품 디스크립션 작성 서비스 였습니다. 각 회사의 핏의 맞는 콘텐츠를 생성하기 위한 에디터를 만들어내는 것은 여러 회사들에서 시도하는 방법이자 꽤나 부담스러운 작업으로도 유명합니다. 그래서 유연한 에디터를 만들기 위해서 다양한 케이스를 고민하고 제품의 스팩을 결정하다보면 오버 스팩을 정의하게 되어 개발 일정이 너무 많이 나온다거나 만들게 되더라도 유지하는데 너무 많은 리소스가 드는 것 같습니다.

 

그렇다면 꼭 필요한 최소한의 스팩으로 개발하고 이터레이션을 통한 고도화를 진행해보면 좋겠다고 판단하였습니다. 그래서 팀은 완전 자유 양식 작성이 가능한 에디터가 아닌 회사의 상품 전시 운영자가 상품상세 디스크립션을 쉽게 만들 수 있도록 하는 템플릿 에디터를 만들게 되었습니다.

 

 

  고민하는 것

한가지 고려해볼 것은 만들어지는 템플릿 에디터는 여러 사이트에 제공될 수 있고 제공하고자 하는 플랫폼에 Plug-in 방식으로 적용할 수 있도록 하는 것이었습니다. 또 만들어질 템플릿 UI 역시 함께 개발되고 배포되는 것이었습니다. 그래서 고민하게 된 것이 모노레포 형식의 프로젝트 세팅이었습니다. 이 글의 아래에서는 모노레포를 세팅하고 결정 방식에 대한 이유를 설명드리겠습니다.

 

 

  프로젝트 세팅하기

UI 패키지에서 만들어지는 UI 컴포넌트를 에디터에서 바로 사용하기 위해서 모노레포를 구성합니다.

 

팀은 PNPM을 사용하여 모노레포를 구성하기로 결정하여 Vite를 사용하여 apps와 packages 디렉터리 내 React 프로젝트를 설정하게 되었습니다. 여기에 프로젝트 별 의존성 빌드를 쉽고 빠르게 관리하기 위해서 Turborepo를 선택하게 되었습니다.PNPM + TurboRepo 기반의 모노레포 환경을 전제로 합니다.

* 세팅에 필요한 실제 명령어는 자세하게 설명하지 않고 문서로 대체합니다.

 

1. 프로젝트 구조

PNPM 프로젝트에는 apps와 packages 디렉터리를 프로젝트를 세팅합니다. 일반적으로 apps 디렉터리 아래로는 실행 가능한 어플리케이션을 구성하고(예: 에디터), packages는 공통으로 관리하고 배포될 라이브러리(예: UI 컴포넌트)를 포함합니다.

https://pnpm.io/ko/installation

 

설치하기 | pnpm

필수 구성 요소

pnpm.io

 

* TurboRepo 설정

TurboRepo를 프로젝트에 추가합니다.

pnpm add turbo --save-dev --ignore-workspace-root-check

 

프로젝트 루트에 turbo.json 파일을 생성하고 파이프라인과 캐싱 규칙을 정의합니다.

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

 

 

주요 TurboRepo 명령어는 아래 접은 글에 간단하게 설명하겠습니다.

더보기

1) 전체 빌드

pnpm turbo run build

 

2) 변경된 워크스페이스만 빌드

pnpm turbo run build --filter=[...changed]

 

3) 특정 워크스페이스 실행

pnpm turbo run build --filter=@my-org/ui

 

4) 캐싱 무효화

pnpm turbo run build --no-cache

 

 

이렇게 아래와 같은 프로젝트가 설정되었습니다.

/my-monorepo
  /apps
    /editor
  /packages
    /ui    
  pnpm-workspace.yaml
  turbo.json

 

 

 

2. 각 프로젝트에 Vite 프로젝트 설치 및 세팅하기

팀은 에디터 프로젝트는 최신 React를 사용하고, UI 컴포넌트 패키지는 다른 레포에서도 호환될 수 있도록 사용될 다른 프로젝트의 React 버전에 맞게 선택했습니다.  

pnpm create vite

https://ko.vite.dev/guide/

 

Vite

Vite, 프런트엔드 개발의 새로운 기준

ko.vite.dev

 

 

UI 컴포넌트의 경우 Github Packages로 배포하여 Organization 내 다른 프로젝트에서도 사용할 수 있도록 하기 위해 약간의 추가 세팅이 필요합니다.

 

packages/ui/vite.config.ts

packages/ui/vite.config.ts 파일을 생성하고 아래와 같이 설정합니다.

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import dts from 'vite-plugin-dts';
import path from 'path';

export default defineConfig({
  plugins: [
    react(),
    dts({
      include: ['src'],
    }),
  ],
  build: {
    lib: {
      entry: path.resolve(__dirname, 'src/index.ts'),
      name: 'UIComponents',
      fileName: (format) => `ui.${format}.js`,
    },
    rollupOptions: {
      external: ['react', 'react-dom'],
      output: {
        globals: {
          react: 'React',
          'react-dom': 'ReactDOM',
        },
      },
    },
  },
});

 

lib: 라이브러리 빌드를 위한 설정

external: React와 ReactDOM은 외부 종속성으로 처리

vite-plugin-dts: TypeScript 타입 정의 파일(.d.ts) 생성

 

packages/ui/tsconfig.json

 

{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "lib": ["dom", "esnext"],
    "jsx": "react-jsx",
    "declaration": true,
    "declarationMap": true,
    "outDir": "dist",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "moduleResolution": "node",
    "baseUrl": ".",
    "paths": {
      "@my-monorepo/ui/*": ["src/*"]
    }
  },
  "include": ["src"]
}

 

packages/ui/package.json

{
  "name": "@scope-name/ui",
  "private": false,
  "version": "0.0.1",
  "repository": {
    "type": "git",
    "url": "https://github.com/my-team/my-monorepo.git"
  },
  "author": "FrontEndTeam",
  "type": "module",
  "main": "./dist/index.es.js",
  "module": "./dist/index.es.js",
  "types": "./dist/types/index.d.ts",
  "files": [
    "dist"
  ],
  "license": "MIT",
  "publishConfig": {
    "<@scop-name>:registry": "https://npm.pkg.github.com/"
  },
  ...
 }

UI 컴포넌트 패키지는 위에서 설명했듯이 Github Organization의 Packages에 배포되고 관리되어야하기 때문에 위 세팅이 필수입니다. 패키지가 배포될 scope-name을 정확히 입력하고 publishConfig를 정확히 설정합니다.

 

첫 배포 후 Github Actions를 사용하는 Organization의 다른 레포에서 사용하기 위해서는 Github Packages 에서 해당 프로젝트를 세팅을 추가로 해줘야합니다. Github Actions의 CI 실행중 해당 패키지를 설치하기 위해서는 https://github.com/orgs/<organization-name>/packages/npm/<package-name>/settings에서 Manage Access Actions 항목에 엑세스 가능한 레포를 추가해줘야 합니다.

 

3. PNPM 프로젝트 실행시 필요한 기본 명령어

1) 워크스페이스 연결

pnpm install

루트 디렉터리에서 실행해 워크스페이스를 연결합니다.

 

2) UI 패키지 빌드

pnpm --filter @my-monorepo/ui build

UI 패키지를 빌드합니다.

 

2-1) UI 패키지 Github Packages에 수동 배포

pnpm --filter <pakage-name> publish --access=public

 

3) 에디터 실행

pnpm --filter @my-monorepo/editor dev

에디터 애플리케이션을 실행합니다.

 

 

  결론

이러한 구성의 프로젝트에서 UI 패키지는 packages/ui에서 빌드되고, apps/editor 애플리케이션에서 로컬 참조됩니다. 또 Vite를 사용해 빠른 개발 환경과 최적화된 빌드를 제공하여 합니다.

 

조만간 새로운 상품상세 디스크립션이 나올 것이 기대됩니다. 이번 프로젝트를 통해 처음 모노레포를 구성하고 공통으로 사용할 UI 패키지를 만들어 배포하는 과정 중 공유할만한 내용을 정리해보았습니다. 언제 어떻게 사용해볼지 고민만 해왔던 방식의 구성을 실행해보니 뿌듯합니다.

 

 

반응형
Comments