ts-deco 1.0.15 → 1.0.18

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 (221) hide show
  1. package/README.md +247 -6
  2. package/dist/app/app.d.ts +5 -0
  3. package/dist/app/app.d.ts.map +1 -0
  4. package/dist/app/app.js +181 -0
  5. package/dist/app/app.js.map +1 -0
  6. package/dist/app/config/env.config.d.ts +14 -0
  7. package/dist/app/config/env.config.d.ts.map +1 -0
  8. package/dist/app/config/env.config.js +22 -0
  9. package/dist/app/config/env.config.js.map +1 -0
  10. package/dist/app/middleware/error-handler.middleware.d.ts +17 -0
  11. package/dist/app/middleware/error-handler.middleware.d.ts.map +1 -0
  12. package/dist/app/middleware/error-handler.middleware.js +38 -0
  13. package/dist/app/middleware/error-handler.middleware.js.map +1 -0
  14. package/dist/app/middleware/logger.middleware.d.ts +6 -0
  15. package/dist/app/middleware/logger.middleware.d.ts.map +1 -0
  16. package/dist/app/middleware/logger.middleware.js +34 -0
  17. package/dist/app/middleware/logger.middleware.js.map +1 -0
  18. package/dist/app/middleware/not-found.middleware.d.ts +6 -0
  19. package/dist/app/middleware/not-found.middleware.d.ts.map +1 -0
  20. package/dist/app/middleware/not-found.middleware.js +18 -0
  21. package/dist/app/middleware/not-found.middleware.js.map +1 -0
  22. package/dist/app/routers/async/controllers/async-example.controller.d.ts +32 -0
  23. package/dist/app/routers/async/controllers/async-example.controller.d.ts.map +1 -0
  24. package/dist/app/routers/async/controllers/async-example.controller.js +205 -0
  25. package/dist/app/routers/async/controllers/async-example.controller.js.map +1 -0
  26. package/dist/app/routers/async/controllers/index.d.ts +2 -0
  27. package/dist/app/routers/async/controllers/index.d.ts.map +1 -0
  28. package/dist/app/routers/async/controllers/index.js +6 -0
  29. package/dist/app/routers/async/controllers/index.js.map +1 -0
  30. package/dist/app/routers/health/controllers/health.controller.d.ts +5 -0
  31. package/dist/app/routers/health/controllers/health.controller.d.ts.map +1 -0
  32. package/dist/app/routers/health/controllers/health.controller.js +35 -0
  33. package/dist/app/routers/health/controllers/health.controller.js.map +1 -0
  34. package/dist/app/routers/health/controllers/index.d.ts +2 -0
  35. package/dist/app/routers/health/controllers/index.d.ts.map +1 -0
  36. package/dist/app/routers/health/controllers/index.js +6 -0
  37. package/dist/app/routers/health/controllers/index.js.map +1 -0
  38. package/dist/app/routers/index.d.ts +6 -0
  39. package/dist/app/routers/index.d.ts.map +1 -0
  40. package/dist/app/routers/index.js +23 -0
  41. package/dist/app/routers/index.js.map +1 -0
  42. package/dist/app/routers/products/controllers/index.d.ts +2 -0
  43. package/dist/app/routers/products/controllers/index.d.ts.map +1 -0
  44. package/dist/app/routers/products/controllers/index.js +6 -0
  45. package/dist/app/routers/products/controllers/index.js.map +1 -0
  46. package/dist/app/routers/products/controllers/products.controller.d.ts +21 -0
  47. package/dist/app/routers/products/controllers/products.controller.d.ts.map +1 -0
  48. package/dist/app/routers/products/controllers/products.controller.js +124 -0
  49. package/dist/app/routers/products/controllers/products.controller.js.map +1 -0
  50. package/dist/app/routers/products/dto/index.d.ts +2 -0
  51. package/dist/app/routers/products/dto/index.d.ts.map +1 -0
  52. package/dist/app/routers/products/dto/index.js +18 -0
  53. package/dist/app/routers/products/dto/index.js.map +1 -0
  54. package/dist/app/routers/products/dto/product.dto.d.ts +23 -0
  55. package/dist/app/routers/products/dto/product.dto.d.ts.map +1 -0
  56. package/dist/app/routers/products/dto/product.dto.js +84 -0
  57. package/dist/app/routers/products/dto/product.dto.js.map +1 -0
  58. package/dist/app/routers/products/services/index.d.ts +2 -0
  59. package/dist/app/routers/products/services/index.d.ts.map +1 -0
  60. package/dist/app/routers/products/services/index.js +6 -0
  61. package/dist/app/routers/products/services/index.js.map +1 -0
  62. package/dist/app/routers/products/services/product.service.d.ts +32 -0
  63. package/dist/app/routers/products/services/product.service.d.ts.map +1 -0
  64. package/dist/app/routers/products/services/product.service.js +62 -0
  65. package/dist/app/routers/products/services/product.service.js.map +1 -0
  66. package/dist/app/routers/test/controllers/index.d.ts +2 -0
  67. package/dist/app/routers/test/controllers/index.d.ts.map +1 -0
  68. package/dist/app/routers/test/controllers/index.js +6 -0
  69. package/dist/app/routers/test/controllers/index.js.map +1 -0
  70. package/dist/app/routers/test/controllers/test.controller.d.ts +6 -0
  71. package/dist/app/routers/test/controllers/test.controller.d.ts.map +1 -0
  72. package/dist/app/routers/test/controllers/test.controller.js +68 -0
  73. package/dist/app/routers/test/controllers/test.controller.js.map +1 -0
  74. package/dist/app/routers/test/dto/index.d.ts +2 -0
  75. package/dist/app/routers/test/dto/index.d.ts.map +1 -0
  76. package/dist/app/routers/test/dto/index.js +18 -0
  77. package/dist/app/routers/test/dto/index.js.map +1 -0
  78. package/dist/app/routers/test/dto/test.dto.d.ts +27 -0
  79. package/dist/app/routers/test/dto/test.dto.d.ts.map +1 -0
  80. package/dist/app/routers/test/dto/test.dto.js +81 -0
  81. package/dist/app/routers/test/dto/test.dto.js.map +1 -0
  82. package/dist/app/routers/users/controllers/index.d.ts +2 -0
  83. package/dist/app/routers/users/controllers/index.d.ts.map +1 -0
  84. package/dist/app/routers/users/controllers/index.js +6 -0
  85. package/dist/app/routers/users/controllers/index.js.map +1 -0
  86. package/dist/app/routers/users/controllers/users.controller.d.ts +6 -0
  87. package/dist/app/routers/users/controllers/users.controller.d.ts.map +1 -0
  88. package/dist/app/routers/users/controllers/users.controller.js +68 -0
  89. package/dist/app/routers/users/controllers/users.controller.js.map +1 -0
  90. package/dist/app/routers/users/dto/index.d.ts +2 -0
  91. package/dist/app/routers/users/dto/index.d.ts.map +1 -0
  92. package/dist/app/routers/users/dto/index.js +18 -0
  93. package/dist/app/routers/users/dto/index.js.map +1 -0
  94. package/dist/app/routers/users/dto/user.dto.d.ts +28 -0
  95. package/dist/app/routers/users/dto/user.dto.d.ts.map +1 -0
  96. package/dist/app/routers/users/dto/user.dto.js +96 -0
  97. package/dist/app/routers/users/dto/user.dto.js.map +1 -0
  98. package/dist/app/routers/users/services/index.d.ts +2 -0
  99. package/dist/app/routers/users/services/index.d.ts.map +1 -0
  100. package/dist/app/routers/users/services/index.js +6 -0
  101. package/dist/app/routers/users/services/index.js.map +1 -0
  102. package/dist/app/routers/users/services/user.service.d.ts +16 -0
  103. package/dist/app/routers/users/services/user.service.d.ts.map +1 -0
  104. package/dist/app/routers/users/services/user.service.js +39 -0
  105. package/dist/app/routers/users/services/user.service.js.map +1 -0
  106. package/dist/src/configs/app-initializer.d.ts +49 -0
  107. package/dist/src/configs/app-initializer.d.ts.map +1 -0
  108. package/dist/src/configs/app-initializer.js +55 -0
  109. package/dist/src/configs/app-initializer.js.map +1 -0
  110. package/dist/src/configs/extractors/auto-extract-dtos.extractor.d.ts +7 -0
  111. package/dist/src/configs/extractors/auto-extract-dtos.extractor.d.ts.map +1 -0
  112. package/dist/src/configs/extractors/auto-extract-dtos.extractor.js +54 -0
  113. package/dist/src/configs/extractors/auto-extract-dtos.extractor.js.map +1 -0
  114. package/dist/src/configs/extractors/extract-controllers-from-module.extractor.d.ts +8 -0
  115. package/dist/src/configs/extractors/extract-controllers-from-module.extractor.d.ts.map +1 -0
  116. package/dist/src/configs/extractors/extract-controllers-from-module.extractor.js +36 -0
  117. package/dist/src/configs/extractors/extract-controllers-from-module.extractor.js.map +1 -0
  118. package/dist/src/configs/extractors/extract-dtos-from-module.extractor.d.ts +8 -0
  119. package/dist/src/configs/extractors/extract-dtos-from-module.extractor.d.ts.map +1 -0
  120. package/dist/src/configs/extractors/extract-dtos-from-module.extractor.js +34 -0
  121. package/dist/src/configs/extractors/extract-dtos-from-module.extractor.js.map +1 -0
  122. package/dist/src/configs/extractors/index.d.ts +4 -0
  123. package/dist/src/configs/extractors/index.d.ts.map +1 -0
  124. package/dist/src/configs/extractors/index.js +10 -0
  125. package/dist/src/configs/extractors/index.js.map +1 -0
  126. package/dist/src/configs/index.d.ts +9 -0
  127. package/dist/src/configs/index.d.ts.map +1 -0
  128. package/dist/src/configs/index.js +31 -0
  129. package/dist/src/configs/index.js.map +1 -0
  130. package/dist/src/configs/middleware/index.d.ts +2 -0
  131. package/dist/src/configs/middleware/index.d.ts.map +1 -0
  132. package/dist/src/configs/middleware/index.js +6 -0
  133. package/dist/src/configs/middleware/index.js.map +1 -0
  134. package/dist/src/configs/middleware/validation.middleware.d.ts +18 -0
  135. package/dist/src/configs/middleware/validation.middleware.d.ts.map +1 -0
  136. package/dist/src/configs/middleware/validation.middleware.js +50 -0
  137. package/dist/src/configs/middleware/validation.middleware.js.map +1 -0
  138. package/dist/src/configs/routes/auto-register-routes.route.d.ts +10 -0
  139. package/dist/src/configs/routes/auto-register-routes.route.d.ts.map +1 -0
  140. package/dist/src/configs/routes/auto-register-routes.route.js +80 -0
  141. package/dist/src/configs/routes/auto-register-routes.route.js.map +1 -0
  142. package/dist/src/configs/routes/auto-scan-controller-methods.route.d.ts +8 -0
  143. package/dist/src/configs/routes/auto-scan-controller-methods.route.d.ts.map +1 -0
  144. package/dist/src/configs/routes/auto-scan-controller-methods.route.js +44 -0
  145. package/dist/src/configs/routes/auto-scan-controller-methods.route.js.map +1 -0
  146. package/dist/src/configs/routes/generate-route-info.route.d.ts +11 -0
  147. package/dist/src/configs/routes/generate-route-info.route.d.ts.map +1 -0
  148. package/dist/src/configs/routes/generate-route-info.route.js +84 -0
  149. package/dist/src/configs/routes/generate-route-info.route.js.map +1 -0
  150. package/dist/src/configs/routes/index.d.ts +4 -0
  151. package/dist/src/configs/routes/index.d.ts.map +1 -0
  152. package/dist/src/configs/routes/index.js +10 -0
  153. package/dist/src/configs/routes/index.js.map +1 -0
  154. package/dist/src/configs/swagger.factory.d.ts +87 -0
  155. package/dist/src/configs/swagger.factory.d.ts.map +1 -0
  156. package/dist/src/configs/swagger.factory.js +689 -0
  157. package/dist/src/configs/swagger.factory.js.map +1 -0
  158. package/dist/src/configs/utils/format-validation-errors.util.d.ts +8 -0
  159. package/dist/src/configs/utils/format-validation-errors.util.d.ts.map +1 -0
  160. package/dist/src/configs/utils/format-validation-errors.util.js +25 -0
  161. package/dist/src/configs/utils/format-validation-errors.util.js.map +1 -0
  162. package/dist/src/configs/utils/has-swagger-decorators.util.d.ts +9 -0
  163. package/dist/src/configs/utils/has-swagger-decorators.util.d.ts.map +1 -0
  164. package/dist/src/configs/utils/has-swagger-decorators.util.js +19 -0
  165. package/dist/src/configs/utils/has-swagger-decorators.util.js.map +1 -0
  166. package/dist/src/configs/utils/index.d.ts +4 -0
  167. package/dist/src/configs/utils/index.d.ts.map +1 -0
  168. package/dist/src/configs/utils/index.js +10 -0
  169. package/dist/src/configs/utils/index.js.map +1 -0
  170. package/dist/src/configs/utils/is-enum.util.d.ts +7 -0
  171. package/dist/src/configs/utils/is-enum.util.d.ts.map +1 -0
  172. package/dist/src/configs/utils/is-enum.util.js +26 -0
  173. package/dist/src/configs/utils/is-enum.util.js.map +1 -0
  174. package/dist/src/decorators/controllers/api-body.decorator.d.ts +20 -0
  175. package/dist/src/decorators/controllers/api-body.decorator.d.ts.map +1 -0
  176. package/dist/src/decorators/controllers/api-body.decorator.js +39 -0
  177. package/dist/src/decorators/controllers/api-body.decorator.js.map +1 -0
  178. package/dist/src/decorators/controllers/api-operation.decorator.d.ts +15 -0
  179. package/dist/src/decorators/controllers/api-operation.decorator.d.ts.map +1 -0
  180. package/dist/src/decorators/controllers/api-operation.decorator.js +23 -0
  181. package/dist/src/decorators/controllers/api-operation.decorator.js.map +1 -0
  182. package/dist/src/decorators/controllers/api-param.decorator.d.ts +20 -0
  183. package/dist/src/decorators/controllers/api-param.decorator.d.ts.map +1 -0
  184. package/dist/src/decorators/controllers/api-param.decorator.js +41 -0
  185. package/dist/src/decorators/controllers/api-param.decorator.js.map +1 -0
  186. package/dist/src/decorators/controllers/api-query.decorator.d.ts +20 -0
  187. package/dist/src/decorators/controllers/api-query.decorator.d.ts.map +1 -0
  188. package/dist/src/decorators/controllers/api-query.decorator.js +41 -0
  189. package/dist/src/decorators/controllers/api-query.decorator.js.map +1 -0
  190. package/dist/src/decorators/controllers/api-response.decorator.d.ts +16 -0
  191. package/dist/src/decorators/controllers/api-response.decorator.d.ts.map +1 -0
  192. package/dist/src/decorators/controllers/api-response.decorator.js +25 -0
  193. package/dist/src/decorators/controllers/api-response.decorator.js.map +1 -0
  194. package/dist/src/decorators/controllers/api-tags.decorator.d.ts +17 -0
  195. package/dist/src/decorators/controllers/api-tags.decorator.d.ts.map +1 -0
  196. package/dist/src/decorators/controllers/api-tags.decorator.js +35 -0
  197. package/dist/src/decorators/controllers/api-tags.decorator.js.map +1 -0
  198. package/dist/src/decorators/controllers/controller-metadata.d.ts +75 -0
  199. package/dist/src/decorators/controllers/controller-metadata.d.ts.map +1 -0
  200. package/dist/src/decorators/controllers/controller-metadata.js +17 -0
  201. package/dist/src/decorators/controllers/controller-metadata.js.map +1 -0
  202. package/dist/src/decorators/controllers/controller.decorator.d.ts +31 -0
  203. package/dist/src/decorators/controllers/controller.decorator.d.ts.map +1 -0
  204. package/dist/src/decorators/controllers/controller.decorator.js +52 -0
  205. package/dist/src/decorators/controllers/controller.decorator.js.map +1 -0
  206. package/dist/src/decorators/controllers/index.d.ts +12 -0
  207. package/dist/src/decorators/controllers/index.d.ts.map +1 -0
  208. package/dist/src/decorators/controllers/index.js +24 -0
  209. package/dist/src/decorators/controllers/index.js.map +1 -0
  210. package/dist/src/decorators/controllers/operation.decorator.d.ts +45 -0
  211. package/dist/src/decorators/controllers/operation.decorator.d.ts.map +1 -0
  212. package/dist/src/decorators/controllers/operation.decorator.js +93 -0
  213. package/dist/src/decorators/controllers/operation.decorator.js.map +1 -0
  214. package/dist/src/decorators/validators/number.decorator.d.ts.map +1 -1
  215. package/dist/src/decorators/validators/number.decorator.js +23 -1
  216. package/dist/src/decorators/validators/number.decorator.js.map +1 -1
  217. package/dist/src/index.d.ts +2 -0
  218. package/dist/src/index.d.ts.map +1 -1
  219. package/dist/src/index.js +10 -1
  220. package/dist/src/index.js.map +1 -1
  221. package/package.json +13 -5
package/README.md CHANGED
@@ -1,8 +1,10 @@
1
1
  # ts-deco
2
2
 
3
- TypeScript decorator utilities with **strict validation standards**
3
+ **검증과 Swagger 작성을 동시에 처리하는 함축적인 데코레이터 라이브러리**
4
4
 
5
- 엄격한 타입 검증과 검증 규칙을 제공하는 Node.js/TypeScript용 DTO 데코레이터 라이브러리입니다.
5
+ TypeScript decorator utilities with **strict validation standards** and **automatic Swagger documentation**
6
+
7
+ 하나의 데코레이터로 **타입 검증과 Swagger API 문서화를 동시에** 처리하는 Node.js/TypeScript용 DTO 데코레이터 라이브러리입니다. **NestJS**와 **Node.js (Express, Fastify 등)** 모든 프로젝트에서 사용할 수 있습니다.
6
8
 
7
9
  ## 설치
8
10
 
@@ -12,6 +14,49 @@ npm install ts-deco
12
14
 
13
15
  ## 빠른 시작
14
16
 
17
+ **`Property` 데코레이터 하나로 검증과 Swagger 문서 작성을 동시에** 처리할 수 있습니다:
18
+
19
+ ### NestJS에서 사용
20
+
21
+ ```typescript
22
+ import { Property } from 'ts-deco';
23
+
24
+ class CreateUserDto {
25
+ @Property({ type: String, optional: false })
26
+ name: string;
27
+
28
+ @Property({ type: Number, min: 0, max: 120, optional: false })
29
+ age: number;
30
+ }
31
+
32
+ // NestJS Controller에서 자동으로 Swagger 문서 생성 및 검증 수행
33
+ @Post()
34
+ create(@Body() dto: CreateUserDto) {
35
+ // 자동 검증 완료, Swagger 문서에 자동 반영
36
+ }
37
+ ```
38
+
39
+ ### Node.js/Express에서 사용
40
+
41
+ ```typescript
42
+ import { Property } from 'ts-deco';
43
+
44
+ class CreateUserDto {
45
+ @Property({ type: String, optional: false })
46
+ name: string;
47
+
48
+ @Property({ type: Number, min: 0, max: 120, optional: false })
49
+ age: number;
50
+ }
51
+
52
+ // Express 라우트에서 검증 및 Swagger 문서 생성
53
+ app.post('/users', validationMiddleware(CreateUserDto), (req, res) => {
54
+ // 자동 검증 완료, Swagger 문서에 자동 반영
55
+ });
56
+ ```
57
+
58
+ ### 기본 사용법
59
+
15
60
  `Property` 데코레이터 하나로 모든 타입을 처리할 수 있습니다:
16
61
 
17
62
  ```typescript
@@ -37,9 +82,18 @@ class CreateUserDto {
37
82
 
38
83
  ## 주요 기능
39
84
 
85
+ ### ⚡ 하나의 데코레이터로 검증 + Swagger 문서화
86
+
87
+ **`Property` 데코레이터는 타입에 따라 자동으로 적절한 검증을 적용하고, 동시에 Swagger API 문서를 자동 생성합니다.**
88
+
89
+ - ✅ **검증**: `class-validator`를 사용한 런타임 타입 검증
90
+ - ✅ **Swagger**: `@nestjs/swagger` 호환 메타데이터로 자동 API 문서 생성
91
+ - ✅ **함축적**: 하나의 데코레이터로 두 가지 작업을 동시에 처리
92
+ - ✅ **호환성**: NestJS와 Node.js (Express, Fastify 등) 모두 지원
93
+
40
94
  ### Property 데코레이터 (권장)
41
95
 
42
- `Property` 데코레이터는 타입에 따라 자동으로 적절한 검증을 적용합니다.
96
+ `Property` 데코레이터는 타입에 따라 자동으로 적절한 검증과 Swagger 스키마를 적용합니다.
43
97
 
44
98
  #### 기본 타입
45
99
 
@@ -204,6 +258,131 @@ class UserDto {
204
258
  - **DtoBoolean**: `optional`, `isNotEmpty`, `isArray`, `validatorMessage`, `notEmptyMessage`, `arrayMessage` 및 Swagger 옵션
205
259
  - **DtoEnum**: `optional`, `isNotEmpty`, `isArray`, `exclude`, `validatorMessage`, `notEmptyMessage`, `arrayMessage` 및 Swagger 옵션
206
260
 
261
+ ## Controller 데코레이터 (NestJS 스타일)
262
+
263
+ **검증과 Swagger 문서 작성을 동시에 처리하는 DTO 데코레이터** 외에도, **API 엔드포인트의 Swagger 문서를 자동 생성하는 Controller 데코레이터**를 제공합니다.
264
+
265
+ ### 사용 예제
266
+
267
+ #### NestJS에서 사용
268
+
269
+ ```typescript
270
+ import { ApiTags, ApiOperation, ApiResponse, ApiParam, ApiBody, ApiQuery } from 'ts-deco';
271
+
272
+ @ApiTags('Users')
273
+ @Controller('users')
274
+ export class UserController {
275
+ @ApiOperation({ summary: '사용자 생성' })
276
+ @ApiResponse({ status: 200, description: '사용자 생성 성공' })
277
+ @ApiResponse({ status: 400, description: 'Validation 실패' })
278
+ @ApiBody({ type: CreateUserDto, required: true })
279
+ @Post()
280
+ create(@Body() dto: CreateUserDto) {
281
+ // ...
282
+ }
283
+
284
+ @ApiOperation({ summary: '사용자 업데이트' })
285
+ @ApiResponse({ status: 200, description: '사용자 업데이트 성공' })
286
+ @ApiParam({ name: 'id', type: String, required: true })
287
+ @ApiBody({ type: UpdateUserDto, required: true })
288
+ @Patch(':id')
289
+ update(@Param('id') id: string, @Body() dto: UpdateUserDto) {
290
+ // ...
291
+ }
292
+
293
+ @ApiOperation({ summary: '사용자 목록 조회' })
294
+ @ApiQuery({ name: 'page', type: Number, required: false, minimum: 1 })
295
+ @ApiQuery({ name: 'limit', type: Number, required: false, minimum: 1, maximum: 100 })
296
+ @Get()
297
+ findAll(@Query('page') page?: number, @Query('limit') limit?: number) {
298
+ // ...
299
+ }
300
+ }
301
+ ```
302
+
303
+ #### Node.js/Express에서 사용
304
+
305
+ ```typescript
306
+ import { ApiTags, ApiOperation, ApiResponse, ApiParam, ApiBody, ApiQuery } from 'ts-deco';
307
+
308
+ class UserRoutes {
309
+ @ApiTags('Users')
310
+ @ApiOperation({ summary: '사용자 생성' })
311
+ @ApiResponse({ status: 200, description: '사용자 생성 성공' })
312
+ @ApiResponse({ status: 400, description: 'Validation 실패' })
313
+ @ApiBody({ type: CreateUserDto, required: true })
314
+ static createUser(req: Request, res: Response) {
315
+ // ...
316
+ }
317
+
318
+ @ApiTags('Users')
319
+ @ApiOperation({ summary: '사용자 업데이트' })
320
+ @ApiResponse({ status: 200, description: '사용자 업데이트 성공' })
321
+ @ApiParam({ name: 'id', type: String, required: true })
322
+ @ApiBody({ type: UpdateUserDto, required: true })
323
+ static updateUser(req: Request, res: Response) {
324
+ // ...
325
+ }
326
+ }
327
+
328
+ app.post('/api/users', validationMiddleware(CreateUserDto), UserRoutes.createUser);
329
+ app.patch('/api/users/:id', validationMiddleware(UpdateUserDto), UserRoutes.updateUser);
330
+ ```
331
+
332
+ ### Controller 데코레이터 목록
333
+
334
+ #### ApiTags
335
+ 클래스 또는 메서드에 Swagger 태그를 설정합니다.
336
+
337
+ ```typescript
338
+ @ApiTags('Users')
339
+ @ApiTags('Admin', 'Users') // 여러 태그
340
+ ```
341
+
342
+ #### ApiOperation
343
+ 메서드에 Swagger 작업(operation) 정보를 설정합니다.
344
+
345
+ ```typescript
346
+ @ApiOperation({
347
+ summary: '사용자 생성',
348
+ description: '새로운 사용자를 생성합니다',
349
+ operationId: 'createUser',
350
+ deprecated: false,
351
+ tags: ['Users']
352
+ })
353
+ ```
354
+
355
+ #### ApiResponse
356
+ 메서드에 Swagger 응답 정보를 설정합니다. 여러 번 사용 가능합니다.
357
+
358
+ ```typescript
359
+ @ApiResponse({ status: 200, description: '성공' })
360
+ @ApiResponse({ status: 400, description: 'Validation 실패' })
361
+ @ApiResponse({ status: 404, description: '사용자를 찾을 수 없음' })
362
+ ```
363
+
364
+ #### ApiParam
365
+ 경로 파라미터에 대한 Swagger 정보를 설정합니다.
366
+
367
+ ```typescript
368
+ @ApiParam({ name: 'id', type: String, required: true, description: '사용자 ID' })
369
+ ```
370
+
371
+ #### ApiQuery
372
+ 쿼리 파라미터에 대한 Swagger 정보를 설정합니다.
373
+
374
+ ```typescript
375
+ @ApiQuery({ name: 'page', type: Number, required: false, minimum: 1 })
376
+ @ApiQuery({ name: 'limit', type: Number, required: false, minimum: 1, maximum: 100 })
377
+ ```
378
+
379
+ #### ApiBody
380
+ 요청 본문에 대한 Swagger 정보를 설정합니다.
381
+
382
+ ```typescript
383
+ @ApiBody({ type: CreateUserDto, description: '사용자 생성 데이터' })
384
+ ```
385
+
207
386
  ## Property 데코레이터 옵션
208
387
 
209
388
  ### 공통 옵션
@@ -235,9 +414,25 @@ class UserDto {
235
414
  |------|------|--------|------|
236
415
  | `exclude` | `any[]` | `[]` | 제외할 enum 값들 |
237
416
 
238
- ### Swagger 옵션 (선택적)
417
+ ### Swagger 옵션 (자동 생성)
418
+
419
+ **`Property` 데코레이터는 자동으로 Swagger API 문서를 생성합니다.** `@nestjs/swagger`의 `ApiPropertyOptions`를 모두 사용할 수 있어 NestJS와 완벽하게 호환됩니다:
420
+
421
+ #### 검증 + Swagger 자동 생성 예제
239
422
 
240
- Swagger API 문서화를 사용하는 경우, `@nestjs/swagger`의 `ApiPropertyOptions`를 모두 사용할 수 있습니다:
423
+ ```typescript
424
+ @Property({
425
+ type: String,
426
+ description: '사용자 이름', // Swagger 문서에 표시
427
+ example: '홍길동', // Swagger 문서에 표시
428
+ optional: false // 검증 규칙 + Swagger required
429
+ })
430
+ name: string;
431
+ ```
432
+
433
+ 이렇게 작성하면:
434
+ - ✅ **검증**: `name`이 필수이며 문자열이어야 함
435
+ - ✅ **Swagger**: API 문서에 `name` 필드가 필수 문자열로 자동 등록됨
241
436
 
242
437
  ```typescript
243
438
  @Property({
@@ -350,6 +545,43 @@ class PostDto {
350
545
  4. 타입 검증 (IsString, IsNumber, IsDate, IsBoolean, IsEnum 등)
351
546
  5. 추가 검증 (min/max, exclude 등)
352
547
 
548
+ ## 왜 ts-deco를 사용해야 할까요?
549
+
550
+ ### 🎯 검증과 Swagger 작성을 한 번에
551
+
552
+ 기존에는 검증 데코레이터와 Swagger 데코레이터를 따로 작성해야 했습니다:
553
+
554
+ ```typescript
555
+ // 기존 방식 (번거로움)
556
+ @IsString()
557
+ @IsNotEmpty()
558
+ @ApiProperty({ type: String, description: '사용자 이름', example: '홍길동' })
559
+ name: string;
560
+ ```
561
+
562
+ **ts-deco를 사용하면 하나의 데코레이터로 처리합니다:**
563
+
564
+ ```typescript
565
+ // ts-deco 방식 (간결함)
566
+ @Property({
567
+ type: String,
568
+ optional: false,
569
+ isNotEmpty: true,
570
+ description: '사용자 이름',
571
+ example: '홍길동'
572
+ })
573
+ name: string;
574
+ ```
575
+
576
+ ### 🌟 주요 장점
577
+
578
+ - ✅ **함축적**: 하나의 데코레이터로 검증과 Swagger 문서화 동시 처리
579
+ - ✅ **간결함**: 코드 중복 제거, 가독성 향상
580
+ - ✅ **타입 안전성**: 엄격한 타입 체크로 컴파일 타임 에러 방지
581
+ - ✅ **자동 문서화**: Swagger API 문서 자동 생성
582
+ - ✅ **호환성**: NestJS와 Node.js (Express, Fastify 등) 모두 지원
583
+ - ✅ **런타임 안정성**: 명확한 에러 메시지 제공
584
+
353
585
  ## 엄격한 검증 규칙
354
586
 
355
587
  `ts-deco`는 다음과 같은 엄격한 검증 규칙을 적용합니다:
@@ -358,6 +590,7 @@ class PostDto {
358
590
  - ✅ **명시적 검증**: 옵션과 검증 규칙이 명확하게 정의되어 있습니다
359
591
  - ✅ **일관된 동작**: 모든 데코레이터가 동일한 기준으로 동작합니다
360
592
  - ✅ **런타임 안정성**: 잘못된 타입이나 값에 대해 명확한 에러를 제공합니다
593
+ - ✅ **자동 문서화**: 검증 규칙이 Swagger 문서에 자동으로 반영됩니다
361
594
 
362
595
  ### 검증 규칙
363
596
 
@@ -388,8 +621,16 @@ class PostDto {
388
621
 
389
622
  > **참고**:
390
623
  > - 별도로 설치할 필요 없이 `npm install ts-deco`만 실행하면 모든 의존성이 자동으로 설치됩니다.
391
- > - **이 라이브러리는 NestJS에 종속되지 않습니다.** `class-validator`와 `class-transformer`는 독립적인 라이브러리이며, 모든 Node.js/TypeScript 프로젝트에서 사용할 수 있습니다.
624
+ > - **이 라이브러리는 NestJS에 종속되지 않습니다.** `class-validator`와 `class-transformer`는 독립적인 라이브러리이며, **NestJS와 Node.js (Express, Fastify 등) 모든 프로젝트에서 사용할 수 있습니다.**
392
625
  > - `@nestjs/swagger`는 Swagger API 문서화 기능을 사용할 때만 필요합니다. 문서화 기능을 사용하지 않더라도 검증 기능은 정상적으로 동작합니다.
626
+ > - **하나의 데코레이터로 검증과 Swagger 문서 작성을 동시에 처리**하므로 코드 중복을 줄이고 유지보수를 쉽게 만듭니다.
627
+
628
+ ## 지원 환경
629
+
630
+ - ✅ **NestJS**: 완벽한 호환성, NestJS 프로젝트에서 바로 사용 가능
631
+ - ✅ **Node.js**: Express, Fastify 등 모든 Node.js 프레임워크에서 사용 가능
632
+ - ✅ **TypeScript**: TypeScript 프로젝트 필수 (타입 안전성 보장)
633
+ - ✅ **Swagger**: 자동 API 문서 생성 지원
393
634
 
394
635
  ## 라이선스
395
636
 
@@ -0,0 +1,5 @@
1
+ import 'reflect-metadata';
2
+ import express from 'express';
3
+ declare const app: express.Express;
4
+ export default app;
5
+ //# sourceMappingURL=app.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../app/app.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAC1B,OAAO,OAAoB,MAAM,SAAS,CAAC;AAsH3C,QAAA,MAAM,GAAG,iBAAc,CAAC;AAwCxB,eAAe,GAAG,CAAC"}
@@ -0,0 +1,181 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ require("reflect-metadata");
40
+ const express_1 = __importDefault(require("express"));
41
+ const configs_1 = require("../src/configs");
42
+ const controllerModule = __importStar(require("./routers"));
43
+ const env_config_1 = require("./config/env.config");
44
+ const logger_middleware_1 = require("./middleware/logger.middleware");
45
+ const error_handler_middleware_1 = require("./middleware/error-handler.middleware");
46
+ const not_found_middleware_1 = require("./middleware/not-found.middleware");
47
+ // 선택적 의존성 (설치되어 있으면 사용)
48
+ let helmet;
49
+ let cors;
50
+ let compression;
51
+ try {
52
+ helmet = require('helmet');
53
+ }
54
+ catch (e) {
55
+ console.warn('helmet 패키지가 설치되지 않았습니다. 보안 헤더가 적용되지 않습니다.');
56
+ }
57
+ try {
58
+ cors = require('cors');
59
+ }
60
+ catch (e) {
61
+ console.warn('cors 패키지가 설치되지 않았습니다. CORS가 적용되지 않습니다.');
62
+ }
63
+ try {
64
+ compression = require('compression');
65
+ }
66
+ catch (e) {
67
+ console.warn('compression 패키지가 설치되지 않았습니다. 압축이 적용되지 않습니다.');
68
+ }
69
+ /**
70
+ * Express 앱 생성 및 설정
71
+ */
72
+ function createApp() {
73
+ const app = (0, express_1.default)();
74
+ // Trust proxy (리버스 프록시 사용 시)
75
+ app.set('trust proxy', true);
76
+ // 보안 미들웨어 (선택적)
77
+ if (helmet) {
78
+ app.use(helmet({
79
+ contentSecurityPolicy: env_config_1.env.NODE_ENV === 'production',
80
+ crossOriginEmbedderPolicy: false,
81
+ }));
82
+ }
83
+ // CORS 설정 (선택적)
84
+ if (cors) {
85
+ app.use(cors({
86
+ origin: env_config_1.env.CORS_ORIGIN === '*' ? true : env_config_1.env.CORS_ORIGIN.split(','),
87
+ credentials: true,
88
+ methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
89
+ allowedHeaders: ['Content-Type', 'Authorization', 'X-Request-ID'],
90
+ }));
91
+ }
92
+ // 압축 미들웨어 (선택적)
93
+ if (compression) {
94
+ app.use(compression());
95
+ }
96
+ // Body 파싱 미들웨어
97
+ app.use(express_1.default.json({ limit: '10mb' }));
98
+ app.use(express_1.default.urlencoded({ extended: true, limit: '10mb' }));
99
+ // 요청 로깅 미들웨어
100
+ app.use(logger_middleware_1.loggerMiddleware);
101
+ // Swagger 및 API 라우트 설정
102
+ if (env_config_1.env.SWAGGER_ENABLED) {
103
+ (0, configs_1.setupApp)({
104
+ app,
105
+ controllers: controllerModule,
106
+ swagger: {
107
+ title: 'API Documentation',
108
+ version: env_config_1.env.API_VERSION,
109
+ description: 'RESTful API with custom decorators',
110
+ servers: [
111
+ {
112
+ url: `http://localhost:${env_config_1.env.PORT}`,
113
+ description: '로컬 개발 서버',
114
+ },
115
+ {
116
+ url: `https://api.example.com`,
117
+ description: '프로덕션 서버',
118
+ },
119
+ ],
120
+ },
121
+ swaggerPath: env_config_1.env.SWAGGER_PATH,
122
+ });
123
+ }
124
+ else {
125
+ // Swagger 없이 라우트만 등록
126
+ const { autoRegisterRoutes } = require('../src/configs');
127
+ autoRegisterRoutes(app, controllerModule);
128
+ }
129
+ // Health check (Swagger 설정 전에 등록)
130
+ app.get('/health', (req, res) => {
131
+ res.json({
132
+ status: 'ok',
133
+ timestamp: new Date().toISOString(),
134
+ uptime: process.uptime(),
135
+ environment: env_config_1.env.NODE_ENV,
136
+ });
137
+ });
138
+ // 에러 핸들링 미들웨어 (모든 라우트 이후)
139
+ app.use(error_handler_middleware_1.errorHandlerMiddleware);
140
+ // 404 핸들러 (모든 라우트 이후, 에러 핸들러 이전)
141
+ app.use(not_found_middleware_1.notFoundMiddleware);
142
+ return app;
143
+ }
144
+ // 앱 인스턴스 생성
145
+ const app = createApp();
146
+ // 서버 시작
147
+ function startServer() {
148
+ const server = app.listen(env_config_1.env.PORT, () => {
149
+ console.log('='.repeat(50));
150
+ console.log(`🚀 Server is running`);
151
+ console.log(`📍 Environment: ${env_config_1.env.NODE_ENV}`);
152
+ console.log(`🌐 Port: ${env_config_1.env.PORT}`);
153
+ console.log(`📚 API Version: ${env_config_1.env.API_VERSION}`);
154
+ if (env_config_1.env.SWAGGER_ENABLED) {
155
+ console.log(`📖 Swagger UI: http://localhost:${env_config_1.env.PORT}${env_config_1.env.SWAGGER_PATH}`);
156
+ }
157
+ console.log(`❤️ Health Check: http://localhost:${env_config_1.env.PORT}/health`);
158
+ console.log('='.repeat(50));
159
+ });
160
+ // Graceful shutdown
161
+ process.on('SIGTERM', () => {
162
+ console.log('SIGTERM signal received: closing HTTP server');
163
+ server.close(() => {
164
+ console.log('HTTP server closed');
165
+ process.exit(0);
166
+ });
167
+ });
168
+ process.on('SIGINT', () => {
169
+ console.log('SIGINT signal received: closing HTTP server');
170
+ server.close(() => {
171
+ console.log('HTTP server closed');
172
+ process.exit(0);
173
+ });
174
+ });
175
+ }
176
+ // 직접 실행 시에만 서버 시작
177
+ if (require.main === module) {
178
+ startServer();
179
+ }
180
+ exports.default = app;
181
+ //# sourceMappingURL=app.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.js","sourceRoot":"","sources":["../../app/app.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * 환경 변수 설정
3
+ */
4
+ export declare const env: {
5
+ readonly NODE_ENV: string;
6
+ readonly PORT: number;
7
+ readonly API_VERSION: string;
8
+ readonly API_PREFIX: string;
9
+ readonly SWAGGER_ENABLED: boolean;
10
+ readonly SWAGGER_PATH: string;
11
+ readonly CORS_ORIGIN: string;
12
+ readonly LOG_LEVEL: string;
13
+ };
14
+ //# sourceMappingURL=env.config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.config.d.ts","sourceRoot":"","sources":["../../../app/config/env.config.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,GAAG;;;;;;;;;CAkBN,CAAC"}
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.env = void 0;
4
+ /**
5
+ * 환경 변수 설정
6
+ */
7
+ exports.env = {
8
+ // 서버 설정
9
+ NODE_ENV: process.env.NODE_ENV || 'development',
10
+ PORT: parseInt(process.env.PORT || '3000', 10),
11
+ // API 설정
12
+ API_VERSION: process.env.API_VERSION || 'v1',
13
+ API_PREFIX: process.env.API_PREFIX || '/api',
14
+ // Swagger 설정
15
+ SWAGGER_ENABLED: process.env.SWAGGER_ENABLED !== 'false',
16
+ SWAGGER_PATH: process.env.SWAGGER_PATH || '/api-docs',
17
+ // CORS 설정
18
+ CORS_ORIGIN: process.env.CORS_ORIGIN || '*',
19
+ // 로깅
20
+ LOG_LEVEL: process.env.LOG_LEVEL || 'info',
21
+ };
22
+ //# sourceMappingURL=env.config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.config.js","sourceRoot":"","sources":["../../../app/config/env.config.ts"],"names":[],"mappings":";;;AAAA;;GAEG;AACU,QAAA,GAAG,GAAG;IACf,QAAQ;IACR,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,aAAa;IAC/C,IAAI,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,MAAM,EAAE,EAAE,CAAC;IAE9C,SAAS;IACT,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,IAAI;IAC5C,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,MAAM;IAE5C,aAAa;IACb,eAAe,EAAE,OAAO,CAAC,GAAG,CAAC,eAAe,KAAK,OAAO;IACxD,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,WAAW;IAErD,UAAU;IACV,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,GAAG;IAE3C,KAAK;IACL,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,MAAM;CACpC,CAAC"}
@@ -0,0 +1,17 @@
1
+ import { Request, Response, NextFunction } from 'express';
2
+ /**
3
+ * 에러 응답 인터페이스
4
+ */
5
+ export interface ErrorResponse {
6
+ statusCode: number;
7
+ message: string;
8
+ error?: string;
9
+ requestId?: string;
10
+ timestamp: string;
11
+ path: string;
12
+ }
13
+ /**
14
+ * 글로벌 에러 핸들링 미들웨어
15
+ */
16
+ export declare function errorHandlerMiddleware(err: Error | any, req: Request, res: Response, next: NextFunction): void;
17
+ //# sourceMappingURL=error-handler.middleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-handler.middleware.d.ts","sourceRoot":"","sources":["../../../app/middleware/error-handler.middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE1D;;GAEG;AACH,MAAM,WAAW,aAAa;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAClC,GAAG,EAAE,KAAK,GAAG,GAAG,EAChB,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ,EACb,IAAI,EAAE,YAAY,GACnB,IAAI,CAoCN"}
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.errorHandlerMiddleware = errorHandlerMiddleware;
4
+ /**
5
+ * 글로벌 에러 핸들링 미들웨어
6
+ */
7
+ function errorHandlerMiddleware(err, req, res, next) {
8
+ const requestId = req.headers['x-request-id'];
9
+ // Validation 에러는 이미 처리됨
10
+ if (res.headersSent) {
11
+ return next(err);
12
+ }
13
+ // 에러 로깅
14
+ console.error('[ERROR HANDLER]', {
15
+ error: err.message,
16
+ stack: process.env.NODE_ENV === 'development' ? err.stack : undefined,
17
+ requestId,
18
+ path: req.path,
19
+ method: req.method,
20
+ });
21
+ // 상태 코드 결정
22
+ const statusCode = err.statusCode || err.status || 500;
23
+ const message = err.message || 'Internal server error';
24
+ // 에러 응답
25
+ const errorResponse = {
26
+ statusCode,
27
+ message: statusCode === 500 ? 'Internal server error' : message,
28
+ requestId,
29
+ timestamp: new Date().toISOString(),
30
+ path: req.path,
31
+ };
32
+ // 개발 환경에서만 상세 에러 정보 포함
33
+ if (process.env.NODE_ENV === 'development' && statusCode === 500) {
34
+ errorResponse.error = err.message;
35
+ }
36
+ res.status(statusCode).json(errorResponse);
37
+ }
38
+ //# sourceMappingURL=error-handler.middleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-handler.middleware.js","sourceRoot":"","sources":["../../../app/middleware/error-handler.middleware.ts"],"names":[],"mappings":";;AAiBA,wDAyCC;AA5CD;;GAEG;AACH,SAAgB,sBAAsB,CAClC,GAAgB,EAChB,GAAY,EACZ,GAAa,EACb,IAAkB;IAElB,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,cAAc,CAAW,CAAC;IAExD,wBAAwB;IACxB,IAAI,GAAG,CAAC,WAAW,EAAE,CAAC;QAClB,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;IACrB,CAAC;IAED,QAAQ;IACR,OAAO,CAAC,KAAK,CAAC,iBAAiB,EAAE;QAC7B,KAAK,EAAE,GAAG,CAAC,OAAO;QAClB,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;QACrE,SAAS;QACT,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,MAAM,EAAE,GAAG,CAAC,MAAM;KACrB,CAAC,CAAC;IAEH,WAAW;IACX,MAAM,UAAU,GAAG,GAAG,CAAC,UAAU,IAAI,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC;IACvD,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,IAAI,uBAAuB,CAAC;IAEvD,QAAQ;IACR,MAAM,aAAa,GAAkB;QACjC,UAAU;QACV,OAAO,EAAE,UAAU,KAAK,GAAG,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC,OAAO;QAC/D,SAAS;QACT,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,IAAI,EAAE,GAAG,CAAC,IAAI;KACjB,CAAC;IAEF,uBAAuB;IACvB,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,aAAa,IAAI,UAAU,KAAK,GAAG,EAAE,CAAC;QAC/D,aAAa,CAAC,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC;IACtC,CAAC;IAED,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;AAC/C,CAAC"}
@@ -0,0 +1,6 @@
1
+ import { Request, Response, NextFunction } from 'express';
2
+ /**
3
+ * 요청 로깅 미들웨어
4
+ */
5
+ export declare function loggerMiddleware(req: Request, res: Response, next: NextFunction): void;
6
+ //# sourceMappingURL=logger.middleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.middleware.d.ts","sourceRoot":"","sources":["../../../app/middleware/logger.middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE1D;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,GAAG,IAAI,CA6BtF"}
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.loggerMiddleware = loggerMiddleware;
4
+ /**
5
+ * 요청 로깅 미들웨어
6
+ */
7
+ function loggerMiddleware(req, res, next) {
8
+ const start = Date.now();
9
+ const requestId = req.headers['x-request-id'] || `req-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
10
+ // 요청 ID를 헤더에 추가
11
+ req.headers['x-request-id'] = requestId;
12
+ res.setHeader('x-request-id', requestId);
13
+ // 응답 완료 시 로깅
14
+ res.on('finish', () => {
15
+ const duration = Date.now() - start;
16
+ const logMessage = {
17
+ method: req.method,
18
+ path: req.path,
19
+ statusCode: res.statusCode,
20
+ duration: `${duration}ms`,
21
+ requestId,
22
+ ip: req.ip || req.socket.remoteAddress,
23
+ userAgent: req.get('user-agent'),
24
+ };
25
+ if (res.statusCode >= 400) {
26
+ console.error('[ERROR]', logMessage);
27
+ }
28
+ else {
29
+ console.log('[REQUEST]', logMessage);
30
+ }
31
+ });
32
+ next();
33
+ }
34
+ //# sourceMappingURL=logger.middleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.middleware.js","sourceRoot":"","sources":["../../../app/middleware/logger.middleware.ts"],"names":[],"mappings":";;AAKA,4CA6BC;AAhCD;;GAEG;AACH,SAAgB,gBAAgB,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB;IAC5E,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACzB,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,cAAc,CAAC,IAAI,OAAO,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;IAEhH,gBAAgB;IAChB,GAAG,CAAC,OAAO,CAAC,cAAc,CAAC,GAAG,SAAmB,CAAC;IAClD,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,SAAmB,CAAC,CAAC;IAEnD,aAAa;IACb,GAAG,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE;QAClB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;QACpC,MAAM,UAAU,GAAG;YACf,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,UAAU,EAAE,GAAG,CAAC,UAAU;YAC1B,QAAQ,EAAE,GAAG,QAAQ,IAAI;YACzB,SAAS;YACT,EAAE,EAAE,GAAG,CAAC,EAAE,IAAI,GAAG,CAAC,MAAM,CAAC,aAAa;YACtC,SAAS,EAAE,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC;SACnC,CAAC;QAEF,IAAI,GAAG,CAAC,UAAU,IAAI,GAAG,EAAE,CAAC;YACxB,OAAO,CAAC,KAAK,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;QACzC,CAAC;aAAM,CAAC;YACJ,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;QACzC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,IAAI,EAAE,CAAC;AACX,CAAC"}