@hamjimin/xplat-back 0.6.2 → 0.6.3
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 +81 -175
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
# @hamjimin/xplat-back
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
NestJS + TypeScript 백엔드 스캐폴딩/모듈 패키지 (zium-backend 기반)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
스토어/어드민 API 분리를 기본으로 하는 **NestJS 도메인 모듈 골격**을 제공하고,
|
|
6
|
+
유틸/보안/스토리지/결제/배송 등 공통 기능은 `xplatSystem` 싱글턴을 통해 접근할 수 있습니다.
|
|
7
|
+
|
|
8
|
+
> 기존 Express 기반 파일 라우팅(createApp/createRouter)은 **레거시 호환용**으로 유지됩니다.
|
|
6
9
|
|
|
7
10
|
## 특징
|
|
8
11
|
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
12
|
+
- 🧩 **NestJS 모듈 구조**: Store/Admin API 분리, 도메인 모듈 확장 전제
|
|
13
|
+
- 🏷️ **버저닝/Prefix 설계**: `/store/v1`, `/admin/v1` 형태의 라우팅 분리
|
|
14
|
+
- 🔌 **xplatSystem 싱글턴**: 인증/JWT, 스토리지(S3), 결제/배송 등 공통 기능 접근
|
|
15
|
+
- 🛠️ **확장 포인트**: 이벤트/잡/도메인 서비스 등 확장 가능한 구조
|
|
16
|
+
- 🔧 **TypeScript 지원**: 타입 정의 포함
|
|
14
17
|
|
|
15
18
|
## 설치
|
|
16
19
|
|
|
@@ -20,145 +23,115 @@ npm install @hamjimin/xplat-back
|
|
|
20
23
|
yarn add @hamjimin/xplat-back
|
|
21
24
|
```
|
|
22
25
|
|
|
23
|
-
## 빠른 시작
|
|
26
|
+
## 빠른 시작 (NestJS 권장)
|
|
24
27
|
|
|
25
|
-
###
|
|
26
|
-
```bash
|
|
27
|
-
npx @hamjimin/xplat-back [project-name]
|
|
28
|
-
```
|
|
28
|
+
### 1. NestJS 앱에 도메인 골격 적용
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
`xplat-back`는 커머스 도메인 골격(`store/admin` 분리)을 포함합니다.
|
|
31
|
+
서비스에서는 해당 구조를 기준으로 모듈/컨트롤러를 확장하는 방식을 권장합니다.
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
예: Store/Admin Health 컨트롤러 구조
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
import { Controller, Get } from '@nestjs/common';
|
|
37
|
+
|
|
38
|
+
@Controller()
|
|
39
|
+
export class StoreHealthController {
|
|
40
|
+
@Get('health')
|
|
41
|
+
health() {
|
|
42
|
+
return {
|
|
43
|
+
ok: true,
|
|
44
|
+
scope: 'store',
|
|
45
|
+
timestamp: new Date().toISOString(),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
34
49
|
```
|
|
35
50
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
51
|
+
```typescript
|
|
52
|
+
import { Controller, Get } from '@nestjs/common';
|
|
53
|
+
|
|
54
|
+
@Controller()
|
|
55
|
+
export class AdminHealthController {
|
|
56
|
+
@Get('health')
|
|
57
|
+
health() {
|
|
58
|
+
return {
|
|
59
|
+
ok: true,
|
|
60
|
+
scope: 'admin',
|
|
61
|
+
timestamp: new Date().toISOString(),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
```
|
|
42
66
|
|
|
43
|
-
###
|
|
67
|
+
### 2. 버저닝/Prefix 참고
|
|
44
68
|
|
|
45
|
-
|
|
69
|
+
Store/Admin API는 템플릿 기준으로 아래 prefix를 사용합니다.
|
|
46
70
|
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
|
|
71
|
+
```
|
|
72
|
+
Store API: /store/v1
|
|
73
|
+
Admin API: /admin/v1
|
|
50
74
|
```
|
|
51
75
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
`src/app.ts`:
|
|
76
|
+
### 3. 공통 기능 사용 (xplatSystem)
|
|
55
77
|
|
|
56
78
|
```typescript
|
|
57
|
-
import
|
|
58
|
-
import * as path from 'path';
|
|
59
|
-
import { createApp } from '@hamjimin/xplat-back';
|
|
60
|
-
|
|
61
|
-
const app: Application = createApp({
|
|
62
|
-
cors: {
|
|
63
|
-
origin: ['http://localhost:3000'],
|
|
64
|
-
credentials: true,
|
|
65
|
-
},
|
|
66
|
-
router: {
|
|
67
|
-
directory: path.join(__dirname, 'restapi'),
|
|
68
|
-
basePath: '/restapi',
|
|
69
|
-
},
|
|
70
|
-
trustProxy: true,
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
const port = parseInt(process.env.PORT || '4000', 10);
|
|
79
|
+
import { xplatSystem } from '@hamjimin/xplat-back';
|
|
74
80
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
export default app;
|
|
81
|
+
const { storePrefix, adminPrefix } = xplatSystem.commerce.getVersioning();
|
|
82
|
+
// storePrefix: /store/v1
|
|
83
|
+
// adminPrefix: /admin/v1
|
|
80
84
|
```
|
|
81
85
|
|
|
82
|
-
|
|
86
|
+
## 모듈 구성 (패키지 제공)
|
|
83
87
|
|
|
84
|
-
`
|
|
88
|
+
- `xplatSystem.app` (레거시 Express 앱 생성)
|
|
89
|
+
- `xplatSystem.router` (레거시 파일 기반 라우팅)
|
|
90
|
+
- `xplatSystem.auth` (JWT/인증 유틸)
|
|
91
|
+
- `xplatSystem.storage` (S3 유틸)
|
|
92
|
+
- `xplatSystem.payment` (Toss/KakaoPay)
|
|
93
|
+
- `xplatSystem.shipping` (CJ/Logen/CVS)
|
|
94
|
+
- `xplatSystem.orm` (쿼리 빌더/ORM 유틸)
|
|
85
95
|
|
|
86
|
-
|
|
87
|
-
import express, { Request, Response } from 'express';
|
|
96
|
+
## CLI (레거시 Express 스캐폴딩)
|
|
88
97
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
router.get('/', (req: Request, res: Response) => {
|
|
92
|
-
res.json({
|
|
93
|
-
success: true,
|
|
94
|
-
message: 'Server is healthy',
|
|
95
|
-
timestamp: new Date().toISOString(),
|
|
96
|
-
});
|
|
97
|
-
});
|
|
98
|
+
> 현재 CLI는 Express 기반 템플릿을 생성합니다.
|
|
99
|
+
> NestJS 전용 스캐폴딩은 별도 정리 후 제공 예정입니다.
|
|
98
100
|
|
|
99
|
-
|
|
101
|
+
```bash
|
|
102
|
+
npx @hamjimin/xplat-back [project-name]
|
|
100
103
|
```
|
|
101
104
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
#### 4. 서버 실행
|
|
105
|
+
옵션:
|
|
105
106
|
|
|
106
107
|
```bash
|
|
107
|
-
|
|
108
|
-
# 또는
|
|
109
|
-
ts-node -r tsconfig-paths/register src/app.ts
|
|
108
|
+
npx @hamjimin/xplat-back my-project --port 3000 --cors "http://localhost:3000,http://localhost:5173"
|
|
110
109
|
```
|
|
111
110
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
111
|
+
사용 가능한 옵션:
|
|
112
|
+
- `-p, --port <port>`: 서버 포트 (기본값: 4000)
|
|
113
|
+
- `-c, --cors <origins>`: CORS 허용 origins (쉼표로 구분)
|
|
114
|
+
- `-r, --router-dir <directory>`: 라우터 디렉토리 (기본값: src/restapi)
|
|
115
|
+
- `--router-path <path>`: 라우터 base path (기본값: /restapi)
|
|
116
|
+
- `--with-next`: Next.js 프론트엔드(web) 템플릿 추가
|
|
115
117
|
|
|
116
|
-
Express
|
|
118
|
+
## 레거시 Express API
|
|
117
119
|
|
|
118
|
-
|
|
120
|
+
NestJS 전환 이전 프로젝트를 위한 호환 API입니다.
|
|
119
121
|
|
|
120
|
-
|
|
121
|
-
interface AppConfig {
|
|
122
|
-
cors?: {
|
|
123
|
-
origin?: string[] | ((origin: string | undefined, callback: (err: Error | null, allow?: boolean) => void) => void);
|
|
124
|
-
credentials?: boolean;
|
|
125
|
-
};
|
|
126
|
-
json?: {
|
|
127
|
-
limit?: string;
|
|
128
|
-
};
|
|
129
|
-
urlencoded?: {
|
|
130
|
-
extended?: boolean;
|
|
131
|
-
};
|
|
132
|
-
router?: {
|
|
133
|
-
directory?: string;
|
|
134
|
-
basePath?: string;
|
|
135
|
-
};
|
|
136
|
-
trustProxy?: boolean;
|
|
137
|
-
middleware?: Array<(req: Request, res: Response, next: NextFunction) => void>;
|
|
138
|
-
}
|
|
139
|
-
```
|
|
122
|
+
### `createApp(config?)`
|
|
140
123
|
|
|
141
|
-
|
|
124
|
+
Express 애플리케이션을 생성합니다.
|
|
142
125
|
|
|
143
126
|
```typescript
|
|
144
127
|
import { createApp } from '@hamjimin/xplat-back';
|
|
128
|
+
import * as path from 'path';
|
|
145
129
|
|
|
146
130
|
const app = createApp({
|
|
147
|
-
cors: {
|
|
148
|
-
origin: process.env.CORS_ALLOW_ORIGINS?.split(',') ?? ['*'],
|
|
149
|
-
credentials: true,
|
|
150
|
-
},
|
|
151
131
|
router: {
|
|
152
132
|
directory: path.join(__dirname, 'restapi'),
|
|
153
133
|
basePath: '/restapi',
|
|
154
134
|
},
|
|
155
|
-
middleware: [
|
|
156
|
-
// 커스텀 미들웨어
|
|
157
|
-
(req, res, next) => {
|
|
158
|
-
console.log(`${req.method} ${req.path}`);
|
|
159
|
-
next();
|
|
160
|
-
},
|
|
161
|
-
],
|
|
162
135
|
});
|
|
163
136
|
```
|
|
164
137
|
|
|
@@ -166,12 +139,6 @@ const app = createApp({
|
|
|
166
139
|
|
|
167
140
|
디렉토리 구조를 기반으로 Express 라우터를 생성합니다.
|
|
168
141
|
|
|
169
|
-
#### Parameters
|
|
170
|
-
|
|
171
|
-
- `directory` (string): 라우트 파일들이 위치한 디렉토리 경로
|
|
172
|
-
|
|
173
|
-
#### 예제
|
|
174
|
-
|
|
175
142
|
```typescript
|
|
176
143
|
import { createRouter } from '@hamjimin/xplat-back';
|
|
177
144
|
import * as path from 'path';
|
|
@@ -180,67 +147,6 @@ const router = createRouter(path.join(__dirname, 'restapi'));
|
|
|
180
147
|
app.use('/restapi', router);
|
|
181
148
|
```
|
|
182
149
|
|
|
183
|
-
#### 라우팅 규칙
|
|
184
|
-
|
|
185
|
-
디렉토리 구조가 다음과 같을 때:
|
|
186
|
-
|
|
187
|
-
```
|
|
188
|
-
restapi/
|
|
189
|
-
├── health.ts → GET /restapi/health
|
|
190
|
-
├── user/
|
|
191
|
-
│ ├── profile.ts → GET /restapi/user/profile
|
|
192
|
-
│ └── settings.ts → GET /restapi/user/settings
|
|
193
|
-
└── admin/
|
|
194
|
-
└── users.ts → GET /restapi/admin/users
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
- 파일명이 endpoint로 변환됩니다 (`.ts`, `.js` 확장자 제거)
|
|
198
|
-
- 디렉토리는 경로의 일부가 됩니다
|
|
199
|
-
- 각 파일은 Express Router를 export해야 합니다
|
|
200
|
-
|
|
201
|
-
## 라우트 파일 작성법
|
|
202
|
-
|
|
203
|
-
각 라우트 파일은 Express Router를 export해야 합니다:
|
|
204
|
-
|
|
205
|
-
```typescript
|
|
206
|
-
import express, { Request, Response } from 'express';
|
|
207
|
-
|
|
208
|
-
const router = express.Router();
|
|
209
|
-
|
|
210
|
-
// GET /restapi/example
|
|
211
|
-
router.get('/', (req: Request, res: Response) => {
|
|
212
|
-
res.json({ message: 'Hello World' });
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
// POST /restapi/example
|
|
216
|
-
router.post('/', (req: Request, res: Response) => {
|
|
217
|
-
res.json({ message: 'Created' });
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
export = router;
|
|
221
|
-
// 또는
|
|
222
|
-
export default router;
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
## 프로젝트 구조 예시
|
|
226
|
-
|
|
227
|
-
```
|
|
228
|
-
my-backend-project/
|
|
229
|
-
├── src/
|
|
230
|
-
│ ├── app.ts # Express 앱 설정
|
|
231
|
-
│ ├── server.ts # 서버 시작 (선택적)
|
|
232
|
-
│ └── restapi/ # API 라우트 파일들
|
|
233
|
-
│ ├── health.ts # GET /restapi/health
|
|
234
|
-
│ ├── user/
|
|
235
|
-
│ │ └── profile.ts # GET /restapi/user/profile
|
|
236
|
-
│ └── admin/
|
|
237
|
-
│ └── users.ts # GET /restapi/admin/users
|
|
238
|
-
├── dist/ # TypeScript 컴파일 결과
|
|
239
|
-
├── tsconfig.json
|
|
240
|
-
├── package.json
|
|
241
|
-
└── .env
|
|
242
|
-
```
|
|
243
|
-
|
|
244
150
|
## 환경변수
|
|
245
151
|
|
|
246
152
|
`.env` 파일에서 설정할 수 있는 환경변수:
|