diff --git a/.github/workflows/template-security-scan.yml b/.github/workflows/template-security-scan.yml
new file mode 100644
index 0000000..821f5c9
--- /dev/null
+++ b/.github/workflows/template-security-scan.yml
@@ -0,0 +1,184 @@
+name: Weekly Template Security Scan
+
+on:
+ schedule:
+ - cron: "0 23 * * 0"
+ workflow_dispatch:
+ inputs:
+ templates:
+ description: "Specific templates to scan (comma-separated, leave empty for all)"
+ required: false
+ default: ""
+
+permissions:
+ contents: read
+ issues: write
+
+jobs:
+ security-scan:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Set up Python
+ uses: actions/setup-python@v5
+ with:
+ python-version: "3.12"
+
+ - name: Install pip-audit
+ run: pip install pip-audit
+
+ - name: Run security scan on templates
+ id: scan
+ run: |
+ TEMPLATE_DIR="src/fastapi_fastkit/fastapi_project_template"
+ RESULTS_FILE="security_scan_results.json"
+ SCAN_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
+
+ # Initialize results
+ echo '{' > $RESULTS_FILE
+ echo ' "scan_date": "'$SCAN_DATE'",' >> $RESULTS_FILE
+ echo ' "templates": [' >> $RESULTS_FILE
+
+ TEMPLATES_INPUT="${{ github.event.inputs.templates }}"
+ FIRST_TEMPLATE=true
+ TOTAL_VULNERABILITIES=0
+ AFFECTED_TEMPLATES=""
+
+ for template_dir in $TEMPLATE_DIR/fastapi-*/; do
+ template_name=$(basename "$template_dir")
+
+ # Skip if specific templates are requested and this isn't one
+ if [ -n "$TEMPLATES_INPUT" ]; then
+ if ! echo "$TEMPLATES_INPUT" | grep -q "$template_name"; then
+ continue
+ fi
+ fi
+
+ req_file="$template_dir/requirements.txt-tpl"
+ if [ -f "$req_file" ]; then
+ echo "๐ Scanning $template_name..."
+
+ # Create temp requirements file
+ temp_req=$(mktemp)
+ cp "$req_file" "$temp_req"
+
+ # Run pip-audit and capture output
+ audit_output=$(pip-audit -r "$temp_req" --format json 2>/dev/null || echo '[]')
+ rm "$temp_req"
+
+ # Count vulnerabilities
+ vuln_count=$(echo "$audit_output" | python3 -c "import sys, json; data = json.load(sys.stdin); print(len(data))" 2>/dev/null || echo "0")
+
+ if [ "$vuln_count" -gt 0 ]; then
+ TOTAL_VULNERABILITIES=$((TOTAL_VULNERABILITIES + vuln_count))
+ AFFECTED_TEMPLATES="$AFFECTED_TEMPLATES $template_name"
+ echo "โ ๏ธ Found $vuln_count vulnerabilities in $template_name"
+ else
+ echo "โ
No vulnerabilities in $template_name"
+ fi
+
+ # Add to JSON
+ if [ "$FIRST_TEMPLATE" = true ]; then
+ FIRST_TEMPLATE=false
+ else
+ echo ' ,' >> $RESULTS_FILE
+ fi
+
+ echo ' {' >> $RESULTS_FILE
+ echo ' "name": "'$template_name'",' >> $RESULTS_FILE
+ echo ' "vulnerability_count": '$vuln_count',' >> $RESULTS_FILE
+ echo ' "vulnerabilities": '$audit_output >> $RESULTS_FILE
+ echo ' }' >> $RESULTS_FILE
+ fi
+ done
+
+ echo ' ],' >> $RESULTS_FILE
+ echo ' "total_vulnerabilities": '$TOTAL_VULNERABILITIES',' >> $RESULTS_FILE
+ echo ' "affected_templates": "'$(echo $AFFECTED_TEMPLATES | xargs)'"' >> $RESULTS_FILE
+ echo '}' >> $RESULTS_FILE
+
+ # Set outputs for later steps
+ echo "total_vulnerabilities=$TOTAL_VULNERABILITIES" >> $GITHUB_OUTPUT
+ echo "affected_templates=$AFFECTED_TEMPLATES" >> $GITHUB_OUTPUT
+
+ - name: Upload scan results
+ uses: actions/upload-artifact@v4
+ if: always()
+ with:
+ name: security-scan-results
+ path: security_scan_results.json
+ retention-days: 30
+
+ - name: Create Issue on Vulnerabilities
+ if: steps.scan.outputs.total_vulnerabilities != '0'
+ uses: actions/github-script@v7
+ with:
+ script: |
+ const fs = require('fs');
+
+ let issueBody = `## ๐ Template Dependency Security Scan Results\n\n`;
+ issueBody += `**Scan Date:** ${new Date().toISOString()}\n`;
+ issueBody += `**Total Vulnerabilities Found:** ${{ steps.scan.outputs.total_vulnerabilities }}\n\n`;
+
+ try {
+ const results = JSON.parse(fs.readFileSync('security_scan_results.json', 'utf8'));
+
+ results.templates.forEach(template => {
+ if (template.vulnerability_count > 0) {
+ issueBody += `### โ ๏ธ ${template.name}\n\n`;
+ issueBody += `Found **${template.vulnerability_count}** vulnerabilities:\n\n`;
+ issueBody += `| Package | Installed | Fix Versions | Vulnerability ID |\n`;
+ issueBody += `|---------|-----------|--------------|------------------|\n`;
+
+ template.vulnerabilities.forEach(vuln => {
+ const fixVersions = vuln.fix_versions ? vuln.fix_versions.join(', ') : 'N/A';
+ issueBody += `| ${vuln.name} | ${vuln.version} | ${fixVersions} | ${vuln.id} |\n`;
+ });
+
+ issueBody += `\n`;
+ }
+ });
+
+ } catch (error) {
+ issueBody += `\nError reading detailed results: ${error.message}\n`;
+ }
+
+ issueBody += `\n---\n**Workflow:** [${context.workflow}](${context.payload.repository.html_url}/actions/runs/${context.runId})`;
+
+ // Determine severity label
+ const totalVulns = parseInt('${{ steps.scan.outputs.total_vulnerabilities }}');
+ let severityLabel = 'security: low';
+ if (totalVulns >= 10) {
+ severityLabel = 'security: critical';
+ } else if (totalVulns >= 5) {
+ severityLabel = 'security: high';
+ } else if (totalVulns >= 2) {
+ severityLabel = 'security: medium';
+ }
+
+ await github.rest.issues.create({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ title: `๐ Template Security Alert: ${totalVulns} vulnerabilities found - ${new Date().toISOString().split('T')[0]}`,
+ body: issueBody,
+ labels: ['security', severityLabel, 'automated', 'template']
+ });
+
+ - name: Report Summary
+ if: always()
+ run: |
+ echo "## ๐ Security Scan Summary" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+
+ if [ "${{ steps.scan.outputs.total_vulnerabilities }}" = "0" ]; then
+ echo "โ
**No vulnerabilities found in any template!**" >> $GITHUB_STEP_SUMMARY
+ else
+ echo "โ ๏ธ **Found ${{ steps.scan.outputs.total_vulnerabilities }} vulnerabilities**" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "Affected templates: ${{ steps.scan.outputs.affected_templates }}" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "A GitHub Issue has been created with detailed information." >> $GITHUB_STEP_SUMMARY
+ fi
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 5130688..1228c85 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -99,6 +99,7 @@ Key commands for contributors:
- `make test` - Run all tests
- `make test-verbose` - Run tests with verbose output
- `make test-coverage` - Run tests with coverage report
+- `make coverage-report` - Generate detailed coverage report (supports FORMAT=html/xml/json/all)
#### Installation and Building
- `make install-test` - Install package for testing (uninstall + reinstall)
@@ -109,6 +110,9 @@ Key commands for contributors:
- `make build-docs` - Build documentation
- `make serve-docs` - Serve documentation locally
+#### Translation
+- `make translate` - Translate documentation (supports LANG, PROVIDER, MODEL parameters)
+
### Development Workflow
1. **Before making changes:**
@@ -170,6 +174,13 @@ Run tests using these commands:
make test-coverage
```
+4. **Detailed coverage report with options:**
+ ```bash
+ make coverage-report # Terminal output
+ make coverage-report FORMAT=html # HTML report (opens in browser)
+ make coverage-report FORMAT=all # All formats (term, html, xml, json)
+ ```
+
### Making PRs
Use these tags in PR title:
@@ -423,7 +434,21 @@ This migrates existing English docs to `docs/en/` and creates language directori
### Translating Documentation
-#### Translate to Specific Language
+#### Using Make Commands (Recommended)
+
+```bash
+# Translate all docs to all configured languages
+make translate
+
+# Translate to specific language
+make translate LANG=ko
+
+# Specify API provider and model
+make translate LANG=ko PROVIDER=github MODEL=gpt-4o-mini
+make translate LANG=ko PROVIDER=openai MODEL=gpt-4
+```
+
+#### Using Script Directly
```bash
# Translate all docs to Korean
diff --git a/Makefile b/Makefile
index cbcc1af..3970ebc 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-.PHONY: help install install-dev install-test uninstall test test-verbose lint format format-check clean build build-docs serve-docs version-update all
+.PHONY: help install install-dev install-test uninstall test test-verbose lint format format-check clean build build-docs serve-docs version-update all translate coverage-report
# Default target
help: ## Show this help message
@@ -137,3 +137,21 @@ all: ## Run complete development workflow
$(MAKE) dev-setup
$(MAKE) dev-check
$(MAKE) build
+
+# Translation commands
+translate: ## Translate documentation - Usage: make translate LANG=ko PROVIDER=github MODEL=gpt-4o-mini
+ @echo "Starting translation..."
+ @CMD="python scripts/translate.py"; \
+ if [ -n "$(LANG)" ]; then CMD="$$CMD --target-lang $(LANG)"; fi; \
+ if [ -n "$(PROVIDER)" ]; then CMD="$$CMD --api-provider $(PROVIDER)"; fi; \
+ if [ -n "$(MODEL)" ]; then CMD="$$CMD --model $(MODEL)"; fi; \
+ echo "Running: $$CMD"; \
+ $$CMD
+
+# Coverage report commands
+coverage-report: ## Generate detailed coverage report - Usage: make coverage-report FORMAT=html
+ @if [ -z "$(FORMAT)" ]; then \
+ ./scripts/coverage-report.sh; \
+ else \
+ ./scripts/coverage-report.sh -f "$(FORMAT)"; \
+ fi
diff --git a/docs/en/contributing/development-setup.md b/docs/en/contributing/development-setup.md
index 4e03306..ec02e86 100644
--- a/docs/en/contributing/development-setup.md
+++ b/docs/en/contributing/development-setup.md
@@ -191,6 +191,7 @@ The project Makefile provides convenient commands for common development tasks:
| `make test-unit` | Run unit tests only |
| `make test-integration` | Run integration tests only |
| `make test-coverage` | Run tests with coverage report |
+| `make coverage-report` | Generate detailed coverage report (FORMAT=html/xml/json/all) |
| `make test-watch` | Run tests in watch mode |
### Documentation Commands
@@ -201,6 +202,12 @@ The project Makefile provides convenient commands for common development tasks:
| `make docs-build` | Build documentation |
| `make docs-deploy` | Deploy documentation to GitHub Pages |
+### Translation Commands
+
+| Command | Description |
+|---------|-----------|
+| `make translate` | Translate documentation (LANG, PROVIDER, MODEL parameters) |
+
### Examples
@@ -231,6 +238,15 @@ src/cli.py 89 5 94%
src/templates.py 67 3 96%
--------------------------------------------
TOTAL 201 10 95%
+
+# Generate HTML coverage report
+$ make coverage-report FORMAT=html
+๐ Opening HTML coverage report in browser...
+
+# Translate documentation to Korean
+$ make translate LANG=ko PROVIDER=github MODEL=gpt-4o-mini
+Starting translation...
+Running: python scripts/translate.py --target-lang ko --api-provider github --model gpt-4o-mini
```
diff --git a/docs/en/contributing/translation-guide.md b/docs/en/contributing/translation-guide.md
index 699cbad..872c664 100644
--- a/docs/en/contributing/translation-guide.md
+++ b/docs/en/contributing/translation-guide.md
@@ -66,7 +66,25 @@ gh auth login
## Usage
-### Translate All Documentation
+### Using Make Commands (Recommended)
+
+The easiest way to run translations:
+
+```bash
+# Translate all docs to all languages
+make translate
+
+# Translate to specific language
+make translate LANG=ko
+
+# Specify API provider and model
+make translate LANG=ko PROVIDER=openai MODEL=gpt-4
+make translate LANG=ko PROVIDER=github MODEL=gpt-4o-mini
+```
+
+### Using Script Directly
+
+#### Translate All Documentation
Translate all documentation to all supported languages:
diff --git a/docs/ko/changelog.md b/docs/ko/changelog.md
new file mode 100644
index 0000000..f4d16fd
--- /dev/null
+++ b/docs/ko/changelog.md
@@ -0,0 +1 @@
+{!CHANGELOG.md!}
diff --git a/docs/ko/index.md b/docs/ko/index.md
new file mode 100644
index 0000000..793d142
--- /dev/null
+++ b/docs/ko/index.md
@@ -0,0 +1,550 @@
+
+
+
+
+FastAPI-fastkit: Python๊ณผ FastAPI ์ ๊ท ์ฌ์ฉ์์ฉ ๋น ๋ฅด๊ณ ์ฌ์ฉํ๊ธฐ ์ฌ์ด ์คํํฐ ํคํธ
+
+
+
+
+
+
+
+
+
+
+
+
+
+---
+
+์ด ํ๋ก์ ํธ๋ Python๊ณผ [FastAPI](https://github.com/fastapi/fastapi) ์ ๊ท ์ฌ์ฉ์๊ฐ Python ๊ธฐ๋ฐ ์น ์ฑ์ ๊ฐ๋ฐํ๋ ๋ฐ ํ์ํ ๊ฐ๋ฐ ํ๊ฒฝ ๊ตฌ์ฑ์ ๋์ฑ ๋น ๋ฅด๊ฒ ํ ์ ์๋๋ก ๋ง๋ค์์ต๋๋ค.
+
+์ด ํ๋ก์ ํธ๋ `SpringBoot initializer` ๋ฐ Python Django์ `django-admin` CLI ๋์์์ ์๊ฐ์ ๋ฐ์์ต๋๋ค.
+
+## ์ฃผ์ ๊ธฐ๋ฅ
+
+- **โก Immediate FastAPI project creation** : [Python Django](https://github.com/django/django)์ `django-admin` ๊ธฐ๋ฅ์์ ์๊ฐ์ ๋ฐ์ CLI๋ฅผ ํตํด ์ด๊ณ ์ FastAPI ์ํฌ์คํ์ด์ค ๋ฐ ํ๋ก์ ํธ ์์ฑ
+- **โจ ๋ํํ ํ๋ก์ ํธ ๋น๋**: ๋ฐ์ดํฐ๋ฒ ์ด์ค, ์ธ์ฆ, ์บ์ฑ, ๋ชจ๋ํฐ๋ง ๋ฑ ๊ธฐ๋ฅ์ ๋จ๊ณ๋ณ๋ก ์๋ดํ์ฌ ์ ํํ๊ณ ์ฝ๋๋ฅผ ์๋ ์์ฑ
+- **๐จ Prettier CLI outputs** : [rich library](https://github.com/Textualize/rich) ๊ธฐ๋ฐ์ ์๋ฆ๋ค์ด CLI ๊ฒฝํ
+- **๐ Standards-based FastAPI project templates** : ๋ชจ๋ FastAPI-fastkit ํ
ํ๋ฆฟ์ Python ํ์ค๊ณผ FastAPI์ ์ผ๋ฐ์ ์ฌ์ฉ ํจํด์ ๊ธฐ๋ฐ์ผ๋ก ๊ตฌ์ฑ
+- **๐ Automated template quality assurance** : ์ฃผ๊ฐ ์๋ํ ํ
์คํธ๋ก ๋ชจ๋ ํ
ํ๋ฆฟ์ด ์ ์ ๋์ํ๋ฉฐ ์ต์ ์ํ๋ก ์ ์ง๋จ์ ๋ณด์ฅ
+- **๐ Multiple project templates** : ๋ค์ํ ์ฌ์ฉ ์ฌ๋ก์ ๋ง์ถ ์ฌ์ ๊ตฌ์ฑ ํ
ํ๋ฆฟ ์ ํ ๊ฐ๋ฅ(async CRUD, Docker, PostgreSQL ๋ฑ)
+- **๐ฆ Multiple package manager support** : ์์กด์ฑ ๊ด๋ฆฌ๋ฅผ ์ํด ์ ํธํ๋ Python ํจํค์ง ๊ด๋ฆฌ์(pip, uv, pdm, poetry)๋ฅผ ์ ํ ๊ฐ๋ฅ
+
+## ์ค์น
+
+Python ํ๊ฒฝ์ `FastAPI-fastkit`๋ฅผ ์ค์นํ์ธ์.
+
+
+
+```console
+$ pip install FastAPI-fastkit
+---> 100%
+```
+
+
+
+
+## ์ฌ์ฉ๋ฒ
+
+### FastAPI ํ๋ก์ ํธ ์ํฌ์คํ์ด์ค ํ๊ฒฝ์ ์ฆ์ ์์ฑ
+
+์ด์ FastAPI-fastkit์ผ๋ก ๋งค์ฐ ๋น ๋ฅด๊ฒ ์ FastAPI ํ๋ก์ ํธ๋ฅผ ์์ํ ์ ์์ต๋๋ค!
+
+๋ค์์ผ๋ก ์ฆ์ ์ FastAPI ํ๋ก์ ํธ ์ํฌ์คํ์ด์ค๋ฅผ ์์ฑํ์ธ์:
+
+
+
+```console
+$ fastkit init
+Enter the project name: my-awesome-project
+Enter the author name: John Doe
+Enter the author email: john@example.com
+Enter the project description: My awesome FastAPI project
+
+ Project Information
+โโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+โ Project Name โ my-awesome-project โ
+โ Author โ John Doe โ
+โ Author Email โ john@example.com โ
+โ Description โ My awesome FastAPI project โ
+โโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+Available Stacks and Dependencies:
+ MINIMAL Stack
+โโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโ
+โ Dependency 1 โ fastapi โ
+โ Dependency 2 โ uvicorn โ
+โ Dependency 3 โ pydantic โ
+โ Dependency 4 โ pydantic-settings โ
+โโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโ
+
+ STANDARD Stack
+โโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโ
+โ Dependency 1 โ fastapi โ
+โ Dependency 2 โ uvicorn โ
+โ Dependency 3 โ sqlalchemy โ
+โ Dependency 4 โ alembic โ
+โ Dependency 5 โ pytest โ
+โ Dependency 6 โ pydantic โ
+โ Dependency 7 โ pydantic-settings โ
+โโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโ
+
+ FULL Stack
+โโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโ
+โ Dependency 1 โ fastapi โ
+โ Dependency 2 โ uvicorn โ
+โ Dependency 3 โ sqlalchemy โ
+โ Dependency 4 โ alembic โ
+โ Dependency 5 โ pytest โ
+โ Dependency 6 โ redis โ
+โ Dependency 7 โ celery โ
+โ Dependency 8 โ pydantic โ
+โ Dependency 9 โ pydantic-settings โ
+โโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโ
+
+Select stack (minimal, standard, full): minimal
+
+Available Package Managers:
+ Package Managers
+โโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+โ PIP โ Standard Python package manager โ
+โ UV โ Fast Python package manager โ
+โ PDM โ Modern Python dependency management โ
+โ POETRY โ Python dependency management and packaging โ
+โโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+Select package manager (pip, uv, pdm, poetry) [uv]: uv
+Do you want to proceed with project creation? [y/N]: y
+FastAPI project will deploy at '~your-project-path~'
+
+โญโโโโโโโโโโโโโโโโโโโโโโโโ Info โโโโโโโโโโโโโโโโโโโโโโโโโฎ
+โ โน Injected metadata into setup.py โ
+โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
+โญโโโโโโโโโโโโโโโโโโโโโโโโ Info โโโโโโโโโโโโโโโโโโโโโโโโโฎ
+โ โน Injected metadata into config file โ
+โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
+
+ Creating Project:
+ my-awesome-project
+โโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโ
+โ Component โ Collected โ
+โ fastapi โ โ โ
+โ uvicorn โ โ โ
+โ pydantic โ โ โ
+โ pydantic-settings โ โ โ
+โโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโ
+
+Creating virtual environment...
+
+โญโโโโโโโโโโโโโโโโโโโโโโโโ Info โโโโโโโโโโโโโโโโโโโโโโโโโฎ
+โ โน venv created at โ
+โ ~your-project-path~/my-awesome-project/.venv โ
+โ To activate the virtual environment, run: โ
+โ โ
+โ source โ
+โ ~your-project-path~/my-awesome-project/.venv/bin/act โ
+โ ivate โ
+โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
+
+Installing dependencies...
+โ Setting up project environment...Collecting
+
+---> 100%
+
+โญโโโโโโโโโโโโโโโโโโโโโโโ Success โโโโโโโโโโโโโโโโโโโโโโโโฎ
+โ โจ Dependencies installed successfully โ
+โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
+โญโโโโโโโโโโโโโโโโโโโโโโโ Success โโโโโโโโโโโโโโโโโโโโโโโโฎ
+โ โจ FastAPI project 'my-awesome-project' has been โ
+โ created successfully and saved to โ
+โ ~your-project-path~! โ
+โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
+โญโโโโโโโโโโโโโโโโโโโโโโโโ Info โโโโโโโโโโโโโโโโโโโโโโโโโฎ
+โ โน To start your project, run 'fastkit runserver' at โ
+โ newly created FastAPI project directory โ
+โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
+```
+
+
+
+์ด ๋ช
๋ น์ Python ๊ฐ์ ํ๊ฒฝ๊ณผ ํจ๊ป ์๋ก์ด FastAPI ํ๋ก์ ํธ ์ํฌ์คํ์ด์ค ํ๊ฒฝ์ ์์ฑํฉ๋๋ค.
+
+### ๋ํํ ๋ชจ๋๋ก ํ๋ก์ ํธ ์์ฑ โจ NEW!
+
+๋ณด๋ค ๋ณต์กํ ํ๋ก์ ํธ์ ๊ฒฝ์ฐ, ์ง๋ฅํ ๊ธฐ๋ฅ ์ ํ์ ํตํด ๋ํํ ๋ชจ๋๋ก FastAPI ์ ํ๋ฆฌ์ผ์ด์
์ ๋จ๊ณ๋ณ๋ก ๊ตฌ์ฑํ์ธ์:
+
+
+
+```console
+$ fastkit init --interactive
+
+โก FastAPI-fastkit Interactive Project Setup โก
+
+๐ Basic Project Information
+Enter the project name: my-fullstack-project
+Enter the author name: John Doe
+Enter the author email: john@example.com
+Enter the project description: Full-stack FastAPI project with PostgreSQL and JWT
+
+๐๏ธ Database Selection
+Select database (PostgreSQL, MySQL, MongoDB, Redis, SQLite, None):
+ 1. PostgreSQL - PostgreSQL database with SQLAlchemy
+ 2. MySQL - MySQL database with SQLAlchemy
+ 3. MongoDB - MongoDB with motor async driver
+ 4. Redis - Redis for caching and session storage
+ 5. SQLite - SQLite database for development
+ 6. None - No database
+
+Select database: 1
+
+๐ Authentication Selection
+Select authentication (JWT, OAuth2, FastAPI-Users, Session-based, None):
+ 1. JWT - JSON Web Token authentication
+ 2. OAuth2 - OAuth2 with password flow
+ 3. FastAPI-Users - Full featured user management
+ 4. Session-based - Cookie-based sessions
+ 5. None - No authentication
+
+Select authentication: 1
+
+โ๏ธ Background Tasks Selection
+Select background tasks (Celery, Dramatiq, None):
+ 1. Celery - Distributed task queue
+ 2. Dramatiq - Fast and reliable task processing
+ 3. None - No background tasks
+
+Select background tasks: 1
+
+๐พ Caching Selection
+Select caching (Redis, fastapi-cache2, None):
+ 1. Redis - Redis caching
+ 2. fastapi-cache2 - Simple caching for FastAPI
+ 3. None - No caching
+
+Select caching: 1
+
+๐ Monitoring Selection
+Select monitoring (Loguru, OpenTelemetry, Prometheus, None):
+ 1. Loguru - Simple and powerful logging
+ 2. OpenTelemetry - Observability framework
+ 3. Prometheus - Metrics and monitoring
+ 4. None - No monitoring
+
+Select monitoring: 3
+```
+
+๐งช ํ
์คํธ ํ๋ ์์ํฌ ์ ํ
+ํ
์คํธ ํ๋ ์์ํฌ๋ฅผ ์ ํํ์ธ์ (Basic, Coverage, Advanced, None):
+ 1. Basic - API ํ
์คํธ๋ฅผ ์ํ pytest + httpx
+ 2. Coverage - Basic + ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง
+ 3. Advanced - Coverage + ํฝ์ค์ฒ๋ฅผ ์ํ faker + factory-boy
+ 4. None - ํ
์คํธ ํ๋ ์์ํฌ ์์
+
+ํ
์คํธ ํ๋ ์์ํฌ ์ ํ: 2
+
+๐ ๏ธ ์ถ๊ฐ ์ ํธ๋ฆฌํฐ
+์ ํธ๋ฆฌํฐ๋ฅผ ์ ํํ์ธ์ (์ผํ๋ก ๊ตฌ๋ถ๋ ์ซ์, ์: 1,3,4):
+ 1. CORS - Cross-Origin Resource Sharing
+ 2. Rate-Limiting - ์์ฒญ ์๋ ์ ํ
+ 3. Pagination - ํ์ด์ง๋ค์ด์
์ง์
+ 4. WebSocket - WebSocket ์ง์
+
+์ ํธ๋ฆฌํฐ ์ ํ: 1
+
+๐ ๋ฐฐํฌ ๊ตฌ์ฑ
+๋ฐฐํฌ ์ต์
์ ์ ํํ์ธ์:
+ 1. Docker - Dockerfile ์์ฑ
+ 2. docker-compose - docker-compose.yml ์์ฑ(Docker ํฌํจ)
+ 3. None - ๋ฐฐํฌ ๊ตฌ์ฑ ์์
+
+๋ฐฐํฌ ์ต์
์ ํ: 2
+
+๐ฆ ํจํค์ง ๋งค๋์ ์ ํ
+ํจํค์ง ๋งค๋์ ๋ฅผ ์ ํํ์ธ์ (pip, uv, pdm, poetry): uv
+
+๐ ์ฌ์ฉ์ ์ ์ ํจํค์ง(์ ํ ์ฌํญ)
+์ฌ์ฉ์ ์ ์ ํจํค์ง ์ด๋ฆ์ ์
๋ ฅํ์ธ์(์ผํ๋ก ๊ตฌ๋ถ, ๊ฑด๋๋ฐ๋ ค๋ฉด Enter):
+
+โโโโโโโโโโโโโโโโโ ์ ํ๋ ๊ตฌ์ฑ โโโโโโโโโโโโโโโโโ
+ํ๋ก์ ํธ: my-fullstack-project
+๋ฐ์ดํฐ๋ฒ ์ด์ค: PostgreSQL
+์ธ์ฆ: JWT
+๋ฐฑ๊ทธ๋ผ์ด๋ ์์
: Celery
+์บ์ฑ: Redis
+๋ชจ๋ํฐ๋ง: Prometheus
+ํ
์คํธ: Coverage
+์ ํธ๋ฆฌํฐ: CORS
+๋ฐฐํฌ: Docker, docker-compose
+ํจํค์ง ๋งค๋์ : uv
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+ํ๋ก์ ํธ ์์ฑ์ ์งํํ์๊ฒ ์ต๋๊น? [Y/n]: y
+
+โญโโโโโโโโโโโโโโโโโโโโโโโ ์ฑ๊ณต โโโโโโโโโโโโโโโโโโโโโโโโฎ
+โ โจ ์ ํํ ๊ธฐ๋ฅ์ด ํฌํจ๋ main.py ์์ฑ โ
+โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
+โญโโโโโโโโโโโโโโโโโโโโโโโ ์ฑ๊ณต โโโโโโโโโโโโโโโโโโโโโโโโฎ
+โ โจ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๊ตฌ์ฑ ์์ฑ โ
+โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
+โญโโโโโโโโโโโโโโโโโโโโโโโ ์ฑ๊ณต โโโโโโโโโโโโโโโโโโโโโโโโฎ
+โ โจ ์ธ์ฆ ๊ตฌ์ฑ ์์ฑ โ
+โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
+โญโโโโโโโโโโโโโโโโโโโโโโโ ์ฑ๊ณต โโโโโโโโโโโโโโโโโโโโโโโโฎ
+โ โจ ํ
์คํธ ๊ตฌ์ฑ ์์ฑ โ
+โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
+โญโโโโโโโโโโโโโโโโโโโโโโโ ์ฑ๊ณต โโโโโโโโโโโโโโโโโโโโโโโโฎ
+โ โจ Docker ๋ฐฐํฌ ํ์ผ ์์ฑ โ
+โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
+
+๊ฐ์ ํ๊ฒฝ์ ์์ฑํ๋ ์ค...
+์ข
์์ฑ์ ์ค์นํ๋ ์ค...
+
+----> 100%
+
+โญโโโโโโโโโโโโโโโโโโโโโโโ ์ฑ๊ณต โโโโโโโโโโโโโโโโโโโโโโโโฎ
+โ โจ FastAPI ํ๋ก์ ํธ 'my-fullstack-project'๊ฐ โ
+โ ์์ฑ๋์์ต๋๋ค! โ
+โ โ
+โ ์์ฑ๋ ํ์ผ: โ
+โ โข main.py (์ ํํ ๋ชจ๋ ๊ธฐ๋ฅ ํฌํจ) โ
+โ โข src/config/database.py โ
+โ โข src/config/auth.py โ
+โ โข tests/conftest.py โ
+โ โข Dockerfile โ
+โ โข docker-compose.yml โ
+โ โข pyproject.toml / requirements.txt โ
+โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
+```
+
+
+
+๋ํํ ๋ชจ๋๊ฐ ์ ๊ณตํ๋ ๊ธฐ๋ฅ:
+- ๋ฐ์ดํฐ๋ฒ ์ด์ค, ์ธ์ฆ, ๋ฐฑ๊ทธ๋ผ์ด๋ ์์
, ์บ์ฑ, ๋ชจ๋ํฐ๋ง ๋ฑ ๊ธฐ๋ฅ์ ๋ํ ๊ฐ์ด๋ํ ์ ํ
+- ์ ํํ ๊ธฐ๋ฅ์ ๋ํ ์๋ ์ฝ๋ ์์ฑ(main.py, ๊ตฌ์ฑ ํ์ผ, Docker ํ์ผ)
+- ์๋ pip ํธํ์ฑ์ ์ค๋งํธ ์ข
์์ฑ ๊ด๋ฆฌ
+- ํธํ๋์ง ์๋ ์กฐํฉ์ ๋ฐฉ์งํ๋ ๊ธฐ๋ฅ ๊ฒ์ฆ
+- ์ต๋ ์ ์ฐ์ฑ์ ์ํ Always Empty project๋ฅผ ๊ธฐ๋ณธ ๋ฒ ์ด์ค๋ก ์ ๊ณต
+
+### FastAPI ํ๋ก์ ํธ์ ์ ๋ผ์ฐํธ๋ฅผ ์ถ๊ฐํ๊ธฐ
+
+`FastAPI-fastkit`์ FastAPI ํ๋ก์ ํธ ํ์ฅ์ ์ฝ๊ฒ ๋ง๋ค์ด ์ค๋๋ค.
+
+๋ค์์ผ๋ก FastAPI ํ๋ก์ ํธ์ ์ ๋ผ์ฐํธ ์๋ํฌ์ธํธ๋ฅผ ์ถ๊ฐํ์ธ์:
+
+
+
+```console
+$ fastkit addroute my-awesome-project user
+ ์ ๋ผ์ฐํธ ์ถ๊ฐ ์ค
+โโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+โ ํ๋ก์ ํธ โ my-awesome-project โ
+โ ๋ผ์ฐํธ ์ด๋ฆ โ user โ
+โ ๋์ ๋๋ ํฐ๋ฆฌ โ ~your-project-path~ โ
+โโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+ํ๋ก์ ํธ 'my-awesome-project'์ ๋ผ์ฐํธ 'user'๋ฅผ ์ถ๊ฐํ์๊ฒ ์ต๋๊น? [Y/n]: y
+
+โญโโโโโโโโโโโโโโโโโโโโโโโโ ์ ๋ณด โโโโโโโโโโโโโโโโโโโโโโโโฎ
+โ โน API ๋ผ์ฐํฐ๋ฅผ ํฌํจํ๋๋ก main.py๋ฅผ ์
๋ฐ์ดํธํ์ต๋๋ค โ
+โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
+โญโโโโโโโโโโโโโโโโโโโโโโโ ์ฑ๊ณต โโโโโโโโโโโโโโโโโโโโโโโโฎ
+โ โจ ์ ๋ผ์ฐํธ 'user'๋ฅผ ํ๋ก์ ํธ์ ์ฑ๊ณต์ ์ผ๋ก ์ถ๊ฐํ์ต๋๋ค โ
+โ `my-awesome-project` โ
+โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
+```
+
+
+
+### ๊ตฌ์กฐํ๋ FastAPI ๋ฐ๋ชจ ํ๋ก์ ํธ๋ฅผ ๋ฐ๋ก ๋ฐฐ์นํ๊ธฐ
+
+๊ตฌ์กฐํ๋ FastAPI ๋ฐ๋ชจ ํ๋ก์ ํธ๋ก ์์ํ ์๋ ์์ต๋๋ค.
+
+๋ฐ๋ชจ ํ๋ก์ ํธ๋ ๋ค์ํ ๊ธฐ์ ์คํ์ผ๋ก ๊ตฌ์ฑ๋์ด ์์ผ๋ฉฐ ๊ฐ๋จํ ์์ดํ
CRUD ์๋ํฌ์ธํธ๊ฐ ๊ตฌํ๋์ด ์์ต๋๋ค.
+
+๋ค์ ๋ช
๋ น์ผ๋ก ๊ตฌ์กฐํ๋ FastAPI ๋ฐ๋ชจ ํ๋ก์ ํธ๋ฅผ ์ฆ์ ๋ฐฐ์นํ์ธ์:
+
+
+
+```console
+$ fastkit startdemo
+ํ๋ก์ ํธ ์ด๋ฆ์ ์
๋ ฅํ์ธ์: my-awesome-demo
+์์ฑ์ ์ด๋ฆ์ ์
๋ ฅํ์ธ์: John Doe
+์์ฑ์ ์ด๋ฉ์ผ์ ์
๋ ฅํ์ธ์: john@example.com
+ํ๋ก์ ํธ ์ค๋ช
์ ์
๋ ฅํ์ธ์: My awesome FastAPI demo
+'fastapi-default' ํ
ํ๋ฆฟ์ ์ฌ์ฉํ์ฌ FastAPI ํ๋ก์ ํธ๋ฅผ ๋ฐฐํฌํฉ๋๋ค
+ํ
ํ๋ฆฟ ๊ฒฝ๋ก:
+/~fastapi_fastkit-package-path~/fastapi_project_template/fastapi-default
+
+ ํ๋ก์ ํธ ์ ๋ณด
+โโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโ
+โ ํ๋ก์ ํธ ์ด๋ฆโ my-awesome-demo โ
+โ ์์ฑ์ โ John Doe โ
+โ ์์ฑ์ ์ด๋ฉ์ผโ john@example.com โ
+โ ์ค๋ช
โ My awesome FastAPI demo โ
+โโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+ ํ
ํ๋ฆฟ ์ข
์์ฑ
+โโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโ
+โ ์ข
์์ฑ 1 โ fastapi โ
+โ ์ข
์์ฑ 2 โ uvicorn โ
+โ ์ข
์์ฑ 3 โ pydantic โ
+โ ์ข
์์ฑ 4 โ pydantic-settings โ
+โ ์ข
์์ฑ 5 โ python-dotenv โ
+โโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโ
+
+์ฌ์ฉ ๊ฐ๋ฅํ ํจํค์ง ๋งค๋์ :
+ ํจํค์ง ๋งค๋์
+โโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+โ PIP โ ํ์ค Python ํจํค์ง ๊ด๋ฆฌ์ โ
+โ UV โ ๋น ๋ฅธ Python ํจํค์ง ๊ด๋ฆฌ์ โ
+โ PDM โ ํ๋์ ์ธ Python ์ข
์์ฑ ๊ด๋ฆฌ โ
+โ POETRY โ Python ์ข
์์ฑ ๊ด๋ฆฌ ๋ฐ ํจํค์ง โ
+โโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+ํจํค์ง ๋งค๋์ ๋ฅผ ์ ํํ์ธ์ (pip, uv, pdm, poetry) [uv]: uv
+ํ๋ก์ ํธ ์์ฑ์ ์งํํ์๊ฒ ์ต๋๊น? [y/N]: y
+FastAPI ํ
ํ๋ฆฟ ํ๋ก์ ํธ๊ฐ '~your-project-path~'์ ๋ฐฐํฌ๋ฉ๋๋ค
+
+---> 100%
+
+โญโโโโโโโโโโโโโโโโโโโโโโโ ์ฑ๊ณต โโโโโโโโโโโโโโโโโโโโโโโโฎ
+โ โจ ์ข
์์ฑ์ด ์ฑ๊ณต์ ์ผ๋ก ์ค์น๋์์ต๋๋ค โ
+โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
+โญโโโโโโโโโโโโโโโโโโโโโโโ ์ฑ๊ณต โโโโโโโโโโโโโโโโโโโโโโโโฎ
+โ โจ FastAPI ํ๋ก์ ํธ 'my-awesome-demo'๊ฐ โ
+โ 'fastapi-default'์์ ์์ฑ๋์ด ๋ค์ ์์น์ ์ ์ฅ๋์์ต๋๋ค โ
+โ ~your-project-path~! โ
+โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
+```
+
+
+
+์ฌ์ฉ ๊ฐ๋ฅํ FastAPI ๋ฐ๋ชจ ๋ชฉ๋ก์ ๋ณด๋ ค๋ฉด ๋ค์์ ํ์ธํ์ธ์:
+
+
+
+```console
+$ fastkit list-templates
+ ์ฌ์ฉ ๊ฐ๋ฅํ ํ
ํ๋ฆฟ
+โโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+โ fastapi-custom-response โ ์ปค์คํ
์๋ต ์์คํ
์ ๊ฐ์ถ ๋น๋๊ธฐ โ
+โ โ ์์ดํ
๊ด๋ฆฌ API โ
+โ fastapi-dockerized โ Docker๋ก ์ปจํ
์ด๋ํ๋ FastAPI โ
+โ โ ์์ดํ
๊ด๋ฆฌ API โ
+โ fastapi-empty โ ์ค๋ช
์์ โ
+โ fastapi-async-crud โ ๋น๋๊ธฐ ์์ดํ
๊ด๋ฆฌ API ์๋ฒ โ
+โ fastapi-psql-orm โ PostgreSQL์ ์ฌ์ฉํ๋ Docker๋ก โ
+โ โ ์ปจํ
์ด๋ํ๋ FastAPI ์์ดํ
๊ด๋ฆฌ API โ
+โ fastapi-default โ ๊ฐ๋จํ FastAPI ํ๋ก์ ํธ โ
+โโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+```
+
+
+
+## ๋ฌธ์
+
+ํฌ๊ด์ ์ธ ๊ฐ์ด๋์ ์์ธํ ์ฌ์ฉ๋ฒ์ ๋ฌธ์๋ฅผ ์ฐธ๊ณ ํ์ธ์:
+
+- ๐ [User Guide](user-guide/quick-start.md) - ์์ธํ ์ค์น ๋ฐ ์ฌ์ฉ ๊ฐ์ด๋
+- ๐ฏ [Tutorial](tutorial/getting-started.md) - ์ด๋ณด์๋ฅผ ์ํ ๋จ๊ณ๋ณ ํํ ๋ฆฌ์ผ
+- ๐ [CLI Reference](user-guide/cli-reference.md) - ์์ ํ ๋ช
๋ น์ด ๋ ํผ๋ฐ์ค
+- ๐ [Template Quality Assurance](reference/template-quality-assurance.md) - ์๋ํ๋ ํ
์คํธ์ ํ์ง ๊ธฐ์ค
+
+## ๐ ํ
ํ๋ฆฟ ๊ธฐ๋ฐ ํํ ๋ฆฌ์ผ
+
+์ฌ์ ๊ตฌ์ฑ๋ ํ
ํ๋ฆฟ์ผ๋ก ์ค์ ์ฌ์ฉ ์ฌ๋ก๋ฅผ ํตํด FastAPI ๊ฐ๋ฐ์ ํ์ตํ์ธ์:
+
+### ๐ ์ฝ์ด ํํ ๋ฆฌ์ผ
+
+- [๊ธฐ๋ณธ API ์๋ฒ ๊ตฌ์ถ](tutorial/basic-api-server.md) - `fastapi-default` ํ
ํ๋ฆฟ์ ์ฌ์ฉํด ์ฒซ FastAPI ์๋ฒ ๋ง๋ค๊ธฐ
+- [๋น๋๊ธฐ CRUD API ๊ตฌ์ถ](tutorial/async-crud-api.md) - `fastapi-async-crud` ํ
ํ๋ฆฟ์ผ๋ก ๊ณ ์ฑ๋ฅ ๋น๋๊ธฐ API ๊ฐ๋ฐ
+
+### ๐๏ธ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ฐ ์ธํ๋ผ
+
+- [๋ฐ์ดํฐ๋ฒ ์ด์ค ํตํฉ](tutorial/database-integration.md) - `fastapi-psql-orm` ํ
ํ๋ฆฟ์ผ๋ก PostgreSQL + SQLAlchemy ํ์ฉ
+- [Dockerizing ๋ฐ ๋ฐฐํฌ](tutorial/docker-deployment.md) - `fastapi-dockerized` ํ
ํ๋ฆฟ์ ์ฌ์ฉํด ํ๋ก๋์
๋ฐฐํฌ ํ๊ฒฝ ๊ตฌ์ฑ
+
+### โก ๊ณ ๊ธ ๊ธฐ๋ฅ
+
+- **[์ปค์คํ
์๋ต ์ฒ๋ฆฌ & ๊ณ ๊ธ API ์ค๊ณ](tutorial/custom-response-handling.md)** - `fastapi-custom-response` ํ
ํ๋ฆฟ์ผ๋ก ์ํฐํ๋ผ์ด์ฆ๊ธ API ๊ตฌ์ถ
+- **[MCP์์ ํตํฉ](tutorial/mcp-integration.md)** - `fastapi-mcp` ํ
ํ๋ฆฟ์ ์ฌ์ฉํด AI ๋ชจ๋ธ๊ณผ ํตํฉ๋ API ์๋ฒ ์์ฑ
+
+๊ฐ ํํ ๋ฆฌ์ผ์ ๋ค์์ ์ ๊ณตํฉ๋๋ค:
+- โ
**์ค์ฉ์ ์ธ ์์ ** - ์ค์ ํ๋ก์ ํธ์์ ๋ฐ๋ก ์ฌ์ฉํ ์ ์๋ ์ฝ๋
+- โ
**๋จ๊ณ๋ณ ๊ฐ์ด๋** - ์ด๋ณด์๋ ์ฝ๊ฒ ๋ฐ๋ผ ํ ์ ์๋ ์์ธ ์ค๋ช
+- โ
**๋ชจ๋ฒ ์ฌ๋ก** - ์
๊ณ ํ์ค ํจํด๊ณผ ๋ณด์ ๊ณ ๋ ค ์ฌํญ
+- โ
**ํ์ฅ ๋ฐฉ๋ฒ** - ํ๋ก์ ํธ๋ฅผ ํ ๋จ๊ณ ๋ ๋ฐ์ ์ํค๋ ๊ฐ์ด๋
+
+## ๊ธฐ์ฌ
+
+์ปค๋ฎค๋ํฐ์ ๊ธฐ์ฌ๋ฅผ ํ์ํฉ๋๋ค! FastAPI-fastkit์ Python๊ณผ FastAPI ์
๋ฌธ์๋ฅผ ๋๊ธฐ ์ํด ์ค๊ณ๋์์ผ๋ฉฐ, ์ฌ๋ฌ๋ถ์ ๊ธฐ์ฌ๋ ํฐ ์ํฅ์ ๋ฏธ์น ์ ์์ต๋๋ค.
+
+### ๊ธฐ์ฌํ ์ ์๋ ํญ๋ชฉ
+
+- ๐ **์๋ก์ด FastAPI ํ
ํ๋ฆฟ** - ๋ค์ํ ์ฌ์ฉ ์ฌ๋ก๋ฅผ ์ํ ํ
ํ๋ฆฟ ์ถ๊ฐ
+- ๐ **๋ฒ๊ทธ ์์ ** - ์์ ์ฑ๊ณผ ์ ๋ขฐ์ฑ ๊ฐ์ ์ ๋์
+- ๐ **๋ฌธ์ํ** - ๊ฐ์ด๋, ์์ , ๋ฒ์ญ ๊ฐ์
+- ๐งช **ํ
์คํธ** - ํ
์คํธ ์ปค๋ฒ๋ฆฌ์ง ํ์ฅ ๋ฐ ํตํฉ ํ
์คํธ ์ถ๊ฐ
+- ๐ก **๊ธฐ๋ฅ** - ์๋ก์ด CLI ๊ธฐ๋ฅ ์ ์ ๋ฐ ๊ตฌํ
+
+### ๊ธฐ์ฌ ์์ํ๊ธฐ
+
+FastAPI-fastkit์ ๊ธฐ์ฌ๋ฅผ ์์ํ๋ ค๋ฉด ๋ค์์ ์ข
ํฉ ๊ฐ์ด๋๋ฅผ ์ฐธ๊ณ ํ์ธ์:
+
+- **[๊ฐ๋ฐ ํ๊ฒฝ ์ค์ ](contributing/development-setup.md)** - ๊ฐ๋ฐ ํ๊ฒฝ ์ค์ ์ ๋ํ ์์ ํ ๊ฐ์ด๋
+- **[์ฝ๋ ๊ฐ์ด๋๋ผ์ธ](contributing/code-guidelines.md)** - ์ฝ๋ฉ ํ์ค ๋ฐ ๋ชจ๋ฒ ์ฌ๋ก
+- **[CONTRIBUTING.md](https://github.com/bnbong/FastAPI-fastkit/blob/main/CONTRIBUTING.md)** - ์ข
ํฉ ๊ธฐ์ฌ ๊ฐ์ด๋
+- **[CODE_OF_CONDUCT.md](https://github.com/bnbong/FastAPI-fastkit/blob/main/CODE_OF_CONDUCT.md)** - ํ๋ก์ ํธ ์์น๊ณผ ์ปค๋ฎค๋ํฐ ๊ธฐ์ค
+- **[SECURITY.md](https://github.com/bnbong/FastAPI-fastkit/blob/main/SECURITY.md)** - ๋ณด์ ์ง์นจ ๋ฐ ์ ๊ณ ๋ฐฉ๋ฒ
+
+## FastAPI-fastkit์ ์์
+
+FastAPI-fastkit์ Python๊ณผ FastAPI ์ ๊ท ์ฌ์ฉ์์๊ฒ ๋น ๋ฅด๊ณ ์ฝ๊ฒ ์ฌ์ฉํ ์ ์๋ ์คํํฐ ํคํธ๋ฅผ ์ ๊ณตํ๋ ๊ฒ์ ๋ชฉํ๋ก ํฉ๋๋ค.
+
+์ด ์์ด๋์ด๋ FastAPI ์
๋ฌธ์๊ฐ ์ฒ์๋ถํฐ ํ์ตํ๋๋ก ๋๊ธฐ ์ํ ์ทจ์ง๋ก ์์๋์์ผ๋ฉฐ, [FastAPI 0.111.0 ๋ฒ์ ์
๋ฐ์ดํธ](https://github.com/fastapi/fastapi/releases/tag/0.111.0)์์ ์ถ๊ฐ๋ FastAPI-cli ํจํค์ง์ ํ๋ก๋์
์ ์์์๋ ๋งฅ๋ฝ์ ๊ฐ์ดํฉ๋๋ค.
+
+์ค๋ซ๋์ FastAPI๋ฅผ ์ฌ์ฉํด ์ค๋ฉฐ ์ฌ๋ํด์จ ์ฌ๋์ผ๋ก์, FastAPI ๊ฐ๋ฐ์ [tiangolo](https://github.com/tiangolo)๊ฐ ๋ฐํ [๋ฉ์ง ๋๊ธฐ](https://github.com/fastapi/fastapi/pull/11522#issuecomment-2264639417)๋ฅผ ์คํํ๋ ๋ฐ ๋์์ด ๋ ํ๋ก์ ํธ๋ฅผ ๋ง๋ค๊ณ ์ถ์์ต๋๋ค.
+
+FastAPI-fastkit์ ๋ค์์ ์ ๊ณตํจ์ผ๋ก์จ ์์ ๋จ๊ณ์ ํ๋ก๋์
์ค๋น ์ ํ๋ฆฌ์ผ์ด์
๊ตฌ์ถ ์ฌ์ด์ ๊ฐ๊ทน์ ๋ฉ์๋๋ค:
+
+- **์ฆ๊ฐ์ ์ธ ์์ฐ์ฑ**: ์ด๊ธฐ ์ค์ ์ ๋ณต์ก์ฑ์ ์๋๋ ์ ์๋ ์ ๊ท ์ฌ์ฉ์์๊ฒ ์ฆ์ ์์ฐ์ฑ์ ์ ๊ณต
+- **๋ชจ๋ฒ ์ฌ๋ก**: ๋ชจ๋ ํ
ํ๋ฆฟ์ ๋ชจ๋ฒ ์ฌ๋ก๊ฐ ๋ด์ฅ๋์ด ์ฌ๋ฐ๋ฅธ FastAPI ํจํด ํ์ต์ ๋์
+- **ํ์ฅ ๊ฐ๋ฅํ ๊ธฐ๋ฐ**: ์ด๋ณด์์์ ์ ๋ฌธ๊ฐ๋ก ์ฑ์ฅํจ์ ๋ฐ๋ผ ํจ๊ป ํ์ฅ๋๋ ๊ธฐ๋ฐ
+- **์ปค๋ฎค๋ํฐ ์ฃผ๋ ํ
ํ๋ฆฟ**: ์ค์ FastAPI ์ฌ์ฉ ํจํด์ ๋ฐ์ํ ์ปค๋ฎค๋ํฐ ์ค์ฌ ํ
ํ๋ฆฟ
+
+## ๋ค์ ๋จ๊ณ
+
+FastAPI-fastkit์ ์์ํ ์ค๋น๊ฐ ๋์
จ๋์? ๋ค์ ๋จ๊ณ๋ฅผ ๋ฐ๋ผ ์งํํ์ธ์:
+
+### ๐ ๋น ๋ฅธ ์์
+
+1. **[์ค์น](user-guide/installation.md)**: FastAPI-fastkit ์ค์น
+2. **[ํต ์คํํธ](user-guide/quick-start.md)**: 5๋ถ ๋ง์ ์ฒซ ํ๋ก์ ํธ ๋ง๋ค๊ธฐ
+3. **[์
๋ฌธ ํํ ๋ฆฌ์ผ](tutorial/getting-started.md)**: ๋จ๊ณ๋ณ ์์ธ ํํ ๋ฆฌ์ผ
+
+### ๐ ์ฌํ ํ์ต
+
+- **[ํ๋ก์ ํธ ์์ฑ](user-guide/creating-projects.md)**: ๋ค์ํ ์คํ์ผ๋ก ํ๋ก์ ํธ ์์ฑ
+- **[๋ผ์ฐํธ ์ถ๊ฐ](user-guide/adding-routes.md)**: ํ๋ก์ ํธ์ API ์๋ํฌ์ธํธ ์ถ๊ฐ
+- **[ํ
ํ๋ฆฟ ์ฌ์ฉ](user-guide/using-templates.md)**: ์ฌ์ ๊ตฌ์ถ๋ ํ๋ก์ ํธ ํ
ํ๋ฆฟ ์ฌ์ฉ
+
+### ๐ ๏ธ ๊ธฐ์ฌํ๊ธฐ
+
+FastAPI-fastkit์ ๊ธฐ์ฌํ๊ณ ์ถ์ผ์ ๊ฐ์?
+
+- **[๊ฐ๋ฐ ํ๊ฒฝ ์ค์ ](contributing/development-setup.md)**: ๊ฐ๋ฐ ํ๊ฒฝ ์ค์
+- **[์ฝ๋ ๊ฐ์ด๋๋ผ์ธ](contributing/code-guidelines.md)**: ์ฝ๋ฉ ํ์ค ๋ฐ ๋ชจ๋ฒ ์ฌ๋ก ์ค์
+- **[๊ธฐ์ฌ ๊ฐ์ด๋๋ผ์ธ](https://github.com/bnbong/FastAPI-fastkit/blob/main/CONTRIBUTING.md)**: ์ข
ํฉ ๊ธฐ์ฌ ๊ฐ์ด๋
+
+### ๐ ์ฐธ๊ณ ์๋ฃ
+
+- **[CLI ๋ ํผ๋ฐ์ค](user-guide/cli-reference.md)**: ์ ์ฒด CLI ๋ช
๋ น ๋ ํผ๋ฐ์ค
+- **[ํ
ํ๋ฆฟ ํ์ง ๋ณด์ฆ](reference/template-quality-assurance.md)**: ์๋ํ๋ ํ
์คํธ ๋ฐ ํ์ง ๊ธฐ์ค
+- **[FAQ](reference/faq.md)**: ์์ฃผ ๋ฌป๋ ์ง๋ฌธ
+- **[GitHub ์ ์ฅ์](https://github.com/bnbong/FastAPI-fastkit)**: ์์ค ์ฝ๋ ๋ฐ ์ด์ ํธ๋ํน
+
+## ๋ผ์ด์ ์ค
+
+์ด ํ๋ก์ ํธ๋ MIT ๋ผ์ด์ ์ค ํ์ ์ ๊ณต๋ฉ๋๋ค - ์์ธํ ๋ด์ฉ์ [LICENSE](https://github.com/bnbong/FastAPI-fastkit/blob/main/LICENSE) ํ์ผ์ ์ฐธ์กฐํ์ธ์.
diff --git a/scripts/translate.py b/scripts/translate.py
index 1794393..0ef7c6f 100755
--- a/scripts/translate.py
+++ b/scripts/translate.py
@@ -13,11 +13,11 @@
# @author bnbong bbbong9@gmail.com
# --------------------------------------------------------------------------
import argparse
-import json
import logging
import os
import subprocess
import sys
+import time
from dataclasses import dataclass, field
from pathlib import Path
from typing import List, Optional, Tuple
@@ -39,10 +39,16 @@ class TranslationConfig:
default_factory=lambda: ["ko", "ja", "zh", "es", "fr", "de"]
)
docs_dir: Path = Path(__file__).parent.parent / "docs" / source_lang
- api_provider: str = "openai" # openai, anthropic, etc.
+ api_provider: str = "openai" # openai, github, etc.
api_key: Optional[str] = None
+ api_base_url: Optional[str] = None # Custom API endpoint (e.g., GitHub Models)
+ model_name: str = "gpt-4o-mini" # Default model name
create_pr: bool = True
branch_prefix: str = "translation"
+ # Rate limit handling settings
+ max_retries: int = 5 # Maximum number of retries on rate limit
+ retry_delay: float = 60.0 # Initial delay between retries (seconds)
+ request_delay: float = 2.0 # Delay between consecutive API requests (seconds)
class TranslationAPI:
@@ -94,40 +100,239 @@ def get_translation_prompt(
class OpenAITranslator(TranslationAPI):
- """OpenAI API translator."""
-
- def translate(self, text: str, target_lang: str, source_lang: str = "en") -> str:
- """Translate using OpenAI API."""
- try:
+ """OpenAI API translator (compatible with GitHub Models API)."""
+
+ # Approximate token limits for different models (conservative estimates)
+ MODEL_TOKEN_LIMITS = {
+ "gpt-5": 3000, # GitHub Models gpt-5 has ~4000 limit, use 3000 for safety
+ "openai/gpt-5": 3000,
+ "gpt-4o": 12000,
+ "openai/gpt-4o": 12000,
+ "gpt-4o-mini": 12000,
+ "openai/gpt-4o-mini": 12000,
+ }
+ DEFAULT_TOKEN_LIMIT = 3000 # Conservative default
+
+ def __init__(
+ self,
+ api_key: str,
+ base_url: Optional[str] = None,
+ model_name: str = "openai/gpt-4o-mini",
+ max_retries: int = 5,
+ retry_delay: float = 60.0,
+ request_delay: float = 2.0,
+ max_chunk_chars: Optional[int] = None,
+ ):
+ super().__init__(api_key)
+ self.base_url = base_url
+ self.model_name = model_name
+ self.max_retries = max_retries
+ self.retry_delay = retry_delay
+ self.request_delay = request_delay
+ self._client = None
+ # Estimate max chars based on token limit (roughly 4 chars per token for English)
+ token_limit = self.MODEL_TOKEN_LIMITS.get(model_name, self.DEFAULT_TOKEN_LIMIT)
+ self.max_chunk_chars = max_chunk_chars or (
+ token_limit * 3
+ ) # Conservative: 3 chars per token
+
+ def _get_client(self):
+ """Lazy initialization of OpenAI client."""
+ if self._client is None:
import openai
- client = openai.OpenAI(api_key=self.api_key)
+ client_kwargs = {"api_key": self.api_key}
+ if self.base_url:
+ client_kwargs["base_url"] = self.base_url
+
+ self._client = openai.OpenAI(**client_kwargs)
+ return self._client
+
+ def _is_rate_limit_error(self, error: Exception) -> bool:
+ """Check if the error is a rate limit error."""
+ error_str = str(error).lower()
+ return any(
+ keyword in error_str
+ for keyword in [
+ "rate limit",
+ "ratelimit",
+ "429",
+ "too many requests",
+ "quota",
+ ]
+ )
+
+ def _is_payload_too_large_error(self, error: Exception) -> bool:
+ """Check if the error is a payload too large error."""
+ error_str = str(error).lower()
+ return any(
+ keyword in error_str
+ for keyword in [
+ "413",
+ "payload too large",
+ "tokens_limit_reached",
+ "too large",
+ ]
+ )
+
+ def _split_text_into_chunks(self, text: str) -> List[str]:
+ """Split text into chunks that fit within token limits.
+
+ Tries to split at natural boundaries (paragraphs, headers) to maintain context.
+ """
+ if len(text) <= self.max_chunk_chars:
+ return [text]
+
+ chunks = []
+ current_chunk = ""
+
+ # Split by double newlines (paragraphs) first
+ paragraphs = text.split("\n\n")
+
+ for para in paragraphs:
+ # If adding this paragraph would exceed the limit
+ if len(current_chunk) + len(para) + 2 > self.max_chunk_chars:
+ if current_chunk:
+ chunks.append(current_chunk.strip())
+ current_chunk = ""
+
+ # If a single paragraph is too large, split by lines
+ if len(para) > self.max_chunk_chars:
+ lines = para.split("\n")
+ for line in lines:
+ if len(current_chunk) + len(line) + 1 > self.max_chunk_chars:
+ if current_chunk:
+ chunks.append(current_chunk.strip())
+ current_chunk = ""
+
+ # If a single line is still too large, force split
+ if len(line) > self.max_chunk_chars:
+ for i in range(0, len(line), self.max_chunk_chars):
+ chunks.append(line[i : i + self.max_chunk_chars])
+ else:
+ current_chunk = line
+ else:
+ current_chunk += ("\n" if current_chunk else "") + line
+ else:
+ current_chunk = para
+ else:
+ current_chunk += ("\n\n" if current_chunk else "") + para
+
+ if current_chunk:
+ chunks.append(current_chunk.strip())
+
+ return chunks
+
+ def _translate_single_chunk(
+ self,
+ text: str,
+ target_lang: str,
+ source_lang: str,
+ is_continuation: bool = False,
+ ) -> str:
+ """Translate a single chunk of text."""
+ client = self._get_client()
+
+ if is_continuation:
+ # Simplified prompt for continuation chunks
+ prompt = f"""Continue translating the following text to {target_lang}.
+Maintain the same style and formatting as before.
+IMPORTANT: Preserve all markdown formatting, code blocks, and technical terms.
+
+Text:
+{text}
+
+Provide only the translated text without any additional commentary."""
+ else:
prompt = self.get_translation_prompt(text, target_lang, source_lang)
- response = client.chat.completions.create(
- model="gpt-5-nano",
- messages=[
- {
- "role": "system",
- "content": "You are a professional technical documentation translator.",
- },
- {"role": "user", "content": prompt},
- ],
- temperature=0.3,
- max_tokens=4096,
- )
+ last_error = None
+ for attempt in range(self.max_retries + 1):
+ try:
+ if attempt > 0:
+ wait_time = self.retry_delay * (2 ** (attempt - 1))
+ logger.warning(
+ f"Rate limit hit. Waiting {wait_time:.1f}s before retry {attempt}/{self.max_retries}..."
+ )
+ time.sleep(wait_time)
+
+ # Build API call parameters - GPT-5 doesn't support temperature
+ api_params = {
+ "model": self.model_name,
+ "messages": [
+ {
+ "role": "system",
+ "content": "You are a professional technical documentation translator.",
+ },
+ {"role": "user", "content": prompt},
+ ],
+ }
+
+ # Only add temperature for models that support it
+ if "gpt-5" not in self.model_name.lower():
+ api_params["temperature"] = 0.3
+
+ response = client.chat.completions.create(**api_params)
+
+ response_content = response.choices[0].message.content
+ if response_content is None:
+ return text
+
+ time.sleep(self.request_delay)
+ return response_content.strip()
- response_content = response.choices[0].message.content
- if response_content is None:
- return text
- return response_content.strip()
+ except Exception as e:
+ last_error = e
+ if self._is_rate_limit_error(e):
+ if attempt < self.max_retries:
+ continue
+ else:
+ logger.error(
+ f"Max retries ({self.max_retries}) exceeded due to rate limiting."
+ )
+ raise
+ else:
+ raise
+
+ if last_error:
+ raise last_error
+ return text
+
+ def translate(self, text: str, target_lang: str, source_lang: str = "en") -> str:
+ """Translate text, automatically chunking if necessary."""
+ try:
+ import openai
except ImportError:
logger.error("OpenAI package not installed. Run: pip install openai")
raise
+
+ # Try direct translation first
+ try:
+ return self._translate_single_chunk(text, target_lang, source_lang)
except Exception as e:
- logger.error(f"Translation failed: {e}")
- raise
+ if not self._is_payload_too_large_error(e):
+ logger.error(f"Translation failed: {e}")
+ raise
+
+ # If payload too large, split into chunks and translate each
+ logger.info(f"Document too large, splitting into chunks for translation...")
+ chunks = self._split_text_into_chunks(text)
+ logger.info(f"Split into {len(chunks)} chunks")
+
+ translated_chunks = []
+ for i, chunk in enumerate(chunks):
+ logger.info(f"Translating chunk {i + 1}/{len(chunks)}...")
+ try:
+ translated = self._translate_single_chunk(
+ chunk, target_lang, source_lang, is_continuation=(i > 0)
+ )
+ translated_chunks.append(translated)
+ except Exception as chunk_error:
+ logger.error(f"Failed to translate chunk {i + 1}: {chunk_error}")
+ raise
+
+ return "\n\n".join(translated_chunks)
class DocumentTranslator:
@@ -144,8 +349,20 @@ def _initialize_translator(self) -> TranslationAPI:
"API key is required. Set via --api-key or environment variable."
)
- if self.config.api_provider == "openai":
- return OpenAITranslator(self.config.api_key)
+ if self.config.api_provider in ("openai", "github"):
+ # For GitHub Models, set default base_url if not provided
+ base_url = self.config.api_base_url
+ if self.config.api_provider == "github" and not base_url:
+ base_url = "https://models.github.ai/inference"
+
+ return OpenAITranslator(
+ api_key=self.config.api_key,
+ base_url=base_url,
+ model_name=self.config.model_name,
+ max_retries=self.config.max_retries,
+ retry_delay=self.config.retry_delay,
+ request_delay=self.config.request_delay,
+ )
else:
raise ValueError(f"Unsupported API provider: {self.config.api_provider}")
@@ -283,14 +500,25 @@ def main() -> None:
parser.add_argument(
"--api-provider",
type=str,
- choices=["openai"],
+ choices=["openai", "github"],
default="openai",
- help="AI API provider for translation",
+ help="AI API provider for translation (openai or github)",
)
parser.add_argument(
"--api-key",
type=str,
- help="API key for translation service. Can also be set via TRANSLATION_API_KEY environment variable.",
+ help="API key for translation service. Can also be set via TRANSLATION_API_KEY (OpenAI) or GITHUB_TOKEN (GitHub) environment variable.",
+ )
+ parser.add_argument(
+ "--api-base-url",
+ type=str,
+ help="Custom API base URL. Defaults to https://models.github.ai/inference for github provider.",
+ )
+ parser.add_argument(
+ "--model",
+ type=str,
+ default="gpt-4o-mini",
+ help="Model name to use for translation (default: gpt-4o-mini). For GitHub, use format like 'openai/gpt-4o-mini'.",
)
parser.add_argument(
"--no-pr", action="store_true", help="Skip creating GitHub Pull Request"
@@ -302,20 +530,51 @@ def main() -> None:
help="Specific files to translate (relative to docs directory)",
)
parser.add_argument("--docs-dir", type=str, help="Path to documentation directory")
+ # Rate limit handling options
+ parser.add_argument(
+ "--max-retries",
+ type=int,
+ default=5,
+ help="Maximum number of retries on rate limit errors (default: 5)",
+ )
+ parser.add_argument(
+ "--retry-delay",
+ type=float,
+ default=60.0,
+ help="Initial delay in seconds between retries (uses exponential backoff, default: 60.0)",
+ )
+ parser.add_argument(
+ "--request-delay",
+ type=float,
+ default=2.0,
+ help="Delay in seconds between consecutive API requests to avoid rate limits (default: 2.0)",
+ )
args = parser.parse_args()
- # Get API key from args or environment
+ # Get API key from args or environment (support both TRANSLATION_API_KEY and GITHUB_TOKEN)
api_key = args.api_key or os.getenv("TRANSLATION_API_KEY")
+ if not api_key and args.api_provider == "github":
+ api_key = os.getenv("GITHUB_TOKEN")
if not api_key:
+ env_var = (
+ "GITHUB_TOKEN" if args.api_provider == "github" else "TRANSLATION_API_KEY"
+ )
logger.error(
- "API key is required. Provide via --api-key or TRANSLATION_API_KEY environment variable."
+ f"API key is required. Provide via --api-key or {env_var} environment variable."
)
sys.exit(1)
# Setup configuration
config = TranslationConfig(
- api_provider=args.api_provider, api_key=api_key, create_pr=not args.no_pr
+ api_provider=args.api_provider,
+ api_key=api_key,
+ api_base_url=args.api_base_url,
+ model_name=args.model,
+ max_retries=args.max_retries,
+ retry_delay=args.retry_delay,
+ request_delay=args.request_delay,
+ create_pr=not args.no_pr,
)
if args.docs_dir: