@fuzionx/framework 0.1.39 → 0.1.41

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 (80) hide show
  1. package/README.md +501 -501
  2. package/bin/fx.js +12 -12
  3. package/cli/db-sync.js +99 -99
  4. package/cli/index.js +493 -493
  5. package/cli/templates/make/app/controllers/HomeController.js +14 -14
  6. package/cli/templates/make/app/routes/api.js +7 -7
  7. package/cli/templates/make/app/routes/web.js +5 -5
  8. package/cli/templates/make/app/views/default/errors/404.html +11 -11
  9. package/cli/templates/make/app/views/default/errors/500.html +14 -14
  10. package/cli/templates/make/app/views/default/layouts/main.html +22 -22
  11. package/cli/templates/make/app/views/default/pages/home.html +11 -11
  12. package/cli/templates/make/controller.js.tpl +40 -40
  13. package/cli/templates/make/event.js.tpl +8 -8
  14. package/cli/templates/make/job.js.tpl +10 -10
  15. package/cli/templates/make/middleware.js.tpl +10 -10
  16. package/cli/templates/make/model.js.tpl +15 -15
  17. package/cli/templates/make/service.js.tpl +15 -15
  18. package/cli/templates/make/task.js.tpl +15 -15
  19. package/cli/templates/make/test.js.tpl +7 -7
  20. package/cli/templates/make/worker.js.tpl +14 -14
  21. package/cli/templates/make/ws.js.tpl +18 -18
  22. package/index.js +67 -67
  23. package/lib/core/AppError.js +46 -46
  24. package/lib/core/Application.js +1006 -1006
  25. package/lib/core/AutoLoader.js +226 -226
  26. package/lib/core/Base.js +64 -64
  27. package/lib/core/Config.js +228 -228
  28. package/lib/core/Context.js +484 -484
  29. package/lib/database/ConnectionManager.js +208 -208
  30. package/lib/database/MariaModel.js +29 -29
  31. package/lib/database/Model.js +247 -247
  32. package/lib/database/ModelRegistry.js +72 -72
  33. package/lib/database/MongoModel.js +232 -232
  34. package/lib/database/Pagination.js +37 -37
  35. package/lib/database/PostgreModel.js +29 -29
  36. package/lib/database/QueryBuilder.js +172 -172
  37. package/lib/database/SQLiteModel.js +27 -27
  38. package/lib/database/SqlModel.js +257 -257
  39. package/lib/database/SqlQueryBuilder.js +332 -332
  40. package/lib/helpers/CryptoHelper.js +48 -48
  41. package/lib/helpers/FileHelper.js +61 -61
  42. package/lib/helpers/HashHelper.js +39 -39
  43. package/lib/helpers/I18nHelper.js +174 -174
  44. package/lib/helpers/Logger.js +108 -108
  45. package/lib/helpers/MediaHelper.js +84 -84
  46. package/lib/http/Controller.js +34 -34
  47. package/lib/http/ErrorHandler.js +136 -136
  48. package/lib/http/Middleware.js +43 -43
  49. package/lib/http/Router.js +109 -109
  50. package/lib/http/Validation.js +125 -125
  51. package/lib/middleware/apiAuth.js +79 -79
  52. package/lib/middleware/auth.js +42 -42
  53. package/lib/middleware/bodyParser.js +19 -19
  54. package/lib/middleware/cors.js +47 -47
  55. package/lib/middleware/csrf.js +32 -32
  56. package/lib/middleware/index.js +13 -13
  57. package/lib/middleware/session.js +27 -27
  58. package/lib/middleware/theme.js +20 -20
  59. package/lib/realtime/RoomManager.js +85 -85
  60. package/lib/realtime/WsHandler.js +107 -107
  61. package/lib/schedule/Job.js +38 -38
  62. package/lib/schedule/Queue.js +103 -103
  63. package/lib/schedule/Scheduler.js +171 -171
  64. package/lib/schedule/Task.js +39 -39
  65. package/lib/schedule/WorkerPool.js +225 -225
  66. package/lib/services/EventBus.js +94 -94
  67. package/lib/services/Service.js +261 -261
  68. package/lib/services/Storage.js +112 -112
  69. package/lib/utilities/ArrUtil.js +112 -112
  70. package/lib/utilities/DateUtil.js +98 -98
  71. package/lib/utilities/FunctionUtil.js +119 -119
  72. package/lib/utilities/NumUtil.js +75 -75
  73. package/lib/utilities/ObjectUtil.js +170 -170
  74. package/lib/utilities/PaginationUtil.js +81 -81
  75. package/lib/utilities/StrUtil.js +105 -105
  76. package/lib/utilities/index.js +18 -18
  77. package/lib/view/OpenAPI.js +231 -231
  78. package/lib/view/View.js +83 -83
  79. package/package.json +2 -2
  80. package/testing/index.js +232 -232
package/README.md CHANGED
@@ -1,501 +1,501 @@
1
- # @fuzionx/framework
2
-
3
- > Laravel-inspired full-stack MVC framework powered by Rust N-API bridge — **500K+ RPS**
4
-
5
- ```
6
- @fuzionx/framework ← npm install (개발자가 설치)
7
- └── @fuzionx/core ← HTTP 엔진 (libuv Fusion, WebSocket, 세션, 크립토)
8
- └── @fuzionx/bridge ← Rust N-API 네이티브 모듈
9
- ```
10
-
11
- ---
12
-
13
- ## Quick Start
14
-
15
- ```bash
16
- # 글로벌 CLI 설치 (fx + create-fuzionx)
17
- npm install -g create-fuzionx
18
-
19
- # 프로젝트 생성
20
- fx new my-app
21
- # 또는: npx create-fuzionx my-app
22
-
23
- cd my-app
24
- npm install
25
-
26
- # 개발 서버
27
- fx dev # 또는: npx fx dev
28
-
29
- # 또는 직접 실행
30
- node app.js
31
- ```
32
-
33
- 서버가 `http://localhost:49080` 에서 시작됩니다.
34
-
35
- ---
36
-
37
- ## 프로젝트 구조
38
-
39
- ```
40
- my-app/
41
- ├── fuzionx.yaml # 서버 + DB + 세션 설정
42
- ├── .env # 환경변수
43
- ├── app.js # 엔트리포인트
44
-
45
- ├── routes/ # 라우트 정의
46
- │ ├── api.js
47
- │ └── web.js
48
-
49
- ├── controllers/ # HTTP 컨트롤러 (자동 스캔)
50
- │ └── UserController.js
51
-
52
- ├── models/ # ORM 모델 (자동 스캔)
53
- │ └── User.js
54
-
55
- ├── services/ # 비즈니스 로직 (자동 스캔)
56
- │ └── UserService.js
57
-
58
- ├── middleware/ # 미들웨어 클래스 (자동 스캔)
59
- │ └── AuthMiddleware.js
60
-
61
- ├── ws/ # WebSocket 핸들러 (자동 스캔)
62
- ├── jobs/ # 스케줄러/큐 Job (자동 스캔)
63
- ├── events/ # 이벤트 리스너
64
-
65
- ├── views/ # 템플릿 (Tera SSR / Bridge 렌더링)
66
- │ └── default/
67
- │ ├── layouts/
68
- │ │ └── main.html
69
- │ └── pages/
70
- │ └── home.html
71
-
72
- ├── locales/ # i18n 번역 파일
73
- │ ├── ko.yaml
74
- │ └── en.yaml
75
-
76
- ├── storage/ # 로그, 업로드
77
- └── public/ # 정적 파일
78
- ```
79
-
80
- ---
81
-
82
- ## 엔트리포인트 (app.js)
83
-
84
- ```javascript
85
- import { Application } from '@fuzionx/framework';
86
- import apiRoutes from './routes/api.js';
87
- import webRoutes from './routes/web.js';
88
-
89
- const app = new Application({ configPath: './fuzionx.yaml' });
90
-
91
- // 라우트 등록
92
- app.routes(apiRoutes);
93
- app.routes(webRoutes);
94
-
95
- // 서비스 등록 (Lazy 싱글톤)
96
- app.register('mailer', () => new MailService(app.config.get('app.mail')));
97
-
98
- // 라이프사이클 훅
99
- app.on('booted', async () => {
100
- console.log(`DB 연결 완료`);
101
- });
102
-
103
- // 서버 시작
104
- app.listen(49080, () => {
105
- console.log('🚀 FuzionX running on http://localhost:49080');
106
- });
107
- ```
108
-
109
- ---
110
-
111
- ## 라우팅 & 컨트롤러
112
-
113
- ### 라우트 파일
114
-
115
- ```javascript
116
- // routes/api.js
117
- import UserController from '../controllers/UserController.js';
118
- import Joi from 'joi';
119
-
120
- export default (r) => {
121
- r.group('/api', { middleware: ['apiAuth'] }, (r) => {
122
- r.group('/users', (r) => {
123
- r.get('/', UserController.index);
124
- r.get('/:id', UserController.show);
125
- r.post('/', UserController.store, {
126
- validate: { body: Joi.object({
127
- name: Joi.string().min(2).required(),
128
- email: Joi.string().email().required(),
129
- }) }
130
- });
131
- r.put('/:id', UserController.update);
132
- r.delete('/:id', UserController.destroy);
133
- });
134
- });
135
- };
136
- ```
137
-
138
- ```javascript
139
- // routes/web.js
140
- import HomeController from '../controllers/HomeController.js';
141
-
142
- export default (r) => {
143
- r.get('/', HomeController.index);
144
- r.get('/about', HomeController.about);
145
- };
146
- ```
147
-
148
- ### 컨트롤러
149
-
150
- ```javascript
151
- // controllers/UserController.js
152
- import { Controller } from '@fuzionx/framework';
153
-
154
- export default class UserController extends Controller {
155
-
156
- async index(ctx) {
157
- const users = await this.db.User.paginate(ctx.query.page || 1, 20);
158
- ctx.json(users);
159
- }
160
-
161
- async show(ctx) {
162
- const user = await this.db.User.findOrFail(ctx.params.id);
163
- ctx.json(user);
164
- }
165
-
166
- async store(ctx) {
167
- const user = await this.service('UserService').register(ctx.body);
168
- ctx.status(201).json(user);
169
- }
170
-
171
- async update(ctx) {
172
- const user = await this.db.User.findOrFail(ctx.params.id);
173
- await user.update(ctx.body);
174
- ctx.json(user);
175
- }
176
-
177
- async destroy(ctx) {
178
- await this.service('UserService').deactivate(ctx.params.id);
179
- ctx.status(204).end();
180
- }
181
- }
182
- ```
183
-
184
- 미들웨어는 `글로벌 → 그룹 → 라우트별` 3단계로 적용됩니다.
185
-
186
- ---
187
-
188
- ## Context (`ctx`)
189
-
190
- 모든 핸들러, 미들웨어에 전달되는 통합 객체:
191
-
192
- ### Request
193
-
194
- ```javascript
195
- ctx.method // 'GET', 'POST', ...
196
- ctx.url // '/users/42?page=1'
197
- ctx.path // '/users/42'
198
- ctx.params // { id: '42' }
199
- ctx.query // { page: '1' }
200
- ctx.body // JSON 자동 파싱된 요청 바디
201
- ctx.headers // 요청 헤더
202
- ctx.ip // 클라이언트 IP
203
- ctx.user // 인증된 사용자 (미들웨어에서 설정)
204
- ctx.session // 세션
205
- ctx.cookies // 파싱된 쿠키 객체
206
- ctx.locale // 현재 locale ('ko', 'en')
207
- ctx.files // 업로드된 파일 메타데이터 배열
208
-
209
- ctx.get(header) // 헤더 값
210
- ctx.is(type) // Content-Type 확인
211
- ctx.accepts('json','html') // Accept 협상
212
- ctx.t(key, vars) // i18n 번역
213
- ```
214
-
215
- ### Response
216
-
217
- ```javascript
218
- ctx.json(data) // JSON 응답
219
- ctx.status(201).json(data) // 상태 코드 + JSON
220
- ctx.send(html) // HTML 응답
221
- ctx.text(string) // text/plain
222
- ctx.render('home', { title }) // 템플릿 렌더링 (Rust Bridge SSR)
223
- ctx.redirect('/login') // 302 리다이렉트
224
- ctx.redirect('/new', 301) // 301 영구 리다이렉트
225
- ctx.back() // Referer로 리다이렉트
226
- ctx.error(404, 'Not found') // 에러 응답
227
- ctx.download(filePath) // 파일 다운로드
228
- ctx.stream(readable) // 스트림 응답
229
- ctx.setHeader(key, value) // 헤더 설정
230
- ctx.cookie(name, value, opts) // 쿠키 설정
231
- ctx.end() // 응답 종료
232
- ```
233
-
234
- ---
235
-
236
- ## Model (ORM)
237
-
238
- ```javascript
239
- // models/User.js
240
- import { Model } from '@fuzionx/framework';
241
-
242
- export default class User extends Model {
243
- static table = 'users';
244
- static timestamps = true;
245
- static hidden = ['password'];
246
-
247
- static columns = {
248
- id: { type: 'increments' },
249
- name: { type: 'string', length: 100 },
250
- email: { type: 'string', length: 150, unique: true },
251
- password: { type: 'string', length: 255 },
252
- active: { type: 'boolean', default: true },
253
- };
254
-
255
- posts() { return this.hasMany('Post'); }
256
- profile() { return this.hasOne('Profile'); }
257
-
258
- static findByEmail(email) {
259
- return this.where('email', email).first();
260
- }
261
- }
262
- ```
263
-
264
- **지원 드라이버**: SQLite · MariaDB · PostgreSQL · MongoDB
265
-
266
- ---
267
-
268
- ## Service
269
-
270
- ```javascript
271
- // services/UserService.js
272
- import { Service } from '@fuzionx/framework';
273
-
274
- export default class UserService extends Service {
275
-
276
- async register(data) {
277
- if (await this.db.User.findByEmail(data.email)) {
278
- throw this.error('이미 존재하는 이메일');
279
- }
280
- const user = await this.transaction(async (trx) => {
281
- const user = await this.db.User.create(data, { trx });
282
- await this.db.Profile.create({ userId: user.id }, { trx });
283
- return user;
284
- });
285
- this.emit('user:created', user);
286
- return user;
287
- }
288
- }
289
- ```
290
-
291
- ---
292
-
293
- ## Middleware
294
-
295
- ```javascript
296
- // middleware/AuthMiddleware.js
297
- import { Middleware } from '@fuzionx/framework';
298
-
299
- export default class AuthMiddleware extends Middleware {
300
- static name = 'auth';
301
-
302
- handle(ctx, next) {
303
- const token = ctx.headers.authorization?.replace('Bearer ', '');
304
- if (!token) return ctx.error(401, 'Unauthorized');
305
- ctx.user = this.service('AuthService').verify(token);
306
- next();
307
- }
308
- }
309
- ```
310
-
311
- ### 내장 미들웨어
312
-
313
- | 이름 | 기능 | 처리 |
314
- |------|------|------|
315
- | `bodyParser` | JSON/Form 바디 파싱 | JS |
316
- | `cors` | CORS 헤더 | Rust |
317
- | `csrf` | CSRF 토큰 검증 | JS |
318
- | `session` | 세션 로드/저장 | Rust |
319
- | `rateLimit` | 요청 제한 | Rust |
320
- | `static` | 정적 파일 서빙 | Rust |
321
-
322
- ---
323
-
324
- ## WebSocket
325
-
326
- ```javascript
327
- // ws/ChatHandler.js
328
- import { WsHandler } from '@fuzionx/framework';
329
-
330
- export default class ChatHandler extends WsHandler {
331
- static namespace = '/chat';
332
- static middleware = ['auth'];
333
-
334
- static events(e) {
335
- e.on('chat', this.handleChat);
336
- e.on('typing', this.handleTyping);
337
- }
338
-
339
- handleChat(socket, data) {
340
- socket.to(`room:${data.roomId}`).send({
341
- type: 'chat',
342
- data: { user: socket.user, message: data.message },
343
- });
344
- }
345
- }
346
- ```
347
-
348
- ---
349
-
350
- ## 설정 (`fuzionx.yaml`)
351
-
352
- ```yaml
353
- bridge:
354
- port: 49080
355
- workers: 4 # 0 = CPU 수 자동
356
- cors:
357
- enabled: true
358
- origins: ["*"]
359
- rate_limit:
360
- enabled: true
361
- per_ip: 1000
362
- session:
363
- enabled: true
364
- store: memory # memory | redis
365
- ttl: 3600
366
- logging:
367
- level: info
368
-
369
- database:
370
- main:
371
- driver: sqlite
372
- path: ./storage/database.sqlite
373
-
374
- app:
375
- name: 'My App'
376
- environment: ${NODE_ENV:development}
377
- auth:
378
- secret: ${JWT_SECRET:change-me}
379
- accessTtl: '15m'
380
- i18n:
381
- default_locale: 'ko'
382
- ```
383
-
384
- ### 환경변수 치환
385
-
386
- ```yaml
387
- host: ${DB_HOST:127.0.0.1} # 없으면 기본값 사용
388
- password: ${DB_PASSWORD} # 필수 — 없으면 부트 시 에러
389
- redis_url: ${REDIS_URL:} # 선택 — 없으면 빈 문자열
390
- ```
391
-
392
- ### Config 접근
393
-
394
- ```javascript
395
- app.config.get('bridge.port') // 49080
396
- app.config.get('app.auth.secret') // JWT secret
397
- app.config.get('app.payment.api_key', null) // 커스텀 설정 + 기본값
398
- ```
399
-
400
- ---
401
-
402
- ## CLI
403
-
404
- ### 프로젝트 생성 (`create-fuzionx`)
405
-
406
- ```bash
407
- npx create-fuzionx my-app # 새 프로젝트 스캐폴딩
408
- cd my-app
409
- npm install
410
- ```
411
-
412
- ### 프로젝트 내 명령어 (`fx`)
413
-
414
- ```bash
415
- npx fx make:controller User # CRUD 컨트롤러
416
- npx fx make:model User # ORM 모델
417
- npx fx make:service User # 서비스
418
- npx fx make:middleware Auth # 미들웨어
419
- npx fx make:job Cleanup # 스케줄 Job
420
- npx fx make:task SendEmail # 큐 Task
421
- npx fx make:ws Chat # WebSocket 핸들러
422
- npx fx make:event user # 이벤트 핸들러
423
- npx fx make:worker Heavy # Worker (worker_threads)
424
- npx fx make:test User # 테스트
425
-
426
- # DB
427
- npx fx db:sync # 모델 ↔ DB 스키마 diff
428
- npx fx db:sync --apply # 안전 변경 적용
429
-
430
- # 개발
431
- npx fx dev # 개발 서버 (--watch)
432
- npx fx dev --port=4000 # 포트 지정
433
- npx fx test # 테스트 실행 (vitest)
434
- npx fx routes # 라우트 테이블 출력
435
- npx fx config # 설정 파싱 결과 출력
436
- ```
437
-
438
- ---
439
-
440
- ## 부트스트랩 순서
441
-
442
- ```
443
- app.js 실행
444
- ├── 1. Config 로드 fuzionx.yaml 파싱
445
- ├── 2. .env 로드 환경변수 주입
446
- ├── 3. Bridge 부트 Rust 네이티브 초기화
447
- │ ★ emit('booting')
448
- ├── 4. DB 연결 Knex/Mongoose
449
- ├── 5. 모델 로드 models/ 자동 스캔
450
- │ ★ emit('booted')
451
- ├── 6. i18n 로드 locales/ 번역 파일
452
- ├── 7~12. 자동 스캔 services, middleware, controllers,
453
- │ ws, jobs, events
454
- │ ★ emit('ready')
455
- └── 13. 서버 시작 startFusionServer(port)
456
- ★ emit('listening')
457
- ```
458
-
459
- ### 라이프사이클 훅
460
-
461
- | 훅 | 시점 | 용도 |
462
- |---|------|------|
463
- | `booting` | Config 로드 직후 | 서비스 등록, 환경 설정 |
464
- | `booted` | DB + 모델 로드 완료 | 캐시 워밍 |
465
- | `ready` | 모든 초기화 완료 | 준비 로그 |
466
- | `listening` | 서버 시작 완료 | 포트 표시 |
467
- | `shutdown` | SIGTERM/SIGINT | 리소스 정리 |
468
-
469
- ---
470
-
471
- ## 에러 처리
472
-
473
- | 에러 타입 | HTTP | JSON | HTML |
474
- |-----------|:----:|------|------|
475
- | ValidationError | 422 | `{ error, fields }` | flash + redirect |
476
- | ServiceError | 400/403/404 | `{ error }` | 테마 에러 페이지 |
477
- | Error (dev) | 500 | `{ error, stack }` | 에러 페이지 + 스택 |
478
- | Error (prod) | 500 | `{ error }` | 에러 페이지 |
479
-
480
- ---
481
-
482
- ## 주요 특징
483
-
484
- - ⚡ **500K+ RPS** — Rust N-API Bridge (libuv + SO_REUSEPORT)
485
- - 🦀 **Rust 네이티브** — HTTP, 세션, CORS, Rate Limit, 암호화 모두 Rust 처리
486
- - 🎯 **MVC 아키텍처** — Controller, Service, Model 분리
487
- - 📦 **자동 스캔** — controllers/, models/, services/ 등 자동 로드
488
- - 🔌 **WebSocket** — 네임스페이스 기반 이벤트 라우팅
489
- - 🗄️ **Multi-DB ORM** — SQLite, MariaDB, PostgreSQL, MongoDB
490
- - 🔐 **Auth & Session** — JWT + 세션 (Memory/Redis)
491
- - 📡 **EventBus** — 도메인 이벤트 + Hub 연동 (멀티서버)
492
- - ⏰ **Scheduler & Queue** — Job (cron), Task (큐)
493
- - 🌍 **i18n** — 다국어 지원, `ctx.t()`, locale 자동 감지
494
- - 🎨 **Tera SSR** — Rust 기반 템플릿 렌더링
495
- - 📄 **OpenAPI** — 자동 Swagger 문서 생성
496
-
497
- ---
498
-
499
- ## License
500
-
501
- MIT
1
+ # @fuzionx/framework
2
+
3
+ > Laravel-inspired full-stack MVC framework powered by Rust N-API bridge — **500K+ RPS**
4
+
5
+ ```
6
+ @fuzionx/framework ← npm install (개발자가 설치)
7
+ └── @fuzionx/core ← HTTP 엔진 (libuv Fusion, WebSocket, 세션, 크립토)
8
+ └── @fuzionx/bridge ← Rust N-API 네이티브 모듈
9
+ ```
10
+
11
+ ---
12
+
13
+ ## Quick Start
14
+
15
+ ```bash
16
+ # 글로벌 CLI 설치 (fx + create-fuzionx)
17
+ npm install -g create-fuzionx
18
+
19
+ # 프로젝트 생성
20
+ fx new my-app
21
+ # 또는: npx create-fuzionx my-app
22
+
23
+ cd my-app
24
+ npm install
25
+
26
+ # 개발 서버
27
+ fx dev # 또는: npx fx dev
28
+
29
+ # 또는 직접 실행
30
+ node app.js
31
+ ```
32
+
33
+ 서버가 `http://localhost:49080` 에서 시작됩니다.
34
+
35
+ ---
36
+
37
+ ## 프로젝트 구조
38
+
39
+ ```
40
+ my-app/
41
+ ├── fuzionx.yaml # 서버 + DB + 세션 설정
42
+ ├── .env # 환경변수
43
+ ├── app.js # 엔트리포인트
44
+
45
+ ├── routes/ # 라우트 정의
46
+ │ ├── api.js
47
+ │ └── web.js
48
+
49
+ ├── controllers/ # HTTP 컨트롤러 (자동 스캔)
50
+ │ └── UserController.js
51
+
52
+ ├── models/ # ORM 모델 (자동 스캔)
53
+ │ └── User.js
54
+
55
+ ├── services/ # 비즈니스 로직 (자동 스캔)
56
+ │ └── UserService.js
57
+
58
+ ├── middleware/ # 미들웨어 클래스 (자동 스캔)
59
+ │ └── AuthMiddleware.js
60
+
61
+ ├── ws/ # WebSocket 핸들러 (자동 스캔)
62
+ ├── jobs/ # 스케줄러/큐 Job (자동 스캔)
63
+ ├── events/ # 이벤트 리스너
64
+
65
+ ├── views/ # 템플릿 (Tera SSR / Bridge 렌더링)
66
+ │ └── default/
67
+ │ ├── layouts/
68
+ │ │ └── main.html
69
+ │ └── pages/
70
+ │ └── home.html
71
+
72
+ ├── locales/ # i18n 번역 파일
73
+ │ ├── ko.yaml
74
+ │ └── en.yaml
75
+
76
+ ├── storage/ # 로그, 업로드
77
+ └── public/ # 정적 파일
78
+ ```
79
+
80
+ ---
81
+
82
+ ## 엔트리포인트 (app.js)
83
+
84
+ ```javascript
85
+ import { Application } from '@fuzionx/framework';
86
+ import apiRoutes from './routes/api.js';
87
+ import webRoutes from './routes/web.js';
88
+
89
+ const app = new Application({ configPath: './fuzionx.yaml' });
90
+
91
+ // 라우트 등록
92
+ app.routes(apiRoutes);
93
+ app.routes(webRoutes);
94
+
95
+ // 서비스 등록 (Lazy 싱글톤)
96
+ app.register('mailer', () => new MailService(app.config.get('app.mail')));
97
+
98
+ // 라이프사이클 훅
99
+ app.on('booted', async () => {
100
+ console.log(`DB 연결 완료`);
101
+ });
102
+
103
+ // 서버 시작
104
+ app.listen(49080, () => {
105
+ console.log('🚀 FuzionX running on http://localhost:49080');
106
+ });
107
+ ```
108
+
109
+ ---
110
+
111
+ ## 라우팅 & 컨트롤러
112
+
113
+ ### 라우트 파일
114
+
115
+ ```javascript
116
+ // routes/api.js
117
+ import UserController from '../controllers/UserController.js';
118
+ import Joi from 'joi';
119
+
120
+ export default (r) => {
121
+ r.group('/api', { middleware: ['apiAuth'] }, (r) => {
122
+ r.group('/users', (r) => {
123
+ r.get('/', UserController.index);
124
+ r.get('/:id', UserController.show);
125
+ r.post('/', UserController.store, {
126
+ validate: { body: Joi.object({
127
+ name: Joi.string().min(2).required(),
128
+ email: Joi.string().email().required(),
129
+ }) }
130
+ });
131
+ r.put('/:id', UserController.update);
132
+ r.delete('/:id', UserController.destroy);
133
+ });
134
+ });
135
+ };
136
+ ```
137
+
138
+ ```javascript
139
+ // routes/web.js
140
+ import HomeController from '../controllers/HomeController.js';
141
+
142
+ export default (r) => {
143
+ r.get('/', HomeController.index);
144
+ r.get('/about', HomeController.about);
145
+ };
146
+ ```
147
+
148
+ ### 컨트롤러
149
+
150
+ ```javascript
151
+ // controllers/UserController.js
152
+ import { Controller } from '@fuzionx/framework';
153
+
154
+ export default class UserController extends Controller {
155
+
156
+ async index(ctx) {
157
+ const users = await this.db.User.paginate(ctx.query.page || 1, 20);
158
+ ctx.json(users);
159
+ }
160
+
161
+ async show(ctx) {
162
+ const user = await this.db.User.findOrFail(ctx.params.id);
163
+ ctx.json(user);
164
+ }
165
+
166
+ async store(ctx) {
167
+ const user = await this.service('UserService').register(ctx.body);
168
+ ctx.status(201).json(user);
169
+ }
170
+
171
+ async update(ctx) {
172
+ const user = await this.db.User.findOrFail(ctx.params.id);
173
+ await user.update(ctx.body);
174
+ ctx.json(user);
175
+ }
176
+
177
+ async destroy(ctx) {
178
+ await this.service('UserService').deactivate(ctx.params.id);
179
+ ctx.status(204).end();
180
+ }
181
+ }
182
+ ```
183
+
184
+ 미들웨어는 `글로벌 → 그룹 → 라우트별` 3단계로 적용됩니다.
185
+
186
+ ---
187
+
188
+ ## Context (`ctx`)
189
+
190
+ 모든 핸들러, 미들웨어에 전달되는 통합 객체:
191
+
192
+ ### Request
193
+
194
+ ```javascript
195
+ ctx.method // 'GET', 'POST', ...
196
+ ctx.url // '/users/42?page=1'
197
+ ctx.path // '/users/42'
198
+ ctx.params // { id: '42' }
199
+ ctx.query // { page: '1' }
200
+ ctx.body // JSON 자동 파싱된 요청 바디
201
+ ctx.headers // 요청 헤더
202
+ ctx.ip // 클라이언트 IP
203
+ ctx.user // 인증된 사용자 (미들웨어에서 설정)
204
+ ctx.session // 세션
205
+ ctx.cookies // 파싱된 쿠키 객체
206
+ ctx.locale // 현재 locale ('ko', 'en')
207
+ ctx.files // 업로드된 파일 메타데이터 배열
208
+
209
+ ctx.get(header) // 헤더 값
210
+ ctx.is(type) // Content-Type 확인
211
+ ctx.accepts('json','html') // Accept 협상
212
+ ctx.t(key, vars) // i18n 번역
213
+ ```
214
+
215
+ ### Response
216
+
217
+ ```javascript
218
+ ctx.json(data) // JSON 응답
219
+ ctx.status(201).json(data) // 상태 코드 + JSON
220
+ ctx.send(html) // HTML 응답
221
+ ctx.text(string) // text/plain
222
+ ctx.render('home', { title }) // 템플릿 렌더링 (Rust Bridge SSR)
223
+ ctx.redirect('/login') // 302 리다이렉트
224
+ ctx.redirect('/new', 301) // 301 영구 리다이렉트
225
+ ctx.back() // Referer로 리다이렉트
226
+ ctx.error(404, 'Not found') // 에러 응답
227
+ ctx.download(filePath) // 파일 다운로드
228
+ ctx.stream(readable) // 스트림 응답
229
+ ctx.setHeader(key, value) // 헤더 설정
230
+ ctx.cookie(name, value, opts) // 쿠키 설정
231
+ ctx.end() // 응답 종료
232
+ ```
233
+
234
+ ---
235
+
236
+ ## Model (ORM)
237
+
238
+ ```javascript
239
+ // models/User.js
240
+ import { Model } from '@fuzionx/framework';
241
+
242
+ export default class User extends Model {
243
+ static table = 'users';
244
+ static timestamps = true;
245
+ static hidden = ['password'];
246
+
247
+ static columns = {
248
+ id: { type: 'increments' },
249
+ name: { type: 'string', length: 100 },
250
+ email: { type: 'string', length: 150, unique: true },
251
+ password: { type: 'string', length: 255 },
252
+ active: { type: 'boolean', default: true },
253
+ };
254
+
255
+ posts() { return this.hasMany('Post'); }
256
+ profile() { return this.hasOne('Profile'); }
257
+
258
+ static findByEmail(email) {
259
+ return this.where('email', email).first();
260
+ }
261
+ }
262
+ ```
263
+
264
+ **지원 드라이버**: SQLite · MariaDB · PostgreSQL · MongoDB
265
+
266
+ ---
267
+
268
+ ## Service
269
+
270
+ ```javascript
271
+ // services/UserService.js
272
+ import { Service } from '@fuzionx/framework';
273
+
274
+ export default class UserService extends Service {
275
+
276
+ async register(data) {
277
+ if (await this.db.User.findByEmail(data.email)) {
278
+ throw this.error('이미 존재하는 이메일');
279
+ }
280
+ const user = await this.transaction(async (trx) => {
281
+ const user = await this.db.User.create(data, { trx });
282
+ await this.db.Profile.create({ userId: user.id }, { trx });
283
+ return user;
284
+ });
285
+ this.emit('user:created', user);
286
+ return user;
287
+ }
288
+ }
289
+ ```
290
+
291
+ ---
292
+
293
+ ## Middleware
294
+
295
+ ```javascript
296
+ // middleware/AuthMiddleware.js
297
+ import { Middleware } from '@fuzionx/framework';
298
+
299
+ export default class AuthMiddleware extends Middleware {
300
+ static name = 'auth';
301
+
302
+ handle(ctx, next) {
303
+ const token = ctx.headers.authorization?.replace('Bearer ', '');
304
+ if (!token) return ctx.error(401, 'Unauthorized');
305
+ ctx.user = this.service('AuthService').verify(token);
306
+ next();
307
+ }
308
+ }
309
+ ```
310
+
311
+ ### 내장 미들웨어
312
+
313
+ | 이름 | 기능 | 처리 |
314
+ |------|------|------|
315
+ | `bodyParser` | JSON/Form 바디 파싱 | JS |
316
+ | `cors` | CORS 헤더 | Rust |
317
+ | `csrf` | CSRF 토큰 검증 | JS |
318
+ | `session` | 세션 로드/저장 | Rust |
319
+ | `rateLimit` | 요청 제한 | Rust |
320
+ | `static` | 정적 파일 서빙 | Rust |
321
+
322
+ ---
323
+
324
+ ## WebSocket
325
+
326
+ ```javascript
327
+ // ws/ChatHandler.js
328
+ import { WsHandler } from '@fuzionx/framework';
329
+
330
+ export default class ChatHandler extends WsHandler {
331
+ static namespace = '/chat';
332
+ static middleware = ['auth'];
333
+
334
+ static events(e) {
335
+ e.on('chat', this.handleChat);
336
+ e.on('typing', this.handleTyping);
337
+ }
338
+
339
+ handleChat(socket, data) {
340
+ socket.to(`room:${data.roomId}`).send({
341
+ type: 'chat',
342
+ data: { user: socket.user, message: data.message },
343
+ });
344
+ }
345
+ }
346
+ ```
347
+
348
+ ---
349
+
350
+ ## 설정 (`fuzionx.yaml`)
351
+
352
+ ```yaml
353
+ bridge:
354
+ port: 49080
355
+ workers: 4 # 0 = CPU 수 자동
356
+ cors:
357
+ enabled: true
358
+ origins: ["*"]
359
+ rate_limit:
360
+ enabled: true
361
+ per_ip: 1000
362
+ session:
363
+ enabled: true
364
+ store: memory # memory | redis
365
+ ttl: 3600
366
+ logging:
367
+ level: info
368
+
369
+ database:
370
+ main:
371
+ driver: sqlite
372
+ path: ./storage/database.sqlite
373
+
374
+ app:
375
+ name: 'My App'
376
+ environment: ${NODE_ENV:development}
377
+ auth:
378
+ secret: ${JWT_SECRET:change-me}
379
+ accessTtl: '15m'
380
+ i18n:
381
+ default_locale: 'ko'
382
+ ```
383
+
384
+ ### 환경변수 치환
385
+
386
+ ```yaml
387
+ host: ${DB_HOST:127.0.0.1} # 없으면 기본값 사용
388
+ password: ${DB_PASSWORD} # 필수 — 없으면 부트 시 에러
389
+ redis_url: ${REDIS_URL:} # 선택 — 없으면 빈 문자열
390
+ ```
391
+
392
+ ### Config 접근
393
+
394
+ ```javascript
395
+ app.config.get('bridge.port') // 49080
396
+ app.config.get('app.auth.secret') // JWT secret
397
+ app.config.get('app.payment.api_key', null) // 커스텀 설정 + 기본값
398
+ ```
399
+
400
+ ---
401
+
402
+ ## CLI
403
+
404
+ ### 프로젝트 생성 (`create-fuzionx`)
405
+
406
+ ```bash
407
+ npx create-fuzionx my-app # 새 프로젝트 스캐폴딩
408
+ cd my-app
409
+ npm install
410
+ ```
411
+
412
+ ### 프로젝트 내 명령어 (`fx`)
413
+
414
+ ```bash
415
+ npx fx make:controller User # CRUD 컨트롤러
416
+ npx fx make:model User # ORM 모델
417
+ npx fx make:service User # 서비스
418
+ npx fx make:middleware Auth # 미들웨어
419
+ npx fx make:job Cleanup # 스케줄 Job
420
+ npx fx make:task SendEmail # 큐 Task
421
+ npx fx make:ws Chat # WebSocket 핸들러
422
+ npx fx make:event user # 이벤트 핸들러
423
+ npx fx make:worker Heavy # Worker (worker_threads)
424
+ npx fx make:test User # 테스트
425
+
426
+ # DB
427
+ npx fx db:sync # 모델 ↔ DB 스키마 diff
428
+ npx fx db:sync --apply # 안전 변경 적용
429
+
430
+ # 개발
431
+ npx fx dev # 개발 서버 (--watch)
432
+ npx fx dev --port=4000 # 포트 지정
433
+ npx fx test # 테스트 실행 (vitest)
434
+ npx fx routes # 라우트 테이블 출력
435
+ npx fx config # 설정 파싱 결과 출력
436
+ ```
437
+
438
+ ---
439
+
440
+ ## 부트스트랩 순서
441
+
442
+ ```
443
+ app.js 실행
444
+ ├── 1. Config 로드 fuzionx.yaml 파싱
445
+ ├── 2. .env 로드 환경변수 주입
446
+ ├── 3. Bridge 부트 Rust 네이티브 초기화
447
+ │ ★ emit('booting')
448
+ ├── 4. DB 연결 Knex/Mongoose
449
+ ├── 5. 모델 로드 models/ 자동 스캔
450
+ │ ★ emit('booted')
451
+ ├── 6. i18n 로드 locales/ 번역 파일
452
+ ├── 7~12. 자동 스캔 services, middleware, controllers,
453
+ │ ws, jobs, events
454
+ │ ★ emit('ready')
455
+ └── 13. 서버 시작 startFusionServer(port)
456
+ ★ emit('listening')
457
+ ```
458
+
459
+ ### 라이프사이클 훅
460
+
461
+ | 훅 | 시점 | 용도 |
462
+ |---|------|------|
463
+ | `booting` | Config 로드 직후 | 서비스 등록, 환경 설정 |
464
+ | `booted` | DB + 모델 로드 완료 | 캐시 워밍 |
465
+ | `ready` | 모든 초기화 완료 | 준비 로그 |
466
+ | `listening` | 서버 시작 완료 | 포트 표시 |
467
+ | `shutdown` | SIGTERM/SIGINT | 리소스 정리 |
468
+
469
+ ---
470
+
471
+ ## 에러 처리
472
+
473
+ | 에러 타입 | HTTP | JSON | HTML |
474
+ |-----------|:----:|------|------|
475
+ | ValidationError | 422 | `{ error, fields }` | flash + redirect |
476
+ | ServiceError | 400/403/404 | `{ error }` | 테마 에러 페이지 |
477
+ | Error (dev) | 500 | `{ error, stack }` | 에러 페이지 + 스택 |
478
+ | Error (prod) | 500 | `{ error }` | 에러 페이지 |
479
+
480
+ ---
481
+
482
+ ## 주요 특징
483
+
484
+ - ⚡ **500K+ RPS** — Rust N-API Bridge (libuv + SO_REUSEPORT)
485
+ - 🦀 **Rust 네이티브** — HTTP, 세션, CORS, Rate Limit, 암호화 모두 Rust 처리
486
+ - 🎯 **MVC 아키텍처** — Controller, Service, Model 분리
487
+ - 📦 **자동 스캔** — controllers/, models/, services/ 등 자동 로드
488
+ - 🔌 **WebSocket** — 네임스페이스 기반 이벤트 라우팅
489
+ - 🗄️ **Multi-DB ORM** — SQLite, MariaDB, PostgreSQL, MongoDB
490
+ - 🔐 **Auth & Session** — JWT + 세션 (Memory/Redis)
491
+ - 📡 **EventBus** — 도메인 이벤트 + Hub 연동 (멀티서버)
492
+ - ⏰ **Scheduler & Queue** — Job (cron), Task (큐)
493
+ - 🌍 **i18n** — 다국어 지원, `ctx.t()`, locale 자동 감지
494
+ - 🎨 **Tera SSR** — Rust 기반 템플릿 렌더링
495
+ - 📄 **OpenAPI** — 자동 Swagger 문서 생성
496
+
497
+ ---
498
+
499
+ ## License
500
+
501
+ MIT