SUB-PJT
[#0 PJT] -구축- monorepo sub 프로젝트 시스템 구축
Dev. Dong
2025. 2. 5. 21:14
monorepo sub 프로젝트 시스템 구축
개요
서브 프로젝트를 Mono Repo 구조 (pnpm + turbo + vite) 로 개발하고 라우터를 통해 관리할 수 있는 시스템을 구축하기 위한 프로젝트이다.
주의사항
- monorepo 관련 내용을 학습하여 다양한 서브 프로젝트를 라우터를 통해 한번에 build하여 볼 수 있는 구조를 구축
- 스스로 학습하여 생각한 구조로 잘못된 구조일 수도 있음 (댓글을 통해 구조의 문제점을 공유해주시면 감사하겠습니다^^)
- 학습을 통해 만들어진 과정이므로 중간에 생략된 과정 및 오류가 있을 수 있으며, 오류 발생 시 문의 바람
- Mono Repo를 통해 프로젝트를 구성한 이유
- 다양한 프로젝트를 한번의 빌드를 통해 확인 가능 (라우팅 경로를 이동하여 프로젝트를 확인 가능)
- 라이브러리 패키지 등을 개별로 관리하여 특정 pjt 수정 시 build 시간 단축
- 공용 컴퍼넌트 및 공용 패키지를 한번에 설치 및 사용 가능 (pnpm과 turbo 사용)
개발 환경
- TypeScript / React / Node / Vite
사용 기술
- pnpm 9.15.4
- turbo 2.4.0
- react-router-dom 7.1.5
개발 진행 방향
- vite를 통해 root pjt / test pjt를 생성
- root와 test를 목적에 맞게 구성
- root pjt => test 뿐 아니라 앞으로 추가될 pjt에 대한 router 경로 매핑과 dist 파일 경로 매핑을 위한 pjt 파일
- test pjt => router가 정상적으로 동작하는지 확인하기 위한 test용 파일
기능
- 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