npm package: @solapi/ui-kit
Description: Solapi 공용 UI 컴포넌트 라이브러리. **Module Federation**(CDN) 우선 로드, CDN 장애 시 **npm 패키지**로 자동 fallback하는 구조입니다.
Latest version: 1.0.8

# @solapi/ui-kit

Solapi 공용 UI 컴포넌트 라이브러리.
**Module Federation**(CDN) 우선 로드, CDN 장애 시 **npm 패키지**로 자동 fallback하는 구조입니다.

## 설치

```bash
npm install @solapi/ui-kit
```

### CSS 스타일 import

npm 패키지 사용 시 CSS를 별도로 import해야 합니다. (Module Federation은 CSS가 자동 포함되므로 불필요)

```ts
import '@solapi/ui-kit/styles'
```

> **주의**: CSS를 import하지 않으면 Modal, Snackbar, Confirm 등 UI 컴포넌트가 렌더링되지만 화면에 보이지 않습니다.

## Module Federation Exposes

| 모듈 | 경로 | 설명 |
|------|------|------|
| `./ui` | `src/export/index.ts` | 전체 export (컴포넌트, 훅, Provider, 테마) |
| `./provider` | `src/export/provider.ts` | GlobalUIProvider, ThemeProvider |
| `./hooks` | `src/export/hooks.ts` | useSnackbar, useModal, useConfirm, useEnhancedMutation |
| `./createMutationConfig` | `src/lib/createMutationConfig.ts` | MFA 처리 포함 Mutation 설정 유틸 |

## Quick Start (Federation + npm Fallback)

### 1. Remote 등록

```js
// vite.remotes.config.js
const remoteApps = {
  '@solapi-frontend/ui-kit': '/ui-kit/stable/assets/remote.entry.js'
}
```

### 2. GlobalUIProvider 감싸기

```tsx
// src/uiKit/safeProviderLoader.tsx
import { createSafeProviderLoader } from '@solapi/ui-kit/federation'

const UI_KIT_PROVIDER = async () => {
  try {
    return await import('@solapi-frontend/ui-kit/provider')
  } catch {
    // npm fallback 시 CSS 로드 (Federation은 CSS 자동 포함)
    await import('@solapi/ui-kit/styles')
    return await import('@solapi/ui-kit')
  }
}

const GlobalUIProvider = createSafeProviderLoader<{
  children?: React.ReactNode
  portalContainer?: () => HTMLElement | null
}>(
  UI_KIT_PROVIDER,
  'GlobalUIProvider',
  { warningMessage: 'UI Kit CDN을 로드할 수 없습니다. npm 패키지로 대체되었습니다.' }
)

export default function RemoteEntry() {
  return (
    <GlobalUIProvider portalContainer={() => document.getElementById('root')}>
      {/* 앱 컨텐츠 */}
    </GlobalUIProvider>
  )
}
```

### 3. 컴포넌트 로더 정의

```tsx
// src/uiKit/safeCompLoader.tsx
import { createSafeComponentLoader } from '@solapi/ui-kit/federation'

const UI_KIT_COMP = async () => {
  try {
    return await import('@solapi-frontend/ui-kit/ui')
  } catch {
    return await import('@solapi/ui-kit')
  }
}

export const Tabs = createSafeComponentLoader(UI_KIT_COMP, 'Tabs')
export const TextInput = createSafeComponentLoader(UI_KIT_COMP, 'TextInput')
export const Button = createSafeComponentLoader(UI_KIT_COMP, 'Button')
export const Box = createSafeComponentLoader(UI_KIT_COMP, 'Box')
// ... 필요한 컴포넌트 추가
```

### 4. Hook 로더 정의

```ts
// src/uiKit/safeHookLoader.ts
import { createSafeHookLoader } from '@solapi/ui-kit/federation'

const UI_KIT_HOOKS = async () => {
  try {
    return await import('@solapi-frontend/ui-kit/hooks')
  } catch {
    return await import('@solapi/ui-kit')
  }
}

export const useSnackbar = createSafeHookLoader(UI_KIT_HOOKS, 'useSnackbar', {
  success: msg => console.log(`[snackbar] ${msg}`),
  error: msg => console.error(`[snackbar] ${msg}`),
  warning: msg => console.warn(`[snackbar] ${msg}`),
  info: msg => console.info(`[snackbar] ${msg}`)
})

export const useModal = createSafeHookLoader(UI_KIT_HOOKS, 'useModal', {
  modals: [],
  openModal: async () => false,
  closeModal: () => {},
  closeAllModals: () => {}
})

export const useConfirm = createSafeHookLoader(UI_KIT_HOOKS, 'useConfirm', {
  confirms: [],
  confirm: ({ message }) => Promise.resolve(window.confirm(message)),
  alert: ({ message }) => { window.alert(message); return Promise.resolve(true) },
  close: () => {}
})
```

### 5. MFA Mutation 설정

```ts
// src/uiKit/safeFunctionLoader.ts
const loadModule = async () => {
  try {
    return await import('@solapi-frontend/ui-kit/createMutationConfig')
  } catch {
    // 메인 entry에서 import하여 GlobalUIProvider와 동일한 store 인스턴스 공유
    return await import('@solapi/ui-kit')
  }
}

export const loadCreateMutationConfig = async () => {
  const mod = await loadModule()
  return mod?.createMutationConfig ?? null
}

export const loadConfigureMfa = async () => {
  const mod = await loadModule()
  return mod?.configureMfa ?? null
}
```

### 6. Error Boundary 감싸기 (선택)

Federation 모듈 로드 실패 시 앱 전체가 깨지는 것을 방지합니다.

```tsx
// src/uiKit/FederationErrorBoundary.tsx
import { FederationErrorBoundary } from '@solapi/ui-kit/federation'

function App() {
  return (
    <FederationErrorBoundary
      onError={(error, errorInfo) => {
        // 에러 로깅 서비스로 전송
        console.error('Federation 모듈 로드 실패:', error)
      }}
      message="UI 모듈을 불러오지 못했습니다."
    >
      <GlobalUIProvider>
        {/* 앱 컨텐츠 */}
      </GlobalUIProvider>
    </FederationErrorBoundary>
  )
}
```

| Props | 타입 | 설명 |
|-------|------|------|
| `fallback` | `React.ComponentType<{ error, onRetry, onReload }>` | 커스텀 에러 UI |
| `onError` | `(error, errorInfo) => void` | 에러 발생 시 콜백 |
| `onRetry` | `() => void` | 재시도 버튼 클릭 시 콜백 |
| `showDetails` | `boolean` | 에러 상세 정보 표시 여부 |
| `message` | `string` | 커스텀 에러 메시지 |

### 7. 사용

```tsx
import { Tabs, TextInput, Button, Box } from 'uiKit/safeCompLoader'
import { useSnackbar, useConfirm } from 'uiKit/safeHookLoader'

function MyPage() {
  const snackbar = useSnackbar()
  const { confirm } = useConfirm()

  const handleSave = async () => {
    const ok = await confirm({ title: '저장', message: '저장하시겠습니까?' })
    if (ok) snackbar.success('저장되었습니다!')
  }

  return (
    <Box padding={16}>
      <Tabs
        items={[{ label: 'Tab 1', value: '1' }, { label: 'Tab 2', value: '2' }]}
        value={tabValue}
        onChange={setTabValue}
      />
      <TextInput label="이름" placeholder="이름을 입력하세요" />
      <Button onClick={handleSave}>저장</Button>
    </Box>
  )
}
```

> Federation이 정상이면 CDN에서 로드 (번들 크기 0), 장애 시 npm 패키지에서 자동 로드됩니다.

## 문서

| 문서 | 설명 |
|------|------|
| [시작하기](docs/getting-started.md) | 설치, 개발, 빌드 |
| [아키텍처](docs/architecture.md) | 패키지/모듈/프로젝트 구조, Provider 계층 |
| [사용 가이드](docs/usage-guide.md) | Federation + npm Fallback 상세 가이드, 마이그레이션 |
| [Federation API](docs/federation-api.md) | createSafeProviderLoader, createSafeComponentLoader, createSafeHookLoader |
| [GlobalUIProvider](docs/global-ui-provider.md) | Props, Portal, MFA, 로컬 개발 설정 |
| [createMutationConfig](docs/create-mutation-config.md) | MFA 처리, Snackbar 연동, Federation 환경 사용법 |
| [컴포넌트 추가](docs/contributing.md) | 새 UI 컴포넌트/Hook 추가 가이드 |
| [안티패턴](docs/anti-patterns.md) | 흔한 실수와 올바른 대안 |