SUB-PJT

[#0 PJT] -구축- monorepo sub 프로젝트 시스템 구축

Dev. Dong 2025. 2. 5. 21:14

monorepo sub 프로젝트 시스템 구축


개요

서브 프로젝트를 Mono Repo 구조 (pnpm + turbo + vite) 로 개발하고 라우터를 통해 관리할 수 있는 시스템을 구축하기 위한 프로젝트이다.

주의사항

  1. monorepo 관련 내용을 학습하여 다양한 서브 프로젝트를 라우터를 통해 한번에 build하여 볼 수 있는 구조를 구축
    • 스스로 학습하여 생각한 구조로 잘못된 구조일 수도 있음 (댓글을 통해 구조의 문제점을 공유해주시면 감사하겠습니다^^)
  2. 학습을 통해 만들어진 과정이므로 중간에 생략된 과정 및 오류가 있을 수 있으며, 오류 발생 시 문의 바람
  3. Mono Repo를 통해 프로젝트를 구성한 이유
    1. 다양한 프로젝트를 한번의 빌드를 통해 확인 가능 (라우팅 경로를 이동하여 프로젝트를 확인 가능)
    2. 라이브러리 패키지 등을 개별로 관리하여 특정 pjt 수정 시 build 시간 단축
    3. 공용 컴퍼넌트 및 공용 패키지를 한번에 설치 및 사용 가능 (pnpm과 turbo 사용)

개발 환경

  • TypeScript / React / Node / Vite

사용 기술

  • pnpm 9.15.4
  • turbo 2.4.0
  • react-router-dom 7.1.5

개발 진행 방향

 

  1. vite를 통해 root pjt / test pjt를 생성
  2. root와 test를 목적에 맞게 구성
    • root pjt => test 뿐 아니라 앞으로 추가될 pjt에 대한 router 경로 매핑과 dist 파일 경로 매핑을 위한 pjt 파일
    • test pjt => router가 정상적으로 동작하는지 확인하기 위한 test용 파일

기능

  1. root 서버의 /test 경로에서 test pjt를 라우팅 하는 기능

 

개발

📁 폴더 구조

pjt-monorepo/
│── apps/
│   ├── root/
│   ├── test/
│── turbo.json
│── package.json
│── pnpm-workspace.yaml

 

1. 프로젝트 Vite 설치

  • pjt-monorepo 폴더 생성 및 pnpm 세팅
mkdir pjt-monorepo && cd pjt-monorepo
npm install -g pnpm
pnpm init
  • packageManger로 pnpm 등록 (package.json 파일 수정)
{
  "name": "pjt-monorepo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "packageManager": "pnpm@9.15.4",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "turbo": "^2.4.0"
  }
}
  • Turborepo 설치
pnpm add turbo -D

2. pnpm workspace 설정

  • pnpm-workspace.yaml 파일 생성 (Monorepo 패키지 관리용)
packages:
  - apps/*
  - packages/*

3. vite 앱 생성 (root, test)

  • vite 앱 생성 (root, test)
mkdir -p apps/root apps/test apps/video
pnpm create vite apps/root --template react-ts
pnpm create vite apps/test --template react-ts
pnpm install

 

4. Turborepo 설정

  • /pjt-monorepo 경로에 turbo.json 파일 생성 (빌드 및 실행 관리용)
    • 모든 앱을 build 작업 후 결과물을 dist/앱 형식으로 저장할 예 
{
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**"]
    },
    "dev": {
      "cache": false
    }
  }
}

5. Vite 앱 설정

  • 빌드 파일을 공통 dist 폴더에 저장되도록 설정 (관리 편리성을 위해)
  • /pjt-monorepo/apps/root/vite.config.ts 수정
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

export default defineConfig({
  plugins: [react()],
  base: "/test/",
  build: {
    outDir: "../../dist/test"
  }
});
  • /pjt-monorepo/apps/test/vite.config.ts 수정
import { defineConfig } from "vite";
import path from "path";

export default defineConfig({
  server: {
    proxy: {}
  },
  build: {
    outDir: "../../dist/root"
  },
  resolve: {
    alias: {
      "@test": path.resolve(__dirname, "../../dist/test"),
    }
  }
});

6. root 앱 정적 파일 서빙 설정

  • root 앱에서 test의 dist 파일을 /test 경로 제공 설정
  • apps/root/index.html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>pjt-monorepo root</title>
  </head>
  <body>
    <h1>Root App</h1>
    <nav>
      <ul>
        <li><a href="/test/">Test 앱</a></li>
      </ul>
    </nav>
  </body>
</html>
  • apps/root/server.js
const express = require("express");
const path = require("path");

const app = express();
const PORT = 3000;

app.use("/test", express.static(path.join(__dirname, "../../dist/test")));

app.get("/test/*", (req, res) => {
  res.sendFile(path.join(__dirname, "../../dist/test/index.html"));
});

app.get("/", (req, res) => {
  res.sendFile(path.join(__dirname, "index.html"));
});

app.listen(PORT, () => {
  console.log(`Server running at http://localhost:${PORT}`);
});

7. 실행 (pjt-monorepo에서 실행)

  • Turbo를 통한 각 앱 병렬 빌드
pnpm turbo run build
  • 빌드 시 root앱의 server.js의 package 미설치로 인한 오류 발생
pnpm add -D @types/node -F root
pnpm add -D express -F root
  • 아래 오류 발생시 app의 package.json 파일의 "type": "module" 지우기
const express = require("express");
                ^

ReferenceError: require is not defined in ES module scope, you can use import instead        
This file is being treated as an ES module because it has a '.js' file extension and '[Path]' contains "type": "module". To tre
at it as a CommonJS script, rename it to use the '.cjs' file extension.
  • root 서버 실행
node apps/root/server.js