22 KiB
컴포넌트 Props 레퍼런스 - Composite
관련 문서: 컴포넌트 Props (Basic/DataGrid/Modal) | 컴포넌트 개발 규칙 | sirsoft-admin_basic 컴포넌트
TL;DR (5초 요약)
1. FileUploader: autoUpload, uploadTriggerEvent, imageCompression, apiEndpoints 커스터마이징
2. DynamicFieldList: columns 정의, enableDrag, errors 자동 매핑 (fields.0.name.ko → per-row)
3. TagInput: creatable, delimiters, splitOnPaste, isMulti, tagVariants 색상 맵핑
4. MultilingualInput: layout('inline'|'tabs'|'compact'), availableLocales, inputType
5. Toggle: size('sm'|'md'|'lg'), description 텍스트 | RadioGroup: inline 가로 배치
6. HtmlContent: isHtml prop으로 HTML/텍스트 분기 — DB *_mode 필드 → isHtml 연동 패턴 필수 숙지
목차
- FileUploader
- DynamicFieldList
- TagInput
- MultilingualInput
- Toggle
- Checkbox
- RadioGroup
- SearchBar
- HtmlContent
FileUploader
템플릿: sirsoft-admin_basic, sirsoft-basic
파일/이미지 업로드 컴포넌트. 드래그앤드롭, 이미지 압축, 병렬 업로드, 대표 이미지 선택을 지원합니다.
Props
| Prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
uploadTriggerEvent |
string | - | 업로드 트리거 이벤트명 (emitEvent와 연동) |
attachmentableType |
string | - | 첨부 대상 모델 타입 (예: 'Product') |
attachmentableId |
number | - | 첨부 대상 모델 ID |
collection |
string | 'default' |
컬렉션명 |
maxFiles |
number | 10 |
최대 첨부 개수 |
maxSize |
number | 10 |
파일당 최대 크기 (MB) |
maxConcurrentUploads |
number | 3 |
동시 업로드 수 |
accept |
string | - | 허용 확장자 (예: "image/*", ".jpg,.png,.pdf") |
imageCompression |
object | - | 이미지 압축 옵션 { maxSizeMB, maxWidthOrHeight } |
autoUpload |
boolean | true |
파일 선택 시 즉시 업로드 여부 |
uploadParams |
object | - | 업로드 시 추가 FormData 파라미터 |
initialFiles |
Attachment[] | - | 초기 첨부파일 목록 |
disabled |
boolean | false |
비활성화 (읽기 전용) |
roleIds |
number[] | - | 접근 가능 역할 ID 배열 |
enablePrimarySelection |
boolean | false |
대표 파일 선택 기능 활성화 |
primaryFileId |
number | null | - | 현재 대표 파일 ID |
confirmBeforeRemove |
boolean | false |
삭제 전 확인 모달 표시 |
confirmRemoveTitle |
string | - | 삭제 확인 모달 제목 |
confirmRemoveMessage |
string | - | 삭제 확인 모달 메시지 |
apiEndpoints |
object | - | API 엔드포인트 커스터마이징 |
className |
string | - | 추가 CSS 클래스 |
apiEndpoints 구조
{
"apiEndpoints": {
"upload": "/api/admin/sirsoft-ecommerce/products/attachments",
"delete": "/api/admin/sirsoft-ecommerce/products/attachments/:id",
"reorder": "/api/admin/sirsoft-ecommerce/products/attachments/reorder"
}
}
imageCompression 구조
{
"imageCompression": {
"maxSizeMB": 1,
"maxWidthOrHeight": 1920
}
}
콜백 이벤트
| 이벤트 | $args[0] 값 | 설명 |
|---|---|---|
onUploadComplete |
Attachment[] | 업로드 완료 시 |
onUploadError |
{ error, file } |
업로드 에러 시 |
onRemove |
number (파일 ID) | 파일 삭제 시 |
onFilesChange |
PendingFile[] | 대기 파일 변경 시 |
onPrimaryChange |
number | null | 대표 파일 변경 시 |
사용 예시
{
"type": "composite",
"name": "FileUploader",
"props": {
"attachmentableType": "Product",
"attachmentableId": "{{route.id}}",
"collection": "images",
"maxFiles": 10,
"maxSize": 10,
"accept": "image/*",
"imageCompression": { "maxSizeMB": 1, "maxWidthOrHeight": 1920 },
"initialFiles": "{{product?.data?.attachments ?? []}}",
"enablePrimarySelection": true,
"primaryFileId": "{{_local.featuredImageId}}"
}
}
수동 업로드 패턴
{
"type": "composite",
"name": "FileUploader",
"props": {
"autoUpload": false,
"uploadTriggerEvent": "upload:product_images",
"uploadParams": { "temp_key": "{{_local.ui.imageTempKey}}" }
}
}
저장 버튼에서
emitEvent핸들러로"upload:product_images"이벤트를 발행하면 업로드가 시작됩니다.
DynamicFieldList
템플릿: sirsoft-admin_basic
동적 필드 목록 컴포넌트. 행 추가/삭제, 드래그 정렬, 다국어 입력, 검증 에러 매핑을 지원합니다.
Props
| Prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
items |
object[] | - | 데이터 항목 배열 |
columns |
DynamicFieldColumn[] | - | 컬럼 정의 |
enableDrag |
boolean | true |
드래그앤드롭 정렬 활성화 |
showIndex |
boolean | true |
순번 표시 |
minItems |
number | 0 |
최소 항목 수 (이하 삭제 불가) |
maxItems |
number | - | 최대 항목 수 (이상 추가 불가) |
addLabel |
string | '항목 추가' |
추가 버튼 라벨 |
emptyMessage |
string | '항목이 없습니다.' |
빈 상태 메시지 |
readOnly |
boolean | false |
읽기 전용 모드 |
errors |
object | - | 검증 에러 객체 |
itemIdKey |
string | '_id' |
항목별 고유 ID 키 |
rowActions |
RowAction[] | - | 행 액션 정의 |
name |
string | - | 폼 필드 이름 |
className |
string | - | 테이블 className |
headerClassName |
string | - | 헤더 className |
rowClassName |
string | - | 행 className |
DynamicFieldColumn 구조
| 필드 | 타입 | 설명 |
|---|---|---|
key |
string | 데이터 필드명 |
label |
string | 컬럼 라벨 |
type |
string | 'input' | 'number' | 'select' | 'textarea' | 'multilingual' | 'custom' |
width |
string | 컬럼 너비 (Tailwind 클래스, 예: 'w-48') |
placeholder |
string | 플레이스홀더 |
required |
boolean | 필수 여부 (헤더에 * 표시) |
options |
array | Select 옵션 (type: 'select'일 때) |
min / max / step |
number | 숫자 범위 (type: 'number'일 때) |
readOnly |
boolean | 컬럼별 읽기 전용 |
readOnlyCondition |
string | 조건부 읽기 전용 (필드명, ! prefix로 부정) |
검증 에러 매핑
서버 검증 에러를 행별로 자동 추출합니다.
서버 응답: { "fields.0.name.ko": ["필수 항목입니다"], "fields.1.content.ko": ["너무 깁니다"] }
↓ 자동 변환
행 0 에러: { "name.ko": ["필수 항목입니다"] }
행 1 에러: { "content.ko": ["너무 깁니다"] }
{
"type": "composite",
"name": "DynamicFieldList",
"props": {
"errors": "{{_local.errors ?? {}}}"
}
}
콜백 이벤트
| 이벤트 | $args[0] 값 | 설명 |
|---|---|---|
onChange |
object[] | 데이터 변경 시 (전체 items 배열) |
onAddItem |
- | 항목 추가 시 |
onRemoveItem |
{ index, item } |
항목 삭제 시 |
onReorder |
object[] | 순서 변경 시 (정렬된 items) |
사용 예시
{
"type": "composite",
"name": "DynamicFieldList",
"props": {
"items": "{{_local.fields ?? []}}",
"columns": [
{ "key": "label", "label": "$t:common.label", "type": "multilingual", "required": true },
{ "key": "value", "label": "$t:common.value", "type": "input" },
{ "key": "count", "label": "$t:common.count", "type": "number", "min": 0 }
],
"enableDrag": true,
"minItems": 1,
"maxItems": 10,
"errors": "{{_local.errors ?? {}}}"
}
}
TagInput
템플릿: sirsoft-admin_basic
태그 선택/생성 컴포넌트. 자동완성, 새 태그 생성, 붙여넣기 분할, 그룹화를 지원합니다.
Props
| Prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
value |
array | string | number | - | 선택된 값 |
options |
TagOption[] | - | 선택 가능한 옵션 목록 |
creatable |
boolean | false |
새 항목 추가 가능 여부 |
isMulti |
boolean | true |
다중 선택 여부 |
isClearable |
boolean | true |
선택 해제 가능 (싱글 모드) |
isSearchable |
boolean | true |
검색 가능 여부 |
maxItems |
number | - | 최대 선택 개수 (멀티 모드) |
delimiters |
string[] | [','] |
태그 구분자 (creatable 모드) |
splitOnPaste |
boolean | true |
붙여넣기 시 자동 분리 |
placeholder |
string | - | 입력창 placeholder |
noOptionsMessage |
string | - | 검색 결과 없을 때 메시지 |
formatCreateLabel |
string | - | 새 항목 추가 라벨 |
createLabelSuffix |
string | '추가' |
새 항목 추가 접미사 |
disabled |
boolean | false |
비활성화 |
tagVariants |
object | - | 태그 값별 색상 맵핑 |
defaultVariant |
string | - | 기본 태그 색상 |
name |
string | - | 폼 필드 이름 |
className |
string | - | 추가 CSS 클래스 |
TagOption 구조
| 필드 | 타입 | 설명 |
|---|---|---|
value |
string | number | 옵션 값 |
label |
string | 표시 라벨 |
count |
number | 카운트 표시 (선택사항) |
isDisabled |
boolean | 비활성화 |
group |
string | 그룹명 (드롭다운 그룹 헤더) |
description |
string | 상세 설명 (옵션 아래 표시) |
tagVariants 색상
사용 가능한 variant: 'blue', 'green', 'amber', 'red', 'purple', 'gray'
{
"tagVariants": {
"active": "green",
"inactive": "gray",
"pending": "amber"
},
"defaultVariant": "blue"
}
콜백 이벤트
| 이벤트 | $args[0] 값 | 설명 |
|---|---|---|
onChange |
(string | number)[] | 값 변경 시 (태그 배열) |
onCreateOption |
string | 새 옵션 생성 시 (입력값) |
onInputChange |
string | 검색 입력 변경 시 (비동기 검색용) |
onBeforeRemove |
TagOption | 삭제 전 확인 (true: 허용) |
사용 예시
{
"type": "composite",
"name": "TagInput",
"props": {
"value": "{{_local.selectedTags ?? []}}",
"options": "{{$computed.tagOptions ?? []}}",
"creatable": true,
"maxItems": 10,
"delimiters": [",", ";"],
"splitOnPaste": true,
"placeholder": "$t:common.select_tags"
},
"actions": [
{
"event": "onChange",
"handler": "setState",
"params": { "target": "local", "selectedTags": "{{$args[0]}}" }
}
]
}
MultilingualInput
템플릿: sirsoft-admin_basic
다국어 입력 컴포넌트. 인라인/탭/컴팩트 레이아웃으로 로케일별 텍스트를 입력합니다.
Props
| Prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
value |
object | - | 다국어 값 ({ ko: "...", en: "..." }) |
inputType |
string | 'text' |
입력 타입: 'text' | 'textarea' |
layout |
string | 'inline' |
레이아웃: 'inline' | 'tabs' | 'compact' |
availableLocales |
LocaleOption[] | - | 사용 가능한 언어 목록 |
defaultLocale |
string | - | 기본 언어 코드 |
placeholder |
string | - | 입력창 placeholder |
required |
boolean | false |
필수 입력 여부 |
disabled |
boolean | false |
비활성화 |
maxLength |
number | - | 최대 길이 |
rows |
number | - | textarea 행 수 (inputType: 'textarea'일 때) |
error |
string | - | 에러 메시지 |
name |
string | - | 폼 필드 이름 |
showCodeOnMobile |
boolean | false |
모바일에서 언어 코드로 표시 |
className |
string | - | 추가 CSS 클래스 |
레이아웃 모드
| 모드 | 설명 |
|---|---|
inline |
모든 언어를 수직으로 나열 (기본) |
tabs |
탭으로 언어 전환 |
compact |
탭과 입력 필드가 한 줄에 표시 |
LocaleOption 구조
{ "code": "ko", "name": "Korean", "nativeName": "한국어" }
availableLocales 정책
availableLocales prop 은 활성 코어 언어팩을 따라가도록 동적 바인딩 합니다. 정적 배열 ([{code:"ko", ...}, {code:"en", ...}]) 로 작성하면 ja 등 추가 언어팩이 활성화돼도 입력 탭이 노출되지 않아 데이터 입력이 거부됩니다.
// ✅ DO: _global.appConfig.supportedLocales 기반 동적 매핑
"availableLocales": "{{(_global.appConfig?.supportedLocales ?? ['ko', 'en']).map(l => ({ code: l, name: _global.appConfig?.localeNames?.[l] ?? l, nativeName: _global.appConfig?.localeNames?.[l] ?? l }))}}"
// ❌ DON'T: ko/en 정적 하드코딩 — 활성 언어팩 ja 미반영
"availableLocales": [
{ "code": "ko", "name": "한국어", "nativeName": "한국어" },
{ "code": "en", "name": "English", "nativeName": "English" }
]
prop 미지정 시 컴포넌트가 G7Core.locale.supported() 로 자동 폴백 — prop 자체를 생략해도 무방 합니다 (가장 권장).
사용 예시
{
"type": "composite",
"name": "MultilingualInput",
"props": {
"value": "{{_local.form.name ?? {}}}",
"layout": "tabs",
"inputType": "text",
"placeholder": "$t:common.enter_name",
"required": true
}
}
Toggle
템플릿: sirsoft-admin_basic
토글 스위치 컴포넌트. 세 가지 크기와 선택적 라벨/설명을 지원합니다.
Props
| Prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
checked |
boolean | - | 체크 상태 |
value |
boolean | - | 체크 상태 (checked의 대체) |
size |
string | 'md' |
크기: 'sm' | 'md' | 'lg' |
label |
string | - | 라벨 텍스트 |
description |
string | - | 설명 텍스트 (라벨 아래 작은 글씨) |
disabled |
boolean | false |
비활성화 |
name |
string | - | 폼 필드 이름 |
className |
string | - | 추가 CSS 클래스 |
크기 비교
| 크기 | 사용 시점 |
|---|---|
sm |
테이블 셀, 밀집 UI |
md |
일반 폼 필드 (기본) |
lg |
강조가 필요한 설정 |
사용 예시
{
"type": "composite",
"name": "Toggle",
"props": {
"checked": "{{_local.form.is_active ?? false}}",
"size": "md",
"label": "$t:common.active",
"description": "$t:admin.settings.active_description"
},
"actions": [
{
"type": "change",
"handler": "setState",
"params": { "target": "local", "form.is_active": "{{$event.target.checked}}" }
}
]
}
Checkbox
템플릿: sirsoft-admin_basic, sirsoft-basic
체크박스 컴포넌트. indeterminate 상태로 부분 선택을 표현합니다.
참고: Checkbox는 Basic 컴포넌트이지만,
indeterminateprop이 특수하므로 여기에 포함합니다.
추가 Props
| Prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
indeterminate |
boolean | false |
부분 선택 상태 (HTML 속성이 아닌 DOM 프로퍼티) |
label |
string | - | 라벨 텍스트 |
표준 HTML input 속성(
checked,disabled,name,value,className등)을 모두 지원합니다.
indeterminate 사용 예시
{
"type": "basic",
"name": "Checkbox",
"props": {
"checked": "{{$computed.allSelected}}",
"indeterminate": "{{$computed.someSelected && !$computed.allSelected}}"
}
}
RadioGroup
템플릿: sirsoft-admin_basic
라디오 버튼 그룹 컴포넌트. 수직/수평 배치와 옵션별 비활성화를 지원합니다.
Props
| Prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
name |
string | - | 그룹 이름 (필수) |
value |
string | - | 현재 선택된 값 |
options |
RadioOption[] | - | 라디오 옵션 배열 |
inline |
boolean | false |
인라인(가로) 배치 |
disabled |
boolean | false |
전체 비활성화 |
size |
string | 'md' |
크기: 'sm' | 'md' | 'lg' |
label |
string | - | 그룹 라벨 |
error |
string | - | 에러 메시지 |
className |
string | - | 추가 CSS 클래스 |
RadioOption 구조
| 필드 | 타입 | 설명 |
|---|---|---|
value |
string | 옵션 값 |
label |
string | 옵션 라벨 |
disabled |
boolean | 개별 옵션 비활성화 |
사용 예시
{
"type": "composite",
"name": "RadioGroup",
"props": {
"name": "shipping_type",
"value": "{{_local.form.shipping_type ?? 'standard'}}",
"options": [
{ "value": "standard", "label": "$t:common.standard" },
{ "value": "express", "label": "$t:common.express" },
{ "value": "pickup", "label": "$t:common.pickup", "disabled": true }
],
"inline": true
},
"actions": [
{
"type": "change",
"handler": "setState",
"params": { "target": "local", "form.shipping_type": "{{$event.target.value}}" }
}
]
}
SearchBar
템플릿: sirsoft-admin_basic, sirsoft-basic
검색 바 컴포넌트. 자동완성 제안과 검색 버튼을 지원합니다.
Props
| Prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
value |
string | - | 검색어 |
placeholder |
string | - | 플레이스홀더 |
showButton |
boolean | false |
검색 버튼 표시 |
suggestions |
SearchSuggestion[] | - | 자동완성 제안 목록 |
showSuggestions |
boolean | - | 제안 목록 표시 여부 |
name |
string | - | 폼 필드 이름 |
className |
string | - | 추가 CSS 클래스 |
style |
object | - | 인라인 스타일 |
SearchSuggestion 구조
{ "id": 1, "text": "검색어 제안" }
콜백 이벤트
| 이벤트 | $args[0] 값 | 설명 |
|---|---|---|
onChange |
ChangeEvent | 검색어 변경 시 (DOM 이벤트) |
onSubmit |
FormEvent | 검색 제출 시 |
onSuggestionClick |
SearchSuggestion | 제안 항목 클릭 시 |
사용 예시
{
"type": "composite",
"name": "SearchBar",
"props": {
"value": "{{_local.searchKeyword ?? ''}}",
"placeholder": "$t:common.search",
"showButton": true
},
"actions": [
{
"type": "change",
"handler": "setState",
"params": { "target": "local", "searchKeyword": "{{$event.target.value}}" }
}
]
}
HtmlContent
HTML 콘텐츠와 일반 텍스트를 안전하게 렌더링하는 컴포넌트입니다.
Props
| Prop | 타입 | 필수 | 기본값 | 설명 |
|---|---|---|---|---|
content |
string |
❌ | '' |
렌더링할 콘텐츠 (HTML 또는 텍스트) |
text |
string |
❌ | - | 레이아웃 JSON용 콘텐츠 (content보다 우선) |
isHtml |
boolean |
❌ | true |
HTML 렌더링 여부 |
className |
string |
❌ | '' |
컨테이너 CSS 클래스 |
purifyConfig |
object |
❌ | - | DOMPurify 커스텀 설정 (isHtml=true 시만) |
isHtml에 따른 렌더링 분기
isHtml |
렌더링 방식 | 스타일 |
|---|---|---|
true |
dangerouslySetInnerHTML + DOMPurify 정화 |
prose dark:prose-invert (Tailwind Typography) |
false |
React 텍스트 노드 (이스케이프됨) | whitespace-pre-wrap (줄바꿈 보존) |
DB *_mode 필드 → isHtml 연동 패턴 (CRITICAL)
G7에서는 콘텐츠의 렌더링 모드를 DB의 *_mode 컬럼('text' / 'html')으로 관리합니다.
이 값이 API를 거쳐 프론트엔드 isHtml prop으로 변환되는 전체 흐름을 이해해야 합니다.
데이터 흐름
1. DB 컬럼 → description_mode: 'text' | 'html'
2. API Resource → 'description_mode' => $this->description_mode
3. 레이아웃 JSON → "isHtml": "{{(product.data?.description_mode ?? 'text') === 'html'}}"
4. HtmlContent → isHtml=true → HTML 렌더링 / isHtml=false → 텍스트 렌더링
적용 사례
| 기능 | DB 컬럼 | API Resource 필드 | 레이아웃 표현식 |
|---|---|---|---|
| 상품 설명 | products.description_mode |
description_mode |
{{(product.data?.description_mode ?? 'text') === 'html'}} |
| 게시글 본문 | posts.content_mode |
content_mode |
{{(post?.data?.content_mode ?? 'text') === 'html'}} |
| 페이지 콘텐츠 | pages.content_mode |
content_mode |
{{(page?.data?.content_mode ?? 'html') === 'html'}} |
| 공통정보 | common_infos.content_mode |
common_info.content_mode |
{{product.data?.common_info?.content_mode === 'html'}} |
레이아웃 JSON 사용 예시
{
"type": "composite",
"name": "HtmlContent",
"props": {
"content": "{{product.data?.description_localized ?? ''}}",
"isHtml": "{{(product.data?.description_mode ?? 'text') === 'html'}}",
"className": "prose dark:prose-invert max-w-none"
}
}
주의사항
1. API Resource에 *_mode 필드 누락 시 → 프론트엔드 기본값('text')으로 fallback → HTML 미렌더링
2. Admin Resource와 Public Resource 양쪽 모두에 *_mode 필드 포함 필수
3. 기본값은 기능별로 다름: 상품/게시글은 'text', 페이지는 'html'
4. isHtml=true 시 DOMPurify 자동 적용 — 별도 sanitize 불필요
5. 백엔드에서도 description_mode='html'일 때 HTMLPurifier로 서버 사이드 정화
보안
isHtml=true: DOMPurify FORBID 방식 —script,iframe,form등 위험 태그 차단, 나머지 허용- 외부 링크(
http://,https://)에rel="noopener noreferrer"자동 추가 purifyConfig로 커스터마이징 가능하나, 보안 기본값(FORBID_TAGS,FORBID_ATTR)은 항상 유지