@kood/claude-code 0.1.3 → 0.1.5
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 +118 -3
- package/package.json +1 -1
- package/templates/hono/docs/commands/git.md +145 -0
- package/templates/hono/docs/deployment/cloudflare.md +8 -18
- package/templates/hono/docs/deployment/docker.md +15 -18
- package/templates/hono/docs/deployment/index.md +0 -3
- package/templates/hono/docs/deployment/railway.md +6 -6
- package/templates/hono/docs/deployment/vercel.md +9 -14
- package/templates/hono/docs/library/hono/index.md +0 -3
- package/templates/npx/docs/commands/git.md +145 -0
- package/templates/tanstack-start/docs/commands/git.md +145 -0
- package/templates/tanstack-start/docs/deployment/cloudflare.md +13 -23
- package/templates/tanstack-start/docs/deployment/index.md +6 -6
- package/templates/tanstack-start/docs/deployment/nitro.md +10 -10
- package/templates/tanstack-start/docs/deployment/railway.md +9 -9
- package/templates/tanstack-start/docs/deployment/vercel.md +12 -17
- package/templates/tanstack-start/docs/library/prisma/index.md +0 -4
- package/templates/tanstack-start/docs/library/prisma/setup.md +0 -7
- package/templates/hono/docs/git/git.md +0 -285
- package/templates/npx/docs/git/git.md +0 -307
- package/templates/tanstack-start/docs/git/git.md +0 -277
package/dist/index.js
CHANGED
|
@@ -183,6 +183,58 @@ var checkExistingSkills = async (targetDir, skills) => {
|
|
|
183
183
|
}
|
|
184
184
|
return existingSkills;
|
|
185
185
|
};
|
|
186
|
+
var getCommandsPath = (template) => {
|
|
187
|
+
return path.join(getTemplatePath(template), "docs", "commands");
|
|
188
|
+
};
|
|
189
|
+
var listAvailableCommands = async (template) => {
|
|
190
|
+
const commandsPath = getCommandsPath(template);
|
|
191
|
+
if (!await fs.pathExists(commandsPath)) {
|
|
192
|
+
return [];
|
|
193
|
+
}
|
|
194
|
+
const items = await fs.readdir(commandsPath);
|
|
195
|
+
const commands = [];
|
|
196
|
+
for (const item of items) {
|
|
197
|
+
const itemPath = path.join(commandsPath, item);
|
|
198
|
+
const stat = await fs.stat(itemPath);
|
|
199
|
+
if (stat.isFile() && item.endsWith(".md")) {
|
|
200
|
+
commands.push(item.replace(".md", ""));
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return commands;
|
|
204
|
+
};
|
|
205
|
+
var copyCommands = async (template, targetDir) => {
|
|
206
|
+
const commandsPath = getCommandsPath(template);
|
|
207
|
+
const targetCommandsDir = path.join(targetDir, ".claude", "commands");
|
|
208
|
+
if (!await fs.pathExists(commandsPath)) {
|
|
209
|
+
return { files: 0, commands: [] };
|
|
210
|
+
}
|
|
211
|
+
let files = 0;
|
|
212
|
+
const installedCommands = [];
|
|
213
|
+
await fs.ensureDir(targetCommandsDir);
|
|
214
|
+
const items = await fs.readdir(commandsPath);
|
|
215
|
+
for (const item of items) {
|
|
216
|
+
const srcPath = path.join(commandsPath, item);
|
|
217
|
+
const stat = await fs.stat(srcPath);
|
|
218
|
+
if (stat.isFile() && item.endsWith(".md")) {
|
|
219
|
+
const destPath = path.join(targetCommandsDir, item);
|
|
220
|
+
await fs.copy(srcPath, destPath);
|
|
221
|
+
files++;
|
|
222
|
+
installedCommands.push(item.replace(".md", ""));
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return { files, commands: installedCommands };
|
|
226
|
+
};
|
|
227
|
+
var checkExistingCommands = async (targetDir, commands) => {
|
|
228
|
+
const existingCommands = [];
|
|
229
|
+
const targetCommandsDir = path.join(targetDir, ".claude", "commands");
|
|
230
|
+
for (const command of commands) {
|
|
231
|
+
const commandPath = path.join(targetCommandsDir, `${command}.md`);
|
|
232
|
+
if (await fs.pathExists(commandPath)) {
|
|
233
|
+
existingCommands.push(command);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return existingCommands;
|
|
237
|
+
};
|
|
186
238
|
|
|
187
239
|
// src/commands/init.ts
|
|
188
240
|
var TEMPLATE_DESCRIPTIONS = {
|
|
@@ -245,6 +297,7 @@ var init = async (options) => {
|
|
|
245
297
|
let totalFiles = 0;
|
|
246
298
|
let totalDirectories = 0;
|
|
247
299
|
const allSkills = [];
|
|
300
|
+
const allCommands = [];
|
|
248
301
|
logger.blank();
|
|
249
302
|
try {
|
|
250
303
|
if (isSingleTemplate) {
|
|
@@ -258,6 +311,8 @@ var init = async (options) => {
|
|
|
258
311
|
logger.success(`${template}: ${result.files} files copied`);
|
|
259
312
|
const availableSkills = await listAvailableSkills(template);
|
|
260
313
|
allSkills.push(...availableSkills);
|
|
314
|
+
const availableCommands = await listAvailableCommands(template);
|
|
315
|
+
allCommands.push(...availableCommands);
|
|
261
316
|
} else {
|
|
262
317
|
logger.info(`Installing ${templates.length} templates...`);
|
|
263
318
|
logger.step(`Target: ${targetDir}/docs/`);
|
|
@@ -273,6 +328,12 @@ var init = async (options) => {
|
|
|
273
328
|
allSkills.push(skill);
|
|
274
329
|
}
|
|
275
330
|
}
|
|
331
|
+
const availableCommands = await listAvailableCommands(template);
|
|
332
|
+
for (const command of availableCommands) {
|
|
333
|
+
if (!allCommands.includes(command)) {
|
|
334
|
+
allCommands.push(command);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
276
337
|
}
|
|
277
338
|
}
|
|
278
339
|
} catch (error) {
|
|
@@ -318,6 +379,44 @@ var init = async (options) => {
|
|
|
318
379
|
logger.blank();
|
|
319
380
|
}
|
|
320
381
|
}
|
|
382
|
+
if (allCommands.length > 0) {
|
|
383
|
+
let installCommands = options.commands;
|
|
384
|
+
if (installCommands === void 0) {
|
|
385
|
+
logger.blank();
|
|
386
|
+
const response = await prompts({
|
|
387
|
+
type: "confirm",
|
|
388
|
+
name: "installCommands",
|
|
389
|
+
message: `Install Claude Code commands? (${allCommands.join(", ")})`,
|
|
390
|
+
initial: true
|
|
391
|
+
});
|
|
392
|
+
installCommands = response.installCommands;
|
|
393
|
+
}
|
|
394
|
+
if (installCommands) {
|
|
395
|
+
const existingCommands = await checkExistingCommands(
|
|
396
|
+
targetDir,
|
|
397
|
+
allCommands
|
|
398
|
+
);
|
|
399
|
+
if (existingCommands.length > 0 && !options.force) {
|
|
400
|
+
logger.warn("The following commands already exist:");
|
|
401
|
+
existingCommands.forEach((c) => logger.step(c));
|
|
402
|
+
logger.blank();
|
|
403
|
+
const response = await prompts({
|
|
404
|
+
type: "confirm",
|
|
405
|
+
name: "overwrite",
|
|
406
|
+
message: "Overwrite existing commands?",
|
|
407
|
+
initial: false
|
|
408
|
+
});
|
|
409
|
+
if (!response.overwrite) {
|
|
410
|
+
logger.info("Skipping commands installation.");
|
|
411
|
+
} else {
|
|
412
|
+
await installAllCommands(templates, targetDir);
|
|
413
|
+
}
|
|
414
|
+
} else {
|
|
415
|
+
await installAllCommands(templates, targetDir);
|
|
416
|
+
}
|
|
417
|
+
logger.blank();
|
|
418
|
+
}
|
|
419
|
+
}
|
|
321
420
|
logger.success("Claude Code documentation installed!");
|
|
322
421
|
logger.blank();
|
|
323
422
|
logger.info("Installed templates:");
|
|
@@ -343,14 +442,29 @@ var installAllSkills = async (templates, targetDir) => {
|
|
|
343
442
|
logger.step(`Location: .claude/skills/`);
|
|
344
443
|
}
|
|
345
444
|
};
|
|
445
|
+
var installAllCommands = async (templates, targetDir) => {
|
|
446
|
+
const installedCommands = [];
|
|
447
|
+
for (const template of templates) {
|
|
448
|
+
const commandsResult = await copyCommands(template, targetDir);
|
|
449
|
+
for (const command of commandsResult.commands) {
|
|
450
|
+
if (!installedCommands.includes(command)) {
|
|
451
|
+
installedCommands.push(command);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
if (installedCommands.length > 0) {
|
|
456
|
+
logger.success(`Commands installed: ${installedCommands.join(", ")}`);
|
|
457
|
+
logger.step(`Location: .claude/commands/`);
|
|
458
|
+
}
|
|
459
|
+
};
|
|
346
460
|
|
|
347
461
|
// src/index.ts
|
|
348
462
|
var program = new Command();
|
|
349
|
-
program.name("claude-code").description("Claude Code documentation installer for projects").version("0.1.
|
|
463
|
+
program.name("claude-code").description("Claude Code documentation installer for projects").version("0.1.5");
|
|
350
464
|
program.option(
|
|
351
465
|
"-t, --template <names>",
|
|
352
466
|
"template names (comma-separated: tanstack-start,hono)"
|
|
353
|
-
).option("-f, --force", "overwrite existing files without prompting").option("-s, --skills", "install Claude Code skills").option("--no-skills", "skip skills installation").option("--cwd <path>", "target directory (default: current directory)").option("--list", "list available templates").action(async (options) => {
|
|
467
|
+
).option("-f, --force", "overwrite existing files without prompting").option("-s, --skills", "install Claude Code skills").option("--no-skills", "skip skills installation").option("-c, --commands", "install Claude Code commands").option("--no-commands", "skip commands installation").option("--cwd <path>", "target directory (default: current directory)").option("--list", "list available templates").action(async (options) => {
|
|
354
468
|
banner();
|
|
355
469
|
if (options.list) {
|
|
356
470
|
const templates = await listAvailableTemplates();
|
|
@@ -363,7 +477,8 @@ program.option(
|
|
|
363
477
|
templates: options.template?.split(",").map((t) => t.trim()),
|
|
364
478
|
force: options.force,
|
|
365
479
|
cwd: options.cwd,
|
|
366
|
-
skills: options.skills
|
|
480
|
+
skills: options.skills,
|
|
481
|
+
commands: options.commands
|
|
367
482
|
});
|
|
368
483
|
});
|
|
369
484
|
program.parse();
|
package/package.json
CHANGED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
현재 git 상태를 확인하고, 아래 규칙에 따라 작업을 진행해주세요.
|
|
2
|
+
|
|
3
|
+
**추가 지시사항**: $ARGUMENTS
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 실행 절차
|
|
8
|
+
|
|
9
|
+
### 추가 지시사항이 없는 경우 (기본 동작)
|
|
10
|
+
1. `git status`로 현재 상태 확인
|
|
11
|
+
2. `git diff`로 변경 내용 분석
|
|
12
|
+
3. 스테이징 안 된 변경사항 → `git add`
|
|
13
|
+
4. 커밋 안 된 변경사항 → 논리적 단위로 분리하여 `git commit`
|
|
14
|
+
5. 최종 `git status`로 완료 확인
|
|
15
|
+
|
|
16
|
+
### 추가 지시사항이 있는 경우
|
|
17
|
+
- 사용자의 지시사항을 우선적으로 따름
|
|
18
|
+
- 예: `/git push` → push 진행, `/git 특정파일만 커밋` → 해당 파일만 처리
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## ⛔ NEVER (절대 금지)
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
❌ 커밋에 "Generated with Claude Code" 포함
|
|
26
|
+
❌ 커밋에 "🤖" 또는 AI 관련 이모지 포함
|
|
27
|
+
❌ 커밋에 "Co-Authored-By:" 헤더 포함
|
|
28
|
+
❌ 커밋에 AI/봇이 작성했다는 어떤 표시도 포함
|
|
29
|
+
❌ 커밋 메시지 여러 줄 작성
|
|
30
|
+
❌ 커밋 메시지에 이모지 사용
|
|
31
|
+
❌ 커밋 메시지에 마침표(.) 사용
|
|
32
|
+
❌ 여러 작업을 하나의 커밋으로 퉁치기
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## ✅ ALWAYS (필수)
|
|
38
|
+
|
|
39
|
+
### 커밋 형식
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
<prefix>: <설명>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**한 줄로 간결하게** 작성합니다. 본문이나 푸터는 작성하지 않습니다.
|
|
46
|
+
|
|
47
|
+
### ⭐ 커밋 분리 원칙
|
|
48
|
+
|
|
49
|
+
**하나의 커밋 = 하나의 논리적 변경 단위**
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# ❌ 잘못된 방식: 모든 작업을 하나로 퉁침
|
|
53
|
+
git add .
|
|
54
|
+
git commit -m "feat: 여러 기능 추가"
|
|
55
|
+
|
|
56
|
+
# ✅ 올바른 방식: 논리적 단위로 분리
|
|
57
|
+
git add src/auth/
|
|
58
|
+
git commit -m "feat: 사용자 인증 기능 추가"
|
|
59
|
+
|
|
60
|
+
git add src/users/
|
|
61
|
+
git commit -m "feat: 사용자 관리 기능 추가"
|
|
62
|
+
|
|
63
|
+
git add docs/
|
|
64
|
+
git commit -m "docs: API 문서 업데이트"
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## 🏷 Prefix 목록
|
|
70
|
+
|
|
71
|
+
| Prefix | 용도 | 예시 |
|
|
72
|
+
|--------|------|------|
|
|
73
|
+
| `feat` | 새로운 기능 | `feat: 사용자 인증 기능 추가` |
|
|
74
|
+
| `fix` | 버그 수정 | `fix: 토큰 검증 오류 수정` |
|
|
75
|
+
| `refactor` | 리팩토링 | `refactor: 인증 로직 분리` |
|
|
76
|
+
| `style` | 코드 스타일 | `style: prettier 적용` |
|
|
77
|
+
| `docs` | 문서 수정 | `docs: API 문서 업데이트` |
|
|
78
|
+
| `test` | 테스트 | `test: 인증 테스트 추가` |
|
|
79
|
+
| `chore` | 빌드/설정 | `chore: 의존성 업데이트` |
|
|
80
|
+
| `perf` | 성능 개선 | `perf: 쿼리 최적화` |
|
|
81
|
+
| `ci` | CI/CD | `ci: GitHub Actions 추가` |
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## ✅ 올바른 예시
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
feat: 사용자 로그인 기능 추가
|
|
89
|
+
fix: 세션 만료 오류 수정
|
|
90
|
+
refactor: 서비스 클래스 구조 개선
|
|
91
|
+
docs: README 설치 가이드 추가
|
|
92
|
+
chore: 의존성 버전 업그레이드
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## ❌ 잘못된 예시
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
# prefix 없음
|
|
101
|
+
사용자 인증 기능 추가함
|
|
102
|
+
|
|
103
|
+
# 마침표 불필요
|
|
104
|
+
feat: 사용자 인증 추가.
|
|
105
|
+
|
|
106
|
+
# 대문자 사용
|
|
107
|
+
FEAT: 사용자 인증 추가
|
|
108
|
+
|
|
109
|
+
# scope 불필요
|
|
110
|
+
feat(auth): 인증 추가
|
|
111
|
+
|
|
112
|
+
# AI 작성 표시 (절대 금지!)
|
|
113
|
+
feat: 로그인 기능 추가
|
|
114
|
+
|
|
115
|
+
🤖 Generated with Claude Code
|
|
116
|
+
|
|
117
|
+
# Co-Author 표시 (절대 금지!)
|
|
118
|
+
feat: 로그인 기능 추가
|
|
119
|
+
|
|
120
|
+
Co-Authored-By: Claude <noreply@anthropic.com>
|
|
121
|
+
|
|
122
|
+
# 여러 줄 본문 (금지)
|
|
123
|
+
feat: 로그인 기능 추가
|
|
124
|
+
|
|
125
|
+
- 이메일 인증 추가
|
|
126
|
+
- 세션 관리 구현
|
|
127
|
+
|
|
128
|
+
# 여러 작업을 하나로 퉁침 (금지)
|
|
129
|
+
feat: 로그인, 회원가입, 프로필 기능 추가
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## 📦 커밋 분리 가이드
|
|
135
|
+
|
|
136
|
+
### 언제 커밋을 분리해야 하나요?
|
|
137
|
+
|
|
138
|
+
| 상황 | 커밋 분리 |
|
|
139
|
+
|------|----------|
|
|
140
|
+
| 서로 다른 기능 구현 | ✅ 분리 |
|
|
141
|
+
| 버그 수정 + 새 기능 | ✅ 분리 |
|
|
142
|
+
| 코드 변경 + 문서 변경 | ✅ 분리 |
|
|
143
|
+
| 리팩토링 + 기능 추가 | ✅ 분리 |
|
|
144
|
+
| 동일 기능의 관련 파일들 | 🔄 묶어도 됨 |
|
|
145
|
+
| 동일 기능의 타입 + 구현 | 🔄 묶어도 됨 |
|
|
@@ -176,7 +176,7 @@ npm install -g wrangler
|
|
|
176
176
|
wrangler login
|
|
177
177
|
|
|
178
178
|
# 빌드
|
|
179
|
-
|
|
179
|
+
yarn build
|
|
180
180
|
|
|
181
181
|
# 배포
|
|
182
182
|
wrangler deploy
|
|
@@ -195,7 +195,7 @@ wrangler dev
|
|
|
195
195
|
2. **빌드 설정**
|
|
196
196
|
```
|
|
197
197
|
Framework preset: None
|
|
198
|
-
Build command:
|
|
198
|
+
Build command: yarn build
|
|
199
199
|
Build output directory: .output/public
|
|
200
200
|
```
|
|
201
201
|
|
|
@@ -463,22 +463,17 @@ jobs:
|
|
|
463
463
|
steps:
|
|
464
464
|
- uses: actions/checkout@v4
|
|
465
465
|
|
|
466
|
-
- name: Setup pnpm
|
|
467
|
-
uses: pnpm/action-setup@v2
|
|
468
|
-
with:
|
|
469
|
-
version: 8
|
|
470
|
-
|
|
471
466
|
- name: Setup Node.js
|
|
472
467
|
uses: actions/setup-node@v4
|
|
473
468
|
with:
|
|
474
469
|
node-version: "20"
|
|
475
|
-
cache: "
|
|
470
|
+
cache: "yarn"
|
|
476
471
|
|
|
477
472
|
- name: Install dependencies
|
|
478
|
-
run:
|
|
473
|
+
run: yarn install --frozen-lockfile
|
|
479
474
|
|
|
480
475
|
- name: Build
|
|
481
|
-
run:
|
|
476
|
+
run: yarn build
|
|
482
477
|
|
|
483
478
|
- name: Deploy to Cloudflare Workers
|
|
484
479
|
uses: cloudflare/wrangler-action@v3
|
|
@@ -503,22 +498,17 @@ jobs:
|
|
|
503
498
|
steps:
|
|
504
499
|
- uses: actions/checkout@v4
|
|
505
500
|
|
|
506
|
-
- name: Setup pnpm
|
|
507
|
-
uses: pnpm/action-setup@v2
|
|
508
|
-
with:
|
|
509
|
-
version: 8
|
|
510
|
-
|
|
511
501
|
- name: Setup Node.js
|
|
512
502
|
uses: actions/setup-node@v4
|
|
513
503
|
with:
|
|
514
504
|
node-version: "20"
|
|
515
|
-
cache: "
|
|
505
|
+
cache: "yarn"
|
|
516
506
|
|
|
517
507
|
- name: Install dependencies
|
|
518
|
-
run:
|
|
508
|
+
run: yarn install --frozen-lockfile
|
|
519
509
|
|
|
520
510
|
- name: Build
|
|
521
|
-
run:
|
|
511
|
+
run: yarn build
|
|
522
512
|
|
|
523
513
|
- name: Deploy to Cloudflare Pages
|
|
524
514
|
uses: cloudflare/pages-action@v1
|
|
@@ -14,14 +14,14 @@ FROM node:20-alpine AS base
|
|
|
14
14
|
|
|
15
15
|
FROM base AS deps
|
|
16
16
|
WORKDIR /app
|
|
17
|
-
COPY package.json
|
|
18
|
-
RUN
|
|
17
|
+
COPY package.json yarn.lock ./
|
|
18
|
+
RUN yarn install --frozen-lockfile
|
|
19
19
|
|
|
20
20
|
FROM base AS builder
|
|
21
21
|
WORKDIR /app
|
|
22
22
|
COPY --from=deps /app/node_modules ./node_modules
|
|
23
23
|
COPY . .
|
|
24
|
-
RUN
|
|
24
|
+
RUN yarn build
|
|
25
25
|
|
|
26
26
|
FROM base AS runner
|
|
27
27
|
WORKDIR /app
|
|
@@ -105,11 +105,10 @@ FROM base AS deps
|
|
|
105
105
|
WORKDIR /app
|
|
106
106
|
|
|
107
107
|
# 패키지 매니저 파일 복사
|
|
108
|
-
COPY package.json
|
|
108
|
+
COPY package.json yarn.lock ./
|
|
109
109
|
|
|
110
|
-
#
|
|
111
|
-
RUN
|
|
112
|
-
pnpm install --frozen-lockfile --prod=false
|
|
110
|
+
# yarn 의존성 설치
|
|
111
|
+
RUN yarn install --frozen-lockfile
|
|
113
112
|
|
|
114
113
|
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
115
114
|
# Stage 3: 빌드
|
|
@@ -124,9 +123,7 @@ COPY --from=deps /app/node_modules ./node_modules
|
|
|
124
123
|
COPY . .
|
|
125
124
|
|
|
126
125
|
# Nitro 빌드
|
|
127
|
-
RUN
|
|
128
|
-
pnpm run build && \
|
|
129
|
-
pnpm store prune
|
|
126
|
+
RUN yarn build
|
|
130
127
|
|
|
131
128
|
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
132
129
|
# Stage 4: 프로덕션 러너
|
|
@@ -333,17 +330,17 @@ FROM node:20-alpine
|
|
|
333
330
|
WORKDIR /app
|
|
334
331
|
|
|
335
332
|
# 패키지 파일 복사
|
|
336
|
-
COPY package.json
|
|
333
|
+
COPY package.json yarn.lock ./
|
|
337
334
|
|
|
338
|
-
#
|
|
339
|
-
RUN
|
|
335
|
+
# yarn 의존성 설치
|
|
336
|
+
RUN yarn install
|
|
340
337
|
|
|
341
338
|
# 소스 코드는 볼륨으로 마운트됨
|
|
342
339
|
|
|
343
340
|
EXPOSE 3000
|
|
344
341
|
|
|
345
342
|
# 개발 서버 실행
|
|
346
|
-
CMD ["
|
|
343
|
+
CMD ["yarn", "dev"]
|
|
347
344
|
```
|
|
348
345
|
|
|
349
346
|
---
|
|
@@ -449,7 +446,7 @@ FROM node:20-alpine
|
|
|
449
446
|
# 불필요한 파일 제외 (.dockerignore 활용)
|
|
450
447
|
|
|
451
448
|
# 프로덕션 의존성만 설치
|
|
452
|
-
RUN
|
|
449
|
+
RUN yarn install --production
|
|
453
450
|
|
|
454
451
|
# 빌드 결과물만 최종 이미지에 포함
|
|
455
452
|
COPY --from=builder /app/.output ./.output
|
|
@@ -459,12 +456,12 @@ COPY --from=builder /app/.output ./.output
|
|
|
459
456
|
|
|
460
457
|
```dockerfile
|
|
461
458
|
# 의존성 파일 먼저 복사 (캐시 활용)
|
|
462
|
-
COPY package.json
|
|
463
|
-
RUN
|
|
459
|
+
COPY package.json yarn.lock ./
|
|
460
|
+
RUN yarn install --frozen-lockfile
|
|
464
461
|
|
|
465
462
|
# 소스 코드는 나중에 복사
|
|
466
463
|
COPY . .
|
|
467
|
-
RUN
|
|
464
|
+
RUN yarn build
|
|
468
465
|
```
|
|
469
466
|
|
|
470
467
|
### 보안 강화
|
|
@@ -135,10 +135,10 @@ Railway는 Dockerfile을 자동으로 감지합니다.
|
|
|
135
135
|
# Dockerfile
|
|
136
136
|
FROM node:20-alpine AS builder
|
|
137
137
|
WORKDIR /app
|
|
138
|
-
COPY package.json
|
|
139
|
-
RUN
|
|
138
|
+
COPY package.json yarn.lock ./
|
|
139
|
+
RUN yarn install --frozen-lockfile
|
|
140
140
|
COPY . .
|
|
141
|
-
RUN
|
|
141
|
+
RUN yarn build
|
|
142
142
|
|
|
143
143
|
FROM node:20-alpine
|
|
144
144
|
WORKDIR /app
|
|
@@ -161,7 +161,7 @@ CMD ["node", ".output/server/index.mjs"]
|
|
|
161
161
|
[build]
|
|
162
162
|
# 빌드 명령어
|
|
163
163
|
builder = "nixpacks"
|
|
164
|
-
buildCommand = "
|
|
164
|
+
buildCommand = "yarn install && yarn build"
|
|
165
165
|
|
|
166
166
|
[deploy]
|
|
167
167
|
# 시작 명령어
|
|
@@ -387,7 +387,7 @@ Railway 대시보드에서 설정:
|
|
|
387
387
|
|
|
388
388
|
| 문제 | 원인 | 해결 |
|
|
389
389
|
|------|------|------|
|
|
390
|
-
| 빌드 실패 | 의존성 오류 | `
|
|
390
|
+
| 빌드 실패 | 의존성 오류 | `yarn.lock` 커밋 확인 |
|
|
391
391
|
| 포트 바인딩 실패 | 하드코딩된 포트 | `process.env.PORT` 사용 |
|
|
392
392
|
| 메모리 초과 | 메모리 누수 | 리소스 제한 증가 또는 코드 최적화 |
|
|
393
393
|
| 헬스체크 실패 | 엔드포인트 없음 | `/health` 엔드포인트 추가 |
|
|
@@ -396,7 +396,7 @@ Railway 대시보드에서 설정:
|
|
|
396
396
|
|
|
397
397
|
```bash
|
|
398
398
|
# 로컬에서 Railway 환경 시뮬레이션
|
|
399
|
-
railway run
|
|
399
|
+
railway run yarn dev
|
|
400
400
|
|
|
401
401
|
# 환경 변수 확인
|
|
402
402
|
railway variables
|
|
@@ -102,10 +102,10 @@ export default defineNitroConfig({
|
|
|
102
102
|
```json
|
|
103
103
|
{
|
|
104
104
|
"$schema": "https://openapi.vercel.sh/vercel.json",
|
|
105
|
-
"buildCommand": "
|
|
105
|
+
"buildCommand": "yarn build",
|
|
106
106
|
"outputDirectory": ".vercel/output",
|
|
107
107
|
"framework": null,
|
|
108
|
-
"installCommand": "
|
|
108
|
+
"installCommand": "yarn install"
|
|
109
109
|
}
|
|
110
110
|
```
|
|
111
111
|
|
|
@@ -114,7 +114,7 @@ export default defineNitroConfig({
|
|
|
114
114
|
```json
|
|
115
115
|
{
|
|
116
116
|
"$schema": "https://openapi.vercel.sh/vercel.json",
|
|
117
|
-
"buildCommand": "
|
|
117
|
+
"buildCommand": "yarn build",
|
|
118
118
|
"outputDirectory": ".vercel/output",
|
|
119
119
|
|
|
120
120
|
"functions": {
|
|
@@ -152,9 +152,9 @@ export default defineNitroConfig({
|
|
|
152
152
|
|
|
153
153
|
2. **빌드 설정**
|
|
154
154
|
- Framework Preset: "Other"
|
|
155
|
-
- Build Command: `
|
|
155
|
+
- Build Command: `yarn build`
|
|
156
156
|
- Output Directory: `.vercel/output`
|
|
157
|
-
- Install Command: `
|
|
157
|
+
- Install Command: `yarn install`
|
|
158
158
|
|
|
159
159
|
3. **환경 변수 설정**
|
|
160
160
|
- Vercel 대시보드에서 Settings → Environment Variables
|
|
@@ -452,22 +452,17 @@ jobs:
|
|
|
452
452
|
steps:
|
|
453
453
|
- uses: actions/checkout@v4
|
|
454
454
|
|
|
455
|
-
- name: Setup pnpm
|
|
456
|
-
uses: pnpm/action-setup@v2
|
|
457
|
-
with:
|
|
458
|
-
version: 8
|
|
459
|
-
|
|
460
455
|
- name: Setup Node.js
|
|
461
456
|
uses: actions/setup-node@v4
|
|
462
457
|
with:
|
|
463
458
|
node-version: "20"
|
|
464
|
-
cache: "
|
|
459
|
+
cache: "yarn"
|
|
465
460
|
|
|
466
461
|
- name: Install dependencies
|
|
467
|
-
run:
|
|
462
|
+
run: yarn install --frozen-lockfile
|
|
468
463
|
|
|
469
464
|
- name: Build
|
|
470
|
-
run:
|
|
465
|
+
run: yarn build
|
|
471
466
|
|
|
472
467
|
- name: Deploy to Vercel
|
|
473
468
|
uses: amondnet/vercel-action@v25
|
|
@@ -528,7 +523,7 @@ export default app;
|
|
|
528
523
|
| Function Timeout | 실행 시간 초과 | `maxDuration` 증가 또는 최적화 |
|
|
529
524
|
| Edge 호환성 오류 | Node.js API 사용 | Edge 호환 API로 변경 |
|
|
530
525
|
| 환경 변수 누락 | 설정 안됨 | Vercel 대시보드에서 설정 |
|
|
531
|
-
| 빌드 실패 | 의존성 문제 | `
|
|
526
|
+
| 빌드 실패 | 의존성 문제 | `yarn.lock` 확인 |
|
|
532
527
|
|
|
533
528
|
### 디버깅
|
|
534
529
|
|