@kood/claude-code 0.1.2 → 0.1.4
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 +129 -5
- 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/commands/git.md +275 -0
- package/templates/hono/docs/deployment/cloudflare.md +527 -190
- package/templates/hono/docs/deployment/docker.md +514 -0
- package/templates/hono/docs/deployment/index.md +179 -214
- package/templates/hono/docs/deployment/railway.md +416 -0
- package/templates/hono/docs/deployment/vercel.md +567 -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 -3
- 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/commands/git.md +275 -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/commands/git.md +275 -0
- package/templates/tanstack-start/docs/deployment/cloudflare.md +223 -50
- package/templates/tanstack-start/docs/deployment/index.md +320 -30
- package/templates/tanstack-start/docs/deployment/nitro.md +195 -14
- package/templates/tanstack-start/docs/deployment/railway.md +302 -150
- package/templates/tanstack-start/docs/deployment/vercel.md +345 -75
- 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 -5
- package/templates/tanstack-start/docs/library/prisma/schema.md +123 -25
- package/templates/tanstack-start/docs/library/prisma/setup.md +0 -7
- 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
- package/templates/tanstack-start/docs/git/index.md +0 -203
|
@@ -1,328 +1,665 @@
|
|
|
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"
|
|
116
|
+
|
|
117
|
+
# KV 네임스페이스 바인딩
|
|
118
|
+
[[kv_namespaces]]
|
|
119
|
+
binding = "KV"
|
|
120
|
+
id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
|
55
121
|
|
|
56
|
-
# R2
|
|
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
|
|
174
|
+
|
|
175
|
+
# 로그인
|
|
176
|
+
wrangler login
|
|
73
177
|
|
|
74
|
-
|
|
178
|
+
# 빌드
|
|
179
|
+
yarn build
|
|
75
180
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
})
|
|
181
|
+
# 배포
|
|
182
|
+
wrangler deploy
|
|
79
183
|
|
|
80
|
-
|
|
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: yarn 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
|
-
|
|
300
|
+
return c.json({ key, value: JSON.parse(value) });
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
// KV 쓰기
|
|
304
|
+
app.put("/cache/:key", async (c) => {
|
|
305
|
+
const key = c.req.param("key");
|
|
306
|
+
const body = await c.req.json();
|
|
181
307
|
|
|
182
|
-
|
|
183
|
-
|
|
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";
|
|
236
402
|
|
|
237
|
-
|
|
238
|
-
|
|
403
|
+
type Bindings = {
|
|
404
|
+
API_SECRET: string;
|
|
405
|
+
NODE_ENV: string;
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
const app = new Hono<{ Bindings: Bindings }>();
|
|
409
|
+
|
|
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
|
|
|
274
466
|
- name: Setup Node.js
|
|
275
467
|
uses: actions/setup-node@v4
|
|
276
468
|
with:
|
|
277
|
-
node-version:
|
|
469
|
+
node-version: "20"
|
|
470
|
+
cache: "yarn"
|
|
278
471
|
|
|
279
472
|
- name: Install dependencies
|
|
280
|
-
run:
|
|
473
|
+
run: yarn install --frozen-lockfile
|
|
474
|
+
|
|
475
|
+
- name: Build
|
|
476
|
+
run: yarn build
|
|
281
477
|
|
|
282
|
-
- name: Deploy
|
|
478
|
+
- name: Deploy to Cloudflare Workers
|
|
283
479
|
uses: cloudflare/wrangler-action@v3
|
|
284
480
|
with:
|
|
285
481
|
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
|
286
|
-
# 환경 지정 시
|
|
287
|
-
# command: deploy --env production
|
|
288
482
|
```
|
|
289
483
|
|
|
290
|
-
### GitHub
|
|
484
|
+
### GitHub Actions (Pages)
|
|
485
|
+
|
|
486
|
+
```yaml
|
|
487
|
+
# .github/workflows/cloudflare-pages.yml
|
|
488
|
+
name: Deploy to Cloudflare Pages
|
|
489
|
+
|
|
490
|
+
on:
|
|
491
|
+
push:
|
|
492
|
+
branches: [main]
|
|
493
|
+
|
|
494
|
+
jobs:
|
|
495
|
+
deploy:
|
|
496
|
+
runs-on: ubuntu-latest
|
|
497
|
+
|
|
498
|
+
steps:
|
|
499
|
+
- uses: actions/checkout@v4
|
|
500
|
+
|
|
501
|
+
- name: Setup Node.js
|
|
502
|
+
uses: actions/setup-node@v4
|
|
503
|
+
with:
|
|
504
|
+
node-version: "20"
|
|
505
|
+
cache: "yarn"
|
|
506
|
+
|
|
507
|
+
- name: Install dependencies
|
|
508
|
+
run: yarn install --frozen-lockfile
|
|
509
|
+
|
|
510
|
+
- name: Build
|
|
511
|
+
run: yarn build
|
|
512
|
+
|
|
513
|
+
- name: Deploy to Cloudflare Pages
|
|
514
|
+
uses: cloudflare/pages-action@v1
|
|
515
|
+
with:
|
|
516
|
+
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
|
517
|
+
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
|
518
|
+
projectName: my-hono-app
|
|
519
|
+
directory: .output/public
|
|
520
|
+
branch: main
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
---
|
|
524
|
+
|
|
525
|
+
## 성능 최적화
|
|
526
|
+
|
|
527
|
+
### 캐싱 설정
|
|
528
|
+
|
|
529
|
+
```typescript
|
|
530
|
+
// src/server.ts
|
|
531
|
+
import { Hono } from "hono";
|
|
532
|
+
import { cache } from "hono/cache";
|
|
533
|
+
|
|
534
|
+
const app = new Hono();
|
|
535
|
+
|
|
536
|
+
// 캐시 미들웨어
|
|
537
|
+
app.use(
|
|
538
|
+
"/api/public/*",
|
|
539
|
+
cache({
|
|
540
|
+
cacheName: "hono-cache",
|
|
541
|
+
cacheControl: "max-age=3600", // 1시간
|
|
542
|
+
})
|
|
543
|
+
);
|
|
544
|
+
|
|
545
|
+
// 또는 수동으로 Cache API 사용
|
|
546
|
+
app.get("/cached-data", async (c) => {
|
|
547
|
+
const cacheKey = new Request(c.req.url);
|
|
548
|
+
const cache = caches.default;
|
|
549
|
+
|
|
550
|
+
// 캐시 확인
|
|
551
|
+
let response = await cache.match(cacheKey);
|
|
552
|
+
if (response) {
|
|
553
|
+
return response;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// 데이터 가져오기
|
|
557
|
+
const data = await fetchData();
|
|
558
|
+
|
|
559
|
+
// 응답 생성 및 캐시
|
|
560
|
+
response = c.json(data);
|
|
561
|
+
response.headers.set("Cache-Control", "max-age=3600");
|
|
562
|
+
|
|
563
|
+
c.executionCtx.waitUntil(cache.put(cacheKey, response.clone()));
|
|
564
|
+
|
|
565
|
+
return response;
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
export default app;
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
### Smart Placement
|
|
572
|
+
|
|
573
|
+
```toml
|
|
574
|
+
# wrangler.toml
|
|
575
|
+
# Smart Placement: 데이터 소스에 가까운 위치에서 실행
|
|
576
|
+
[placement]
|
|
577
|
+
mode = "smart"
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
---
|
|
581
|
+
|
|
582
|
+
## 로컬 개발
|
|
583
|
+
|
|
584
|
+
### Wrangler Dev
|
|
585
|
+
|
|
586
|
+
```bash
|
|
587
|
+
# 로컬 개발 서버
|
|
588
|
+
wrangler dev
|
|
589
|
+
|
|
590
|
+
# D1 로컬 모드
|
|
591
|
+
wrangler dev --local --persist
|
|
592
|
+
|
|
593
|
+
# 특정 포트
|
|
594
|
+
wrangler dev --port 3000
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
### Miniflare (고급)
|
|
291
598
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
599
|
+
```typescript
|
|
600
|
+
// miniflare.config.ts
|
|
601
|
+
import { Miniflare } from "miniflare";
|
|
602
|
+
|
|
603
|
+
const mf = new Miniflare({
|
|
604
|
+
script: ".output/server/index.mjs",
|
|
605
|
+
modules: true,
|
|
606
|
+
d1Databases: ["DB"],
|
|
607
|
+
kvNamespaces: ["KV"],
|
|
608
|
+
r2Buckets: ["BUCKET"],
|
|
609
|
+
});
|
|
610
|
+
```
|
|
295
611
|
|
|
296
612
|
---
|
|
297
613
|
|
|
298
|
-
##
|
|
614
|
+
## 문제 해결
|
|
299
615
|
|
|
300
|
-
###
|
|
616
|
+
### 일반적인 문제
|
|
617
|
+
|
|
618
|
+
| 문제 | 원인 | 해결 |
|
|
619
|
+
|------|------|------|
|
|
620
|
+
| "Worker exceeded size limit" | 번들 크기 초과 | 의존성 최적화, externals 설정 |
|
|
621
|
+
| "D1 not found" | 바인딩 설정 오류 | wrangler.toml 확인 |
|
|
622
|
+
| "Compatibility date" 오류 | 날짜 형식 오류 | YYYY-MM-DD 형식 확인 |
|
|
623
|
+
| Node.js API 오류 | 호환성 플래그 누락 | `nodejs_compat` 플래그 추가 |
|
|
624
|
+
|
|
625
|
+
### 디버깅
|
|
301
626
|
|
|
302
627
|
```bash
|
|
303
|
-
|
|
628
|
+
# 로그 확인 (실시간)
|
|
629
|
+
wrangler tail
|
|
630
|
+
|
|
631
|
+
# 배포 상태 확인
|
|
632
|
+
wrangler deployments list
|
|
633
|
+
|
|
634
|
+
# 설정 검증
|
|
635
|
+
wrangler deploy --dry-run
|
|
304
636
|
```
|
|
305
637
|
|
|
306
|
-
###
|
|
638
|
+
### 번들 크기 최적화
|
|
307
639
|
|
|
308
640
|
```typescript
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
641
|
+
// nitro.config.ts
|
|
642
|
+
import { defineNitroConfig } from "nitro/config";
|
|
643
|
+
|
|
644
|
+
export default defineNitroConfig({
|
|
645
|
+
preset: "cloudflare_module",
|
|
646
|
+
compatibilityDate: "2024-09-19",
|
|
647
|
+
|
|
648
|
+
// 외부 패키지 제외
|
|
649
|
+
externals: ["@prisma/client"],
|
|
650
|
+
|
|
651
|
+
// 압축
|
|
652
|
+
minify: true,
|
|
653
|
+
});
|
|
321
654
|
```
|
|
322
655
|
|
|
323
656
|
---
|
|
324
657
|
|
|
325
658
|
## 관련 문서
|
|
326
659
|
|
|
327
|
-
- [배포 개요](./index.md)
|
|
328
|
-
- [
|
|
660
|
+
- [배포 가이드 개요](./index.md)
|
|
661
|
+
- [Docker 배포](./docker.md)
|
|
662
|
+
- [Railway 배포](./railway.md)
|
|
663
|
+
- [Vercel 배포](./vercel.md)
|
|
664
|
+
- [Cloudflare Workers 공식 문서](https://developers.cloudflare.com/workers/)
|
|
665
|
+
- [Cloudflare Pages 공식 문서](https://developers.cloudflare.com/pages/)
|