@iyulab/enterprise
v0.10.2
Published
Enterprise utilities and components for iyulab framework
Readme
@iyulab/enterprise
iyulab 프레임워크의 엔터프라이즈 통합 패키지. 폼 레이아웃 컴포넌트·API 설정·도메인 헬퍼를 제공합니다.
Installation
npm install @iyulab/enterprise무엇을 제공하나
1. 고유 export (enterprise에서만 제공)
| Export | 종류 | 용도 |
|--------|------|------|
| FormSection | React | 제목 + 세로 스택 폼 섹션 |
| FormRow | React | 2컬럼 그리드 폼 행(full로 1컬럼) |
| ApiConfig | class | baseUrl/OData·API prefix·dev 판별 중앙 설정 |
| createODataService | factory | OData v4 + custom REST CRUD 서비스(401·토스트·에러파싱) |
| ApiError | class | HTTP status 를 실은 API 호출 실패 에러 |
| createAuthClient | factory | 쿠키 세션 인증(fetchMe/login/logout) — 제네릭 user/자격증명 |
| createPermissionStore · hasPermission 외 | store | 권한 스냅샷 store + 판정 free 함수 |
| CurrencyHelper | class | 통화 포맷(formatKRW 등) |
| DateHelper | class | 날짜 포맷/파싱 |
| ProgressHelper | class | 진행률 계산 |
| UrgencyHelper | class | 긴급도 계산 |
2. 심볼 출처 (v0.3.0부터 하위 패키지 재노출 제거됨)
@iyulab/enterprise는 자기 고유 export만 제공합니다(위 표). @iyulab/components(컴포넌트), @iyulab/data-components(데이터 그리드/시트), @iyulab/modern-app(앱 프레임워크, 라우팅)은 각 패키지에서 직접 import하세요.
v0.2.x까지는 위 3개 패키지를
export *로 재노출했으나, 심볼 출처 혼란과 exact-pin(v0.2.2 이전) 조합 시 이중 인스턴스 위험 때문에 v0.3.0에서 제거했습니다. 재노출에 의존하던 코드는@iyulab/enterprise가 아닌 각 하위 패키지에서 직접 import하도록 수정해야 합니다.
Usage
폼 레이아웃 (React)
import { FormSection, FormRow } from '@iyulab/enterprise'
<FormSection title="기본 정보">
<FormRow>
<u-input label="이름" />
<u-input label="코드" />
</FormRow>
<FormRow full>
<u-textarea label="비고" />
</FormRow>
</FormSection>FormRow는 기본 2컬럼 그리드입니다. 한 칸을 차지하려면full을, 다른 열 수가 필요하면columns를 씁니다.- 두 컴포넌트 모두
className·style을 받아 기본값 뒤에 병합합니다 — 블록을 복제하지 않고 조정하는 정규 경로입니다.FormSection은 제목 줄만 바꾸는titleStyle도 받습니다.
<FormRow columns={3}>…</FormRow>
<FormSection title="기본 정보" style={{ marginBottom: 32 }}>…</FormSection>이 계층이 무엇을 소유하고 무엇을 소유하지 않는지, 새 패턴이 언제 추가되는지는 LOB 계층 헌장 이 정합니다. 패턴을 제안하기 전에 읽어 주세요.
API 설정
import { ApiConfig } from '@iyulab/enterprise'
ApiConfig.initialize({ baseUrl: '/', odataPrefix: '$data', apiPrefix: 'api' })
ApiConfig.getODataUrl('Orders') // → /$data/Orders
ApiConfig.getApiUrl('auth/me') // → /api/auth/me
ApiConfig.getUrlWithParams('report', { year: 2026, active: true })
ApiConfig.isDevelopment // 환경 판별OData 서비스 (createODataService)
OData v4 + custom REST CRUD 를 한 번에 구성한다. 401 세션 처리·에러 메시지 추출·성공 토스트·204 빈 바디 안전 파싱이 내장돼 있고, 도메인/로케일 요소는 전부 주입으로 앱 adapter 에 남긴다.
import { createODataService } from '@iyulab/enterprise'
import { app } from '@iyulab/modern-app'
// 앱 adapter (예: src/lib/odata.ts) — 라이브러리는 이 배선만 받는다.
export const svc = createODataService({
baseUrl: window.location.origin,
onUnauthorized: () => { window.location.href = '/' }, // 세션 만료 리다이렉트는 앱이 결정
notify: { success: (m) => app.success(m), error: (m) => app.error(m) },
messages: { // 기본은 영어 — 로케일 오버라이드
saved: '저장되었습니다', updated: '수정되었습니다', deleted: '삭제되었습니다',
sessionExpired: '세션이 만료되었습니다. 다시 로그인하세요.',
},
})
await svc.odataGet<Order>('Orders', { $top: '20' }) // value 배열 언랩
await svc.odataPost<Order>('Orders', { name: 'A', note: '' }) // '' → null 정규화 + 성공 토스트
svc.odataUrl('Orders') // flex-table useODataSource 엔드포인트
svc.sourceDefaults // { baseUrl, onUnauthorized } 주입용
// custom REST — GET/POST/PUT/PATCH/DELETE 전부, 204 빈 바디 안전 파싱 포함
await svc.apiGet<Order>('reports/summary')
await svc.apiPost<Order>('orders', { name: 'A' })
await svc.apiPut<Order>('orders/7', { name: 'A (revised)' }) // 리소스 전체 교체
await svc.apiPatch<Order>('orders/7', { note: 'urgent' })
await svc.apiDelete('orders/7') // 204 안전
// body가 FormData 인스턴스면 그대로(직렬화 없이) 멀티파트로 전송된다 —
// Content-Type은 브라우저가 boundary와 함께 자동 설정한다. apiPost/apiPut/apiPatch 전부 동일.
const form = new FormData()
form.append('file', file)
await svc.apiPost<Order>('orders/7/attachments', form)주입 항목:
| config | 용도 |
|--------|------|
| baseUrl | 모든 요청의 오리진 (필수) |
| odataPrefix / apiPrefix | 엔드포인트 prefix (기본 $data / api) |
| onUnauthorized(status) | 401 시 호출 — 리다이렉트/재진입 가드는 앱이 처리 |
| notify.success/error | 토스트 훅 (생략 시 토스트 없음 — 순수) |
| messages | 사용자 대면 문구 (기본 영어, 지정 키만 대체) |
| formatError(info) | 에러 메시지 포매팅 오버라이드 (앱별 정책) |
도메인 액션(상태 전이 등)·엔티티 목록·권한 코드는 라이브러리에 넣지 말고 앱 adapter 에 둔다.
인증 + 권한 (createAuthClient · 권한 store)
쿠키 세션 인증 흐름과 권한 스냅샷을 승격. 사용자·자격증명 형태는 앱이 제네릭으로 정의하고, 권한 코드는 불투명 문자열로만 다룬다. getPermissions 를 주면 로그인/세션 조회 성공 시 권한 store 가 자동 갱신된다.
import { createAuthClient, hasPermission, setPermissions } from '@iyulab/enterprise'
interface User { Id: string; Permissions: string[] } // 도메인 형태 = 앱 소유
export const auth = createAuthClient<User, { Username: string; Password: string }>({
meUrl: '/api/auth/me', loginUrl: '/api/auth/login', logoutUrl: '/api/auth/logout',
getPermissions: (u) => u.Permissions, // 성공 시 권한 store 자동 set
messages: { invalidCredentials: '사용자명 또는 비밀번호가 올바르지 않습니다.' },
})
// 부팅 게이트
const user = await auth.fetchMe() // null → 미인증(로그인 화면)
// 어디서나 권한 판정(부팅 스냅샷)
if (hasPermission('orders.write')) { /* 저장 버튼 노출 */ }fetchMe()는 401/네트워크 오류 시null— 이 신호가 로그인 게이트를 구동한다(라이브러리가 리다이렉트하지 않음).- 격리가 필요하면
createPermissionStore()로 별도 store 를 만들어permissionStore로 주입한다. - 도메인 판정(
isPortalUser등)·권한 코드 상수는 라이브러리가 아니라 앱 adapter 에 둔다.
도메인 헬퍼
import { CurrencyHelper, DateHelper } from '@iyulab/enterprise'
CurrencyHelper.formatKRW(1234000) // ₩1,234,000Development
npm run buildLicense
MIT
