@solapi/ui-kit
v1.0.4
Published
Solapi 공용 UI 컴포넌트 라이브러리. **Module Federation**(CDN) 우선 로드, CDN 장애 시 **npm 패키지**로 자동 fallback하는 구조입니다.
Readme
@solapi/ui-kit
Solapi 공용 UI 컴포넌트 라이브러리. Module Federation(CDN) 우선 로드, CDN 장애 시 npm 패키지로 자동 fallback하는 구조입니다.
설치
npm install @solapi/ui-kitCSS 스타일 import
npm 패키지 사용 시 CSS를 별도로 import해야 합니다. (Module Federation은 CSS가 자동 포함되므로 불필요)
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 등록
// vite.remotes.config.js
const remoteApps = {
'@solapi-frontend/ui-kit': '/ui-kit/stable/assets/remote.entry.js'
}2. GlobalUIProvider 감싸기
// 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. 컴포넌트 로더 정의
// 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 로더 정의
// 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 설정
// 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 모듈 로드 실패 시 앱 전체가 깨지는 것을 방지합니다.
// 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. 사용
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 패키지에서 자동 로드됩니다.
문서
| 문서 | 설명 | |------|------| | 시작하기 | 설치, 개발, 빌드 | | 아키텍처 | 패키지/모듈/프로젝트 구조, Provider 계층 | | 사용 가이드 | Federation + npm Fallback 상세 가이드, 마이그레이션 | | Federation API | createSafeProviderLoader, createSafeComponentLoader, createSafeHookLoader | | GlobalUIProvider | Props, Portal, MFA, 로컬 개발 설정 | | createMutationConfig | MFA 처리, Snackbar 연동, Federation 환경 사용법 | | 컴포넌트 추가 | 새 UI 컴포넌트/Hook 추가 가이드 | | 안티패턴 | 흔한 실수와 올바른 대안 |
