@simplysm/service-server 14.0.51 → 14.0.52
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/package.json +7 -8
- package/README.md +0 -142
- package/docs/auth/auth-token-payload.md +0 -18
- package/docs/auth/sign-jwt.md +0 -30
- package/docs/auth/verify-jwt.md +0 -35
- package/docs/core/auth.md +0 -58
- package/docs/core/define-service.md +0 -76
- package/docs/core/execute-service-method.md +0 -38
- package/docs/core/service-context.md +0 -79
- package/docs/legacy/handle-v1-connection.md +0 -25
- package/docs/main/create-service-server.md +0 -32
- package/docs/main/service-server.md +0 -106
- package/docs/protocol/server-protocol-wrapper.md +0 -35
- package/docs/services/app-structure-service.md +0 -54
- package/docs/services/auto-update-service.md +0 -29
- package/docs/services/orm-service.md +0 -38
- package/docs/transport-http/handle-http-request.md +0 -33
- package/docs/transport-http/handle-static-file.md +0 -29
- package/docs/transport-http/handle-upload.md +0 -33
- package/docs/transport-socket/service-socket.md +0 -64
- package/docs/transport-socket/websocket-handler.md +0 -57
- package/docs/types/service-server-options.md +0 -36
- package/docs/utils/get-config.md +0 -24
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@simplysm/service-server",
|
|
3
|
-
"version": "14.0.
|
|
3
|
+
"version": "14.0.52",
|
|
4
4
|
"description": "심플리즘 패키지 - 서비스 (server)",
|
|
5
5
|
"author": "심플리즘",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -14,8 +14,7 @@
|
|
|
14
14
|
"types": "./dist/index.d.ts",
|
|
15
15
|
"files": [
|
|
16
16
|
"dist",
|
|
17
|
-
"src"
|
|
18
|
-
"docs"
|
|
17
|
+
"src"
|
|
19
18
|
],
|
|
20
19
|
"sideEffects": false,
|
|
21
20
|
"dependencies": {
|
|
@@ -31,11 +30,11 @@
|
|
|
31
30
|
"semver": "^7.7.4",
|
|
32
31
|
"utf-8-validate": "^6.0.6",
|
|
33
32
|
"ws": "^8.20.0",
|
|
34
|
-
"@simplysm/
|
|
35
|
-
"@simplysm/core-node": "14.0.
|
|
36
|
-
"@simplysm/
|
|
37
|
-
"@simplysm/orm-
|
|
38
|
-
"@simplysm/
|
|
33
|
+
"@simplysm/core-common": "14.0.52",
|
|
34
|
+
"@simplysm/core-node": "14.0.52",
|
|
35
|
+
"@simplysm/orm-common": "14.0.52",
|
|
36
|
+
"@simplysm/orm-node": "14.0.52",
|
|
37
|
+
"@simplysm/service-common": "14.0.52"
|
|
39
38
|
},
|
|
40
39
|
"devDependencies": {
|
|
41
40
|
"@types/semver": "^7.7.1",
|
package/README.md
DELETED
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
# @simplysm/service-server
|
|
2
|
-
|
|
3
|
-
Fastify 기반 서비스 서버. WebSocket/HTTP 이중 전송, JWT 인증, ORM 브리지, 자동 업데이트를 제공한다.
|
|
4
|
-
|
|
5
|
-
## Installation
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npm install @simplysm/service-server
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## API Overview
|
|
12
|
-
|
|
13
|
-
### Main
|
|
14
|
-
|
|
15
|
-
| Entry | Kind | Description |
|
|
16
|
-
|-------|------|-------------|
|
|
17
|
-
| [`ServiceServer`](./docs/main/service-server.md) | class | Fastify 래핑 서버. WebSocket/HTTP 라우팅, JWT 인증, 이벤트 브로드캐스트, graceful shutdown을 처리한다 |
|
|
18
|
-
| [`ServerEventProxy`](./docs/main/service-server.md#servereventproxy) | interface | `getEvent()`가 반환하는 서버 이벤트 프록시 (`emit` 메서드만 포함) |
|
|
19
|
-
| [`createServiceServer`](./docs/main/create-service-server.md) | function | `ServiceServer` 인스턴스를 생성하는 팩토리 함수 |
|
|
20
|
-
|
|
21
|
-
### Types
|
|
22
|
-
|
|
23
|
-
| Entry | Kind | Description |
|
|
24
|
-
|-------|------|-------------|
|
|
25
|
-
| [`ServiceServerOptions`](./docs/types/service-server-options.md) | interface | 서버 생성 옵션 (rootPath, port, ssl, auth, services) |
|
|
26
|
-
|
|
27
|
-
### Auth
|
|
28
|
-
|
|
29
|
-
| Entry | Kind | Description |
|
|
30
|
-
|-------|------|-------------|
|
|
31
|
-
| [`AuthTokenPayload`](./docs/auth/auth-token-payload.md) | interface | JWT 페이로드. `roles`와 `data`를 포함하며 `JWTPayload`를 확장한다 |
|
|
32
|
-
| [`signJwt`](./docs/auth/sign-jwt.md) | function | HS256/12시간 유효기간으로 JWT 토큰을 서명한다 |
|
|
33
|
-
| [`verifyJwt`](./docs/auth/verify-jwt.md) | function | JWT 토큰을 검증하고 페이로드를 반환한다 |
|
|
34
|
-
| [`decodeJwt`](./docs/auth/verify-jwt.md#decodejwt) | function | JWT 토큰을 검증 없이 디코딩한다 |
|
|
35
|
-
|
|
36
|
-
### Core
|
|
37
|
-
|
|
38
|
-
| Entry | Kind | Description |
|
|
39
|
-
|-------|------|-------------|
|
|
40
|
-
| [`ServiceContext`](./docs/core/service-context.md) | interface | 서비스 팩토리에 전달되는 컨텍스트. 인증 정보, 클라이언트 경로, 설정 접근을 제공한다 |
|
|
41
|
-
| [`createServiceContext`](./docs/core/service-context.md#createservicecontext) | function | `ServiceContext` 인스턴스를 생성한다 |
|
|
42
|
-
| [`auth`](./docs/core/auth.md) | function | 서비스/메서드에 인증을 요구하는 래퍼 함수 |
|
|
43
|
-
| [`getServiceAuthPermissions`](./docs/core/auth.md#getserviceauthpermissions) | function | `auth()`로 래핑된 함수에서 인증 권한 배열을 읽는다 |
|
|
44
|
-
| [`ServiceDefinition`](./docs/core/define-service.md#servicedefinition) | interface | 서비스 정의 구조체 (name, factory, authPermissions) |
|
|
45
|
-
| [`defineService`](./docs/core/define-service.md) | function | 이름과 팩토리로 서비스를 정의한다 |
|
|
46
|
-
| [`ServiceMethods`](./docs/core/define-service.md#servicemethods) | type | `ServiceDefinition`에서 메서드 시그니처를 추출하는 유틸리티 타입 |
|
|
47
|
-
| [`executeServiceMethod`](./docs/core/execute-service-method.md) | function | 서비스 조회 → 컨텍스트 생성 → 인증 확인 → 메서드 실행 파이프라인 |
|
|
48
|
-
|
|
49
|
-
### Transport - Socket
|
|
50
|
-
|
|
51
|
-
| Entry | Kind | Description |
|
|
52
|
-
|-------|------|-------------|
|
|
53
|
-
| [`WebSocketHandler`](./docs/transport-socket/websocket-handler.md) | interface | 다중 WebSocket 연결 관리, 메시지 라우팅, 이벤트 브로드캐스트 인터페이스 |
|
|
54
|
-
| [`createWebSocketHandler`](./docs/transport-socket/websocket-handler.md#createwebsockethandler) | function | `WebSocketHandler` 인스턴스를 생성한다 |
|
|
55
|
-
| [`ServiceSocket`](./docs/transport-socket/service-socket.md) | interface | 프로토콜 인코딩/디코딩, ping/pong, 이벤트 리스너 추적이 포함된 단일 WebSocket 연결 |
|
|
56
|
-
| [`createServiceSocket`](./docs/transport-socket/service-socket.md#createservicesocket) | function | `ServiceSocket` 인스턴스를 생성한다 |
|
|
57
|
-
|
|
58
|
-
### Transport - HTTP
|
|
59
|
-
|
|
60
|
-
| Entry | Kind | Description |
|
|
61
|
-
|-------|------|-------------|
|
|
62
|
-
| [`handleHttpRequest`](./docs/transport-http/handle-http-request.md) | function | GET/POST `/api/:service/:method` 요청을 처리한다 |
|
|
63
|
-
| [`handleUpload`](./docs/transport-http/handle-upload.md) | function | `/upload` 경로의 multipart 파일 업로드를 처리한다 |
|
|
64
|
-
| [`handleStaticFile`](./docs/transport-http/handle-static-file.md) | function | 정적 파일 서빙 (경로 탐색 공격 방지 포함) |
|
|
65
|
-
|
|
66
|
-
### Protocol
|
|
67
|
-
|
|
68
|
-
| Entry | Kind | Description |
|
|
69
|
-
|-------|------|-------------|
|
|
70
|
-
| [`ServerProtocolWrapper`](./docs/protocol/server-protocol-wrapper.md) | interface | 메시지 인코딩/디코딩 래퍼. 무거운 작업은 worker 스레드에 위임한다 |
|
|
71
|
-
| [`createServerProtocolWrapper`](./docs/protocol/server-protocol-wrapper.md#createserverprotocolwrapper) | function | `ServerProtocolWrapper` 인스턴스를 생성한다 |
|
|
72
|
-
|
|
73
|
-
### Services
|
|
74
|
-
|
|
75
|
-
| Entry | Kind | Description |
|
|
76
|
-
|-------|------|-------------|
|
|
77
|
-
| [`OrmService`](./docs/services/orm-service.md) | const | ORM 브리지 서비스 정의. WebSocket 전용, 인증 필수 |
|
|
78
|
-
| [`OrmServiceType`](./docs/services/orm-service.md#ormservicetype) | type | `OrmService`의 메서드 시그니처 타입 |
|
|
79
|
-
| [`AutoUpdateService`](./docs/services/auto-update-service.md) | const | 자동 업데이트 서비스 정의. 플랫폼별 최신 버전 파일을 탐색한다 |
|
|
80
|
-
| [`AutoUpdateServiceType`](./docs/services/auto-update-service.md#autoupdateservicetype) | type | `AutoUpdateService`의 메서드 시그니처 타입 |
|
|
81
|
-
| [`AppStructureService`](./docs/services/app-structure-service.md) | function | 앱 구조 정보 서비스를 생성하는 팩토리 함수 |
|
|
82
|
-
| [`AppStructureServiceType`](./docs/services/app-structure-service.md#appstructureservicetype) | type | `AppStructureService`가 반환하는 서비스의 메서드 시그니처 타입 |
|
|
83
|
-
|
|
84
|
-
### Utils
|
|
85
|
-
|
|
86
|
-
| Entry | Kind | Description |
|
|
87
|
-
|-------|------|-------------|
|
|
88
|
-
| [`getConfig`](./docs/utils/get-config.md) | function | `.config.json` 파일을 읽고 캐싱한다. 파일 변경 시 자동 리로드된다 |
|
|
89
|
-
|
|
90
|
-
### Legacy
|
|
91
|
-
|
|
92
|
-
| Entry | Kind | Description |
|
|
93
|
-
|-------|------|-------------|
|
|
94
|
-
| [`handleV1Connection`](./docs/legacy/handle-v1-connection.md) | function | V1 레거시 WebSocket 프로토콜 호환 레이어. 자동 업데이트만 지원한다 |
|
|
95
|
-
|
|
96
|
-
## Usage Examples
|
|
97
|
-
|
|
98
|
-
### 서버 생성 및 시작
|
|
99
|
-
|
|
100
|
-
```typescript
|
|
101
|
-
import { createServiceServer, defineService, auth } from "@simplysm/service-server";
|
|
102
|
-
|
|
103
|
-
const HealthService = defineService("Health", (ctx) => ({
|
|
104
|
-
check: () => ({ status: "ok" }),
|
|
105
|
-
}));
|
|
106
|
-
|
|
107
|
-
const UserService = defineService("User", auth((ctx) => ({
|
|
108
|
-
getProfile: () => ctx.authInfo,
|
|
109
|
-
})));
|
|
110
|
-
|
|
111
|
-
const server = createServiceServer<{ userId: string }>({
|
|
112
|
-
rootPath: "/app",
|
|
113
|
-
port: 3000,
|
|
114
|
-
auth: { jwtSecret: "my-secret" },
|
|
115
|
-
services: [HealthService, UserService],
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
await server.listen();
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
### JWT 토큰 발급 및 검증
|
|
122
|
-
|
|
123
|
-
```typescript
|
|
124
|
-
const token = await server.signAuthToken({
|
|
125
|
-
roles: ["admin"],
|
|
126
|
-
data: { userId: "123" },
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
const payload = await server.verifyAuthToken(token);
|
|
130
|
-
// payload.data.userId === "123"
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
### 이벤트 브로드캐스트
|
|
134
|
-
|
|
135
|
-
```typescript
|
|
136
|
-
import { defineEvent } from "@simplysm/service-common";
|
|
137
|
-
|
|
138
|
-
export const UserUpdatedEvent = defineEvent<{ userId: string }, { name: string }>("UserUpdated");
|
|
139
|
-
|
|
140
|
-
const userUpdatedEvt = server.getEvent<typeof UserUpdatedEvent>("UserUpdated");
|
|
141
|
-
await userUpdatedEvt.emit((info) => info.userId === "123", { name: "새 이름" });
|
|
142
|
-
```
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
# AuthTokenPayload
|
|
2
|
-
|
|
3
|
-
JWT 페이로드 인터페이스. `jose` 라이브러리의 `JWTPayload`를 확장한다.
|
|
4
|
-
|
|
5
|
-
```typescript
|
|
6
|
-
interface AuthTokenPayload<TAuthInfo = unknown> extends JWTPayload {
|
|
7
|
-
roles: string[];
|
|
8
|
-
data: TAuthInfo;
|
|
9
|
-
}
|
|
10
|
-
```
|
|
11
|
-
|
|
12
|
-
## Fields
|
|
13
|
-
|
|
14
|
-
| Field | Type | Description |
|
|
15
|
-
|-------|------|-------------|
|
|
16
|
-
| `roles` | `string[]` | 사용자 역할 배열. `auth(["admin"], ...)` 등에서 역할 검사에 사용된다 |
|
|
17
|
-
| `data` | `TAuthInfo` | 사용자 정의 인증 데이터. `ServiceContext.authInfo`로 접근 가능하다 |
|
|
18
|
-
| (JWTPayload 상속) | — | `iss`, `sub`, `aud`, `exp`, `nbf`, `iat`, `jti` 등 표준 JWT 클레임 |
|
package/docs/auth/sign-jwt.md
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
# signJwt
|
|
2
|
-
|
|
3
|
-
HS256 알고리즘과 12시간 유효기간으로 JWT 토큰을 서명한다.
|
|
4
|
-
|
|
5
|
-
```typescript
|
|
6
|
-
async function signJwt<TAuthInfo = unknown>(
|
|
7
|
-
jwtSecret: string,
|
|
8
|
-
payload: AuthTokenPayload<TAuthInfo>,
|
|
9
|
-
): Promise<string>;
|
|
10
|
-
```
|
|
11
|
-
|
|
12
|
-
## Parameters
|
|
13
|
-
|
|
14
|
-
| Param | Type | Description |
|
|
15
|
-
|-------|------|-------------|
|
|
16
|
-
| `jwtSecret` | `string` | HMAC 서명 시크릿 |
|
|
17
|
-
| `payload` | [`AuthTokenPayload<TAuthInfo>`](./auth-token-payload.md) | JWT 페이로드 |
|
|
18
|
-
|
|
19
|
-
## Returns
|
|
20
|
-
|
|
21
|
-
`Promise<string>` — 서명된 JWT 토큰 문자열.
|
|
22
|
-
|
|
23
|
-
## Usage
|
|
24
|
-
|
|
25
|
-
```typescript
|
|
26
|
-
const token = await signJwt("my-secret", {
|
|
27
|
-
roles: ["admin"],
|
|
28
|
-
data: { userId: "123" },
|
|
29
|
-
});
|
|
30
|
-
```
|
package/docs/auth/verify-jwt.md
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
# verifyJwt
|
|
2
|
-
|
|
3
|
-
JWT 토큰을 검증하고 페이로드를 반환한다. 만료된 토큰은 "토큰이 만료되었습니다." 에러를, 그 외 유효하지 않은 토큰은 "유효하지 않은 토큰입니다." 에러를 던진다.
|
|
4
|
-
|
|
5
|
-
```typescript
|
|
6
|
-
async function verifyJwt<TAuthInfo = unknown>(
|
|
7
|
-
jwtSecret: string,
|
|
8
|
-
token: string,
|
|
9
|
-
): Promise<AuthTokenPayload<TAuthInfo>>;
|
|
10
|
-
```
|
|
11
|
-
|
|
12
|
-
## Parameters
|
|
13
|
-
|
|
14
|
-
| Param | Type | Description |
|
|
15
|
-
|-------|------|-------------|
|
|
16
|
-
| `jwtSecret` | `string` | HMAC 서명 시크릿 |
|
|
17
|
-
| `token` | `string` | 검증할 JWT 토큰 문자열 |
|
|
18
|
-
|
|
19
|
-
## Returns
|
|
20
|
-
|
|
21
|
-
`Promise<AuthTokenPayload<TAuthInfo>>` — 검증된 JWT 페이로드.
|
|
22
|
-
|
|
23
|
-
## Related Types
|
|
24
|
-
|
|
25
|
-
### `decodeJwt`
|
|
26
|
-
|
|
27
|
-
JWT 토큰을 검증 없이 디코딩한다. 서명 검증이나 만료 확인을 수행하지 않는다.
|
|
28
|
-
|
|
29
|
-
```typescript
|
|
30
|
-
function decodeJwt<TAuthInfo = unknown>(token: string): AuthTokenPayload<TAuthInfo>;
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
| Param | Type | Description |
|
|
34
|
-
|-------|------|-------------|
|
|
35
|
-
| `token` | `string` | 디코딩할 JWT 토큰 문자열 |
|
package/docs/core/auth.md
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
# auth
|
|
2
|
-
|
|
3
|
-
서비스 팩토리 또는 메서드에 인증을 요구하는 래퍼 함수. 래핑된 함수에 `AUTH_PERMISSIONS` 심볼로 권한 배열을 부착한다.
|
|
4
|
-
|
|
5
|
-
```typescript
|
|
6
|
-
function auth<TFunction extends (...args: any[]) => any>(fn: TFunction): TFunction;
|
|
7
|
-
function auth<TFunction extends (...args: any[]) => any>(
|
|
8
|
-
permissions: string[],
|
|
9
|
-
fn: TFunction,
|
|
10
|
-
): TFunction;
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
## Overloads
|
|
14
|
-
|
|
15
|
-
| Overload | Description |
|
|
16
|
-
|----------|-------------|
|
|
17
|
-
| `auth(fn)` | 로그인만 요구 (역할 검사 없음). 권한 배열은 `[]` |
|
|
18
|
-
| `auth(permissions, fn)` | 지정된 역할 중 하나를 가진 사용자만 허용 |
|
|
19
|
-
|
|
20
|
-
사용 위치:
|
|
21
|
-
- 서비스 수준: `defineService("Name", auth((ctx) => ({ ... })))` — 모든 메서드에 인증 적용
|
|
22
|
-
- 메서드 수준: `{ methodName: auth(() => result) }` — 해당 메서드만 인증 적용
|
|
23
|
-
- 메서드 수준 권한이 서비스 수준 권한보다 우선한다
|
|
24
|
-
|
|
25
|
-
## Related Types
|
|
26
|
-
|
|
27
|
-
### `getServiceAuthPermissions`
|
|
28
|
-
|
|
29
|
-
`auth()`로 래핑된 함수에서 인증 권한 배열을 읽는다. 래핑되지 않은 함수는 `undefined`를 반환한다.
|
|
30
|
-
|
|
31
|
-
```typescript
|
|
32
|
-
function getServiceAuthPermissions(fn: Function): string[] | undefined;
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
| Param | Type | Description |
|
|
36
|
-
|-------|------|-------------|
|
|
37
|
-
| `fn` | `Function` | 검사할 함수 |
|
|
38
|
-
|
|
39
|
-
## Usage
|
|
40
|
-
|
|
41
|
-
```typescript
|
|
42
|
-
// 서비스 수준: 모든 메서드에 로그인 필요
|
|
43
|
-
const UserService = defineService("User", auth((ctx) => ({
|
|
44
|
-
getProfile: () => ctx.authInfo,
|
|
45
|
-
})));
|
|
46
|
-
|
|
47
|
-
// 역할 지정
|
|
48
|
-
const AdminService = defineService("Admin", auth(["admin"], (ctx) => ({
|
|
49
|
-
deleteAll: () => { /* ... */ },
|
|
50
|
-
})));
|
|
51
|
-
|
|
52
|
-
// 메서드 수준
|
|
53
|
-
const MixedService = defineService("Mixed", (ctx) => ({
|
|
54
|
-
publicMethod: () => "ok",
|
|
55
|
-
privateMethod: auth(() => ctx.authInfo),
|
|
56
|
-
adminMethod: auth(["admin"], () => "admin only"),
|
|
57
|
-
}));
|
|
58
|
-
```
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
# defineService
|
|
2
|
-
|
|
3
|
-
이름과 팩토리 함수로 서비스를 정의한다. 팩토리가 `auth()`로 래핑되어 있으면 자동으로 `authPermissions`를 추출한다.
|
|
4
|
-
|
|
5
|
-
```typescript
|
|
6
|
-
function defineService<TMethods extends Record<string, (...args: any[]) => any>>(
|
|
7
|
-
name: string,
|
|
8
|
-
factory: (ctx: ServiceContext) => TMethods,
|
|
9
|
-
): ServiceDefinition<TMethods>;
|
|
10
|
-
```
|
|
11
|
-
|
|
12
|
-
## Parameters
|
|
13
|
-
|
|
14
|
-
| Param | Type | Description |
|
|
15
|
-
|-------|------|-------------|
|
|
16
|
-
| `name` | `string` | 서비스 이름. HTTP에서 `/api/{name}/{method}`, WebSocket에서 `{name}.{method}`로 라우팅된다 |
|
|
17
|
-
| `factory` | `(ctx: ServiceContext) => TMethods` | 요청마다 호출되는 팩토리 함수. 메서드 객체를 반환한다 |
|
|
18
|
-
|
|
19
|
-
## Returns
|
|
20
|
-
|
|
21
|
-
[`ServiceDefinition<TMethods>`](#servicedefinition) — 서비스 정의 객체.
|
|
22
|
-
|
|
23
|
-
## Related Types
|
|
24
|
-
|
|
25
|
-
### `ServiceDefinition`
|
|
26
|
-
|
|
27
|
-
서비스 정의 구조체.
|
|
28
|
-
|
|
29
|
-
```typescript
|
|
30
|
-
interface ServiceDefinition<TMethods = Record<string, (...args: any[]) => any>> {
|
|
31
|
-
name: string;
|
|
32
|
-
factory: (ctx: ServiceContext) => TMethods;
|
|
33
|
-
authPermissions?: string[];
|
|
34
|
-
}
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
| Field | Type | Description |
|
|
38
|
-
|-------|------|-------------|
|
|
39
|
-
| `name` | `string` | 서비스 이름 |
|
|
40
|
-
| `factory` | `(ctx: ServiceContext) => TMethods` | 요청마다 호출되는 팩토리 함수 |
|
|
41
|
-
| `authPermissions` | `string[]` (optional) | `auth()`로 래핑된 팩토리의 서비스 수준 권한 배열 |
|
|
42
|
-
|
|
43
|
-
### `ServiceMethods`
|
|
44
|
-
|
|
45
|
-
`ServiceDefinition`에서 메서드 시그니처를 추출하는 유틸리티 타입. 클라이언트 측 타입 공유에 사용한다.
|
|
46
|
-
|
|
47
|
-
```typescript
|
|
48
|
-
type ServiceMethods<TDefinition> =
|
|
49
|
-
TDefinition extends ServiceDefinition<infer M> ? M : never;
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
사용 예:
|
|
53
|
-
|
|
54
|
-
```typescript
|
|
55
|
-
export type UserServiceType = ServiceMethods<typeof UserService>;
|
|
56
|
-
// 클라이언트: client.getService<UserServiceType>("User");
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
## Usage
|
|
60
|
-
|
|
61
|
-
```typescript
|
|
62
|
-
// 기본 서비스 (인증 불필요)
|
|
63
|
-
const HealthService = defineService("Health", (ctx) => ({
|
|
64
|
-
check: () => ({ status: "ok" }),
|
|
65
|
-
}));
|
|
66
|
-
|
|
67
|
-
// 서비스 수준 인증 (모든 메서드에 로그인 필요)
|
|
68
|
-
const UserService = defineService("User", auth((ctx) => ({
|
|
69
|
-
getProfile: () => ctx.authInfo,
|
|
70
|
-
// 메서드 수준 인증 (admin 역할 필요)
|
|
71
|
-
deleteUser: auth(["admin"], (id: number) => { /* ... */ }),
|
|
72
|
-
})));
|
|
73
|
-
|
|
74
|
-
// 클라이언트에 타입 공유
|
|
75
|
-
export type UserServiceType = ServiceMethods<typeof UserService>;
|
|
76
|
-
```
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
# executeServiceMethod
|
|
2
|
-
|
|
3
|
-
서비스 조회 → 컨텍스트 생성 → 인증 확인 → 메서드 실행 파이프라인을 수행한다.
|
|
4
|
-
|
|
5
|
-
```typescript
|
|
6
|
-
async function executeServiceMethod(
|
|
7
|
-
server: ServiceServer,
|
|
8
|
-
def: {
|
|
9
|
-
serviceName: string;
|
|
10
|
-
methodName: string;
|
|
11
|
-
params: unknown[];
|
|
12
|
-
socket?: ServiceSocket;
|
|
13
|
-
http?: { clientName: string; authTokenPayload?: AuthTokenPayload };
|
|
14
|
-
},
|
|
15
|
-
): Promise<unknown>;
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
## Parameters
|
|
19
|
-
|
|
20
|
-
| Param | Type | Description |
|
|
21
|
-
|-------|------|-------------|
|
|
22
|
-
| `server` | `ServiceServer` | 서버 인스턴스 |
|
|
23
|
-
| `def.serviceName` | `string` | 호출할 서비스 이름 |
|
|
24
|
-
| `def.methodName` | `string` | 호출할 메서드 이름 |
|
|
25
|
-
| `def.params` | `unknown[]` | 메서드 매개변수 배열 |
|
|
26
|
-
| `def.socket` | `ServiceSocket` (optional) | WebSocket 연결 (WebSocket 요청 시) |
|
|
27
|
-
| `def.http` | `{ clientName: string; authTokenPayload? }` (optional) | HTTP 요청 정보 |
|
|
28
|
-
|
|
29
|
-
## Returns
|
|
30
|
-
|
|
31
|
-
`Promise<unknown>` — 메서드 실행 결과.
|
|
32
|
-
|
|
33
|
-
인증 검사 로직:
|
|
34
|
-
|
|
35
|
-
1. 메서드 수준 `auth()` 권한이 있으면 이를 사용하고, 없으면 서비스 수준 `authPermissions`를 사용한다
|
|
36
|
-
2. 인증이 필요한데 `server.options.auth`가 `undefined`이면 설정 오류로 에러를 던진다
|
|
37
|
-
3. `server.options.auth`가 `false`이면 인증 검사를 스킵한다
|
|
38
|
-
4. 인증이 활성화되어 있으면 토큰 존재 여부를 확인하고, 역할 배열이 비어있지 않으면 역할 매칭을 수행한다
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
# ServiceContext
|
|
2
|
-
|
|
3
|
-
서비스 팩토리 함수에 전달되는 컨텍스트 인터페이스. 전송 방식(WebSocket/HTTP)에 무관하게 동일한 인터페이스를 제공한다.
|
|
4
|
-
|
|
5
|
-
```typescript
|
|
6
|
-
interface ServiceContext<TAuthInfo = unknown> {
|
|
7
|
-
server: ServiceServer<TAuthInfo>;
|
|
8
|
-
socket?: ServiceSocket;
|
|
9
|
-
http?: {
|
|
10
|
-
clientName: string;
|
|
11
|
-
authTokenPayload?: AuthTokenPayload<TAuthInfo>;
|
|
12
|
-
};
|
|
13
|
-
legacy?: {
|
|
14
|
-
clientName?: string;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
get authInfo(): TAuthInfo | undefined;
|
|
18
|
-
get clientName(): string | undefined;
|
|
19
|
-
get clientPath(): string | undefined;
|
|
20
|
-
getConfig<T>(section: string): Promise<T>;
|
|
21
|
-
}
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
## Members
|
|
25
|
-
|
|
26
|
-
| Member | Kind | Type | Description |
|
|
27
|
-
|--------|------|------|-------------|
|
|
28
|
-
| `server` | property | `ServiceServer<TAuthInfo>` | 서버 인스턴스 참조 |
|
|
29
|
-
| `socket` | property | `ServiceSocket` (optional) | WebSocket 요청일 때만 존재하는 소켓 객체 |
|
|
30
|
-
| `http` | property | `{ clientName: string; authTokenPayload?: AuthTokenPayload<TAuthInfo> }` (optional) | HTTP 요청일 때만 존재 |
|
|
31
|
-
| `legacy` | property | `{ clientName?: string }` (optional) | V1 레거시 컨텍스트 (자동 업데이트 전용) |
|
|
32
|
-
| `authInfo` | getter | `TAuthInfo \| undefined` | 토큰에서 추출한 사용자 데이터. WebSocket은 소켓의 `authTokenPayload.data`, HTTP는 헤더의 `authTokenPayload.data`에서 읽는다 |
|
|
33
|
-
| `clientName` | getter | `string \| undefined` | 클라이언트 앱 이름. `..`, `/`, `\`를 포함하면 에러를 던진다 (경로 탐색 공격 방지) |
|
|
34
|
-
| `clientPath` | getter | `string \| undefined` | `{rootPath}/www/{clientName}` 경로. `clientName`이 없으면 `undefined` |
|
|
35
|
-
| `getConfig<T>(section)` | method | `Promise<T>` | `.config.json`에서 섹션을 읽는다. 루트 설정을 먼저 읽고 클라이언트별 설정으로 덮어쓴다. 섹션이 없으면 에러를 던진다 |
|
|
36
|
-
|
|
37
|
-
## Related Types
|
|
38
|
-
|
|
39
|
-
### `createServiceContext`
|
|
40
|
-
|
|
41
|
-
`ServiceContext` 인스턴스를 생성한다.
|
|
42
|
-
|
|
43
|
-
```typescript
|
|
44
|
-
function createServiceContext<TAuthInfo = unknown>(
|
|
45
|
-
server: ServiceServer<TAuthInfo>,
|
|
46
|
-
socket?: ServiceSocket,
|
|
47
|
-
http?: { clientName: string; authTokenPayload?: AuthTokenPayload<TAuthInfo> },
|
|
48
|
-
legacy?: { clientName?: string },
|
|
49
|
-
): ServiceContext<TAuthInfo>;
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
| Param | Type | Description |
|
|
53
|
-
|-------|------|-------------|
|
|
54
|
-
| `server` | `ServiceServer<TAuthInfo>` | 서버 인스턴스 |
|
|
55
|
-
| `socket` | `ServiceSocket` (optional) | WebSocket 연결 (WebSocket 요청 시) |
|
|
56
|
-
| `http` | `{ clientName: string; authTokenPayload? }` (optional) | HTTP 요청 정보 |
|
|
57
|
-
| `legacy` | `{ clientName?: string }` (optional) | V1 레거시 컨텍스트 |
|
|
58
|
-
|
|
59
|
-
## Usage
|
|
60
|
-
|
|
61
|
-
```typescript
|
|
62
|
-
const MyService = defineService("My", (ctx) => ({
|
|
63
|
-
getProfile: () => {
|
|
64
|
-
// 인증 정보 접근
|
|
65
|
-
return ctx.authInfo;
|
|
66
|
-
},
|
|
67
|
-
|
|
68
|
-
readConfig: async () => {
|
|
69
|
-
// 설정 파일 읽기
|
|
70
|
-
const config = await ctx.getConfig<{ apiKey: string }>("myapp");
|
|
71
|
-
return config.apiKey;
|
|
72
|
-
},
|
|
73
|
-
|
|
74
|
-
socketOnly: () => {
|
|
75
|
-
if (ctx.socket == null) throw new Error("WebSocket만 허용");
|
|
76
|
-
return ctx.socket.clientName;
|
|
77
|
-
},
|
|
78
|
-
}));
|
|
79
|
-
```
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
# handleV1Connection
|
|
2
|
-
|
|
3
|
-
V1 레거시 WebSocket 프로토콜 호환 레이어. 자동 업데이트(`SdAutoUpdateService.getLastVersion`)만 지원하고, 그 외 모든 요청은 업그레이드 필요 에러를 반환한다.
|
|
4
|
-
|
|
5
|
-
```typescript
|
|
6
|
-
function handleV1Connection(
|
|
7
|
-
socket: WebSocket,
|
|
8
|
-
autoUpdateMethods: { getLastVersion: (platform: string) => Promise<any> },
|
|
9
|
-
clientNameSetter?: (clientName: string | undefined) => void,
|
|
10
|
-
): void;
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
## Parameters
|
|
14
|
-
|
|
15
|
-
| Param | Type | Description |
|
|
16
|
-
|-------|------|-------------|
|
|
17
|
-
| `socket` | `WebSocket` | 기저 WebSocket 인스턴스 |
|
|
18
|
-
| `autoUpdateMethods` | `{ getLastVersion: (platform: string) => Promise<any> }` | AutoUpdateService의 메서드 객체 |
|
|
19
|
-
| `clientNameSetter` | `(clientName: string \| undefined) => void` (optional) | V1 요청의 `clientName`을 레거시 컨텍스트에 설정하는 콜백 |
|
|
20
|
-
|
|
21
|
-
V1 프로토콜:
|
|
22
|
-
- 연결 시 `{ name: "connected" }` 메시지를 전송한다
|
|
23
|
-
- 요청 형식: `{ uuid: string; command: string; params: unknown[]; clientName?: string }`
|
|
24
|
-
- 응답 형식: `{ name: "response"; reqUuid: string; state: "success" | "error"; body: unknown }`
|
|
25
|
-
- `command`가 `"SdAutoUpdateService.getLastVersion"`이면 처리하고, 그 외는 `UPGRADE_REQUIRED` 에러를 반환한다
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
# createServiceServer
|
|
2
|
-
|
|
3
|
-
`ServiceServer` 인스턴스를 생성하는 팩토리 함수.
|
|
4
|
-
|
|
5
|
-
```typescript
|
|
6
|
-
function createServiceServer<TAuthInfo = unknown>(
|
|
7
|
-
options: ServiceServerOptions,
|
|
8
|
-
): ServiceServer<TAuthInfo>;
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## Parameters
|
|
12
|
-
|
|
13
|
-
| Param | Type | Description |
|
|
14
|
-
|-------|------|-------------|
|
|
15
|
-
| `options` | [`ServiceServerOptions`](../types/service-server-options.md) | 서버 설정 옵션 |
|
|
16
|
-
|
|
17
|
-
## Returns
|
|
18
|
-
|
|
19
|
-
`ServiceServer<TAuthInfo>` — 생성된 서버 인스턴스. `listen()`을 호출해야 실제로 시작된다.
|
|
20
|
-
|
|
21
|
-
## Usage
|
|
22
|
-
|
|
23
|
-
```typescript
|
|
24
|
-
const server = createServiceServer<MyAuthInfo>({
|
|
25
|
-
rootPath: "/app",
|
|
26
|
-
port: 3000,
|
|
27
|
-
auth: { jwtSecret: "secret" },
|
|
28
|
-
services: [UserService, OrmService],
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
await server.listen();
|
|
32
|
-
```
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
# ServiceServer
|
|
2
|
-
|
|
3
|
-
Fastify를 래핑한 서비스 서버 클래스. WebSocket/HTTP 이중 전송, JWT 인증, 이벤트 브로드캐스트, graceful shutdown을 처리한다. `EventEmitter<{ ready: void; close: void }>`를 확장한다.
|
|
4
|
-
|
|
5
|
-
```typescript
|
|
6
|
-
class ServiceServer<TAuthInfo = unknown> extends EventEmitter<{
|
|
7
|
-
ready: void;
|
|
8
|
-
close: void;
|
|
9
|
-
}> {
|
|
10
|
-
isOpen: boolean;
|
|
11
|
-
readonly fastify: FastifyInstance;
|
|
12
|
-
readonly options: ServiceServerOptions;
|
|
13
|
-
|
|
14
|
-
constructor(options: ServiceServerOptions);
|
|
15
|
-
|
|
16
|
-
async listen(): Promise<void>;
|
|
17
|
-
async close(): Promise<void>;
|
|
18
|
-
getEvent<TEventDef extends ServiceEventDef>(eventName: string): ServerEventProxy<TEventDef>;
|
|
19
|
-
async emitEvent<TEventDef extends ServiceEventDef>(
|
|
20
|
-
eventName: string,
|
|
21
|
-
infoSelector: (item: TEventDef["$info"]) => boolean,
|
|
22
|
-
data: TEventDef["$data"],
|
|
23
|
-
): Promise<void>;
|
|
24
|
-
async signAuthToken(payload: AuthTokenPayload<TAuthInfo>): Promise<string>;
|
|
25
|
-
async verifyAuthToken(token: string): Promise<AuthTokenPayload<TAuthInfo>>;
|
|
26
|
-
}
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
## Members
|
|
30
|
-
|
|
31
|
-
| Member | Kind | Type | Description |
|
|
32
|
-
|--------|------|------|-------------|
|
|
33
|
-
| `isOpen` | property | `boolean` | 서버가 리스닝 중인지 여부 |
|
|
34
|
-
| `fastify` | property | `FastifyInstance` | 내부 Fastify 인스턴스. 직접 접근이 필요할 때 사용 |
|
|
35
|
-
| `options` | property | `ServiceServerOptions` | 생성 시 전달된 옵션 |
|
|
36
|
-
| `listen()` | method | `Promise<void>` | 서버를 시작한다. 플러그인 등록, 라우트 설정, SIGINT/SIGTERM 핸들러 등록을 수행한다. 완료 시 `ready` 이벤트를 발생시킨다 |
|
|
37
|
-
| `close()` | method | `Promise<void>` | 모든 WebSocket 연결을 닫고 Fastify 서버를 종료한다. 완료 시 `close` 이벤트를 발생시킨다 |
|
|
38
|
-
| `getEvent(eventName)` | method | `ServerEventProxy<TEventDef>` | 타입 안전한 이벤트 프록시를 반환한다. `emit(infoSelector, data)` 메서드를 포함 |
|
|
39
|
-
| `emitEvent(eventName, infoSelector, data)` | method | `Promise<void>` | `infoSelector`에 매칭되는 WebSocket 클라이언트에 이벤트를 브로드캐스트한다 |
|
|
40
|
-
| `signAuthToken(payload)` | method | `Promise<string>` | JWT 토큰을 서명한다. `options.auth`가 설정되지 않으면 에러를 던진다 |
|
|
41
|
-
| `verifyAuthToken(token)` | method | `Promise<AuthTokenPayload<TAuthInfo>>` | JWT 토큰을 검증하고 페이로드를 반환한다. `options.auth`가 설정되지 않으면 에러를 던진다 |
|
|
42
|
-
|
|
43
|
-
`listen()` 시 등록되는 라우트:
|
|
44
|
-
|
|
45
|
-
| Route | Method | Handler |
|
|
46
|
-
|-------|--------|---------|
|
|
47
|
-
| `/api/:service/:method` | GET/POST | `handleHttpRequest` — 서비스 메서드 호출 |
|
|
48
|
-
| `/upload` | POST | `handleUpload` — multipart 파일 업로드 |
|
|
49
|
-
| `/`, `/ws` | WebSocket | WebSocket 핸들러. `ver=2` 쿼리 시 V2 프로토콜, 그 외 V1 레거시 |
|
|
50
|
-
| `/*` | GET/POST/PUT/DELETE/PATCH/HEAD | `handleStaticFile` — 정적 파일 서빙 |
|
|
51
|
-
|
|
52
|
-
`listen()` 시 Fastify 플러그인 등록 순서: `@fastify/websocket` → `@fastify/helmet` → `@fastify/multipart` → `@fastify/static` → `@fastify/cors`
|
|
53
|
-
|
|
54
|
-
Graceful shutdown: `SIGINT`/`SIGTERM` 시그널 수신 시 `close()`를 호출하고, 10초 내에 종료되지 않으면 `process.exit(1)`로 강제 종료한다.
|
|
55
|
-
|
|
56
|
-
## Related Types
|
|
57
|
-
|
|
58
|
-
### `ServerEventProxy`
|
|
59
|
-
|
|
60
|
-
`getEvent()`가 반환하는 타입 안전한 이벤트 프록시 인터페이스.
|
|
61
|
-
|
|
62
|
-
```typescript
|
|
63
|
-
interface ServerEventProxy<TEventDef extends ServiceEventDef> {
|
|
64
|
-
emit(
|
|
65
|
-
infoSelector: (item: TEventDef["$info"]) => boolean,
|
|
66
|
-
data: TEventDef["$data"],
|
|
67
|
-
): Promise<void>;
|
|
68
|
-
}
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
| Method | Description |
|
|
72
|
-
|--------|-------------|
|
|
73
|
-
| `emit(infoSelector, data)` | `infoSelector`에 매칭되는 이벤트 리스너를 가진 WebSocket 클라이언트에 이벤트를 브로드캐스트한다 |
|
|
74
|
-
|
|
75
|
-
## Usage
|
|
76
|
-
|
|
77
|
-
```typescript
|
|
78
|
-
import { createServiceServer, defineService, auth } from "@simplysm/service-server";
|
|
79
|
-
|
|
80
|
-
const HealthService = defineService("Health", (ctx) => ({
|
|
81
|
-
check: () => ({ status: "ok" }),
|
|
82
|
-
}));
|
|
83
|
-
|
|
84
|
-
const UserService = defineService("User", auth((ctx) => ({
|
|
85
|
-
getProfile: () => ctx.authInfo,
|
|
86
|
-
})));
|
|
87
|
-
|
|
88
|
-
const server = createServiceServer<{ userId: string }>({
|
|
89
|
-
rootPath: "/app",
|
|
90
|
-
port: 3000,
|
|
91
|
-
auth: { jwtSecret: "my-secret" },
|
|
92
|
-
services: [HealthService, UserService],
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
await server.listen();
|
|
96
|
-
|
|
97
|
-
// JWT 토큰 발급
|
|
98
|
-
const token = await server.signAuthToken({
|
|
99
|
-
roles: ["admin"],
|
|
100
|
-
data: { userId: "123" },
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
// 이벤트 브로드캐스트
|
|
104
|
-
const evt = server.getEvent<typeof MyEvent>("MyEvent");
|
|
105
|
-
await evt.emit((info) => info.userId === "123", { name: "새 이름" });
|
|
106
|
-
```
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
# ServerProtocolWrapper
|
|
2
|
-
|
|
3
|
-
메시지 인코딩/디코딩 래퍼 인터페이스. 무거운 메시지는 worker 스레드에 자동으로 위임하고, 가벼운 작업은 메인 스레드에서 처리한다.
|
|
4
|
-
|
|
5
|
-
```typescript
|
|
6
|
-
interface ServerProtocolWrapper {
|
|
7
|
-
encode(uuid: string, message: ServiceMessage): Promise<{ chunks: Bytes[]; totalSize: number }>;
|
|
8
|
-
decode(bytes: Bytes): Promise<ServiceMessageDecodeResult<ServiceMessage>>;
|
|
9
|
-
dispose(): void;
|
|
10
|
-
}
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
## Members
|
|
14
|
-
|
|
15
|
-
| Member | Kind | Type | Description |
|
|
16
|
-
|--------|------|------|-------------|
|
|
17
|
-
| `encode(uuid, message)` | method | `Promise<{ chunks: Bytes[]; totalSize: number }>` | 메시지를 인코딩한다. 바이너리 데이터(Uint8Array)가 포함된 메시지는 worker 스레드에 위임한다 |
|
|
18
|
-
| `decode(bytes)` | method | `Promise<ServiceMessageDecodeResult<ServiceMessage>>` | 메시지를 디코딩한다. 30KB 초과 메시지는 worker 스레드에 위임한다 |
|
|
19
|
-
| `dispose()` | method | `void` | 내부 프로토콜 리소스를 해제한다 |
|
|
20
|
-
|
|
21
|
-
Worker 위임 기준:
|
|
22
|
-
- **encode**: 메시지 body가 `Uint8Array`이거나, 배열 내에 `Uint8Array` 요소가 있을 때 worker 사용
|
|
23
|
-
- **decode**: 메시지 크기가 30KB(30 * 1024 바이트)를 초과할 때 worker 사용
|
|
24
|
-
|
|
25
|
-
Worker는 지연 싱글턴으로 생성되며, `maxOldGenerationSizeMb: 4096`의 리소스 제한이 설정되어 있다.
|
|
26
|
-
|
|
27
|
-
## Related Types
|
|
28
|
-
|
|
29
|
-
### `createServerProtocolWrapper`
|
|
30
|
-
|
|
31
|
-
`ServerProtocolWrapper` 인스턴스를 생성한다. 내부적으로 `@simplysm/service-common`의 `createServiceProtocol()`을 사용한다.
|
|
32
|
-
|
|
33
|
-
```typescript
|
|
34
|
-
function createServerProtocolWrapper(): ServerProtocolWrapper;
|
|
35
|
-
```
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
# AppStructureService
|
|
2
|
-
|
|
3
|
-
앱 구조 정보 서비스를 생성하는 팩토리 함수. `defineService`를 래핑하여 `Record<string, AppStructureItem[]>` 맵을 받아 서비스 정의를 반환한다. 인증 불필요.
|
|
4
|
-
|
|
5
|
-
```typescript
|
|
6
|
-
function AppStructureService(
|
|
7
|
-
itemsMap: Record<string, AppStructureItem[]>,
|
|
8
|
-
): ServiceDefinition;
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## Parameters
|
|
12
|
-
|
|
13
|
-
| Param | Type | Description |
|
|
14
|
-
|-------|------|-------------|
|
|
15
|
-
| `itemsMap` | `Record<string, AppStructureItem[]>` | 앱 구조 아이템 맵. `AppStructureItem`은 `@simplysm/service-common`에서 import한다 |
|
|
16
|
-
|
|
17
|
-
## Returns
|
|
18
|
-
|
|
19
|
-
`ServiceDefinition` — `defineService("AppStructure", ...)`로 생성된 서비스 정의.
|
|
20
|
-
|
|
21
|
-
제공 메서드:
|
|
22
|
-
|
|
23
|
-
| Method | Signature | Description |
|
|
24
|
-
|--------|-----------|-------------|
|
|
25
|
-
| `getItems` | `() => Record<string, AppStructureItem[]>` | 생성 시 전달된 `itemsMap`을 그대로 반환한다 |
|
|
26
|
-
|
|
27
|
-
## Related Types
|
|
28
|
-
|
|
29
|
-
### `AppStructureServiceType`
|
|
30
|
-
|
|
31
|
-
`AppStructureService`가 반환하는 서비스의 메서드 시그니처 타입.
|
|
32
|
-
|
|
33
|
-
```typescript
|
|
34
|
-
type AppStructureServiceType = ServiceMethods<ReturnType<typeof AppStructureService>>;
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
## Usage
|
|
38
|
-
|
|
39
|
-
```typescript
|
|
40
|
-
import { AppStructureService } from "@simplysm/service-server";
|
|
41
|
-
import type { AppStructureItem } from "@simplysm/service-common";
|
|
42
|
-
|
|
43
|
-
const itemsMap: Record<string, AppStructureItem[]> = {
|
|
44
|
-
"my-client": [
|
|
45
|
-
{ title: "홈", path: "/home" },
|
|
46
|
-
{ title: "설정", path: "/settings" },
|
|
47
|
-
],
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
const server = createServiceServer({
|
|
51
|
-
services: [AppStructureService(itemsMap)],
|
|
52
|
-
// ...
|
|
53
|
-
});
|
|
54
|
-
```
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
# AutoUpdateService
|
|
2
|
-
|
|
3
|
-
자동 업데이트 서비스 정의. `defineService("AutoUpdate", (ctx) => ...)`로 정의되어 있다. 인증 불필요.
|
|
4
|
-
|
|
5
|
-
```typescript
|
|
6
|
-
const AutoUpdateService: ServiceDefinition;
|
|
7
|
-
```
|
|
8
|
-
|
|
9
|
-
## Members
|
|
10
|
-
|
|
11
|
-
| Method | Signature | Description |
|
|
12
|
-
|--------|-----------|-------------|
|
|
13
|
-
| `getLastVersion` | `(platform: string) => Promise<{ version: string; downloadPath: string } \| undefined>` | `{clientPath}/{platform}/updates/` 디렉토리에서 최신 버전 파일을 찾아 반환한다 |
|
|
14
|
-
|
|
15
|
-
`getLastVersion` 동작:
|
|
16
|
-
- `platform`이 `"android"`이면 `.apk` 파일을, 그 외에는 `.exe` 파일을 탐색한다
|
|
17
|
-
- 파일명이 `{version}.{ext}` 형식이어야 하며 (예: `1.2.3.apk`), `semver.maxSatisfying`으로 최대 버전을 결정한다
|
|
18
|
-
- `clientPath`가 없으면 에러를 던진다
|
|
19
|
-
- 업데이트 디렉토리나 매칭 파일이 없으면 `undefined`를 반환한다
|
|
20
|
-
|
|
21
|
-
## Related Types
|
|
22
|
-
|
|
23
|
-
### `AutoUpdateServiceType`
|
|
24
|
-
|
|
25
|
-
`AutoUpdateService`의 메서드 시그니처 타입.
|
|
26
|
-
|
|
27
|
-
```typescript
|
|
28
|
-
type AutoUpdateServiceType = ServiceMethods<typeof AutoUpdateService>;
|
|
29
|
-
```
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
# OrmService
|
|
2
|
-
|
|
3
|
-
ORM 브리지 서비스 정의. WebSocket 전용이며 `auth()`로 래핑되어 로그인이 필수다. `defineService("Orm", auth((ctx) => ...))`로 정의되어 있다.
|
|
4
|
-
|
|
5
|
-
```typescript
|
|
6
|
-
const OrmService: ServiceDefinition;
|
|
7
|
-
```
|
|
8
|
-
|
|
9
|
-
소켓별 DB 연결 관리:
|
|
10
|
-
- `WeakMap<ServiceSocket, Map<number, DbConn>>`으로 소켓별 연결 상태를 관리한다
|
|
11
|
-
- 소켓이 닫히면 해당 소켓의 열린 DB 연결을 모두 자동 종료한다
|
|
12
|
-
- `getConfig("orm")`에서 `configName`으로 DB 연결 설정을 읽는다
|
|
13
|
-
|
|
14
|
-
HTTP 요청 시 "WebSocket 연결이 필요합니다" 에러를 던진다.
|
|
15
|
-
|
|
16
|
-
## Members
|
|
17
|
-
|
|
18
|
-
| Method | Signature | Description |
|
|
19
|
-
|--------|-----------|-------------|
|
|
20
|
-
| `getInfo` | `(opt: DbConnOptions & { configName: string }) => Promise<{ dialect: Dialect; database?: string; schema?: string }>` | DB 연결 정보를 반환한다. `mssql-azure` dialect은 `mssql`로 변환된다 |
|
|
21
|
-
| `connect` | `(opt: DbConnOptions & { configName: string }) => Promise<number>` | DB에 연결하고 연결 ID를 반환한다 |
|
|
22
|
-
| `close` | `(connId: number) => Promise<void>` | DB 연결을 종료한다 |
|
|
23
|
-
| `beginTransaction` | `(connId: number, isolationLevel?: IsolationLevel) => Promise<void>` | 트랜잭션을 시작한다 |
|
|
24
|
-
| `commitTransaction` | `(connId: number) => Promise<void>` | 트랜잭션을 커밋한다 |
|
|
25
|
-
| `rollbackTransaction` | `(connId: number) => Promise<void>` | 트랜잭션을 롤백한다 |
|
|
26
|
-
| `executeParametrized` | `(connId: number, query: string, params?: unknown[]) => Promise<unknown[][]>` | 파라미터화된 쿼리를 실행한다 |
|
|
27
|
-
| `executeDefs` | `(connId: number, defs: QueryDef[], options?: (ResultMeta \| undefined)[]) => Promise<unknown[][]>` | QueryDef 배열을 SQL로 변환하여 실행한다. `options`가 모두 `undefined`이면 쿼리를 합쳐 한 번에 실행한다 |
|
|
28
|
-
| `bulkInsert` | `(connId: number, tableName: string, columnDefs: Record<string, ColumnMeta>, records: Record<string, unknown>[]) => Promise<void>` | 대량 삽입을 수행한다 |
|
|
29
|
-
|
|
30
|
-
## Related Types
|
|
31
|
-
|
|
32
|
-
### `OrmServiceType`
|
|
33
|
-
|
|
34
|
-
`OrmService`의 메서드 시그니처 타입. 클라이언트 측 타입 공유에 사용한다.
|
|
35
|
-
|
|
36
|
-
```typescript
|
|
37
|
-
type OrmServiceType = ServiceMethods<typeof OrmService>;
|
|
38
|
-
```
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
# handleHttpRequest
|
|
2
|
-
|
|
3
|
-
GET/POST `/api/:service/:method` 경로의 HTTP 요청을 처리한다. `x-sd-client-name` 헤더가 필수이며, `Authorization` 헤더가 있으면 JWT 토큰을 검증한다.
|
|
4
|
-
|
|
5
|
-
```typescript
|
|
6
|
-
async function handleHttpRequest<TAuthInfo = unknown>(
|
|
7
|
-
req: FastifyRequest,
|
|
8
|
-
reply: FastifyReply,
|
|
9
|
-
jwtSecret: string | undefined,
|
|
10
|
-
runMethod: (def: {
|
|
11
|
-
serviceName: string;
|
|
12
|
-
methodName: string;
|
|
13
|
-
params: unknown[];
|
|
14
|
-
http: { clientName: string; authTokenPayload?: AuthTokenPayload<TAuthInfo> };
|
|
15
|
-
}) => Promise<unknown>,
|
|
16
|
-
): Promise<void>;
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
## Parameters
|
|
20
|
-
|
|
21
|
-
| Param | Type | Description |
|
|
22
|
-
|-------|------|-------------|
|
|
23
|
-
| `req` | `FastifyRequest` | Fastify 요청 객체 |
|
|
24
|
-
| `reply` | `FastifyReply` | Fastify 응답 객체 |
|
|
25
|
-
| `jwtSecret` | `string \| undefined` | JWT 시크릿. `Authorization` 헤더가 있는데 시크릿이 없으면 에러를 던진다 |
|
|
26
|
-
| `runMethod` | `(def) => Promise<unknown>` | 서비스 메서드 실행 콜백 |
|
|
27
|
-
|
|
28
|
-
요청 매개변수 파싱:
|
|
29
|
-
- **GET**: `?json=` 쿼리 파라미터에서 JSON 배열을 파싱한다
|
|
30
|
-
- **POST**: 요청 본문이 JSON 배열이어야 한다. 배열이 아니면 400을 반환한다
|
|
31
|
-
- **그 외**: 405 Method Not Allowed를 반환한다
|
|
32
|
-
|
|
33
|
-
인증 실패 시 401 응답을 반환한다.
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
# handleStaticFile
|
|
2
|
-
|
|
3
|
-
정적 파일 서빙을 처리한다. 경로 탐색 공격 방지와 숨김 파일 접근 차단이 포함되어 있다.
|
|
4
|
-
|
|
5
|
-
```typescript
|
|
6
|
-
async function handleStaticFile(
|
|
7
|
-
req: FastifyRequest,
|
|
8
|
-
reply: FastifyReply,
|
|
9
|
-
rootPath: string,
|
|
10
|
-
urlPath: string,
|
|
11
|
-
): Promise<void>;
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
## Parameters
|
|
15
|
-
|
|
16
|
-
| Param | Type | Description |
|
|
17
|
-
|-------|------|-------------|
|
|
18
|
-
| `req` | `FastifyRequest` | Fastify 요청 객체 |
|
|
19
|
-
| `reply` | `FastifyReply` | Fastify 응답 객체 |
|
|
20
|
-
| `rootPath` | `string` | 서버 루트 경로. `{rootPath}/www/` 하위에서 파일을 찾는다 |
|
|
21
|
-
| `urlPath` | `string` | 요청 URL 경로 (슬래시 제거됨) |
|
|
22
|
-
|
|
23
|
-
보안 처리:
|
|
24
|
-
- `{rootPath}/www/` 외부 경로 접근 시 에러를 던진다 (경로 탐색 공격 방지)
|
|
25
|
-
- `.`으로 시작하는 파일은 403 Forbidden을 반환한다
|
|
26
|
-
|
|
27
|
-
디렉토리 처리:
|
|
28
|
-
- 디렉토리 요청 시 끝에 슬래시가 없으면 슬래시를 추가하여 리다이렉트한다
|
|
29
|
-
- 디렉토리에 대해 `index.html`을 자동으로 서빙한다
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
# handleUpload
|
|
2
|
-
|
|
3
|
-
`/upload` 경로의 multipart 파일 업로드를 처리한다. 인증 필수 (Authorization 헤더 필수).
|
|
4
|
-
|
|
5
|
-
```typescript
|
|
6
|
-
async function handleUpload(
|
|
7
|
-
req: FastifyRequest,
|
|
8
|
-
reply: FastifyReply,
|
|
9
|
-
rootPath: string,
|
|
10
|
-
jwtSecret: string | undefined,
|
|
11
|
-
): Promise<void>;
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
## Parameters
|
|
15
|
-
|
|
16
|
-
| Param | Type | Description |
|
|
17
|
-
|-------|------|-------------|
|
|
18
|
-
| `req` | `FastifyRequest` | Fastify 요청 객체 |
|
|
19
|
-
| `reply` | `FastifyReply` | Fastify 응답 객체 |
|
|
20
|
-
| `rootPath` | `string` | 서버 루트 경로. 파일은 `{rootPath}/www/uploads/`에 저장된다 |
|
|
21
|
-
| `jwtSecret` | `string \| undefined` | JWT 시크릿 |
|
|
22
|
-
|
|
23
|
-
동작:
|
|
24
|
-
- 파일명은 UUID로 변환되고 원래 확장자를 유지한다
|
|
25
|
-
- 파일 크기 제한 초과 시 에러를 던진다
|
|
26
|
-
- 에러 발생 시 불완전한 파일과 이미 저장된 파일을 모두 정리한다
|
|
27
|
-
|
|
28
|
-
응답: `ServiceUploadResult[]` (from `@simplysm/service-common`)
|
|
29
|
-
|
|
30
|
-
```typescript
|
|
31
|
-
// ServiceUploadResult 구조
|
|
32
|
-
{ path: "uploads/{uuid}.ext", filename: "원본파일명.ext", size: number }
|
|
33
|
-
```
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
# ServiceSocket
|
|
2
|
-
|
|
3
|
-
프로토콜 인코딩/디코딩, ping/pong 연결 유지, 이벤트 리스너 추적이 포함된 단일 WebSocket 연결 인터페이스.
|
|
4
|
-
|
|
5
|
-
```typescript
|
|
6
|
-
interface ServiceSocket {
|
|
7
|
-
readonly connectedAtDateTime: DateTime;
|
|
8
|
-
readonly clientName: string;
|
|
9
|
-
readonly connReq: FastifyRequest;
|
|
10
|
-
authTokenPayload?: AuthTokenPayload;
|
|
11
|
-
|
|
12
|
-
close(): void;
|
|
13
|
-
send(uuid: string, msg: ServiceServerMessage): Promise<number>;
|
|
14
|
-
addListener(key: string, eventName: string, info: unknown): void;
|
|
15
|
-
removeListener(key: string): void;
|
|
16
|
-
getEventListeners(eventName: string): Array<{ key: string; info: unknown }>;
|
|
17
|
-
filterEventTargetKeys(targetKeys: string[]): string[];
|
|
18
|
-
on(event: "error", handler: (err: Error) => void): void;
|
|
19
|
-
on(event: "close", handler: (code: number) => void): void;
|
|
20
|
-
on(event: "message", handler: (data: { uuid: string; msg: ServiceClientMessage }) => void): void;
|
|
21
|
-
}
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
## Members
|
|
25
|
-
|
|
26
|
-
| Member | Kind | Type | Description |
|
|
27
|
-
|--------|------|------|-------------|
|
|
28
|
-
| `connectedAtDateTime` | property | `DateTime` | 연결 시점의 DateTime 객체 |
|
|
29
|
-
| `clientName` | property | `string` | 클라이언트 앱 이름 |
|
|
30
|
-
| `connReq` | property | `FastifyRequest` | 연결 시점의 Fastify 요청 객체 |
|
|
31
|
-
| `authTokenPayload` | property | `AuthTokenPayload` (optional) | WebSocket `auth` 메시지로 검증된 토큰 페이로드 |
|
|
32
|
-
| `close()` | method | `void` | WebSocket 연결을 종료한다 (`socket.terminate()`) |
|
|
33
|
-
| `send(uuid, msg)` | method | `Promise<number>` | 메시지를 프로토콜 인코딩하여 전송한다. 전송된 바이트 수를 반환한다. 소켓이 닫혀있으면 0을 반환한다 |
|
|
34
|
-
| `addListener(key, eventName, info)` | method | `void` | key/name/info로 이벤트 리스너를 등록한다 |
|
|
35
|
-
| `removeListener(key)` | method | `void` | key로 이벤트 리스너를 제거한다 |
|
|
36
|
-
| `getEventListeners(eventName)` | method | `Array<{ key: string; info: unknown }>` | 특정 이벤트 이름에 해당하는 모든 리스너의 배열을 반환한다 |
|
|
37
|
-
| `filterEventTargetKeys(targetKeys)` | method | `string[]` | 이 소켓의 리스너에 존재하는 대상 키만 필터링하여 반환한다 |
|
|
38
|
-
| `on(event, handler)` | method | `void` | `"error"`, `"close"`, `"message"` 이벤트 핸들러를 등록한다 |
|
|
39
|
-
|
|
40
|
-
ping/pong: 5초 간격으로 ping을 전송하고, 클라이언트의 `0x01` 바이트(ping) 수신 시 `0x02` 바이트(pong)를 응답한다. pong 미수신 시 연결을 종료한다.
|
|
41
|
-
|
|
42
|
-
프로토콜 메시지 처리: `createServerProtocolWrapper`를 사용하여 메시지를 인코딩/디코딩한다. 수신 메시지의 디코딩 결과가 `"progress"` 타입이면 진행률 메시지를 클라이언트에 전송한다.
|
|
43
|
-
|
|
44
|
-
## Related Types
|
|
45
|
-
|
|
46
|
-
### `createServiceSocket`
|
|
47
|
-
|
|
48
|
-
`ServiceSocket` 인스턴스를 생성한다.
|
|
49
|
-
|
|
50
|
-
```typescript
|
|
51
|
-
function createServiceSocket(
|
|
52
|
-
socket: WebSocket,
|
|
53
|
-
clientId: string,
|
|
54
|
-
clientName: string,
|
|
55
|
-
connReq: FastifyRequest,
|
|
56
|
-
): ServiceSocket;
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
| Param | Type | Description |
|
|
60
|
-
|-------|------|-------------|
|
|
61
|
-
| `socket` | `WebSocket` | 기저 WebSocket 인스턴스 (`ws` 라이브러리) |
|
|
62
|
-
| `clientId` | `string` | 클라이언트 고유 식별자 |
|
|
63
|
-
| `clientName` | `string` | 클라이언트 앱 이름 |
|
|
64
|
-
| `connReq` | `FastifyRequest` | 연결 시점의 Fastify 요청 객체 |
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
# WebSocketHandler
|
|
2
|
-
|
|
3
|
-
다중 WebSocket 연결을 관리하고, 메시지를 서비스로 라우팅하며, 이벤트 브로드캐스팅을 처리하는 인터페이스.
|
|
4
|
-
|
|
5
|
-
```typescript
|
|
6
|
-
interface WebSocketHandler {
|
|
7
|
-
addSocket(socket: WebSocket, clientId: string, clientName: string, connReq: FastifyRequest): void;
|
|
8
|
-
closeAll(): void;
|
|
9
|
-
emit<TEventDef extends ServiceEventDef>(
|
|
10
|
-
eventName: string,
|
|
11
|
-
infoSelector: (item: TEventDef["$info"]) => boolean,
|
|
12
|
-
data: TEventDef["$data"],
|
|
13
|
-
): Promise<void>;
|
|
14
|
-
}
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
## Members
|
|
18
|
-
|
|
19
|
-
| Member | Kind | Type | Description |
|
|
20
|
-
|--------|------|------|-------------|
|
|
21
|
-
| `addSocket(socket, clientId, clientName, connReq)` | method | `void` | 새 WebSocket 연결을 추가한다. 동일 `clientId`의 이전 연결이 있으면 해제한다 |
|
|
22
|
-
| `closeAll()` | method | `void` | 모든 활성 연결을 닫는다 |
|
|
23
|
-
| `emit(eventName, infoSelector, data)` | method | `Promise<void>` | `infoSelector`에 매칭되는 클라이언트에 이벤트를 브로드캐스트한다 |
|
|
24
|
-
|
|
25
|
-
메시지 라우팅 (`processRequest` 내부):
|
|
26
|
-
|
|
27
|
-
| `message.name` | 처리 |
|
|
28
|
-
|-----------------|------|
|
|
29
|
-
| `"SvcName.methodName"` (`.` 포함) | `runMethod`로 서비스 메서드 실행 |
|
|
30
|
-
| `"evt:add"` | 이벤트 리스너 등록 (`key`, `name`, `info`) |
|
|
31
|
-
| `"evt:remove"` | 이벤트 리스너 제거 (`key`) |
|
|
32
|
-
| `"evt:gets"` | 전체 소켓의 특정 이벤트 리스너 조회 |
|
|
33
|
-
| `"evt:emit"` | 지정된 키 대상 이벤트 발송 |
|
|
34
|
-
| `"auth"` | JWT 토큰 검증 후 소켓에 `authTokenPayload` 저장 |
|
|
35
|
-
|
|
36
|
-
## Related Types
|
|
37
|
-
|
|
38
|
-
### `createWebSocketHandler`
|
|
39
|
-
|
|
40
|
-
`WebSocketHandler` 인스턴스를 생성한다.
|
|
41
|
-
|
|
42
|
-
```typescript
|
|
43
|
-
function createWebSocketHandler(
|
|
44
|
-
runMethod: (def: {
|
|
45
|
-
serviceName: string;
|
|
46
|
-
methodName: string;
|
|
47
|
-
params: unknown[];
|
|
48
|
-
socket?: ServiceSocket;
|
|
49
|
-
}) => Promise<unknown>,
|
|
50
|
-
jwtSecret: string | undefined,
|
|
51
|
-
): WebSocketHandler;
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
| Param | Type | Description |
|
|
55
|
-
|-------|------|-------------|
|
|
56
|
-
| `runMethod` | `(def: { serviceName, methodName, params, socket? }) => Promise<unknown>` | 서비스 메서드 실행 콜백 |
|
|
57
|
-
| `jwtSecret` | `string \| undefined` | JWT 시크릿. `undefined`이면 `auth` 메시지 처리 시 에러를 던진다 |
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
# ServiceServerOptions
|
|
2
|
-
|
|
3
|
-
서버 생성 시 전달하는 옵션 인터페이스.
|
|
4
|
-
|
|
5
|
-
```typescript
|
|
6
|
-
interface ServiceServerOptions {
|
|
7
|
-
rootPath: string;
|
|
8
|
-
port: number;
|
|
9
|
-
ssl?: {
|
|
10
|
-
pfxBytes: Uint8Array;
|
|
11
|
-
passphrase: string;
|
|
12
|
-
};
|
|
13
|
-
auth?: {
|
|
14
|
-
jwtSecret: string;
|
|
15
|
-
} | false;
|
|
16
|
-
services: ServiceDefinition[];
|
|
17
|
-
}
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
## Fields
|
|
21
|
-
|
|
22
|
-
| Field | Type | Description |
|
|
23
|
-
|-------|------|-------------|
|
|
24
|
-
| `rootPath` | `string` | 서버 루트 경로. 정적 파일은 `{rootPath}/www/`에서 서빙되고, 설정 파일은 `{rootPath}/.config.json`에서 읽는다 |
|
|
25
|
-
| `port` | `number` | 리스닝 포트 번호 |
|
|
26
|
-
| `ssl` | `{ pfxBytes: Uint8Array; passphrase: string }` (optional) | HTTPS 설정. PFX 인증서 바이트와 비밀번호. 설정하지 않으면 HTTP로 동작한다 |
|
|
27
|
-
| `auth` | `{ jwtSecret: string } \| false` (optional) | JWT 인증 설정. 세 가지 상태가 있다 |
|
|
28
|
-
| `services` | `ServiceDefinition[]` | 등록할 서비스 정의 배열 |
|
|
29
|
-
|
|
30
|
-
`auth` 필드의 세 가지 상태:
|
|
31
|
-
|
|
32
|
-
| 값 | 의미 |
|
|
33
|
-
|----|------|
|
|
34
|
-
| `{ jwtSecret: "..." }` | 인증 활성화. `auth()`로 래핑된 서비스/메서드는 토큰 검증을 수행한다 |
|
|
35
|
-
| `false` | 인증 의도적 비활성화. `auth()`로 래핑된 서비스/메서드도 인증 검사를 스킵한다 |
|
|
36
|
-
| `undefined` (미설정) | 인증 미사용. `auth()`로 래핑된 서비스가 있으면 `listen()` 시 에러를 던진다 |
|
package/docs/utils/get-config.md
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
# getConfig
|
|
2
|
-
|
|
3
|
-
`.config.json` 파일을 읽고 캐싱한다. 파일 변경 시 자동 리로드되며, 캐시는 1시간 후 만료된다.
|
|
4
|
-
|
|
5
|
-
```typescript
|
|
6
|
-
async function getConfig<TConfig>(filePath: string): Promise<TConfig | undefined>;
|
|
7
|
-
```
|
|
8
|
-
|
|
9
|
-
## Parameters
|
|
10
|
-
|
|
11
|
-
| Param | Type | Description |
|
|
12
|
-
|-------|------|-------------|
|
|
13
|
-
| `filePath` | `string` | `.config.json` 파일의 절대 경로 |
|
|
14
|
-
|
|
15
|
-
## Returns
|
|
16
|
-
|
|
17
|
-
`Promise<TConfig | undefined>` — 파싱된 설정 객체. 파일이 존재하지 않으면 `undefined`.
|
|
18
|
-
|
|
19
|
-
캐싱 동작:
|
|
20
|
-
- `LazyGcMap`을 사용하여 캐시를 관리한다 (10분 간격 GC, 1시간 후 만료)
|
|
21
|
-
- 캐시 히트 시 파일을 다시 읽지 않는다 (접근 시간이 자동 갱신됨)
|
|
22
|
-
- `FsWatcher`로 파일 변경을 감시하고, 변경 시 100ms 지연 후 리로드한다
|
|
23
|
-
- 파일이 삭제되면 캐시와 워처를 모두 해제한다
|
|
24
|
-
- 캐시 만료 시 워처도 함께 해제한다
|