create-express-esm 1.2.4 → 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.
Files changed (43) hide show
  1. package/README.md +39 -58
  2. package/bin/cli.js +58 -70
  3. package/package.json +1 -1
  4. package/template/common/docker-compose.yml.mariadb +14 -0
  5. package/template/common/docker-compose.yml.mongodb +15 -0
  6. package/template/common/{docker-compose.yml → docker-compose.yml.postgresql} +1 -1
  7. package/template/common/prisma/schema.prisma.mariadb +14 -0
  8. package/template/common/prisma/schema.prisma.mongodb +14 -0
  9. package/template/common/src/js/controllers/userController.js +10 -0
  10. package/template/common/src/js/lib/prisma.js +1 -0
  11. package/template/common/src/js/middlewares/errorMiddleware.js +9 -0
  12. package/template/common/src/js/services/userService.js +7 -0
  13. package/template/common/src/js/utils/appError.js +10 -0
  14. package/template/common/src/ts/controllers/userController.ts +11 -0
  15. package/template/common/src/ts/lib/prisma.ts +1 -0
  16. package/template/common/src/ts/middlewares/errorMiddleware.ts +11 -0
  17. package/template/{ts/src → common/src/ts}/routes/userRoutes.ts +2 -2
  18. package/template/common/src/ts/services/userService.ts +12 -0
  19. package/template/common/src/{utils → ts/utils}/appError.ts +3 -1
  20. package/template/js/_env +0 -3
  21. package/template/js/src/app.js +3 -7
  22. package/template/js/src/server.js +1 -1
  23. package/template/ts/_env +0 -3
  24. package/template/ts/src/server.ts +1 -1
  25. package/template/common/src/controllers/userController.js +0 -0
  26. package/template/common/src/controllers/userController.ts +0 -42
  27. package/template/common/src/lib/prisma.js +0 -0
  28. package/template/common/src/lib/prisma.ts +0 -0
  29. package/template/common/src/middlewares/errorMiddleware.ts +0 -19
  30. package/template/common/src/routes/userRoutes.js +0 -0
  31. package/template/common/src/routes/userRoutes.ts +0 -0
  32. package/template/common/src/services/userService.js +0 -0
  33. package/template/common/src/services/userService.ts +0 -0
  34. package/template/js/package-lock.json +0 -3366
  35. package/template/js/src/controllers/userController.js +0 -10
  36. package/template/js/src/services/userService.js +0 -8
  37. package/template/ts/package-lock.json +0 -4020
  38. package/template/ts/src/controllers/userController.ts +0 -17
  39. package/template/ts/src/services/userService.ts +0 -14
  40. /package/template/common/prisma/{schema.prisma → schema.prisma.postgresql} +0 -0
  41. /package/template/{js/src → common/src/js}/app.test.js +0 -0
  42. /package/template/{js/src → common/src/js}/routes/userRoutes.js +0 -0
  43. /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 & Error Handling**
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
- > CommonJS(require)가 아닌, 최신 ES Modules(import/export) 문법을 기반으로 하며, 데이터베이스 연동 전문적인 에러 핸들링까지 포함된 Express 프로젝트 구조를 자동으로 생성해주는 CLI 도구입니다.
7
+ > 최신 ES Modules(import/export) 기반으로, 사용자가 선택한 데이터베이스(PostgreSQL, MySQL, MariaDB, MongoDB) 환경을 즉시 구축해주는 지능형 CLI 도구입니다.
8
8
 
9
9
  [![npm version](https://img.shields.io/npm/v/create-express-esm.svg)](https://www.npmjs.com/package/create-express-esm)
10
10
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
@@ -15,19 +15,19 @@
15
15
 
16
16
  ## ✨ Key Features (핵심 기능)
17
17
 
18
- 기존 `express-generator`의 한계를 분석하고 개선하여 개발했습니다. v1.2.0 업데이트를 통해 실무형 풀스택 베이스를 제공합니다.
18
+ v1.2.4 업데이트를 통해 더욱 강력해진 실무형 풀스택 베이스를 제공합니다.
19
19
 
20
- - **🌐 Multi-Language Support**: JavaScript(ESM)와 **TypeScript**원하는 개발 환경을 선택할 수 있습니다.
21
- - **🗄️ Database Integration**: **Prisma ORM**과 **PostgreSQL** 환경을 즉시 구축합니다. (Docker Compose 자동 생성)
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
- - **⚡️ Modern Execution**: `ts-node`의 ESM 호환성 문제를 해결한 **`tsx`**를 채택하여 쾌적한 개발 환경을 제공합니다.
26
- - **📦 Smart Auto-Installation**: 프로젝트 생성 즉시 의존성 설치 환경 변수(`.env`) 세팅을 완료합니다.
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
- DB 옵션을 선택했다면, 단 몇 줄의 명령어로 개발 준비가 끝납니다.
39
-
40
- ```bash
38
+ ```
41
39
  # 1. 프로젝트 폴더 이동
42
40
  cd my-app
43
41
 
44
- # 2. Docker를 통한 PostgreSQL 실행
42
+ # 2. Docker를 통한 데이터베이스 실행
45
43
  npm run db:up
46
44
 
47
- # 3. Prisma 스키마를 DB에 반영 (테이블 생성)
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
- ```bash
57
- npm install -g create-express-esm
58
- create-express-esm
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 & Migrations
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
- ├── server.ts # 🚀 서버 진입점 (Entry Point)
78
- │ └── app.test.ts # 🧪 Vitest 샘플 테스트 코드
78
+ └── server.ts # 🚀 서버 진입점 (Entry Point)
79
79
  ├── .env # 🔐 환경 변수 (DATABASE_URL 자동 생성)
80
- ├── docker-compose.yml # 🐳 PostgreSQL 컨테이너 설정
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] **TypeScript Support**: `.ts` 템플릿 `tsx` 환경 최적화
112
- - [x] **Test Environment**: Vitest 및 Supertest 설정 자동화
113
- - [x] **Interactive UI Upgrade**: `Clack` 라이브러리를 통한 시각적 CLI UI 개선
114
- - [x] **Database Integration**: Prisma/PostgreSQL 및 Docker 선택 옵션 추가
115
- - [ ] **Authentication Template**: JWT/Passport를 이용한 기본 인증 로직 추가
116
- - [ ] **Deployment Guide**: AWS/Render 등 주요 플랫폼 배포 가이드라인 추가
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. 시작 인사 (v1.2.2 버전 반영)
18
- p.intro(`${chalk.bgBlue.white(' create-express-esm ')} ${chalk.dim('v1.2.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,10 +45,9 @@ 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
- // [New] DB 타입 선택 (useDb가 true일 때만 실행)
52
51
  dbType: ({ results }) => {
53
52
  if (!results.useDb) return;
54
53
  return p.select({
@@ -56,6 +55,8 @@ async function run() {
56
55
  options: [
57
56
  { value: 'postgresql', label: 'PostgreSQL' },
58
57
  { value: 'mysql', label: 'MySQL' },
58
+ { value: 'mariadb', label: 'MariaDB' },
59
+ { value: 'mongodb', label: 'MongoDB (NoSQL)' },
59
60
  ],
60
61
  });
61
62
  },
@@ -75,91 +76,82 @@ async function run() {
75
76
 
76
77
  // 3. 파일 구성 시작
77
78
  const s = p.spinner();
78
- s.start('프로젝트 템플릿을 복사하는 중...');
79
+ s.start('프로젝트 템플릿을 생성하는 중...');
79
80
 
80
- // 기본 언어 템플릿 복사
81
+ // (1) 기본 언어 템플릿 복사 (js 또는 ts)
81
82
  await fs.copy(templatePath, targetPath);
82
83
 
83
- // 도트 파일 변환 (_env -> .env 등)
84
- const renameMap = {
85
- 'gitignore': '.gitignore',
86
- '_gitignore': '.gitignore',
87
- '_env': '.env'
88
- };
89
-
90
- for (const [oldName, newName] of Object.entries(renameMap)) {
91
- const oldFilePath = path.join(targetPath, oldName);
92
- if (await fs.pathExists(oldFilePath)) {
93
- 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도 같이 생성
94
92
  if (newName === '.env') {
95
93
  await fs.copy(path.join(targetPath, '.env'), path.join(targetPath, '.env.example'));
96
94
  }
97
95
  }
98
96
  }
99
97
 
100
- // 4. DB 및 에러 핸들링 선택 시 추가 파일 복사 코드 주입
98
+ // 4. DB 선택 시 기능 병합 (Common 소스 복사)
101
99
  if (useDb) {
102
- // (1) Prisma 설정 Docker Compose 복사 (선택한 DB 타입에 맞춤)
100
+ // (1) Prisma & Docker 설정 복사
103
101
  await fs.ensureDir(path.join(targetPath, 'prisma'));
104
-
105
- const prismaTemplate = `schema.prisma.${dbType}`;
106
- const dockerTemplate = `docker-compose.yml.${dbType}`;
107
-
108
102
  await fs.copy(
109
- path.join(commonPath, 'prisma', prismaTemplate),
103
+ path.join(commonPath, 'prisma', `schema.prisma.${dbType}`),
110
104
  path.join(targetPath, 'prisma', 'schema.prisma')
111
105
  );
112
-
113
106
  await fs.copy(
114
- path.join(commonPath, dockerTemplate),
107
+ path.join(commonPath, `docker-compose.yml.${dbType}`),
115
108
  path.join(targetPath, 'docker-compose.yml')
116
109
  );
117
110
 
118
- // (2) 소스 코드 복사 (기존 로직 유지)
119
- const sourceFolders = ['lib', 'services', 'controllers', 'routes', 'utils', 'middlewares'];
120
- for (const folder of sourceFolders) {
121
- const srcFolderPath = path.join(commonPath, 'src', folder);
122
- const destFolderPath = path.join(targetPath, 'src', folder);
123
-
124
- if (await fs.pathExists(srcFolderPath)) {
125
- await fs.ensureDir(destFolderPath);
126
- const files = await fs.readdir(srcFolderPath);
127
- for (const file of files) {
128
- if (file.endsWith(`.${language}`)) {
129
- await fs.copy(path.join(srcFolderPath, file), path.join(destFolderPath, file));
130
- }
131
- }
132
- }
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
+ });
133
119
  }
134
120
 
135
121
  // (3) app.ts / app.js 에 코드 주입
136
122
  const mainFilePath = path.join(targetPath, `src/app.${language}`);
137
123
  if (await fs.pathExists(mainFilePath)) {
138
124
  let content = await fs.readFile(mainFilePath, 'utf-8');
139
- const imports = [
140
- `import userRoutes from './routes/userRoutes.js';`,
141
- `import { errorHandler } from './middlewares/errorMiddleware.js';`
142
- ].join('\n');
143
- content = imports + '\n' + content;
144
- content = content.replace('app.use(express.json());', `app.use(express.json());\napp.use('/users', userRoutes);`);
145
- const errorMiddlewareCode = `\n// 전역 에러 핸들러\napp.use(errorHandler);\n`;
146
- content = content.includes('export default app;')
147
- ? content.replace('export default app;', `${errorMiddlewareCode}\nexport default app;`)
148
- : content + `\n${errorMiddlewareCode}`;
149
- await fs.writeFile(mainFilePath, content);
125
+
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);
143
+ }
150
144
  }
151
145
 
152
146
  // (4) .env 파일에 DB 타입별 DATABASE_URL 추가
153
147
  const envPath = path.join(targetPath, '.env');
154
- let dbUrlContent = '';
155
-
156
- if (dbType === 'postgresql') {
157
- dbUrlContent = `\n# PostgreSQL Connection\nDATABASE_URL="postgresql://myuser:mypassword@localhost:5433/mydb?schema=public"\n`;
158
- } else if (dbType === 'mysql') {
159
- dbUrlContent = `\n# MySQL Connection\nDATABASE_URL="mysql://root:mypassword@localhost:3307/mydb"\n`;
160
- }
161
-
162
- await fs.appendFile(envPath, dbUrlContent);
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`);
163
155
  }
164
156
 
165
157
  // 5. package.json 동적 최적화
@@ -177,8 +169,6 @@ async function run() {
177
169
  await fs.remove(path.join(targetPath, `vitest.config.${configExt}`));
178
170
  await fs.remove(path.join(targetPath, `src/app.test.${configExt}`));
179
171
  delete pkg.scripts.test;
180
- delete pkg.scripts["test:ui"];
181
- delete pkg.scripts["test:run"];
182
172
  delete pkg.devDependencies.vitest;
183
173
  delete pkg.devDependencies.supertest;
184
174
  }
@@ -186,14 +176,12 @@ async function run() {
186
176
  if (useDb) {
187
177
  pkg.scripts["db:up"] = "docker-compose up -d";
188
178
  pkg.scripts["db:push"] = "prisma db push";
189
- pkg.scripts["prisma:generate"] = "prisma generate";
190
- pkg.scripts["prisma:studio"] = "prisma studio";
191
179
  pkg.dependencies["@prisma/client"] = "^5.0.0";
192
180
  pkg.devDependencies["prisma"] = "^5.0.0";
193
181
  }
194
182
 
195
183
  await fs.writeJson(pkgPath, pkg, { spaces: 2 });
196
- s.stop('파일 구성 완료!');
184
+ s.stop('프로젝트 파일 구성 완료!');
197
185
 
198
186
  // 6. 의존성 설치
199
187
  const installSpinner = p.spinner();
@@ -202,14 +190,14 @@ async function run() {
202
190
  execSync('npm install', { cwd: targetPath, stdio: 'ignore' });
203
191
  installSpinner.stop('설치 완료!');
204
192
  } catch (e) {
205
- installSpinner.stop(chalk.red('설치 실패 (수동 설치가 필요할 있습니다)'));
193
+ installSpinner.stop(chalk.red('설치 실패 (수동으로 npm install을 실행해 주세요)'));
206
194
  }
207
195
 
208
- // 7. 마무리
196
+ // 7. 마무리 안내
209
197
  let nextSteps = `cd ${projectName}\n`;
210
198
  if (useDb) {
211
- nextSteps += `${chalk.bold('npm run db:up')} (DB 실행)\n`;
212
- nextSteps += `${chalk.bold('npm run db:push')} (테이블 생성)\n`;
199
+ nextSteps += `${chalk.bold('npm run db:up')} (Docker 실행)\n`;
200
+ nextSteps += `${chalk.bold('npm run db:push')} (DB 스키마 반영)\n`;
213
201
  }
214
202
  nextSteps += `npm run dev`;
215
203
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-express-esm",
3
- "version": "1.2.4",
3
+ "version": "1.2.5",
4
4
  "description": "A modern CLI tool to bootstrap Express.js applications with ES Modules and Layered Architecture.",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -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:
@@ -8,7 +8,7 @@ services:
8
8
  - POSTGRES_PASSWORD=mypassword
9
9
  - POSTGRES_DB=mydb
10
10
  ports:
11
- - '5432:5432'
11
+ - '5433:5432'
12
12
  volumes:
13
13
  - db-data:/var/lib/postgresql/data
14
14
 
@@ -0,0 +1,14 @@
1
+ datasource db {
2
+ provider = "mysql"
3
+ url = env("DATABASE_URL")
4
+ }
5
+
6
+ generator client {
7
+ provider = "prisma-client-js"
8
+ }
9
+
10
+ model User {
11
+ id Int @id @default(autoincrement())
12
+ email String @unique
13
+ name String?
14
+ }
@@ -0,0 +1,14 @@
1
+ datasource db {
2
+ provider = "mongodb"
3
+ url = env("DATABASE_URL")
4
+ }
5
+
6
+ generator client {
7
+ provider = "prisma-client-js"
8
+ }
9
+
10
+ model User {
11
+ id String @id @default(auto()) @map("_id") @db.ObjectId
12
+ email String @unique
13
+ name String?
14
+ }
@@ -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,9 @@
1
+ export const errorHandler = (err, req, res, next) => {
2
+ err.statusCode = err.statusCode || 500;
3
+ err.status = err.status || 'error';
4
+
5
+ res.status(err.statusCode).json({
6
+ status: err.status,
7
+ message: err.message,
8
+ });
9
+ };
@@ -0,0 +1,7 @@
1
+ export const findAllUsers = async () => {
2
+ // 실제 DB 연동 전 샘플 데이터
3
+ return [
4
+ { id: 1, name: "Developer", role: "Fullstack" },
5
+ { id: 2, name: "Gemius AI", role: "Assistant" }
6
+ ];
7
+ };
@@ -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,7 +1,7 @@
1
- import { Router } from 'express';
1
+ import express from 'express';
2
2
  import * as userController from '../controllers/userController.js';
3
3
 
4
- const router = Router();
4
+ const router = express.Router();
5
5
 
6
6
  router.get('/', userController.getUsers);
7
7
 
@@ -0,0 +1,12 @@
1
+ interface User {
2
+ id: number;
3
+ name: string;
4
+ role: string;
5
+ }
6
+
7
+ export const findAllUsers = async (): Promise<User[]> => {
8
+ return [
9
+ { id: 1, name: "Developer", role: "Fullstack" },
10
+ { id: 2, name: "Genius AI", role: "Assistant" }
11
+ ];
12
+ };
@@ -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.isOperational = true; // 예측 가능한 에러임을 표시
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
@@ -1,3 +0,0 @@
1
- # PostgreSQL Connection String
2
- # 형식: postgresql://사용자:비밀번호@호스트:포트/데이터베이스명?schema=public
3
- DATABASE_URL="postgresql://johndoe:randompassword@localhost:5432/myapp?schema=public"
@@ -3,17 +3,13 @@ import cors from 'cors';
3
3
  import morgan from 'morgan';
4
4
  import dotenv from 'dotenv';
5
5
 
6
- import userRoutes from './routes/userRoutes.js';
7
-
6
+ dotenv.config();
8
7
  const app = express();
9
8
 
10
9
  app.use(cors());
11
10
  app.use(morgan('dev'));
12
- app.use(express.json());
13
-
14
- // 라우터 연결 (Layered Arch 시작점)
15
- app.use('/api/users', userRoutes);
11
+ app.use(express.json()); // cli.js가 이 줄 바로 아래에 라우터를 꽂습니다.
16
12
 
17
13
  app.get('/', (req, res) => res.send('Welcome to Modern Express!'));
18
14
 
19
- export default app;
15
+ export default app; // cli.js가 이 줄 바로 위에 에러 핸들러를 꽂습니다.
@@ -2,7 +2,7 @@ import app from './app.js';
2
2
  import dotenv from 'dotenv';
3
3
 
4
4
  dotenv.config();
5
- const PORT = process.env.PORT || 3000;
5
+ const PORT = process.env.PORT || 8080;
6
6
 
7
7
  app.listen(PORT, () => {
8
8
  console.log(`🚀 Server running on http://localhost:${PORT}`);
package/template/ts/_env CHANGED
@@ -1,3 +0,0 @@
1
- # PostgreSQL Connection String
2
- # 형식: postgresql://사용자:비밀번호@호스트:포트/데이터베이스명?schema=public
3
- DATABASE_URL="postgresql://johndoe:randompassword@localhost:5432/myapp?schema=public"
@@ -1,7 +1,7 @@
1
1
  import 'dotenv/config';
2
2
  import app from './app.js';
3
3
 
4
- const PORT = process.env.PORT || 3000;
4
+ const PORT = process.env.PORT || 8080;
5
5
 
6
6
  app.listen(PORT, () => {
7
7
  console.log(`🚀 Server is running on http://localhost:${PORT}`);
File without changes