Google Sheets, Airtable, Notion, CSV, XLSX 등 다양한 데이터 소스에서 에러 콘텐츠를 가져와 JSON 파일로 변환하는 CLI 도구입니다.
설치
pnpm add -D @sanghyuk-2i/huh-clinpm install -D @sanghyuk-2i/huh-cliyarn add -D @sanghyuk-2i/huh-cli설치 후 huh 명령어를 사용할 수 있습니다.
명령어
huh init
현재 디렉토리에 .huh.config.ts 설정 파일 템플릿을 생성합니다.
npx huh init생성되는 파일에는 Google Sheets, Airtable, Notion, CSV, XLSX 5가지 데이터 소스의 설정 예시가 포함되어 있습니다. 사용할 소스의 주석을 해제하세요.
TIP
이미 설정 파일이 있으면 덮어쓰지 않고 경고를 출력합니다.
huh pull
설정된 데이터 소스에서 데이터를 가져와 JSON 파일을 생성합니다.
npx huh pulli18n 설정이 있으면 로케일별 파일(ko.json, en.json, index.ts)이 output 디렉토리에 생성됩니다. 자세한 내용은 i18n 다국어 지원 가이드를 참고하세요.
실행 흐름:
설정 파일 읽기
.huh.config.json 파일을 읽어 데이터 소스 및 출력 경로를 확인합니다.
데이터 fetch
Adapter 패턴으로 설정된 데이터 소스에서 데이터를 가져옵니다. i18n 모드에서는 각 로케일별로 데이터를 가져옵니다.
JSON DSL 변환parseSheetData로 가져온 데이터를 JSON DSL 형식으로 변환합니다.
유효성 검증
validateConfig로 각 로케일별 검증을 수행합니다. i18n 모드에서는 추가로 validateLocales로 cross-locale 검증을 수행합니다. - 경고만 있으면 출력 후 계속 진행 - 에러가 있으면 에러 출력 후 종료 (exit code 1)
JSON 파일 생성
지정된 output 경로에 JSON 파일을 생성합니다. i18n 모드에서는 로케일별 JSON 파일과 index.ts barrel 파일을 생성합니다.
설정 파일
현재 v0.1에서는 .huh.config.json 형식을 지원합니다.
Google Sheets
{
"source": {
"type": "google-sheets",
"sheetId": "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms",
"range": "Sheet1"
},
"output": "./src/huh.json"
}인증:
| 방법 | 설정 |
|---|---|
| API Key (환경변수) | GOOGLE_API_KEY 환경변수 설정 |
| API Key (설정파일) | source.apiKey에 직접 지정 |
| Service Account | source.credentials에 JSON 키 파일 경로 지정 |
GOOGLE_API_KEY=AIza... npx huh pull자세한 내용: Google Sheets 설정 가이드
Airtable
{
"source": {
"type": "airtable",
"baseId": "appXXXXXXXXXXXXXX",
"tableId": "tblYYYYYYYYYYYYYY"
},
"output": "./src/huh.json"
}인증: AIRTABLE_TOKEN 환경변수 또는 source.token에 직접 지정
자세한 내용: Airtable 연동 가이드
Notion
{
"source": {
"type": "notion",
"databaseId": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
},
"output": "./src/huh.json"
}인증: NOTION_TOKEN 환경변수 또는 source.token에 직접 지정
자세한 내용: Notion 연동 가이드
CSV
{
"source": {
"type": "csv",
"filePath": "./errors.csv"
},
"output": "./src/huh.json"
}인증 불필요. RFC 4180 호환 CSV 파서를 사용합니다.
자세한 내용: CSV 파일 가이드
XLSX
{
"source": {
"type": "xlsx",
"filePath": "./errors.xlsx",
"sheet": "Sheet1"
},
"output": "./src/huh.json"
}인증 불필요. sheet를 지정하지 않으면 첫 번째 시트를 사용합니다.
자세한 내용: XLSX 파일 가이드
huh validate [file]
생성된 JSON 파일의 유효성을 검증합니다.
# 기본 경로 (src/huh.json) 검증
npx huh validate
# 특정 파일 검증
npx huh validate ./path/to/errors.json출력 예시 (성공):
Validating ./src/huh.json...
Found 5 error entries.
Validation passed!출력 예시 (경고 + 에러):
Validating ./src/huh.json...
Found 3 error entries.
2 warning(s):
- [ERR_TOAST] title: Toast errors typically do not display a title
- [ERR_PAGE] action: Page errors should provide an action for user navigation
1 error(s):
- [ERR_REDIRECT] action.target: Action type "REDIRECT" requires a target URL
Validation failed.에러가 1개라도 있으면 exit code 1로 종료합니다. 경고만 있으면 통과입니다.
CI/CD 통합
name: Huh Error Content Sync
on:
schedule:
- cron: '0 9 * * 1-5' # 평일 오전 9시
workflow_dispatch:
jobs:
sync:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
with:
version: 9
- uses: actions/setup-node@v4
with:
node-version: 20
cache: pnpm
- run: pnpm install
- name: Pull error content
run: npx huh pull
env:
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
# Airtable 사용 시: AIRTABLE_TOKEN: ${{ secrets.AIRTABLE_TOKEN }}
# Notion 사용 시: NOTION_TOKEN: ${{ secrets.NOTION_TOKEN }}
- name: Validate
run: npx huh validate
- name: Commit changes
run: |
git config user.name "github-actions"
git config user.email "github-actions@github.com"
git add src/huh.json
git diff --staged --quiet || git commit -m "chore: sync error content"
git push# .husky/pre-commit
npx huh validateWARNING
API 토큰(GOOGLE_API_KEY, AIRTABLE_TOKEN, NOTION_TOKEN)은 반드시 환경변수나 CI/CD 시크릿으로 관리하세요. 설정 파일에 토큰을 직접 넣는 경우 .gitignore에 해당 파일을 추가하여 저장소에 커밋되지 않도록 해야 합니다.
defineConfig
타입 안전한 설정을 위한 헬퍼 함수입니다.
import { defineConfig } from '@sanghyuk-2i/huh-cli';
import type { HuhCliConfig } from '@sanghyuk-2i/huh-cli';interface HuhCliConfig {
source: HuhSource;
output: string; // JSON 파일 출력 경로 (i18n 모드에서는 디렉토리 경로)
i18n?: I18nConfig; // 다국어 설정 (선택)
}
interface I18nConfig {
defaultLocale: string;
locales: Record<string, LocaleSourceOverride>;
}
interface LocaleSourceOverride {
sheet?: string; // XLSX 시트 이름
range?: string; // Google Sheets 탭 이름
filePath?: string; // CSV 파일 경로
tableId?: string; // Airtable 테이블 ID
databaseId?: string; // Notion 데이터베이스 ID
}
// Google Sheets
type GoogleSheetsSource = {
type: 'google-sheets';
sheetId: string;
range?: string; // 기본값: 'Sheet1'
apiKey?: string;
credentials?: string; // 서비스 계정 JSON 키 파일 경로
};
// Airtable
type AirtableSource = {
type: 'airtable';
baseId: string;
tableId: string;
token?: string;
};
// Notion
type NotionSource = {
type: 'notion';
databaseId: string;
token?: string;
};
// CSV (로컬 파일)
type CsvSource = {
type: 'csv';
filePath: string;
};
// XLSX (로컬 파일)
type XlsxSource = {
type: 'xlsx';
filePath: string;
sheet?: string; // 미지정 시 첫 번째 시트
};
type HuhSource = GoogleSheetsSource | AirtableSource | NotionSource | CsvSource | XlsxSource;