반응형
enterprise-monorepo/
├── .github/
│ └── workflows/
│ ├── ci-cd-crm.yml
│ ├── ci-cd-wms.yml
│ └── ci-cd-edi.yml
├── apps/
│ ├── crm-system/ # React 17
│ │ ├── package.json
│ │ ├── tsconfig.json
│ │ ├── webpack.config.js
│ │ ├── src/
│ │ └── public/
│ ├── wms-system/ # React 18
│ │ ├── package.json
│ │ ├── tsconfig.json
│ │ ├── vite.config.ts
│ │ ├── src/
│ │ └── public/
│ └── edi-system/ # React 19
│ ├── package.json
│ ├── tsconfig.json
│ ├── vite.config.ts
│ ├── src/
│ └── public/
├── packages/
│ ├── shared-ui/ # 공통 UI 컴포넌트
│ │ ├── package.json
│ │ ├── tsconfig.json
│ │ └── src/
│ ├── shared-utils/ # 공통 유틸리티
│ │ ├── package.json
│ │ ├── tsconfig.json
│ │ └── src/
│ └── shared-types/ # 공통 타입 정의
│ ├── package.json
│ ├── tsconfig.json
│ └── src/
├── backend/
│ ├── crm-api/ # Spring Boot + iBATIS
│ │ ├── pom.xml
│ │ └── src/
│ ├── wms-api/
│ │ ├── pom.xml
│ │ └── src/
│ ├── edi-api/
│ │ ├── pom.xml
│ │ └── src/
│ └── shared/
│ ├── common/
│ └── database/
├── tools/
│ ├── build-scripts/
│ └── docker/
├── docs/
├── package.json # Root package.json
├── pnpm-workspace.yaml
├── tsconfig.base.json
└── .env.example
1. 환경 설정
Node.js 버전 관리 (Windows)
powershell# Chocolatey 설치 (관리자 권한으로 PowerShell 실행)
Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
# Node.js 및 도구 설치
choco install nodejs-lts
choco install git
choco install openjdk17
choco install maven
# pnpm 설치 (권장)
npm install -g pnpm
# 또는 Yarn 설치
npm install -g yarn
# nvm-windows 설치 (선택사항)
choco install nvm
MariaDB 설치 및 설정
powershell# MariaDB 설치
choco install mariadb
# 서비스 시작
net start MySQL
# 데이터베이스 생성
mysql -u root -p
-- 데이터베이스 생성
CREATE DATABASE enterprise_crm CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE DATABASE enterprise_wms CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE DATABASE enterprise_edi CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- 사용자 생성 및 권한 부여
CREATE USER 'enterprise_user'@'localhost' IDENTIFIED BY 'enterprise_password';
GRANT ALL PRIVILEGES ON enterprise_*.* TO 'enterprise_user'@'localhost';
FLUSH PRIVILEGES;
2. Root 설정 파일들
package.json (Root)
{
"name": "enterprise-monorepo",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "pnpm run --parallel dev",
"dev:crm": "pnpm --filter crm-system dev",
"dev:wms": "pnpm --filter wms-system dev",
"dev:edi": "pnpm --filter edi-system dev",
"build": "pnpm run --filter './apps/*' build",
"build:crm": "pnpm --filter crm-system build",
"build:wms": "pnpm --filter wms-system build",
"build:edi": "pnpm --filter edi-system build",
"test": "pnpm run --recursive test",
"lint": "pnpm run --recursive lint",
"type-check": "pnpm run --recursive type-check",
"clean": "pnpm run --recursive clean",
"backend:dev": "concurrently \"cd backend/crm-api && mvn spring-boot:run\" \"cd backend/wms-api && mvn spring-boot:run\" \"cd backend/edi-api && mvn spring-boot:run\"",
"backend:build": "cd backend && mvn clean install",
"docker:build": "docker-compose build",
"docker:up": "docker-compose up -d",
"docker:down": "docker-compose down"
},
"devDependencies": {
"concurrently": "^8.2.2",
"cross-env": "^7.0.3",
"husky": "^8.0.3",
"lint-staged": "^15.2.0",
"typescript": "^5.3.3"
},
"engines": {
"node": ">=18.0.0",
"pnpm": ">=8.0.0"
}
}
pnpm-workspace.yaml
packages:
- 'apps/*'
- 'packages/*'
- 'backend/*'
tsconfig.base.json
{
"compilerOptions": {
"target": "ES2020",
"lib": ["dom", "dom.iterable", "ES6"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"baseUrl": ".",
"paths": {
"@shared/ui": ["packages/shared-ui/src"],
"@shared/utils": ["packages/shared-utils/src"],
"@shared/types": ["packages/shared-types/src"]
}
},
"exclude": ["node_modules", "**/node_modules/**", "dist", "**/dist/**"]
}
3. 공통 패키지 설정
packages/shared-types/package.json
{
"name": "@enterprise/shared-types",
"version": "1.0.0",
"private": true,
"main": "./src/index.ts",
"types": "./src/index.ts",
"scripts": {
"build": "tsc --build",
"clean": "rimraf dist",
"type-check": "tsc --noEmit"
},
"devDependencies": {
"rimraf": "^5.0.5",
"typescript": "^5.3.3"
},
"peerDependencies": {
"typescript": ">=5.0.0"
}
}
packages/shared-ui/package.json
{
"name": "@enterprise/shared-ui",
"version": "1.0.0",
"private": true,
"main": "./src/index.ts",
"types": "./src/index.ts",
"scripts": {
"build": "rollup -c",
"dev": "rollup -c -w",
"clean": "rimraf dist",
"type-check": "tsc --noEmit",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
},
"dependencies": {
"@enterprise/shared-types": "workspace:*",
"@enterprise/shared-utils": "workspace:*"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-typescript": "^11.1.5",
"@storybook/addon-essentials": "^7.6.6",
"@storybook/react": "^7.6.6",
"@storybook/react-vite": "^7.6.6",
"@types/react": "^18.2.45",
"rimraf": "^5.0.5",
"rollup": "^4.9.1",
"storybook": "^7.6.6",
"typescript": "^5.3.3"
},
"peerDependencies": {
"react": ">=17.0.0"
}
}
4. 각 앱별 설정
CRM 시스템 (React 17)
apps/crm-system/package.json
{
"name": "crm-system",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "webpack serve --mode development",
"build": "cross-env NODE_ENV=production webpack --mode production",
"test": "jest",
"lint": "eslint src --ext .ts,.tsx",
"type-check": "tsc --noEmit",
"clean": "rimraf dist"
},
"dependencies": {
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-router-dom": "^6.2.1",
"@enterprise/shared-ui": "workspace:*",
"@enterprise/shared-utils": "workspace:*",
"@enterprise/shared-types": "workspace:*",
"axios": "^1.6.2",
"antd": "^4.24.15"
},
"devDependencies": {
"@types/react": "^17.0.52",
"@types/react-dom": "^17.0.18",
"typescript": "^5.3.3",
"webpack": "^5.89.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.15.1",
"html-webpack-plugin": "^5.6.0",
"ts-loader": "^9.5.1",
"css-loader": "^6.8.1",
"style-loader": "^3.3.3",
"cross-env": "^7.0.3",
"rimraf": "^5.0.5"
}
}
4. 각 앱별 설정
CRM 시스템 (React 17)
apps/crm-system/package.json
{
"name": "crm-system",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "webpack serve --mode development",
"build": "cross-env NODE_ENV=production webpack --mode production",
"test": "jest",
"lint": "eslint src --ext .ts,.tsx",
"type-check": "tsc --noEmit",
"clean": "rimraf dist"
},
"dependencies": {
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-router-dom": "^6.2.1",
"@enterprise/shared-ui": "workspace:*",
"@enterprise/shared-utils": "workspace:*",
"@enterprise/shared-types": "workspace:*",
"axios": "^1.6.2",
"antd": "^4.24.15"
},
"devDependencies": {
"@types/react": "^17.0.52",
"@types/react-dom": "^17.0.18",
"typescript": "^5.3.3",
"webpack": "^5.89.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.15.1",
"html-webpack-plugin": "^5.6.0",
"ts-loader": "^9.5.1",
"css-loader": "^6.8.1",
"style-loader": "^3.3.3",
"cross-env": "^7.0.3",
"rimraf": "^5.0.5"
}
}
apps/crm-system/webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
module.exports = {
entry: './src/index.tsx',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.[contenthash].js',
clean: true,
},
resolve: {
extensions: ['.ts', '.tsx', '.js', '.jsx'],
alias: {
'@shared/ui': path.resolve(__dirname, '../../packages/shared-ui/src'),
'@shared/utils': path.resolve(__dirname, '../../packages/shared-utils/src'),
'@shared/types': path.resolve(__dirname, '../../packages/shared-types/src'),
},
},
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html',
}),
],
devServer: {
port: 3001,
open: true,
historyApiFallback: true,
},
};
WMS 시스템 (React 18)
apps/wms-system/package.json
{
"name": "wms-system",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"test": "vitest",
"lint": "eslint src --ext .ts,.tsx",
"type-check": "tsc --noEmit",
"clean": "rimraf dist"
},
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.8.0",
"@enterprise/shared-ui": "workspace:*",
"@enterprise/shared-utils": "workspace:*",
"@enterprise/shared-types": "workspace:*",
"axios": "^1.6.2",
"@mui/material": "^5.15.1",
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0"
},
"devDependencies": {
"@types/react": "^18.2.45",
"@types/react-dom": "^18.2.18",
"@vitejs/plugin-react": "^4.2.1",
"typescript": "^5.3.3",
"vite": "^5.0.10",
"vitest": "^1.1.0",
"cross-env": "^7.0.3",
"rimraf": "^5.0.5"
}
}
WMS 시스템 (React 18)
apps/wms-system/package.json
{
"name": "wms-system",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"test": "vitest",
"lint": "eslint src --ext .ts,.tsx",
"type-check": "tsc --noEmit",
"clean": "rimraf dist"
},
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.8.0",
"@enterprise/shared-ui": "workspace:*",
"@enterprise/shared-utils": "workspace:*",
"@enterprise/shared-types": "workspace:*",
"axios": "^1.6.2",
"@mui/material": "^5.15.1",
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0"
},
"devDependencies": {
"@types/react": "^18.2.45",
"@types/react-dom": "^18.2.18",
"@vitejs/plugin-react": "^4.2.1",
"typescript": "^5.3.3",
"vite": "^5.0.10",
"vitest": "^1.1.0",
"cross-env": "^7.0.3",
"rimraf": "^5.0.5"
}
}
apps/wms-system/vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
'@shared/ui': path.resolve(__dirname, '../../packages/shared-ui/src'),
'@shared/utils': path.resolve(__dirname, '../../packages/shared-utils/src'),
'@shared/types': path.resolve(__dirname, '../../packages/shared-types/src'),
},
},
server: {
port: 3002,
open: true,
},
build: {
outDir: 'dist',
},
});
EDI 시스템 (React 19)
apps/edi-system/package.json
{
"name": "edi-system",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"test": "vitest",
"lint": "eslint src --ext .ts,.tsx",
"type-check": "tsc --noEmit",
"clean": "rimraf dist"
},
"dependencies": {
"react": "^19.0.0-rc.0",
"react-dom": "^19.0.0-rc.0",
"react-router-dom": "^6.20.0",
"@enterprise/shared-ui": "workspace:*",
"@enterprise/shared-utils": "workspace:*",
"@enterprise/shared-types": "workspace:*",
"axios": "^1.6.2",
"@chakra-ui/react": "^2.8.2",
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0"
},
"devDependencies": {
"@types/react": "npm:types-react@rc",
"@types/react-dom": "npm:types-react-dom@rc",
"@vitejs/plugin-react": "^4.2.1",
"typescript": "^5.3.3",
"vite": "^5.0.10",
"vitest": "^1.1.0",
"cross-env": "^7.0.3",
"rimraf": "^5.0.5"
},
"overrides": {
"@types/react": "npm:types-react@rc",
"@types/react-dom": "npm:types-react-dom@rc"
}
}
5. 백엔드 설정
backend/crm-api/pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.1</version>
<relativePath/>
</parent>
<groupId>com.enterprise</groupId>
<artifactId>crm-api</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<java.version>17</java.version>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<mybatis.version>3.0.3</mybatis.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.version}</version>
</dependency>
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
backend/crm-api/src/main/resources/application.yml
server:
port: 8081
servlet:
context-path: /api/crm
spring:
datasource:
driver-class-name: org.mariadb.jdbc.Driver
url: jdbc:mariadb://localhost:3306/enterprise_crm
username: enterprise_user
password: enterprise_password
security:
cors:
allowed-origins: "http://localhost:3001"
allowed-methods: "*"
allowed-headers: "*"
allow-credentials: true
mybatis:
mapper-locations: classpath:mapper/*.xml
configuration:
map-underscore-to-camel-case: true
logging:
level:
com.enterprise: DEBUG
org.mybatis: DEBUG
backend/crm-api/src/main/resources/application.yml
server:
port: 8081
servlet:
context-path: /api/crm
spring:
datasource:
driver-class-name: org.mariadb.jdbc.Driver
url: jdbc:mariadb://localhost:3306/enterprise_crm
username: enterprise_user
password: enterprise_password
security:
cors:
allowed-origins: "http://localhost:3001"
allowed-methods: "*"
allowed-headers: "*"
allow-credentials: true
mybatis:
mapper-locations: classpath:mapper/*.xml
configuration:
map-underscore-to-camel-case: true
logging:
level:
com.enterprise: DEBUG
org.mybatis: DEBUG
6. Docker 설정
docker-compose.yml
version: '3.8'
services:
mariadb:
image: mariadb:10.11
container_name: enterprise-db
environment:
MYSQL_ROOT_PASSWORD: rootpassword
MYSQL_DATABASE: enterprise_crm
MYSQL_USER: enterprise_user
MYSQL_PASSWORD: enterprise_password
ports:
- "3306:3306"
volumes:
- mariadb_data:/var/lib/mysql
- ./database/init:/docker-entrypoint-initdb.d
networks:
- enterprise-network
crm-api:
build:
context: ./backend/crm-api
dockerfile: Dockerfile
container_name: crm-api
ports:
- "8081:8081"
depends_on:
- mariadb
environment:
SPRING_DATASOURCE_URL: jdbc:mariadb://mariadb:3306/enterprise_crm
SPRING_DATASOURCE_USERNAME: enterprise_user
SPRING_DATASOURCE_PASSWORD: enterprise_password
networks:
- enterprise-network
wms-api:
build:
context: ./backend/wms-api
dockerfile: Dockerfile
container_name: wms-api
ports:
- "8082:8082"
depends_on:
- mariadb
environment:
SPRING_DATASOURCE_URL: jdbc:mariadb://mariadb:3306/enterprise_wms
networks:
- enterprise-network
edi-api:
build:
context: ./backend/edi-api
dockerfile: Dockerfile
container_name: edi-api
ports:
- "8083:8083"
depends_on:
- mariadb
environment:
SPRING_DATASOURCE_URL: jdbc:mariadb://mariadb:3306/enterprise_edi
networks:
- enterprise-network
nginx:
image: nginx:alpine
container_name: enterprise-nginx
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
- ./nginx/ssl:/etc/nginx/ssl
depends_on:
- crm-api
- wms-api
- edi-api
networks:
- enterprise-network
volumes:
mariadb_data:
networks:
enterprise-network:
driver: bridge
7. 개발 환경 실행 명령어
전체 개발 환경 시작
# 1. 의존성 설치
pnpm install
# 2. 백엔드 시작 (별도 터미널)
pnpm run backend:dev
# 3. 프론트엔드 개발 서버 시작 (별도 터미널들)
pnpm run dev:crm # http://localhost:3001
pnpm run dev:wms # http://localhost:3002
pnpm run dev:edi # http://localhost:3003
# 또는 모든 프론트엔드 동시 시작
pnpm run dev
개별 앱 개발
# CRM 시스템만 개발
cd apps/crm-system
pnpm dev
# WMS 시스템만 개발
cd apps/wms-system
pnpm dev
# EDI 시스템만 개발
cd apps/edi-system
pnpm dev
빌드 및 배포
# 전체 빌드
pnpm run build
# 개별 빌드
pnpm run build:crm
pnpm run build:wms
pnpm run build:edi
# Docker로 배포
pnpm run docker:build
pnpm run docker:up
8. 주요 특징 및 장점
React 버전 격리
- 각 앱이 독립적인 package.json을 가져 서로 다른 React 버전 사용 가능
- pnpm의 workspace 기능으로 의존성 충돌 방지
- 공통 패키지는 peerDependencies로 React 버전에 유연하게 대응
코드 공유
- @enterprise/shared-* 패키지를 통한 공통 코드 재사용
- TypeScript 경로 매핑으로 편리한 임포트
- 각 앱에서 필요한 공통 컴포넌트만 선택적 사용
독립적인 빌드 도구
- CRM: Webpack (레거시 지원)
- WMS: Vite (최신 개발 경험)
- EDI: Vite with React 19 (최신 기능)
통합 백엔드 관리
- Spring Boot 기반 마이크로서비스
- iBATIS/MyBatis ORM 사용
- 각 시스템별 독립적인 데이터베이스
9. 트러블슈팅
Node.js 버전 충돌
# .nvmrc 파일 생성
echo "18.19.0" > .nvmrc
# 프로젝트별 Node.js 버전 사용
nvm use
TypeScript 타입 충돌
// tsconfig.json에서 경로 매핑 확인
{
"compilerOptions": {
"skipLibCheck": true,
"paths": {
"@shared/*": ["packages/shared-*/src"]
}
}
}
Hot Reload 문제
// webpack.config.js 또는 vite.config.ts에서 설정
module.exports = {
watchOptions: {
poll: 1000,
aggregateTimeout: 300,
}
};
반응형
'테스트 플렛폼' 카테고리의 다른 글
베이스라인 스위트 구현 방법 (0) | 2025.08.19 |
---|---|
테스트 관리 도구 데이터 (1) | 2025.08.19 |
Vinci 모노레포: pnpm과 터보레포를 활용한 다중 스택, 다중 버전 개발 지침서 (3) | 2025.08.18 |
환경 설정 (2) | 2025.08.18 |
Vinci Monorepo: Multi‑App (React17 & React19) + TypeScript + Spring/iBatis/MariaDB (3) | 2025.08.18 |