create-express-esm 1.2.3 → 1.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -58
- package/bin/cli.js +78 -80
- package/package.json +1 -1
- package/template/common/docker-compose.yml.mariadb +14 -0
- package/template/common/docker-compose.yml.mongodb +15 -0
- package/template/common/docker-compose.yml.mysql +14 -0
- package/template/common/{docker-compose.yml → docker-compose.yml.postgresql} +1 -1
- package/template/common/prisma/schema.prisma.mariadb +14 -0
- package/template/common/prisma/schema.prisma.mongodb +14 -0
- package/template/common/prisma/schema.prisma.mysql +14 -0
- package/template/common/src/js/controllers/userController.js +10 -0
- package/template/common/src/js/lib/prisma.js +1 -0
- package/template/common/src/js/middlewares/errorMiddleware.js +9 -0
- package/template/common/src/js/services/userService.js +7 -0
- package/template/common/src/js/utils/appError.js +10 -0
- package/template/common/src/ts/controllers/userController.ts +11 -0
- package/template/common/src/ts/lib/prisma.ts +1 -0
- package/template/common/src/ts/middlewares/errorMiddleware.ts +11 -0
- package/template/{ts/src → common/src/ts}/routes/userRoutes.ts +2 -2
- package/template/common/src/ts/services/userService.ts +12 -0
- package/template/common/src/{utils → ts/utils}/appError.ts +3 -1
- package/template/js/_env +0 -3
- package/template/js/src/app.js +3 -7
- package/template/js/src/server.js +1 -1
- package/template/ts/_env +0 -3
- package/template/ts/src/server.ts +1 -1
- package/template/common/src/controllers/userController.js +0 -0
- package/template/common/src/controllers/userController.ts +0 -42
- package/template/common/src/lib/prisma.js +0 -0
- package/template/common/src/lib/prisma.ts +0 -0
- package/template/common/src/middlewares/errorMiddleware.ts +0 -19
- package/template/common/src/routes/userRoutes.js +0 -0
- package/template/common/src/routes/userRoutes.ts +0 -0
- package/template/common/src/services/userService.js +0 -0
- package/template/common/src/services/userService.ts +0 -0
- package/template/js/package-lock.json +0 -3366
- package/template/js/src/controllers/userController.js +0 -10
- package/template/js/src/services/userService.js +0 -8
- package/template/ts/package-lock.json +0 -4020
- package/template/ts/src/controllers/userController.ts +0 -17
- package/template/ts/src/services/userService.ts +0 -14
- /package/template/common/prisma/{schema.prisma → schema.prisma.postgresql} +0 -0
- /package/template/{js/src → common/src/js}/app.test.js +0 -0
- /package/template/{js/src → common/src/js}/routes/userRoutes.js +0 -0
- /package/template/{ts/src → common/src/ts}/app.test.ts +0 -0
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# 🚀 Create Express ESM (CLI)
|
|
2
2
|
|
|
3
|
-
> **Modern Express Generator with Database &
|
|
3
|
+
> **Modern Express Generator with Multi-Database & Zero-Config**
|
|
4
4
|
>
|
|
5
|
-
> "1초 만에 완성하는 Modern Express(ESM) + Prisma + Docker 환경"
|
|
5
|
+
> "1초 만에 완성하는 Modern Express(ESM) + Prisma + Multi-DB Docker 환경"
|
|
6
6
|
>
|
|
7
|
-
>
|
|
7
|
+
> 최신 ES Modules(import/export)를 기반으로, 사용자가 선택한 데이터베이스(PostgreSQL, MySQL, MariaDB, MongoDB) 환경을 즉시 구축해주는 지능형 CLI 도구입니다.
|
|
8
8
|
|
|
9
9
|
[](https://www.npmjs.com/package/create-express-esm)
|
|
10
10
|
[](https://opensource.org/licenses/MIT)
|
|
@@ -15,19 +15,19 @@
|
|
|
15
15
|
|
|
16
16
|
## ✨ Key Features (핵심 기능)
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
v1.2.4 업데이트를 통해 더욱 강력해진 실무형 풀스택 베이스를 제공합니다.
|
|
19
19
|
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
- **🚨 Professional Error Handling**: `AppError` 클래스와 **Global Error Middleware**를 통한 중앙 집중식 에러 처리 시스템을 제공합니다.
|
|
23
|
-
- **🧪 Integrated Testing**: 최신 테스트 프레임워크인 **Vitest**와 API 테스트용 **Supertest** 환경을 자동 설정합니다.
|
|
20
|
+
- **🗄️ Multi-Database Support**: **PostgreSQL, MySQL, MariaDB, MongoDB** 중 프로젝트에 필요한 DB를 선택할 수 있습니다.
|
|
21
|
+
- **🐳 Smart Docker Orchestration**: 각 DB 엔진에 최적화된 Docker Compose 설정을 자동 생성합니다. (포트 충돌 방지 설계 적용)
|
|
24
22
|
- **🏗 Layered Architecture**: 실무 표준인 `Controller` - `Service` - `Route` - `Middleware` 계층 구조를 제공합니다.
|
|
25
|
-
-
|
|
26
|
-
-
|
|
23
|
+
- **🚨 Professional Error Handling**: `AppError` 클래스와 **Global Error Middleware**를 통한 중앙 집중식 에러 관리 시스템을 내장하고 있습니다.
|
|
24
|
+
- **🧪 Integrated Testing**: 최신 테스트 프레임워크인 **Vitest**와 API 테스트용 **Supertest** 환경을 자동 설정합니다.
|
|
25
|
+
- **⚡ Modern Execution**: `ts-node`보다 빠르고 ESM 호환성이 뛰어난 **`tsx`**를 채택했습니다.
|
|
26
|
+
- **📦 Zero-Config Setup**: 프로젝트 생성 즉시 의존성 설치, `.env` 환경 변수 세팅 및 Prisma 스키마 최적화를 완료합니다.
|
|
27
27
|
|
|
28
28
|
## 🚀 Quick Start (사용법)
|
|
29
29
|
|
|
30
|
-
별도의 설치 없이 `npx` 명령어로 즉시
|
|
30
|
+
별도의 설치 없이 `npx` 명령어로 즉시 실행하여 인터렉티브하게 프로젝트를 생성하세요.
|
|
31
31
|
|
|
32
32
|
```bash
|
|
33
33
|
npx create-express-esm
|
|
@@ -35,37 +35,38 @@ npx create-express-esm
|
|
|
35
35
|
|
|
36
36
|
### 프로젝트 생성 후 DB 시작하기
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
```bash
|
|
38
|
+
```
|
|
41
39
|
# 1. 프로젝트 폴더 이동
|
|
42
40
|
cd my-app
|
|
43
41
|
|
|
44
|
-
# 2. Docker를 통한
|
|
42
|
+
# 2. Docker를 통한 데이터베이스 실행
|
|
45
43
|
npm run db:up
|
|
46
44
|
|
|
47
|
-
# 3. Prisma
|
|
45
|
+
# 3. Prisma 스키마 반영 (테이블/컬렉션 생성)
|
|
48
46
|
npm run db:push
|
|
49
47
|
|
|
50
|
-
# 4. 서버 실행
|
|
48
|
+
# 4. 개발 서버 실행
|
|
51
49
|
npm run dev
|
|
52
50
|
```
|
|
53
51
|
|
|
54
|
-
|
|
52
|
+
## 🗄️ Database Options
|
|
55
53
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
54
|
+
v1.2.4에서는 각 환경별 최적화된 기본 포트를 제공하여 윈도우 시스템(Hyper-V)과의 포트 충돌을 최소화하고, 선택한 DB에 맞는 전용 환경을 구축합니다.
|
|
55
|
+
|
|
56
|
+
| Database | Docker Image | Default Port | Prisma Provider |
|
|
57
|
+
| :------------- | :------------------- | :----------- | :-------------- |
|
|
58
|
+
| **PostgreSQL** | `postgres:15-alpine` | `5433` | `postgresql` |
|
|
59
|
+
| **MySQL** | `mysql:8.0` | `4306` | `mysql` |
|
|
60
|
+
| **MariaDB** | `mariadb:10.11` | `5306` | `mysql` |
|
|
61
|
+
| **MongoDB** | `mongo:6.0` | `27017` | `mongodb` |
|
|
60
62
|
|
|
61
63
|
## 📂 Project Structure (폴더 구조)
|
|
62
64
|
|
|
63
|
-
이 도구는 **Layered Architecture (계층형 아키텍처)**를 기반으로 프로젝트를
|
|
64
|
-
관심사 분리(Separation of Concerns) 원칙을 적용하여 유지보수가 쉬운 구조를 제공합니다.
|
|
65
|
+
이 도구는 **Layered Architecture (계층형 아키텍처)**를 기반으로 프로젝트를 생성하여 높은 유지보수성을 보장합니다.
|
|
65
66
|
|
|
66
67
|
```text
|
|
67
68
|
my-app/
|
|
68
|
-
├── prisma/ # 🗄️ Prisma Schema
|
|
69
|
+
├── prisma/ # 🗄️ Prisma Schema (선택한 DB 전용 템플릿)
|
|
69
70
|
├── src/
|
|
70
71
|
│ ├── controllers/ # 🕹️ 요청 처리 및 응답 반환 (Controller Layer)
|
|
71
72
|
│ ├── services/ # 🧠 비즈니스 로직 처리 (Service Layer)
|
|
@@ -74,46 +75,26 @@ my-app/
|
|
|
74
75
|
│ ├── utils/ # 🛠️ AppError 클래스 등 공통 유틸리티
|
|
75
76
|
│ ├── lib/ # 🖇️ Prisma Client 인스턴스 (Singleton)
|
|
76
77
|
│ ├── app.ts # 🏗️ Express 앱 설정 및 미들웨어
|
|
77
|
-
│
|
|
78
|
-
│ └── app.test.ts # 🧪 Vitest 샘플 테스트 코드
|
|
78
|
+
│ └── server.ts # 🚀 서버 진입점 (Entry Point)
|
|
79
79
|
├── .env # 🔐 환경 변수 (DATABASE_URL 자동 생성)
|
|
80
|
-
├── docker-compose.yml # 🐳
|
|
80
|
+
├── docker-compose.yml # 🐳 DB 컨테이너 설정 (Port conflict 방지 설계)
|
|
81
81
|
├── vitest.config.ts # 🧪 Vitest 설정 파일
|
|
82
|
-
├── tsconfig.json # ⚙️ TS 컴파일러 설정 (TS 선택 시)
|
|
83
82
|
└── package.json # 📦 의존성 및 스크립트
|
|
84
83
|
```
|
|
85
84
|
|
|
86
|
-
## 🛠 Tech Stack (기술 스택)
|
|
87
|
-
|
|
88
|
-
- **Runtime**: Node.js (v20+)
|
|
89
|
-
- **Framework**: Express.js
|
|
90
|
-
- **Language**: JavaScript (ESM) / TypeScript 5.x
|
|
91
|
-
- **ORM**: Prisma (PostgreSQL)
|
|
92
|
-
- **Infrastructure**: Docker Compose
|
|
93
|
-
- **Testing**: Vitest, Supertest
|
|
94
|
-
- **Dev Tools**:
|
|
95
|
-
- `tsx` (TypeScript Execution Engine)
|
|
96
|
-
- `nodemon` (Hot Reload)
|
|
97
|
-
- `@clack/prompts` (Interactive CLI UI)
|
|
98
|
-
- `dotenv` (Environment Variables)
|
|
99
|
-
- `cors` (Cross-Origin Resource Sharing)
|
|
100
|
-
- `chalk` (CLI Styling)
|
|
101
|
-
|
|
102
|
-
## 📝 Retrospective
|
|
103
|
-
|
|
104
|
-
- **표준화된 환경의 중요성**: CJS에서 ESM으로 넘어가는 과도기적 문제를 해결하며 모던 자바스크립트 모듈 시스템에 대한 깊은 이해를 얻었습니다.
|
|
105
|
-
- **에러 핸들링의 중앙화**: 개별 컨트롤러에서 반복되던 에러 처리 로직을 전역 미들웨어로 위임하여 코드 가독성과 유지보수성을 극대화했습니다.
|
|
106
|
-
- **인프라 환경 이슈 해결**: 로컬 PostgreSQL과의 포트 충돌(5432 vs 5433) 및 도커 볼륨 인증 문제를 해결하며, 사용자에게 가장 안정적인 DB 연결 가이드를 제공하는 데 성공했습니다.
|
|
107
|
-
- **UX 기반 설계**: 사용자가 프로젝트를 생성하자마자 `npm run dev`와 `npm test`를 즉시 실행할 수 있는 "Zero-Config" 환경을 제공하는 데 집중했습니다.
|
|
108
|
-
|
|
109
85
|
## 🗺️ Roadmap (Future Plans)
|
|
110
86
|
|
|
111
|
-
- [x]
|
|
112
|
-
|
|
113
|
-
- [x]
|
|
114
|
-
|
|
115
|
-
- [
|
|
116
|
-
|
|
87
|
+
- [x] TypeScript & tsx: 최신 TS 환경 완벽 지원
|
|
88
|
+
|
|
89
|
+
- [x] Testing Setup: Vitest/Supertest 자동화
|
|
90
|
+
|
|
91
|
+
- [x] Multi-DB Support: 4종 DB(Postgres, MySQL, MariaDB, Mongo) 지원
|
|
92
|
+
|
|
93
|
+
- [ ] Authentication Template: JWT/Passport 기반 인증 로직 추가
|
|
94
|
+
|
|
95
|
+
- [ ] Validation Layer: Zod를 이용한 요청 값 검증 미들웨어 추가
|
|
96
|
+
|
|
97
|
+
- [ ] API Documentation: Swagger(OpenAPI) 자동 생성 지원
|
|
117
98
|
|
|
118
99
|
## 📝 License
|
|
119
100
|
|
package/bin/cli.js
CHANGED
|
@@ -14,8 +14,8 @@ const __dirname = path.dirname(__filename);
|
|
|
14
14
|
async function run() {
|
|
15
15
|
console.clear();
|
|
16
16
|
|
|
17
|
-
// 1. 시작 인사
|
|
18
|
-
p.intro(`${chalk.bgBlue.white(' create-express-esm ')} ${chalk.dim('v1.2.
|
|
17
|
+
// 1. 시작 인사 (v1.2.4 버전 반영)
|
|
18
|
+
p.intro(`${chalk.bgBlue.white(' create-express-esm ')} ${chalk.dim('v1.2.4')}`);
|
|
19
19
|
|
|
20
20
|
try {
|
|
21
21
|
// 2. 사용자 질문 그룹
|
|
@@ -45,9 +45,21 @@ async function run() {
|
|
|
45
45
|
}),
|
|
46
46
|
useDb: () =>
|
|
47
47
|
p.confirm({
|
|
48
|
-
message: 'Prisma ORM
|
|
48
|
+
message: 'Prisma ORM 및 데이터베이스 환경을 추가하시겠습니까?',
|
|
49
49
|
initialValue: false,
|
|
50
50
|
}),
|
|
51
|
+
dbType: ({ results }) => {
|
|
52
|
+
if (!results.useDb) return;
|
|
53
|
+
return p.select({
|
|
54
|
+
message: '사용할 데이터베이스를 선택하세요:',
|
|
55
|
+
options: [
|
|
56
|
+
{ value: 'postgresql', label: 'PostgreSQL' },
|
|
57
|
+
{ value: 'mysql', label: 'MySQL' },
|
|
58
|
+
{ value: 'mariadb', label: 'MariaDB' },
|
|
59
|
+
{ value: 'mongodb', label: 'MongoDB (NoSQL)' },
|
|
60
|
+
],
|
|
61
|
+
});
|
|
62
|
+
},
|
|
51
63
|
},
|
|
52
64
|
{
|
|
53
65
|
onCancel: () => {
|
|
@@ -57,93 +69,89 @@ async function run() {
|
|
|
57
69
|
}
|
|
58
70
|
);
|
|
59
71
|
|
|
60
|
-
const { projectName, language, useTest, useDb } = project;
|
|
72
|
+
const { projectName, language, useTest, useDb, dbType } = project;
|
|
61
73
|
const targetPath = path.join(process.cwd(), projectName);
|
|
62
74
|
const templatePath = path.join(__dirname, '../template', language);
|
|
63
75
|
const commonPath = path.join(__dirname, '../template/common');
|
|
64
76
|
|
|
65
77
|
// 3. 파일 구성 시작
|
|
66
78
|
const s = p.spinner();
|
|
67
|
-
s.start('프로젝트 템플릿을
|
|
79
|
+
s.start('프로젝트 템플릿을 생성하는 중...');
|
|
68
80
|
|
|
69
|
-
// 기본 언어 템플릿 복사
|
|
81
|
+
// (1) 기본 언어 템플릿 복사 (js 또는 ts)
|
|
70
82
|
await fs.copy(templatePath, targetPath);
|
|
71
83
|
|
|
72
|
-
// 도트 파일 변환 (_env -> .env 등)
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
const oldFilePath = path.join(targetPath, oldName);
|
|
81
|
-
if (await fs.pathExists(oldFilePath)) {
|
|
82
|
-
await fs.move(oldFilePath, path.join(targetPath, newName), { overwrite: true });
|
|
84
|
+
// (2) 도트 파일 변환 (_env -> .env 등)
|
|
85
|
+
const dotFiles = ['gitignore', '_gitignore', '_env'];
|
|
86
|
+
for (const f of dotFiles) {
|
|
87
|
+
const oldPath = path.join(targetPath, f);
|
|
88
|
+
if (await fs.pathExists(oldPath)) {
|
|
89
|
+
const newName = f.startsWith('_') ? f.replace('_', '.') : `.${f}`;
|
|
90
|
+
await fs.move(oldPath, path.join(targetPath, newName), { overwrite: true });
|
|
91
|
+
// .env 파일 생성 시 .env.example도 같이 생성
|
|
83
92
|
if (newName === '.env') {
|
|
84
93
|
await fs.copy(path.join(targetPath, '.env'), path.join(targetPath, '.env.example'));
|
|
85
94
|
}
|
|
86
95
|
}
|
|
87
96
|
}
|
|
88
97
|
|
|
89
|
-
// 4. DB
|
|
98
|
+
// 4. DB 선택 시 기능 병합 (Common 소스 복사)
|
|
90
99
|
if (useDb) {
|
|
91
|
-
// (1) Prisma
|
|
92
|
-
await fs.
|
|
93
|
-
await fs.copy(
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
100
|
+
// (1) Prisma & Docker 설정 복사
|
|
101
|
+
await fs.ensureDir(path.join(targetPath, 'prisma'));
|
|
102
|
+
await fs.copy(
|
|
103
|
+
path.join(commonPath, 'prisma', `schema.prisma.${dbType}`),
|
|
104
|
+
path.join(targetPath, 'prisma', 'schema.prisma')
|
|
105
|
+
);
|
|
106
|
+
await fs.copy(
|
|
107
|
+
path.join(commonPath, `docker-compose.yml.${dbType}`),
|
|
108
|
+
path.join(targetPath, 'docker-compose.yml')
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
// (2) ⭐ [핵심 수정] 언어별 Common 소스 코드 병합
|
|
112
|
+
// template/common/src/js (또는 ts) 폴더 내부를 target/src로 병합 복사
|
|
113
|
+
const commonSrcPath = path.join(commonPath, 'src', language);
|
|
114
|
+
if (await fs.pathExists(commonSrcPath)) {
|
|
115
|
+
await fs.copy(commonSrcPath, path.join(targetPath, 'src'), {
|
|
116
|
+
overwrite: true,
|
|
117
|
+
errorOnExist: false
|
|
118
|
+
});
|
|
111
119
|
}
|
|
112
120
|
|
|
113
|
-
// (3) app.ts / app.js 에 코드 주입
|
|
121
|
+
// (3) app.ts / app.js 에 코드 주입
|
|
114
122
|
const mainFilePath = path.join(targetPath, `src/app.${language}`);
|
|
115
123
|
if (await fs.pathExists(mainFilePath)) {
|
|
116
124
|
let content = await fs.readFile(mainFilePath, 'utf-8');
|
|
117
125
|
|
|
118
|
-
//
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
content = content.replace(
|
|
133
|
-
|
|
134
|
-
|
|
126
|
+
// 중복 주입 방지 체크
|
|
127
|
+
if (!content.includes('userRoutes')) {
|
|
128
|
+
const imports = [
|
|
129
|
+
`import userRoutes from './routes/userRoutes.js';`,
|
|
130
|
+
`import { errorHandler } from './middlewares/errorMiddleware.js';`
|
|
131
|
+
].join('\n');
|
|
132
|
+
|
|
133
|
+
content = imports + '\n' + content;
|
|
134
|
+
// express.json() 다음에 라우터 연결
|
|
135
|
+
content = content.replace(/app\.use\(express\.json\(\)\);?/g, (match) => {
|
|
136
|
+
return `${match}\napp.use('/api/users', userRoutes);`;
|
|
137
|
+
});
|
|
138
|
+
// export default 앞에 에러 핸들러 연결
|
|
139
|
+
const errorMiddlewareCode = `\n// 전역 에러 핸들러\napp.use(errorHandler);\n`;
|
|
140
|
+
content = content.replace(/export default app;?/g, `${errorMiddlewareCode}\nexport default app;`);
|
|
141
|
+
|
|
142
|
+
await fs.writeFile(mainFilePath, content);
|
|
135
143
|
}
|
|
136
|
-
|
|
137
|
-
await fs.writeFile(mainFilePath, content);
|
|
138
144
|
}
|
|
139
145
|
|
|
140
|
-
// (4) .env 파일에 DATABASE_URL 추가
|
|
146
|
+
// (4) .env 파일에 DB 타입별 DATABASE_URL 추가
|
|
141
147
|
const envPath = path.join(targetPath, '.env');
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
148
|
+
const dbUrls = {
|
|
149
|
+
postgresql: 'postgresql://myuser:mypassword@localhost:5433/mydb?schema=public',
|
|
150
|
+
mysql: 'mysql://root:mypassword@localhost:4306/mydb',
|
|
151
|
+
mariadb: 'mysql://root:mypassword@localhost:5306/mydb',
|
|
152
|
+
mongodb: 'mongodb://root:mypassword@localhost:27017/mydb?authSource=admin'
|
|
153
|
+
};
|
|
154
|
+
await fs.appendFile(envPath, `\n# Database Connection\nDATABASE_URL="${dbUrls[dbType]}"\n`);
|
|
147
155
|
}
|
|
148
156
|
|
|
149
157
|
// 5. package.json 동적 최적화
|
|
@@ -156,50 +164,40 @@ DATABASE_URL="postgresql://myuser:mypassword@localhost:5432/mydb?schema=public"
|
|
|
156
164
|
pkg.devDependencies["tsx"] = "^4.7.0";
|
|
157
165
|
}
|
|
158
166
|
|
|
159
|
-
// 테스트 환경 설정 (비사용 시 관련 파일 및 패키지 제거)
|
|
160
167
|
if (!useTest) {
|
|
161
168
|
const configExt = language === 'ts' ? 'ts' : 'js';
|
|
162
|
-
const testFileExt = language === 'ts' ? 'ts' : 'js';
|
|
163
169
|
await fs.remove(path.join(targetPath, `vitest.config.${configExt}`));
|
|
164
|
-
await fs.remove(path.join(targetPath, `src/app.test.${
|
|
170
|
+
await fs.remove(path.join(targetPath, `src/app.test.${configExt}`));
|
|
165
171
|
delete pkg.scripts.test;
|
|
166
|
-
delete pkg.scripts["test:ui"];
|
|
167
|
-
delete pkg.scripts["test:run"];
|
|
168
172
|
delete pkg.devDependencies.vitest;
|
|
169
173
|
delete pkg.devDependencies.supertest;
|
|
170
|
-
if (pkg.devDependencies["@types/supertest"]) delete pkg.devDependencies["@types/supertest"];
|
|
171
174
|
}
|
|
172
175
|
|
|
173
|
-
// DB 의존성 및 스크립트 추가
|
|
174
176
|
if (useDb) {
|
|
175
177
|
pkg.scripts["db:up"] = "docker-compose up -d";
|
|
176
178
|
pkg.scripts["db:push"] = "prisma db push";
|
|
177
|
-
pkg.scripts["prisma:generate"] = "prisma generate";
|
|
178
|
-
pkg.scripts["prisma:studio"] = "prisma studio";
|
|
179
|
-
|
|
180
179
|
pkg.dependencies["@prisma/client"] = "^5.0.0";
|
|
181
180
|
pkg.devDependencies["prisma"] = "^5.0.0";
|
|
182
181
|
}
|
|
183
182
|
|
|
184
183
|
await fs.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
185
|
-
s.stop('파일 구성 완료!');
|
|
184
|
+
s.stop('프로젝트 파일 구성 완료!');
|
|
186
185
|
|
|
187
186
|
// 6. 의존성 설치
|
|
188
187
|
const installSpinner = p.spinner();
|
|
189
188
|
installSpinner.start('의존성 패키지를 설치하는 중... (npm install)');
|
|
190
|
-
|
|
191
189
|
try {
|
|
192
190
|
execSync('npm install', { cwd: targetPath, stdio: 'ignore' });
|
|
193
191
|
installSpinner.stop('설치 완료!');
|
|
194
192
|
} catch (e) {
|
|
195
|
-
installSpinner.stop(chalk.red('설치 실패 (
|
|
193
|
+
installSpinner.stop(chalk.red('설치 실패 (수동으로 npm install을 실행해 주세요)'));
|
|
196
194
|
}
|
|
197
195
|
|
|
198
|
-
// 7. 마무리
|
|
196
|
+
// 7. 마무리 안내
|
|
199
197
|
let nextSteps = `cd ${projectName}\n`;
|
|
200
198
|
if (useDb) {
|
|
201
|
-
nextSteps += `${chalk.bold('npm run db:up')} (
|
|
202
|
-
nextSteps += `${chalk.bold('npm run db:push')} (
|
|
199
|
+
nextSteps += `${chalk.bold('npm run db:up')} (Docker 실행)\n`;
|
|
200
|
+
nextSteps += `${chalk.bold('npm run db:push')} (DB 스키마 반영)\n`;
|
|
203
201
|
}
|
|
204
202
|
nextSteps += `npm run dev`;
|
|
205
203
|
|
package/package.json
CHANGED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
services:
|
|
2
|
+
db:
|
|
3
|
+
image: mariadb:10.11
|
|
4
|
+
restart: always
|
|
5
|
+
ports:
|
|
6
|
+
- "5306:3306"
|
|
7
|
+
environment:
|
|
8
|
+
MARIADB_ROOT_PASSWORD: ${DB_PASSWORD:-mypassword}
|
|
9
|
+
MARIADB_DATABASE: ${DB_NAME:-mydb}
|
|
10
|
+
volumes:
|
|
11
|
+
- mariadb_data:/var/lib/mysql
|
|
12
|
+
|
|
13
|
+
volumes:
|
|
14
|
+
mariadb_data:
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
services:
|
|
2
|
+
db:
|
|
3
|
+
image: mongo:6.0
|
|
4
|
+
restart: always
|
|
5
|
+
ports:
|
|
6
|
+
- "27017:27017"
|
|
7
|
+
environment:
|
|
8
|
+
MONGO_INITDB_ROOT_USERNAME: ${DB_USER:-root}
|
|
9
|
+
MONGO_INITDB_ROOT_PASSWORD: ${DB_PASSWORD:-mypassword}
|
|
10
|
+
MONGO_INITDB_DATABASE: ${DB_NAME:-mydb}
|
|
11
|
+
volumes:
|
|
12
|
+
- mongo_data:/data/db
|
|
13
|
+
|
|
14
|
+
volumes:
|
|
15
|
+
mongo_data:
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as userService from '../services/userService.js';
|
|
2
|
+
|
|
3
|
+
export const getUsers = async (req, res, next) => {
|
|
4
|
+
try {
|
|
5
|
+
const users = await userService.findAllUsers();
|
|
6
|
+
res.status(200).json(users);
|
|
7
|
+
} catch (error) {
|
|
8
|
+
next(error); // 에러 미들웨어로 전달
|
|
9
|
+
}
|
|
10
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import { PrismaClient } from '@prisma/client'; export const prisma = new PrismaClient();
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export class AppError extends Error {
|
|
2
|
+
constructor(message, statusCode) {
|
|
3
|
+
super(message);
|
|
4
|
+
this.statusCode = statusCode;
|
|
5
|
+
this.status = `${statusCode}`.startsWith('4') ? 'fail' : 'error';
|
|
6
|
+
this.isOperational = true;
|
|
7
|
+
|
|
8
|
+
Error.captureStackTrace(this, this.constructor);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Request, Response, NextFunction } from 'express';
|
|
2
|
+
import * as userService from '../services/userService.js';
|
|
3
|
+
|
|
4
|
+
export const getUsers = async (req: Request, res: Response, next: NextFunction) => {
|
|
5
|
+
try {
|
|
6
|
+
const users = await userService.findAllUsers();
|
|
7
|
+
res.status(200).json(users);
|
|
8
|
+
} catch (error) {
|
|
9
|
+
next(error);
|
|
10
|
+
}
|
|
11
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import { PrismaClient } from '@prisma/client'; export const prisma: PrismaClient = new PrismaClient();
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Request, Response, NextFunction } from 'express';
|
|
2
|
+
|
|
3
|
+
export const errorHandler = (err: any, req: Request, res: Response, next: NextFunction) => {
|
|
4
|
+
const statusCode = err.statusCode || 500;
|
|
5
|
+
const status = err.status || 'error';
|
|
6
|
+
|
|
7
|
+
res.status(statusCode).json({
|
|
8
|
+
status,
|
|
9
|
+
message: err.message,
|
|
10
|
+
});
|
|
11
|
+
};
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
export class AppError extends Error {
|
|
2
2
|
public statusCode: number;
|
|
3
|
+
public status: string;
|
|
3
4
|
public isOperational: boolean;
|
|
4
5
|
|
|
5
6
|
constructor(message: string, statusCode: number) {
|
|
6
7
|
super(message);
|
|
7
8
|
this.statusCode = statusCode;
|
|
8
|
-
this.
|
|
9
|
+
this.status = `${statusCode}`.startsWith('4') ? 'fail' : 'error';
|
|
10
|
+
this.isOperational = true;
|
|
9
11
|
|
|
10
12
|
Error.captureStackTrace(this, this.constructor);
|
|
11
13
|
}
|
package/template/js/_env
CHANGED