Skip to content

전체 흐름

데이터 소스 (비개발자가 에러 메시지 작성)

    ├─ Google Sheets (탭 = 언어별 분리 가능)
    ├─ Airtable
    ├─ Notion
    ├─ CSV (로컬 파일, 언어별 파일 분리 가능)
    └─ XLSX (로컬 파일, 시트 = 언어별 분리 가능)


@sanghyuk-2i/huh-cli  ─── pull 명령어

    ├─ Adapter 패턴으로 데이터 소스별 fetch
    ├─ @sanghyuk-2i/huh-core의 parseSheetData로 파싱
    ├─ @sanghyuk-2i/huh-core의 validateConfig로 검증
    ├─ (i18n) @sanghyuk-2i/huh-core의 validateLocales로 cross-locale 검증


huh.json  또는  huh/ 디렉토리 (빌드 타임 산출물)
    │              ├─ ko.json, en.json (로케일별)
    │              └─ index.ts (자동 생성 barrel)

@sanghyuk-2i/huh-react, @sanghyuk-2i/huh-vue, @sanghyuk-2i/huh-svelte  ─── HuhProvider

    ├─ source prop으로 JSON 데이터 주입 (단일 언어)
    ├─ locales prop으로 다국어 데이터 주입 (i18n)
    ├─ huh(code, variables)로 에러 트리거 (trackId 직접 또는 errorMap 경유)
    ├─ @sanghyuk-2i/huh-core의 resolveError로 조회 + 변수 치환


사용자 제공 커스텀 렌더러  ─── TOAST / MODAL / PAGE / 커스텀 타입

패키지 의존 관계

@sanghyuk-2i/huh-react          ──depends──▶  @sanghyuk-2i/huh-core
@sanghyuk-2i/huh-vue            ──depends──▶  @sanghyuk-2i/huh-core
@sanghyuk-2i/huh-svelte         ──depends──▶  @sanghyuk-2i/huh-core
@sanghyuk-2i/huh-cli            ──depends──▶  @sanghyuk-2i/huh-core
@sanghyuk-2i/huh-plugin-sentry   ──depends──▶  @sanghyuk-2i/huh-core  (+ @sentry/browser peer dep)
@sanghyuk-2i/huh-plugin-datadog  ──depends──▶  @sanghyuk-2i/huh-core  (+ @datadog/browser-logs peer dep)

@sanghyuk-2i/huh-core    ──  (zero dependencies)
  • core는 외부 의존성이 없어 어디서든 사용 가능합니다.
  • react는 core에 의존하며, react/react-dom은 peer dependency입니다.
  • vue는 core에 의존하며, vue >= 3.3은 peer dependency입니다.
  • svelte는 core에 의존하며, svelte >= 5.0은 peer dependency입니다.
  • cli는 core에 의존하며, commander/googleapis/picocolors/xlsx를 사용합니다.
  • plugin-sentry는 core에 의존하며, @sentry/browser는 peer dependency입니다.
  • plugin-datadog는 core에 의존하며, @datadog/browser-logs는 peer dependency입니다.

패키지별 역할

@sanghyuk-2i/huh-core

모듈역할
schema.ts모든 타입 정의 (ErrorConfig, ErrorEntry, ERROR_TYPES, ACTION_TYPES, HuhPlugin 등)
plugin.ts플러그인 실행 유틸리티 (runPluginHook)
parser.ts시트 raw 데이터(2D 문자열 배열) → ErrorConfig 변환
template.ts 플레이스홀더 치환 유틸리티
resolver.tstrackId로 에러 조회 + 변수 치환 적용
validator.tsErrorConfig 유효성 검증 (errors + warnings)
locale-validator.ts다국어 cross-locale 검증 (trackId 일관성, type/actionType 불일치)

@sanghyuk-2i/huh-react

모듈역할
ErrorContentProvider.tsxContext Provider, 에러 상태 관리, 렌더러 호출
useErrorContent.tshuh/clearError 훅
types.tsErrorRenderProps, RendererMap 타입 정의

@sanghyuk-2i/huh-vue

모듈역할
ErrorContentProvider.tsprovide/inject 기반 Provider, 에러 상태 관리, render function으로 렌더러 호출
useErrorContent.tsinject 기반 useHuh composable
types.tsErrorRenderProps, RendererMap (Vue Component) 타입 정의

@sanghyuk-2i/huh-svelte

모듈역할
HuhProvider.sveltesetContext 기반 Provider, $state로 에러 상태 관리, 동적 컴포넌트 렌더링
useErrorContent.tsgetContext 기반 useHuh
context.tsSymbol injection key 분리
types.tsErrorRenderProps, RendererMap (Svelte Component) 타입 정의

@sanghyuk-2i/huh-cli

모듈역할
commands/init.ts설정 파일 템플릿 생성, 소스 타입 정의
commands/pull.tsAdapter를 통해 데이터 fetch → parse → validate → JSON 파일 생성
commands/validate.tsJSON 파일 유효성 검증
adapters/types.tsSourceAdapter 인터페이스 정의
adapters/registry.tsAdapter 등록/조회 레지스트리
adapters/google-sheets.tsGoogle Sheets API v4 adapter
adapters/airtable.tsAirtable REST API adapter
adapters/notion.tsNotion API adapter
adapters/csv.ts로컬 CSV 파일 adapter (RFC 4180 호환 파서)
adapters/xlsx.ts로컬 XLSX 파일 adapter (SheetJS)
adapters/index.tsBarrel — import 시 모든 adapter 자동 등록
fetch-sheet.tsGoogle Sheets API v4 데이터 fetch (adapter에서 사용)
fetch-airtable.tsRe-export shim → adapters/airtable
fetch-notion.tsRe-export shim → adapters/notion
generate.tsErrorConfig → JSON 파일 쓰기, i18n 모드에서 로케일별 파일 + index.ts barrel 생성

JSON DSL 스키마

ErrorConfig (Record<trackId, ErrorEntry>)

├── trackId: string (고유 식별자)

└── ErrorEntry
    ├── type: ErrorType (대문자. 기본: 'TOAST' | 'MODAL' | 'PAGE' + 커스텀 타입)
    ├── message: string ({{변수}} 템플릿 지원)
    ├── title?: string
    ├── image?: string
    ├── severity?: Severity (대문자. 기본: 'INFO' | 'WARNING' | 'ERROR' | 'CRITICAL' + 커스텀 심각도)
    └── action?: ErrorAction
        ├── label: string
        ├── type: ActionType (대문자. 기본: 'REDIRECT' | 'RETRY' | 'BACK' | 'DISMISS' + 커스텀 액션)
        └── target?: string (REDIRECT 시 필수)

타입 확장성

ErrorTypeActionType은 열린(open-ended) 문자열 타입입니다:

  • 기본 제공 타입: ERROR_TYPES (TOAST, MODAL, PAGE), ACTION_TYPES (REDIRECT, RETRY, BACK, DISMISS), SEVERITY_LEVELS (INFO, WARNING, ERROR, CRITICAL)
  • 데이터 소스에서 커스텀 타입(예: BANNER, SNACKBAR, OPEN_CHAT)을 자유롭게 추가 가능
  • 파서가 자동으로 대문자로 변환 (소문자 입력 허용)
  • 프레임워크별 RendererMap에 해당 타입의 렌더러를 등록하면 자동으로 동작
  • 커스텀 액션 타입은 HuhProvideronCustomAction 콜백으로 처리

렌더링 전략

Provider는 기본 렌더러를 제공하지 않습니다. 이는 의도적인 설계입니다:

  • 각 프로젝트의 디자인 시스템이 다르기 때문에 기본 UI를 제공해도 커스터마이징이 필요합니다.
  • 사용자가 RendererMap을 통해 사용하는 에러 타입별 렌더러를 직접 구현합니다. 기본 제공 타입(TOAST, MODAL, PAGE) 외에도 커스텀 타입에 대한 렌더러를 추가할 수 있습니다.
  • 렌더러에는 ErrorRenderProps가 전달되며, onActiononDismiss 콜백이 자동 생성됩니다.

액션 처리 흐름

사용자가 액션 버튼 클릭 → onAction() 호출

    ├── REDIRECT → router 제공 시 router.push(target), 미제공 시 window.location.href = target
    ├── BACK     → router 제공 시 router.back(), 미제공 시 window.history.back()
    ├── RETRY    → clearError() + onRetry 콜백
    ├── DISMISS  → clearError()
    └── 커스텀    → clearError() + onCustomAction 콜백

플러그인 시스템

플러그인은 에러 모니터링, 분석, 커스텀 연동을 위한 확장 포인트를 제공합니다. 각 프레임워크 Provider는 plugins prop을 지원합니다.

플러그인 라이프사이클

huh(code, variables)

    ├── resolveError → 활성 에러 설정
    ├── runPluginHook(plugins, 'onError', resolved, context)  ◀── 플러그인 훅


사용자가 액션 버튼 클릭 → onAction()

    ├── runPluginHook(plugins, 'onAction', error, action)     ◀── 플러그인 훅
    └── 액션 실행 (REDIRECT / RETRY / DISMISS / 등)

에러 격리

플러그인 에러는 catch 후 경고로 로깅됩니다. 실패한 플러그인이 에러 렌더링 흐름을 깨뜨리지 않습니다.

공식 플러그인

패키지설명
@sanghyuk-2i/huh-plugin-sentrySentry 연동 — 에러 시 captureMessage, 액션 시 addBreadcrumb
@sanghyuk-2i/huh-plugin-datadogDatadog 연동 — 에러 시 datadogLogs.logger, 액션 시 info 레벨 로깅

빌드 설정

모노레포는 다음 도구로 관리됩니다:

도구역할
pnpm workspace패키지 매니저 + 워크스페이스
turborepo빌드/테스트 파이프라인 (캐싱, 의존 순서)
tsupTypeScript 번들링 (CJS + ESM + IIFE + d.ts)
vitest테스트 러너

빌드 출력물

파일포맷용도
dist/index.jsCJSrequire()
dist/index.mjsESMimport
dist/index.global.jsIIFE (minified)CDN, <script>
dist/index.d.tsTypeScript 선언타입 지원

빌드 순서

Turborepo가 의존 관계를 분석하여 자동으로 순서를 결정합니다:

1. @sanghyuk-2i/huh-core (의존성 없음, 먼저 빌드)
2. @sanghyuk-2i/huh-react + @sanghyuk-2i/huh-vue + @sanghyuk-2i/huh-svelte + @sanghyuk-2i/huh-cli (core 빌드 후 병렬 빌드)

디렉토리 구조

/
├── package.json               # 루트 (모노레포 스크립트)
├── pnpm-workspace.yaml
├── turbo.json
├── tsconfig.base.json
├── vitest.workspace.ts
├── docs/                      # 문서
│   ├── getting-started.md
│   ├── google-sheet-guide.md
│   ├── airtable-guide.md
│   ├── notion-guide.md
│   ├── api-core.md
│   ├── api-react.md
│   ├── api-cli.md
│   └── architecture.md
└── packages/
    ├── core/
    │   ├── package.json
    │   ├── tsconfig.json
    │   ├── tsup.config.ts
    │   └── src/
    │       ├── index.ts
    │       ├── schema.ts
    │       ├── parser.ts
    │       ├── resolver.ts
    │       ├── template.ts
    │       ├── validator.ts
    │       ├── locale-validator.ts
    │       └── __tests__/
    ├── react/
    │   ├── package.json
    │   ├── tsup.config.ts
    │   └── src/
    │       ├── index.ts
    │       ├── ErrorContentProvider.tsx
    │       ├── useErrorContent.ts
    │       ├── types.ts
    │       └── __tests__/
    ├── vue/
    │   ├── package.json
    │   ├── tsup.config.ts
    │   └── src/
    │       ├── index.ts
    │       ├── ErrorContentProvider.ts
    │       ├── useErrorContent.ts
    │       ├── types.ts
    │       └── __tests__/
    ├── svelte/
    │   ├── package.json
    │   ├── svelte.config.js
    │   └── src/
    │       ├── index.ts
    │       ├── HuhProvider.svelte
    │       ├── useErrorContent.ts
    │       ├── context.ts
    │       ├── types.ts
    │       └── __tests__/
    └── cli/
        ├── package.json
        ├── tsconfig.json
        ├── tsup.config.ts
        └── src/
            ├── index.ts
            ├── fetch-sheet.ts
            ├── fetch-airtable.ts      # re-export → adapters/airtable
            ├── fetch-notion.ts        # re-export → adapters/notion
            ├── generate.ts
            ├── adapters/
            │   ├── index.ts           # barrel (auto-registers all adapters)
            │   ├── types.ts           # SourceAdapter interface
            │   ├── registry.ts        # register/get/clear
            │   ├── google-sheets.ts
            │   ├── airtable.ts
            │   ├── notion.ts
            │   ├── csv.ts
            │   └── xlsx.ts
            ├── commands/
            │   ├── init.ts
            │   ├── pull.ts
            │   └── validate.ts
            └── __tests__/

Released under the MIT License.