entity-server-client 0.2.5 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -1
- package/build.mjs +11 -1
- package/docs/api/alimtalk.md +62 -0
- package/docs/api/auth.md +256 -0
- package/docs/api/email.md +37 -0
- package/docs/api/entity.md +273 -0
- package/docs/api/file.md +80 -0
- package/docs/api/health.md +41 -0
- package/docs/api/identity.md +32 -0
- package/docs/api/import.md +34 -0
- package/docs/api/packet.md +25 -0
- package/docs/api/pg.md +90 -0
- package/docs/api/push.md +107 -0
- package/docs/api/react.md +141 -0
- package/docs/api/setup.md +43 -0
- package/docs/api/sms.md +45 -0
- package/docs/api/smtp.md +33 -0
- package/docs/api/transaction.md +50 -0
- package/docs/api/utils.md +52 -0
- package/docs/apis.md +22 -787
- package/docs/react.md +64 -7
- package/package.json +7 -1
- package/src/EntityServerClient.ts +28 -546
- package/src/client/base.ts +305 -0
- package/src/client/packet.ts +16 -52
- package/src/client/request.ts +46 -9
- package/src/client/utils.ts +17 -2
- package/src/hooks/useEntityServer.ts +3 -4
- package/src/mixins/auth.ts +143 -0
- package/src/mixins/entity.ts +205 -0
- package/src/mixins/file.ts +99 -0
- package/src/mixins/push.ts +109 -0
- package/src/mixins/smtp.ts +20 -0
- package/src/mixins/utils.ts +106 -0
- package/src/packet.ts +84 -0
- package/src/types.ts +93 -1
- package/tests/packet.test.mjs +50 -0
- package/dist/EntityServerClient.d.ts +0 -203
- package/dist/client/hmac.d.ts +0 -8
- package/dist/client/packet.d.ts +0 -22
- package/dist/client/request.d.ts +0 -16
- package/dist/client/utils.d.ts +0 -4
- package/dist/hooks/useEntityServer.d.ts +0 -63
- package/dist/index.d.ts +0 -4
- package/dist/index.js +0 -2
- package/dist/index.js.map +0 -7
- package/dist/react.d.ts +0 -1
- package/dist/react.js +0 -2
- package/dist/react.js.map +0 -7
- package/dist/types.d.ts +0 -165
package/README.md
CHANGED
|
@@ -39,7 +39,6 @@ import { entityServer } from "entity-server-client";
|
|
|
39
39
|
entityServer.configure({
|
|
40
40
|
baseUrl: import.meta.env.VITE_ENTITY_SERVER_URL,
|
|
41
41
|
token: localStorage.getItem("auth_access_token") ?? "",
|
|
42
|
-
packetMagicLen: Number(import.meta.env.VITE_PACKET_MAGIC_LEN ?? 4),
|
|
43
42
|
});
|
|
44
43
|
|
|
45
44
|
const res = await entityServer.list("account", { page: 1, limit: 20 });
|
|
@@ -91,6 +90,38 @@ export function AccountPage() {
|
|
|
91
90
|
> `submit` / `del` / `query` 호출 시 `isPending`, `error` 상태가 자동으로 관리됩니다.
|
|
92
91
|
> 컴포넌트마다 독립 인스턴스가 필요하면 `singleton: false` 옵션을 사용하세요.
|
|
93
92
|
|
|
93
|
+
## Packet 코어
|
|
94
|
+
|
|
95
|
+
`entity-server-client`는 HTTP 클라이언트뿐 아니라 패킷 암복호 프로토콜 코어도 함께 제공합니다.
|
|
96
|
+
브라우저 프론트, Node.js 서비스, `entity-app-server` 같은 중간 계층이 동일한 패킷 포맷을 공유해야 할 때 사용합니다.
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
import {
|
|
100
|
+
derivePacketKey,
|
|
101
|
+
packetMagicLenFromKey,
|
|
102
|
+
encryptPacket,
|
|
103
|
+
decryptPacket,
|
|
104
|
+
} from "entity-server-client/packet";
|
|
105
|
+
|
|
106
|
+
const key = derivePacketKey("anon.v1.example-token");
|
|
107
|
+
const magicLen = packetMagicLenFromKey(key);
|
|
108
|
+
|
|
109
|
+
const encrypted = encryptPacket(
|
|
110
|
+
new TextEncoder().encode(JSON.stringify({ ok: true })),
|
|
111
|
+
key,
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
const plaintext = decryptPacket(encrypted, key);
|
|
115
|
+
console.log(magicLen, new TextDecoder().decode(plaintext));
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
용도는 다음처럼 구분하는 것이 좋습니다.
|
|
119
|
+
|
|
120
|
+
- `entity-server-client`: 요청 전송, 응답 복호화, 인증/헬스체크를 포함한 SDK
|
|
121
|
+
- `entity-server-client/packet`: 순수 패킷 프로토콜 코어
|
|
122
|
+
|
|
123
|
+
서버 프레임워크 훅, 쿠키 처리, Fastify 요청/응답 주입 같은 로직은 이 서브패스에 두지 않고 각 서버 런타임에서 따로 구현해야 합니다.
|
|
124
|
+
|
|
94
125
|
## 문서
|
|
95
126
|
|
|
96
127
|
- [함수별 사용법](docs/apis.md)
|
package/build.mjs
CHANGED
|
@@ -4,20 +4,30 @@ const shared = {
|
|
|
4
4
|
bundle: true,
|
|
5
5
|
minify: true,
|
|
6
6
|
sourcemap: true,
|
|
7
|
-
platform: "browser",
|
|
8
7
|
format: "esm",
|
|
9
8
|
target: "es2022",
|
|
10
9
|
packages: "external", // peerDependencies (react, @noble/*) 외부 처리
|
|
11
10
|
};
|
|
12
11
|
|
|
12
|
+
// 코어 클라이언트: browser/Node.js 양쪽에서 동작하도록 neutral 플랫폼 사용
|
|
13
13
|
await build({
|
|
14
14
|
...shared,
|
|
15
|
+
platform: "neutral",
|
|
15
16
|
entryPoints: ["src/index.ts"],
|
|
16
17
|
outfile: "dist/index.js",
|
|
17
18
|
});
|
|
18
19
|
|
|
19
20
|
await build({
|
|
20
21
|
...shared,
|
|
22
|
+
platform: "neutral",
|
|
23
|
+
entryPoints: ["src/packet.ts"],
|
|
24
|
+
outfile: "dist/packet.js",
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// React 훅: 브라우저 전용
|
|
28
|
+
await build({
|
|
29
|
+
...shared,
|
|
30
|
+
platform: "browser",
|
|
21
31
|
entryPoints: ["src/react.ts"],
|
|
22
32
|
outfile: "dist/react.js",
|
|
23
33
|
external: ["react", "react-dom"],
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# 알림톡 / 친구톡
|
|
2
|
+
|
|
3
|
+
카카오 비즈메시지(알림톡/친구톡)를 발송합니다.
|
|
4
|
+
|
|
5
|
+
## `alimtalkSend(req)`
|
|
6
|
+
|
|
7
|
+
카카오 알림톡을 발송합니다. 사전 승인된 템플릿을 사용합니다.
|
|
8
|
+
|
|
9
|
+
| 필드 | 타입 | 설명 |
|
|
10
|
+
| -------------- | -------- | ------------------------- |
|
|
11
|
+
| `to` | `string` | 수신 전화번호 |
|
|
12
|
+
| `templateCode` | `string` | 승인된 알림톡 템플릿 코드 |
|
|
13
|
+
| `variables` | `object` | 템플릿 변수 (키-값 맵) |
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
const res = await client.alimtalkSend({
|
|
17
|
+
to: "01012345678",
|
|
18
|
+
templateCode: "ORDER_CONFIRM",
|
|
19
|
+
variables: {
|
|
20
|
+
orderId: "20260201-001",
|
|
21
|
+
totalAmount: "50,000",
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
res.seq; // 발송 로그 seq
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## `alimtalkStatus(seq)`
|
|
28
|
+
|
|
29
|
+
알림톡 발송 처리 상태를 조회합니다.
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
const res = await client.alimtalkStatus(7);
|
|
33
|
+
res.status; // "sent" | "failed" | "pending"
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## `alimtalkTemplates()`
|
|
37
|
+
|
|
38
|
+
등록된 알림톡 템플릿 목록을 조회합니다.
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
const res = await client.alimtalkTemplates();
|
|
42
|
+
res.items; // 템플릿 배열 [{ code, name, content, ... }]
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## `friendtalkSend(req)`
|
|
46
|
+
|
|
47
|
+
카카오 친구톡을 발송합니다. 승인된 채널 친구에게만 발송 가능합니다.
|
|
48
|
+
|
|
49
|
+
| 필드 | 타입 | 설명 |
|
|
50
|
+
| --------- | -------- | ------------------------------------ |
|
|
51
|
+
| `to` | `string` | 수신 전화번호 |
|
|
52
|
+
| `type` | `string` | 메시지 타입 (`"text"`, `"image"` 등) |
|
|
53
|
+
| `content` | `string` | 메시지 본문 |
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
const res = await client.friendtalkSend({
|
|
57
|
+
to: "01012345678",
|
|
58
|
+
type: "text",
|
|
59
|
+
content: "안녕하세요! 신상품이 입고되었습니다.",
|
|
60
|
+
});
|
|
61
|
+
res.seq; // 발송 로그 seq
|
|
62
|
+
```
|
package/docs/api/auth.md
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
# 인증
|
|
2
|
+
|
|
3
|
+
## `login(email, password)`
|
|
4
|
+
|
|
5
|
+
이메일 + 비밀번호로 로그인합니다. 성공 시 내부 토큰이 자동으로 설정됩니다.
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
const auth = await client.login("admin@example.com", "password");
|
|
9
|
+
// auth.access_token — 이후 요청에 자동 사용됨
|
|
10
|
+
// auth.refresh_token — 만료 후 재발급에 사용
|
|
11
|
+
// auth.expires_in — 만료까지 남은 초
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## `refreshToken(refreshToken)`
|
|
15
|
+
|
|
16
|
+
Refresh Token으로 Access Token을 재발급받습니다.
|
|
17
|
+
앱이 명시적으로 호출해야 하며, 성공하면 내부 `this.token`을 새 Access Token으로 교체합니다.
|
|
18
|
+
|
|
19
|
+
> **패키지는 401을 감지해 자동으로 `refreshToken()`을 호출하지 않습니다.**
|
|
20
|
+
> 401 발생 시 앱이 직접 catch해서 호출해야 합니다.
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
const result = await client.refreshToken(auth.refresh_token);
|
|
24
|
+
// 성공 → this.token이 result.access_token으로 교체됨
|
|
25
|
+
// result.access_token
|
|
26
|
+
// result.expires_in
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## `logout(refreshToken)`
|
|
30
|
+
|
|
31
|
+
서버에 로그아웃을 요청하고 내부 토큰을 초기화합니다.
|
|
32
|
+
Refresh Token을 서버에 전달해 무효화하므로 해당 토큰으로 더 이상 재발급이 불가능합니다.
|
|
33
|
+
Refresh Token이 이미 만료된 경우에도 서버는 성공으로 응답합니다.
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
await client.logout(auth.refresh_token);
|
|
37
|
+
// 이후 client 내부 token = "" 으로 초기화됨
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## 토큰 만료 처리
|
|
41
|
+
|
|
42
|
+
**패키지가 자동으로 처리하는 것:**
|
|
43
|
+
|
|
44
|
+
| 동작 | 내부 토큰 갱신 |
|
|
45
|
+
| --------------------- | -------------- |
|
|
46
|
+
| `login()` 성공 | ✅ 자동 세팅 |
|
|
47
|
+
| `refreshToken()` 성공 | ✅ 자동 교체 |
|
|
48
|
+
| `logout()` 호출 | ✅ `""` 초기화 |
|
|
49
|
+
|
|
50
|
+
**패키지가 처리하지 않는 것:**
|
|
51
|
+
|
|
52
|
+
- **401 자동 재시도 없음**: access_token이 만료되어 서버가 401을 반환하면 패키지는 에러를 throw합니다. 앱이 직접 잡아서 `refreshToken()`을 호출하고 재시도해야 합니다.
|
|
53
|
+
- **토큰 영속성 없음**: 페이지 새로고침 시 싱글톤 인스턴스가 초기화되어 내부 token이 `""` 로 리셋됩니다. 앱이 직접 복원 후 `setToken()` 또는 `configure({ token })` 으로 재세팅해야 합니다.
|
|
54
|
+
|
|
55
|
+
**401 발생 후 앱의 처리 흐름:**
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
API 요청 → 401 응답 → entityServer.refreshToken(refresh_token) 호출
|
|
59
|
+
↓ 성공 ↓ 실패 (refresh_token도 만료)
|
|
60
|
+
setToken(new_access_token) 로그인 페이지로 이동
|
|
61
|
+
원래 요청 재시도
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**주의:** `refreshToken()`이 성공해 패키지 내부 토큰이 교체되더라도, 앱 내 다른 HTTP 클라이언트(axios 등)에는 별도로 토큰을 전달해야 합니다.
|
|
65
|
+
|
|
66
|
+
## `keepSession` — 세션 유지 (Silent Refresh)
|
|
67
|
+
|
|
68
|
+
`keepSession: true` 옵션 설정 시, `login()` 성공 후 만료 `refreshBuffer`초 전에 패키지 내부 타이머가 자동으로 `refreshToken()`을 호출합니다.
|
|
69
|
+
갱신이 성공하면 타이머를 다시 예약해 로그인 상태를 계속 유지합니다.
|
|
70
|
+
`logout()` 또는 `stopKeepSession()` 호출 시 타이머가 정리됩니다.
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
entityServer.configure({
|
|
74
|
+
keepSession: true,
|
|
75
|
+
refreshBuffer: 60, // 만료 60초 전에 갱신 (기본값)
|
|
76
|
+
onTokenRefreshed: (accessToken, expiresIn) => {
|
|
77
|
+
// 갱신 성공 — 앱 레벨 저장소 업데이트
|
|
78
|
+
localStorage.setItem("auth_access_token", accessToken);
|
|
79
|
+
},
|
|
80
|
+
onSessionExpired: (error) => {
|
|
81
|
+
// refresh_token 만료 등으로 갱신 실패
|
|
82
|
+
console.error("세션 만료:", error);
|
|
83
|
+
window.location.href = "/login";
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// 이후 login() 하면 타이머 자동 시작
|
|
88
|
+
const auth = await entityServer.login(email, password);
|
|
89
|
+
// expires_in=3600이면 3540초 후 자동 갱신, 이후 반복
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**수동으로 타이머 중지:**
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
entityServer.stopKeepSession();
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
> **페이지 새로고침 시**: 타이머는 메모리에만 존재하므로 새로고침 시 초기화됩니다.
|
|
99
|
+
> `useEntityServer`의 `resumeSession` 옵션을 사용하면 페이지 새로고침 후에도 세션을 자동으로 복원할 수 있습니다.
|
|
100
|
+
|
|
101
|
+
## OAuth 연동
|
|
102
|
+
|
|
103
|
+
### `oauthLink(provider, code, state?)`
|
|
104
|
+
|
|
105
|
+
OAuth 프로바이더를 현재 계정에 연동합니다.
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
await client.oauthLink("google", "auth-code-from-callback");
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### `oauthUnlink(provider)`
|
|
112
|
+
|
|
113
|
+
OAuth 프로바이더 연동을 해제합니다.
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
await client.oauthUnlink("google");
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### `oauthProviders()`
|
|
120
|
+
|
|
121
|
+
현재 계정에 연동된 OAuth 프로바이더 목록을 반환합니다.
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
const res = await client.oauthProviders();
|
|
125
|
+
res.data; // [{ provider: "google", email: "...", linked_at: "..." }]
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### `oauthTokenRefresh(provider)`
|
|
129
|
+
|
|
130
|
+
특정 OAuth 프로바이더의 액세스 토큰을 갱신합니다.
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
const res = await client.oauthTokenRefresh("google");
|
|
134
|
+
res.access_token;
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## 2단계 인증 (2FA)
|
|
138
|
+
|
|
139
|
+
### `twoFactorSetup()`
|
|
140
|
+
|
|
141
|
+
2FA 설정을 시작하고 QR 코드 / 시크릿을 반환합니다.
|
|
142
|
+
|
|
143
|
+
```ts
|
|
144
|
+
const res = await client.twoFactorSetup();
|
|
145
|
+
res.qr_url; // QR 코드 URL
|
|
146
|
+
res.secret; // TOTP 시크릿
|
|
147
|
+
res.setup_token;
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### `twoFactorSetupVerify(code, setupToken)`
|
|
151
|
+
|
|
152
|
+
TOTP 코드로 2FA 설정을 완료합니다.
|
|
153
|
+
|
|
154
|
+
```ts
|
|
155
|
+
const res = await client.twoFactorSetupVerify("123456", setupToken);
|
|
156
|
+
res.recovery_codes; // 복구 코드 목록
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### `twoFactorDisable(code)`
|
|
160
|
+
|
|
161
|
+
2FA를 비활성화합니다.
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
await client.twoFactorDisable("123456");
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### `twoFactorStatus()`
|
|
168
|
+
|
|
169
|
+
2FA 활성화 여부를 조회합니다.
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
const res = await client.twoFactorStatus();
|
|
173
|
+
res.enabled; // true | false
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### `twoFactorVerify(twoFactorToken, code)`
|
|
177
|
+
|
|
178
|
+
로그인 후 발급된 임시 토큰으로 TOTP 코드를 검증하여 최종 JWT를 발급받습니다.
|
|
179
|
+
|
|
180
|
+
```ts
|
|
181
|
+
const res = await client.twoFactorVerify(twoFactorToken, "123456");
|
|
182
|
+
res.access_token;
|
|
183
|
+
res.refresh_token;
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### `twoFactorRecovery(twoFactorToken, recoveryCode)`
|
|
187
|
+
|
|
188
|
+
복구 코드로 2FA를 우회하여 최종 JWT를 발급받습니다.
|
|
189
|
+
|
|
190
|
+
```ts
|
|
191
|
+
const res = await client.twoFactorRecovery(twoFactorToken, "recovery-code");
|
|
192
|
+
res.access_token;
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### `twoFactorRegenerateRecovery(code)`
|
|
196
|
+
|
|
197
|
+
복구 코드를 재생성합니다.
|
|
198
|
+
|
|
199
|
+
```ts
|
|
200
|
+
const res = await client.twoFactorRegenerateRecovery("123456");
|
|
201
|
+
res.recovery_codes;
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## 계정 관리
|
|
205
|
+
|
|
206
|
+
### `me()`
|
|
207
|
+
|
|
208
|
+
현재 로그인된 사용자 정보를 반환합니다.
|
|
209
|
+
|
|
210
|
+
```ts
|
|
211
|
+
const res = await client.me();
|
|
212
|
+
res.data; // 계정 정보
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### `changePassword(currentPasswd, newPasswd)`
|
|
216
|
+
|
|
217
|
+
비밀번호를 변경합니다.
|
|
218
|
+
|
|
219
|
+
```ts
|
|
220
|
+
await client.changePassword("current-pw", "new-pw");
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### `withdraw(passwd?)`
|
|
224
|
+
|
|
225
|
+
회원 탈퇴를 요청합니다.
|
|
226
|
+
|
|
227
|
+
```ts
|
|
228
|
+
await client.withdraw("current-pw");
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### `reactivate(params)`
|
|
232
|
+
|
|
233
|
+
휴면 계정을 재활성화합니다.
|
|
234
|
+
|
|
235
|
+
```ts
|
|
236
|
+
const auth = await client.reactivate({
|
|
237
|
+
email: "user@example.com",
|
|
238
|
+
passwd: "password",
|
|
239
|
+
});
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### `passwordResetRequest(email)`
|
|
243
|
+
|
|
244
|
+
비밀번호 재설정 메일을 요청합니다. 인증 불필요 (공개 API).
|
|
245
|
+
|
|
246
|
+
```ts
|
|
247
|
+
await client.passwordResetRequest("user@example.com");
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### `passwordResetConfirm(token, newPasswd)`
|
|
251
|
+
|
|
252
|
+
이메일로 전달된 토큰으로 비밀번호를 재설정합니다. 인증 불필요 (공개 API).
|
|
253
|
+
|
|
254
|
+
```ts
|
|
255
|
+
await client.passwordResetConfirm("reset-token", "new-password");
|
|
256
|
+
```
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# 이메일 인증 / 변경
|
|
2
|
+
|
|
3
|
+
## `emailVerificationSend(email)`
|
|
4
|
+
|
|
5
|
+
이메일 인증 코드 또는 링크를 발송합니다. 인증 불필요 (공개 API).
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
await client.emailVerificationSend("user@example.com");
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## `emailVerificationConfirm(token)`
|
|
12
|
+
|
|
13
|
+
이메일로 받은 인증 토큰을 검증합니다. 인증 불필요 (공개 API).
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
await client.emailVerificationConfirm("verify-token-from-email");
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## `emailVerificationStatus()`
|
|
20
|
+
|
|
21
|
+
현재 로그인된 계정의 이메일 인증 상태를 조회합니다. JWT 필요.
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
const res = await client.emailVerificationStatus();
|
|
25
|
+
res.verified; // true | false
|
|
26
|
+
res.email; // 현재 이메일
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## `emailChange(newEmail, code?)`
|
|
30
|
+
|
|
31
|
+
이메일 주소를 변경합니다.
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
await client.emailChange("new@example.com");
|
|
35
|
+
// 서버 설정에 따라 code가 필요할 수 있음
|
|
36
|
+
await client.emailChange("new@example.com", "123456");
|
|
37
|
+
```
|