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.
- package/README.md +39 -58
- package/bin/cli.js +58 -70
- 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 → 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/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. 시작 인사 (v1.2.
|
|
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,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
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
|
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',
|
|
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,
|
|
107
|
+
path.join(commonPath, `docker-compose.yml.${dbType}`),
|
|
115
108
|
path.join(targetPath, 'docker-compose.yml')
|
|
116
109
|
);
|
|
117
110
|
|
|
118
|
-
// (2)
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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')} (
|
|
212
|
-
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`;
|
|
213
201
|
}
|
|
214
202
|
nextSteps += `npm run dev`;
|
|
215
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
package/template/js/src/app.js
CHANGED
|
@@ -3,17 +3,13 @@ import cors from 'cors';
|
|
|
3
3
|
import morgan from 'morgan';
|
|
4
4
|
import dotenv from 'dotenv';
|
|
5
5
|
|
|
6
|
-
|
|
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가 이 줄 바로 위에 에러 핸들러를 꽂습니다.
|
package/template/ts/_env
CHANGED
|
File without changes
|