@kood/claude-code 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +81 -38
- package/package.json +2 -2
- package/templates/hono/CLAUDE.md +20 -2
- package/templates/hono/docs/architecture/architecture.md +909 -0
- package/templates/hono/docs/deployment/cloudflare.md +537 -190
- package/templates/hono/docs/deployment/docker.md +517 -0
- package/templates/hono/docs/deployment/index.md +181 -213
- package/templates/hono/docs/deployment/railway.md +416 -0
- package/templates/hono/docs/deployment/vercel.md +572 -0
- package/templates/hono/docs/git/git.md +285 -0
- package/templates/hono/docs/library/ai-sdk/index.md +427 -0
- package/templates/hono/docs/library/ai-sdk/openrouter.md +479 -0
- package/templates/hono/docs/library/ai-sdk/providers.md +468 -0
- package/templates/hono/docs/library/ai-sdk/streaming.md +447 -0
- package/templates/hono/docs/library/ai-sdk/structured-output.md +493 -0
- package/templates/hono/docs/library/ai-sdk/tools.md +513 -0
- package/templates/hono/docs/library/hono/env-setup.md +458 -0
- package/templates/hono/docs/library/hono/index.md +1 -0
- package/templates/hono/docs/library/pino/index.md +437 -0
- package/templates/hono/docs/library/prisma/cloudflare-d1.md +503 -0
- package/templates/hono/docs/library/prisma/config.md +362 -0
- package/templates/hono/docs/library/prisma/index.md +86 -13
- package/templates/hono/docs/skills/gemini-review/SKILL.md +116 -116
- package/templates/hono/docs/skills/gemini-review/references/checklists.md +125 -125
- package/templates/hono/docs/skills/gemini-review/references/prompt-templates.md +191 -191
- package/templates/npx/CLAUDE.md +309 -0
- package/templates/npx/docs/git/git.md +307 -0
- package/templates/npx/docs/library/commander/index.md +164 -0
- package/templates/npx/docs/library/fs-extra/index.md +171 -0
- package/templates/npx/docs/library/prompts/index.md +253 -0
- package/templates/npx/docs/mcp/index.md +60 -0
- package/templates/npx/docs/skills/gemini-review/SKILL.md +220 -0
- package/templates/npx/docs/skills/gemini-review/references/checklists.md +134 -0
- package/templates/npx/docs/skills/gemini-review/references/prompt-templates.md +301 -0
- package/templates/tanstack-start/CLAUDE.md +43 -5
- package/templates/tanstack-start/docs/architecture/architecture.md +134 -4
- package/templates/tanstack-start/docs/deployment/cloudflare.md +234 -51
- package/templates/tanstack-start/docs/deployment/index.md +322 -32
- package/templates/tanstack-start/docs/deployment/nitro.md +201 -20
- package/templates/tanstack-start/docs/deployment/railway.md +305 -153
- package/templates/tanstack-start/docs/deployment/vercel.md +353 -78
- package/templates/tanstack-start/docs/git/{index.md → git.md} +81 -7
- package/templates/tanstack-start/docs/guides/best-practices.md +203 -1
- package/templates/tanstack-start/docs/guides/env-setup.md +450 -0
- package/templates/tanstack-start/docs/library/ai-sdk/hooks.md +472 -0
- package/templates/tanstack-start/docs/library/ai-sdk/index.md +264 -0
- package/templates/tanstack-start/docs/library/ai-sdk/openrouter.md +371 -0
- package/templates/tanstack-start/docs/library/ai-sdk/providers.md +403 -0
- package/templates/tanstack-start/docs/library/ai-sdk/streaming.md +320 -0
- package/templates/tanstack-start/docs/library/ai-sdk/structured-output.md +454 -0
- package/templates/tanstack-start/docs/library/ai-sdk/tools.md +473 -0
- package/templates/tanstack-start/docs/library/pino/index.md +320 -0
- package/templates/tanstack-start/docs/library/prisma/cloudflare-d1.md +404 -0
- package/templates/tanstack-start/docs/library/prisma/config.md +377 -0
- package/templates/tanstack-start/docs/library/prisma/index.md +3 -1
- package/templates/tanstack-start/docs/library/prisma/schema.md +123 -25
- package/templates/tanstack-start/docs/library/tanstack-start/server-functions.md +80 -2
- package/templates/tanstack-start/docs/skills/gemini-review/SKILL.md +116 -116
- package/templates/tanstack-start/docs/skills/gemini-review/references/checklists.md +138 -144
- package/templates/tanstack-start/docs/skills/gemini-review/references/prompt-templates.md +186 -187
- package/templates/hono/docs/git/index.md +0 -180
|
@@ -1,328 +1,675 @@
|
|
|
1
|
-
# Cloudflare
|
|
1
|
+
# Nitro v3 - Cloudflare 배포
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> **상위 문서**: [배포 가이드](./index.md)
|
|
4
|
+
|
|
5
|
+
Cloudflare Workers/Pages를 사용하여 Hono + Nitro 애플리케이션을 Edge에 배포합니다.
|
|
4
6
|
|
|
5
7
|
---
|
|
6
8
|
|
|
7
|
-
##
|
|
9
|
+
## 🚀 Quick Reference (복사용)
|
|
8
10
|
|
|
9
|
-
|
|
10
|
-
npm create hono@latest my-app
|
|
11
|
-
# cloudflare-workers 템플릿 선택
|
|
11
|
+
### Cloudflare Workers
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
```typescript
|
|
14
|
+
// nitro.config.ts
|
|
15
|
+
import { defineNitroConfig } from "nitro/config";
|
|
16
|
+
|
|
17
|
+
export default defineNitroConfig({
|
|
18
|
+
preset: "cloudflare_module",
|
|
19
|
+
compatibilityDate: "2024-09-19",
|
|
20
|
+
cloudflare: {
|
|
21
|
+
deployConfig: true,
|
|
22
|
+
nodeCompat: true,
|
|
23
|
+
},
|
|
24
|
+
});
|
|
15
25
|
```
|
|
16
26
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
## 프로젝트 구조
|
|
27
|
+
### Cloudflare Pages
|
|
20
28
|
|
|
29
|
+
```typescript
|
|
30
|
+
// nitro.config.ts
|
|
31
|
+
import { defineNitroConfig } from "nitro/config";
|
|
32
|
+
|
|
33
|
+
export default defineNitroConfig({
|
|
34
|
+
preset: "cloudflare_pages",
|
|
35
|
+
compatibilityDate: "2024-09-19",
|
|
36
|
+
cloudflare: {
|
|
37
|
+
deployConfig: true,
|
|
38
|
+
nodeCompat: true,
|
|
39
|
+
},
|
|
40
|
+
});
|
|
21
41
|
```
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
└── tsconfig.json
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# Wrangler CLI 배포
|
|
45
|
+
wrangler login
|
|
46
|
+
wrangler deploy
|
|
28
47
|
```
|
|
29
48
|
|
|
30
49
|
---
|
|
31
50
|
|
|
32
|
-
##
|
|
51
|
+
## Cloudflare Workers vs Pages
|
|
52
|
+
|
|
53
|
+
| 기능 | Workers | Pages |
|
|
54
|
+
|------|---------|-------|
|
|
55
|
+
| 용도 | 순수 API/서버리스 | 정적 사이트 + API |
|
|
56
|
+
| 정적 파일 | ❌ | ✅ |
|
|
57
|
+
| 무료 요청 | 100,000/일 | 무제한 정적 |
|
|
58
|
+
| 커스텀 도메인 | ✅ | ✅ |
|
|
59
|
+
| D1/KV/R2 | ✅ | ✅ |
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Cloudflare Workers 설정
|
|
64
|
+
|
|
65
|
+
### nitro.config.ts
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
// nitro.config.ts
|
|
69
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
70
|
+
// Cloudflare Workers 배포용 Nitro 설정
|
|
71
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
72
|
+
import { defineNitroConfig } from "nitro/config";
|
|
73
|
+
|
|
74
|
+
export default defineNitroConfig({
|
|
75
|
+
// Cloudflare Workers (ES Module 형식)
|
|
76
|
+
preset: "cloudflare_module",
|
|
77
|
+
|
|
78
|
+
// 호환성 날짜 (필수)
|
|
79
|
+
compatibilityDate: "2024-09-19",
|
|
80
|
+
|
|
81
|
+
// Cloudflare 설정
|
|
82
|
+
cloudflare: {
|
|
83
|
+
// wrangler.toml 자동 생성
|
|
84
|
+
deployConfig: true,
|
|
85
|
+
|
|
86
|
+
// Node.js 호환성 활성화
|
|
87
|
+
nodeCompat: true,
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
```
|
|
33
91
|
|
|
34
92
|
### wrangler.toml
|
|
35
93
|
|
|
36
94
|
```toml
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
95
|
+
# wrangler.toml
|
|
96
|
+
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
97
|
+
# Cloudflare Workers 설정
|
|
98
|
+
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
99
|
+
|
|
100
|
+
name = "hono-nitro-app"
|
|
101
|
+
main = ".output/server/index.mjs"
|
|
102
|
+
compatibility_date = "2024-09-19"
|
|
103
|
+
|
|
104
|
+
# Node.js 호환성 플래그
|
|
40
105
|
compatibility_flags = ["nodejs_compat"]
|
|
41
106
|
|
|
107
|
+
# 환경 변수
|
|
42
108
|
[vars]
|
|
43
109
|
NODE_ENV = "production"
|
|
44
110
|
|
|
45
|
-
#
|
|
46
|
-
[[kv_namespaces]]
|
|
47
|
-
binding = "MY_KV"
|
|
48
|
-
id = "your-kv-namespace-id"
|
|
49
|
-
|
|
50
|
-
# D1 Database
|
|
111
|
+
# D1 데이터베이스 바인딩
|
|
51
112
|
[[d1_databases]]
|
|
52
113
|
binding = "DB"
|
|
53
114
|
database_name = "my-database"
|
|
54
|
-
database_id = "
|
|
115
|
+
database_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
|
55
116
|
|
|
56
|
-
#
|
|
117
|
+
# KV 네임스페이스 바인딩
|
|
118
|
+
[[kv_namespaces]]
|
|
119
|
+
binding = "KV"
|
|
120
|
+
id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
|
121
|
+
|
|
122
|
+
# R2 버킷 바인딩
|
|
57
123
|
[[r2_buckets]]
|
|
58
|
-
binding = "
|
|
124
|
+
binding = "BUCKET"
|
|
59
125
|
bucket_name = "my-bucket"
|
|
126
|
+
|
|
127
|
+
# 시크릿 (wrangler secret put 으로 설정)
|
|
128
|
+
# API_SECRET = "..."
|
|
60
129
|
```
|
|
61
130
|
|
|
62
|
-
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Cloudflare Pages 설정
|
|
134
|
+
|
|
135
|
+
### nitro.config.ts
|
|
63
136
|
|
|
64
137
|
```typescript
|
|
65
|
-
|
|
138
|
+
// nitro.config.ts
|
|
139
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
140
|
+
// Cloudflare Pages 배포용 Nitro 설정
|
|
141
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
142
|
+
import { defineNitroConfig } from "nitro/config";
|
|
143
|
+
|
|
144
|
+
export default defineNitroConfig({
|
|
145
|
+
// Cloudflare Pages
|
|
146
|
+
preset: "cloudflare_pages",
|
|
147
|
+
|
|
148
|
+
// 호환성 날짜
|
|
149
|
+
compatibilityDate: "2024-09-19",
|
|
150
|
+
|
|
151
|
+
// Cloudflare 설정
|
|
152
|
+
cloudflare: {
|
|
153
|
+
deployConfig: true,
|
|
154
|
+
nodeCompat: true,
|
|
155
|
+
|
|
156
|
+
// Pages 전용 설정
|
|
157
|
+
pages: {
|
|
158
|
+
// 정적 파일 디렉토리
|
|
159
|
+
staticDir: "public",
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
```
|
|
66
164
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## 배포 방법
|
|
168
|
+
|
|
169
|
+
### 방법 1: Wrangler CLI (Workers)
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
# Wrangler CLI 설치
|
|
173
|
+
npm install -g wrangler
|
|
73
174
|
|
|
74
|
-
|
|
175
|
+
# 로그인
|
|
176
|
+
wrangler login
|
|
75
177
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
})
|
|
178
|
+
# 빌드
|
|
179
|
+
pnpm build
|
|
79
180
|
|
|
80
|
-
|
|
181
|
+
# 배포
|
|
182
|
+
wrangler deploy
|
|
183
|
+
|
|
184
|
+
# 개발 서버
|
|
185
|
+
wrangler dev
|
|
81
186
|
```
|
|
82
187
|
|
|
83
|
-
|
|
188
|
+
### 방법 2: GitHub 연동 (Pages)
|
|
84
189
|
|
|
85
|
-
|
|
190
|
+
1. **Cloudflare Pages 프로젝트 생성**
|
|
191
|
+
- [Cloudflare Dashboard](https://dash.cloudflare.com) → Pages
|
|
192
|
+
- "Create a project" → "Connect to Git"
|
|
193
|
+
- GitHub 저장소 선택
|
|
86
194
|
|
|
87
|
-
|
|
195
|
+
2. **빌드 설정**
|
|
196
|
+
```
|
|
197
|
+
Framework preset: None
|
|
198
|
+
Build command: pnpm build
|
|
199
|
+
Build output directory: .output/public
|
|
200
|
+
```
|
|
88
201
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
return c.json({ success: true })
|
|
101
|
-
})
|
|
202
|
+
3. **환경 변수 설정**
|
|
203
|
+
- Settings → Environment variables
|
|
204
|
+
|
|
205
|
+
### 방법 3: Wrangler CLI (Pages)
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
# Pages 배포
|
|
209
|
+
wrangler pages deploy .output/public
|
|
210
|
+
|
|
211
|
+
# 프로덕션 배포
|
|
212
|
+
wrangler pages deploy .output/public --branch main
|
|
102
213
|
```
|
|
103
214
|
|
|
104
|
-
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## Bindings (D1, KV, R2)
|
|
218
|
+
|
|
219
|
+
### D1 데이터베이스
|
|
105
220
|
|
|
106
221
|
```typescript
|
|
107
|
-
|
|
222
|
+
// src/server.ts
|
|
223
|
+
import { Hono } from "hono";
|
|
224
|
+
|
|
225
|
+
// Cloudflare Bindings 타입 정의
|
|
226
|
+
type Bindings = {
|
|
227
|
+
DB: D1Database;
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
const app = new Hono<{ Bindings: Bindings }>();
|
|
231
|
+
|
|
232
|
+
// D1 쿼리
|
|
233
|
+
app.get("/users", async (c) => {
|
|
108
234
|
const { results } = await c.env.DB.prepare(
|
|
109
|
-
|
|
110
|
-
).all()
|
|
111
|
-
return c.json({ users: results })
|
|
112
|
-
})
|
|
113
|
-
|
|
114
|
-
app.post('/users', async (c) => {
|
|
115
|
-
const { name, email } = await c.req.json()
|
|
116
|
-
const { success } = await c.env.DB.prepare(
|
|
117
|
-
'INSERT INTO users (name, email) VALUES (?, ?)'
|
|
118
|
-
).bind(name, email).run()
|
|
119
|
-
return c.json({ success }, 201)
|
|
120
|
-
})
|
|
121
|
-
```
|
|
235
|
+
"SELECT * FROM users"
|
|
236
|
+
).all();
|
|
122
237
|
|
|
123
|
-
|
|
238
|
+
return c.json(results);
|
|
239
|
+
});
|
|
124
240
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
const key = c.req.param('key')
|
|
128
|
-
await c.env.MY_BUCKET.put(key, c.req.body)
|
|
129
|
-
return c.json({ success: true })
|
|
130
|
-
})
|
|
241
|
+
app.post("/users", async (c) => {
|
|
242
|
+
const { name, email } = await c.req.json();
|
|
131
243
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
244
|
+
const result = await c.env.DB.prepare(
|
|
245
|
+
"INSERT INTO users (name, email) VALUES (?, ?)"
|
|
246
|
+
)
|
|
247
|
+
.bind(name, email)
|
|
248
|
+
.run();
|
|
135
249
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}
|
|
250
|
+
return c.json({ id: result.meta.last_row_id });
|
|
251
|
+
});
|
|
139
252
|
|
|
140
|
-
|
|
141
|
-
headers: {
|
|
142
|
-
'Content-Type': object.httpMetadata?.contentType || 'application/octet-stream',
|
|
143
|
-
},
|
|
144
|
-
})
|
|
145
|
-
})
|
|
253
|
+
export default app;
|
|
146
254
|
```
|
|
147
255
|
|
|
148
|
-
|
|
256
|
+
### D1 마이그레이션
|
|
149
257
|
|
|
150
|
-
|
|
258
|
+
```bash
|
|
259
|
+
# 마이그레이션 생성
|
|
260
|
+
wrangler d1 migrations create my-database create_users
|
|
151
261
|
|
|
152
|
-
|
|
262
|
+
# 마이그레이션 적용
|
|
263
|
+
wrangler d1 migrations apply my-database
|
|
153
264
|
|
|
265
|
+
# 로컬 D1 실행
|
|
266
|
+
wrangler d1 execute my-database --local --command="SELECT * FROM users"
|
|
154
267
|
```
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
268
|
+
|
|
269
|
+
```sql
|
|
270
|
+
-- migrations/0001_create_users.sql
|
|
271
|
+
CREATE TABLE users (
|
|
272
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
273
|
+
name TEXT NOT NULL,
|
|
274
|
+
email TEXT UNIQUE NOT NULL,
|
|
275
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
276
|
+
);
|
|
158
277
|
```
|
|
159
278
|
|
|
160
|
-
###
|
|
279
|
+
### KV 스토리지
|
|
161
280
|
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
npx wrangler secret put DATABASE_URL
|
|
281
|
+
```typescript
|
|
282
|
+
// src/server.ts
|
|
283
|
+
import { Hono } from "hono";
|
|
166
284
|
|
|
167
|
-
|
|
168
|
-
|
|
285
|
+
type Bindings = {
|
|
286
|
+
KV: KVNamespace;
|
|
287
|
+
};
|
|
169
288
|
|
|
170
|
-
|
|
171
|
-
npx wrangler secret delete JWT_SECRET
|
|
172
|
-
```
|
|
289
|
+
const app = new Hono<{ Bindings: Bindings }>();
|
|
173
290
|
|
|
174
|
-
|
|
291
|
+
// KV 읽기
|
|
292
|
+
app.get("/cache/:key", async (c) => {
|
|
293
|
+
const key = c.req.param("key");
|
|
294
|
+
const value = await c.env.KV.get(key);
|
|
175
295
|
|
|
176
|
-
|
|
296
|
+
if (!value) {
|
|
297
|
+
return c.json({ error: "Not found" }, 404);
|
|
298
|
+
}
|
|
177
299
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
npx wrangler types
|
|
300
|
+
return c.json({ key, value: JSON.parse(value) });
|
|
301
|
+
});
|
|
181
302
|
|
|
182
|
-
|
|
183
|
-
|
|
303
|
+
// KV 쓰기
|
|
304
|
+
app.put("/cache/:key", async (c) => {
|
|
305
|
+
const key = c.req.param("key");
|
|
306
|
+
const body = await c.req.json();
|
|
307
|
+
|
|
308
|
+
// TTL 설정 (초 단위)
|
|
309
|
+
await c.env.KV.put(key, JSON.stringify(body), {
|
|
310
|
+
expirationTtl: 3600, // 1시간
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
return c.json({ success: true });
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
export default app;
|
|
184
317
|
```
|
|
185
318
|
|
|
186
|
-
###
|
|
319
|
+
### R2 스토리지
|
|
187
320
|
|
|
188
321
|
```typescript
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
322
|
+
// src/server.ts
|
|
323
|
+
import { Hono } from "hono";
|
|
324
|
+
|
|
325
|
+
type Bindings = {
|
|
326
|
+
BUCKET: R2Bucket;
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
const app = new Hono<{ Bindings: Bindings }>();
|
|
330
|
+
|
|
331
|
+
// 파일 업로드
|
|
332
|
+
app.post("/upload/:filename", async (c) => {
|
|
333
|
+
const filename = c.req.param("filename");
|
|
334
|
+
const body = await c.req.arrayBuffer();
|
|
335
|
+
|
|
336
|
+
await c.env.BUCKET.put(filename, body, {
|
|
337
|
+
httpMetadata: {
|
|
338
|
+
contentType: c.req.header("Content-Type") || "application/octet-stream",
|
|
339
|
+
},
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
return c.json({ success: true, filename });
|
|
343
|
+
});
|
|
197
344
|
|
|
198
|
-
|
|
345
|
+
// 파일 다운로드
|
|
346
|
+
app.get("/download/:filename", async (c) => {
|
|
347
|
+
const filename = c.req.param("filename");
|
|
348
|
+
const object = await c.env.BUCKET.get(filename);
|
|
199
349
|
|
|
200
|
-
|
|
201
|
-
{
|
|
202
|
-
"compilerOptions": {
|
|
203
|
-
"types": [
|
|
204
|
-
"@cloudflare/workers-types",
|
|
205
|
-
"./worker-configuration.d.ts"
|
|
206
|
-
]
|
|
350
|
+
if (!object) {
|
|
351
|
+
return c.json({ error: "Not found" }, 404);
|
|
207
352
|
}
|
|
208
|
-
|
|
353
|
+
|
|
354
|
+
c.header("Content-Type", object.httpMetadata?.contentType || "application/octet-stream");
|
|
355
|
+
return c.body(object.body);
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
export default app;
|
|
209
359
|
```
|
|
210
360
|
|
|
211
361
|
---
|
|
212
362
|
|
|
213
|
-
##
|
|
363
|
+
## 환경 변수
|
|
214
364
|
|
|
215
|
-
###
|
|
365
|
+
### wrangler.toml에서 설정
|
|
216
366
|
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
367
|
+
```toml
|
|
368
|
+
# wrangler.toml
|
|
369
|
+
[vars]
|
|
370
|
+
NODE_ENV = "production"
|
|
371
|
+
API_BASE_URL = "https://api.example.com"
|
|
372
|
+
|
|
373
|
+
# 환경별 설정
|
|
374
|
+
[env.staging.vars]
|
|
375
|
+
NODE_ENV = "staging"
|
|
376
|
+
API_BASE_URL = "https://staging-api.example.com"
|
|
377
|
+
|
|
378
|
+
[env.production.vars]
|
|
379
|
+
NODE_ENV = "production"
|
|
380
|
+
API_BASE_URL = "https://api.example.com"
|
|
221
381
|
```
|
|
222
382
|
|
|
223
|
-
###
|
|
383
|
+
### 시크릿 설정 (CLI)
|
|
224
384
|
|
|
225
385
|
```bash
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
386
|
+
# 시크릿 추가
|
|
387
|
+
wrangler secret put API_SECRET
|
|
388
|
+
wrangler secret put DATABASE_URL
|
|
389
|
+
|
|
390
|
+
# 시크릿 목록
|
|
391
|
+
wrangler secret list
|
|
392
|
+
|
|
393
|
+
# 시크릿 삭제
|
|
394
|
+
wrangler secret delete API_SECRET
|
|
229
395
|
```
|
|
230
396
|
|
|
231
|
-
###
|
|
397
|
+
### 코드에서 사용
|
|
232
398
|
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
|
|
399
|
+
```typescript
|
|
400
|
+
// src/server.ts
|
|
401
|
+
import { Hono } from "hono";
|
|
402
|
+
|
|
403
|
+
type Bindings = {
|
|
404
|
+
API_SECRET: string;
|
|
405
|
+
NODE_ENV: string;
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
const app = new Hono<{ Bindings: Bindings }>();
|
|
236
409
|
|
|
237
|
-
|
|
238
|
-
|
|
410
|
+
app.get("/config", (c) => {
|
|
411
|
+
return c.json({
|
|
412
|
+
environment: c.env.NODE_ENV,
|
|
413
|
+
hasSecret: !!c.env.API_SECRET,
|
|
414
|
+
});
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
export default app;
|
|
239
418
|
```
|
|
240
419
|
|
|
241
|
-
|
|
420
|
+
---
|
|
242
421
|
|
|
243
|
-
|
|
244
|
-
name = "my-app"
|
|
245
|
-
main = "src/index.ts"
|
|
422
|
+
## 도메인 설정
|
|
246
423
|
|
|
247
|
-
|
|
248
|
-
name = "my-app-staging"
|
|
249
|
-
vars = { NODE_ENV = "staging" }
|
|
424
|
+
### Workers 커스텀 도메인
|
|
250
425
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
426
|
+
```toml
|
|
427
|
+
# wrangler.toml
|
|
428
|
+
routes = [
|
|
429
|
+
{ pattern = "api.example.com/*", zone_name = "example.com" }
|
|
430
|
+
]
|
|
431
|
+
|
|
432
|
+
# 또는
|
|
433
|
+
[triggers]
|
|
434
|
+
routes = [
|
|
435
|
+
"api.example.com/*"
|
|
436
|
+
]
|
|
254
437
|
```
|
|
255
438
|
|
|
439
|
+
### Pages 커스텀 도메인
|
|
440
|
+
|
|
441
|
+
1. Cloudflare Dashboard → Pages → 프로젝트 선택
|
|
442
|
+
2. Custom domains → Add custom domain
|
|
443
|
+
3. DNS 설정 자동 구성
|
|
444
|
+
|
|
256
445
|
---
|
|
257
446
|
|
|
258
|
-
##
|
|
447
|
+
## CI/CD 설정
|
|
448
|
+
|
|
449
|
+
### GitHub Actions (Workers)
|
|
259
450
|
|
|
260
451
|
```yaml
|
|
452
|
+
# .github/workflows/cloudflare-workers.yml
|
|
261
453
|
name: Deploy to Cloudflare Workers
|
|
262
454
|
|
|
263
455
|
on:
|
|
264
456
|
push:
|
|
265
|
-
branches:
|
|
266
|
-
- main
|
|
457
|
+
branches: [main]
|
|
267
458
|
|
|
268
459
|
jobs:
|
|
269
460
|
deploy:
|
|
270
461
|
runs-on: ubuntu-latest
|
|
462
|
+
|
|
271
463
|
steps:
|
|
272
464
|
- uses: actions/checkout@v4
|
|
273
465
|
|
|
466
|
+
- name: Setup pnpm
|
|
467
|
+
uses: pnpm/action-setup@v2
|
|
468
|
+
with:
|
|
469
|
+
version: 8
|
|
470
|
+
|
|
274
471
|
- name: Setup Node.js
|
|
275
472
|
uses: actions/setup-node@v4
|
|
276
473
|
with:
|
|
277
|
-
node-version:
|
|
474
|
+
node-version: "20"
|
|
475
|
+
cache: "pnpm"
|
|
278
476
|
|
|
279
477
|
- name: Install dependencies
|
|
280
|
-
run:
|
|
478
|
+
run: pnpm install
|
|
479
|
+
|
|
480
|
+
- name: Build
|
|
481
|
+
run: pnpm build
|
|
281
482
|
|
|
282
|
-
- name: Deploy
|
|
483
|
+
- name: Deploy to Cloudflare Workers
|
|
283
484
|
uses: cloudflare/wrangler-action@v3
|
|
284
485
|
with:
|
|
285
486
|
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
|
286
|
-
# 환경 지정 시
|
|
287
|
-
# command: deploy --env production
|
|
288
487
|
```
|
|
289
488
|
|
|
290
|
-
### GitHub
|
|
489
|
+
### GitHub Actions (Pages)
|
|
490
|
+
|
|
491
|
+
```yaml
|
|
492
|
+
# .github/workflows/cloudflare-pages.yml
|
|
493
|
+
name: Deploy to Cloudflare Pages
|
|
494
|
+
|
|
495
|
+
on:
|
|
496
|
+
push:
|
|
497
|
+
branches: [main]
|
|
498
|
+
|
|
499
|
+
jobs:
|
|
500
|
+
deploy:
|
|
501
|
+
runs-on: ubuntu-latest
|
|
502
|
+
|
|
503
|
+
steps:
|
|
504
|
+
- uses: actions/checkout@v4
|
|
505
|
+
|
|
506
|
+
- name: Setup pnpm
|
|
507
|
+
uses: pnpm/action-setup@v2
|
|
508
|
+
with:
|
|
509
|
+
version: 8
|
|
510
|
+
|
|
511
|
+
- name: Setup Node.js
|
|
512
|
+
uses: actions/setup-node@v4
|
|
513
|
+
with:
|
|
514
|
+
node-version: "20"
|
|
515
|
+
cache: "pnpm"
|
|
516
|
+
|
|
517
|
+
- name: Install dependencies
|
|
518
|
+
run: pnpm install
|
|
519
|
+
|
|
520
|
+
- name: Build
|
|
521
|
+
run: pnpm build
|
|
522
|
+
|
|
523
|
+
- name: Deploy to Cloudflare Pages
|
|
524
|
+
uses: cloudflare/pages-action@v1
|
|
525
|
+
with:
|
|
526
|
+
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
|
527
|
+
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
|
528
|
+
projectName: my-hono-app
|
|
529
|
+
directory: .output/public
|
|
530
|
+
branch: main
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
---
|
|
534
|
+
|
|
535
|
+
## 성능 최적화
|
|
536
|
+
|
|
537
|
+
### 캐싱 설정
|
|
538
|
+
|
|
539
|
+
```typescript
|
|
540
|
+
// src/server.ts
|
|
541
|
+
import { Hono } from "hono";
|
|
542
|
+
import { cache } from "hono/cache";
|
|
543
|
+
|
|
544
|
+
const app = new Hono();
|
|
545
|
+
|
|
546
|
+
// 캐시 미들웨어
|
|
547
|
+
app.use(
|
|
548
|
+
"/api/public/*",
|
|
549
|
+
cache({
|
|
550
|
+
cacheName: "hono-cache",
|
|
551
|
+
cacheControl: "max-age=3600", // 1시간
|
|
552
|
+
})
|
|
553
|
+
);
|
|
291
554
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
555
|
+
// 또는 수동으로 Cache API 사용
|
|
556
|
+
app.get("/cached-data", async (c) => {
|
|
557
|
+
const cacheKey = new Request(c.req.url);
|
|
558
|
+
const cache = caches.default;
|
|
559
|
+
|
|
560
|
+
// 캐시 확인
|
|
561
|
+
let response = await cache.match(cacheKey);
|
|
562
|
+
if (response) {
|
|
563
|
+
return response;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// 데이터 가져오기
|
|
567
|
+
const data = await fetchData();
|
|
568
|
+
|
|
569
|
+
// 응답 생성 및 캐시
|
|
570
|
+
response = c.json(data);
|
|
571
|
+
response.headers.set("Cache-Control", "max-age=3600");
|
|
572
|
+
|
|
573
|
+
c.executionCtx.waitUntil(cache.put(cacheKey, response.clone()));
|
|
574
|
+
|
|
575
|
+
return response;
|
|
576
|
+
});
|
|
577
|
+
|
|
578
|
+
export default app;
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
### Smart Placement
|
|
582
|
+
|
|
583
|
+
```toml
|
|
584
|
+
# wrangler.toml
|
|
585
|
+
# Smart Placement: 데이터 소스에 가까운 위치에서 실행
|
|
586
|
+
[placement]
|
|
587
|
+
mode = "smart"
|
|
588
|
+
```
|
|
295
589
|
|
|
296
590
|
---
|
|
297
591
|
|
|
298
|
-
##
|
|
592
|
+
## 로컬 개발
|
|
299
593
|
|
|
300
|
-
###
|
|
594
|
+
### Wrangler Dev
|
|
301
595
|
|
|
302
596
|
```bash
|
|
303
|
-
|
|
597
|
+
# 로컬 개발 서버
|
|
598
|
+
wrangler dev
|
|
599
|
+
|
|
600
|
+
# D1 로컬 모드
|
|
601
|
+
wrangler dev --local --persist
|
|
602
|
+
|
|
603
|
+
# 특정 포트
|
|
604
|
+
wrangler dev --port 3000
|
|
304
605
|
```
|
|
305
606
|
|
|
306
|
-
###
|
|
607
|
+
### Miniflare (고급)
|
|
307
608
|
|
|
308
609
|
```typescript
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
610
|
+
// miniflare.config.ts
|
|
611
|
+
import { Miniflare } from "miniflare";
|
|
612
|
+
|
|
613
|
+
const mf = new Miniflare({
|
|
614
|
+
script: ".output/server/index.mjs",
|
|
615
|
+
modules: true,
|
|
616
|
+
d1Databases: ["DB"],
|
|
617
|
+
kvNamespaces: ["KV"],
|
|
618
|
+
r2Buckets: ["BUCKET"],
|
|
619
|
+
});
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
---
|
|
623
|
+
|
|
624
|
+
## 문제 해결
|
|
625
|
+
|
|
626
|
+
### 일반적인 문제
|
|
627
|
+
|
|
628
|
+
| 문제 | 원인 | 해결 |
|
|
629
|
+
|------|------|------|
|
|
630
|
+
| "Worker exceeded size limit" | 번들 크기 초과 | 의존성 최적화, externals 설정 |
|
|
631
|
+
| "D1 not found" | 바인딩 설정 오류 | wrangler.toml 확인 |
|
|
632
|
+
| "Compatibility date" 오류 | 날짜 형식 오류 | YYYY-MM-DD 형식 확인 |
|
|
633
|
+
| Node.js API 오류 | 호환성 플래그 누락 | `nodejs_compat` 플래그 추가 |
|
|
634
|
+
|
|
635
|
+
### 디버깅
|
|
636
|
+
|
|
637
|
+
```bash
|
|
638
|
+
# 로그 확인 (실시간)
|
|
639
|
+
wrangler tail
|
|
640
|
+
|
|
641
|
+
# 배포 상태 확인
|
|
642
|
+
wrangler deployments list
|
|
643
|
+
|
|
644
|
+
# 설정 검증
|
|
645
|
+
wrangler deploy --dry-run
|
|
646
|
+
```
|
|
647
|
+
|
|
648
|
+
### 번들 크기 최적화
|
|
649
|
+
|
|
650
|
+
```typescript
|
|
651
|
+
// nitro.config.ts
|
|
652
|
+
import { defineNitroConfig } from "nitro/config";
|
|
653
|
+
|
|
654
|
+
export default defineNitroConfig({
|
|
655
|
+
preset: "cloudflare_module",
|
|
656
|
+
compatibilityDate: "2024-09-19",
|
|
657
|
+
|
|
658
|
+
// 외부 패키지 제외
|
|
659
|
+
externals: ["@prisma/client"],
|
|
660
|
+
|
|
661
|
+
// 압축
|
|
662
|
+
minify: true,
|
|
663
|
+
});
|
|
321
664
|
```
|
|
322
665
|
|
|
323
666
|
---
|
|
324
667
|
|
|
325
668
|
## 관련 문서
|
|
326
669
|
|
|
327
|
-
- [배포 개요](./index.md)
|
|
328
|
-
- [
|
|
670
|
+
- [배포 가이드 개요](./index.md)
|
|
671
|
+
- [Docker 배포](./docker.md)
|
|
672
|
+
- [Railway 배포](./railway.md)
|
|
673
|
+
- [Vercel 배포](./vercel.md)
|
|
674
|
+
- [Cloudflare Workers 공식 문서](https://developers.cloudflare.com/workers/)
|
|
675
|
+
- [Cloudflare Pages 공식 문서](https://developers.cloudflare.com/pages/)
|