레이아웃 scripts[] 에만 있던 원격 스크립트 차단 게이트가 다른 주입 경로에는 없어, 저장측(SafeLayoutExpressions·NoExternalUrls)이 외부 URL 저장을 422 로 막아도 런타임 디스패치 한 번으로 임의 원격 코드가 로드됐다. 저장 검증이 통째로 무의미해지는 상태였고 오류도 경고도 남지 않았다. 판정식을 support/scriptSrcPolicy.ts 로 분리해 런타임 SSoT 를 하나로 두고, loadScript 액션 · 확장 핸들러 재로드 · 편집기 프리뷰 · 편집기 자산 매니페스트 · G7Core.asset.loadScript · PG 플러그인 SDK 주입이 모두 그것을 경유하게 했다. 사본을 두지 않은 이유는 차집합이 그대로 우회로가 되기 때문이다. - 게이트는 캐시 검사보다 앞에 둔다 (뒤에 두면 이미 로드된 미신뢰 스크립트가 캐시 히트로 통과한다) - NoExternalUrls 순회를 9키로 확대 — 번들 레이아웃 601개 사전 스캔으로 정당한 외부 URL 0건을 확인한 뒤 넓혔다 (저장 회귀 없음) - callExternal 은 생성자를 참조 동일성으로 거부하고(별칭 전역 포함), 프로토타입 경로 세그먼트를 읽기·매핑 키 양쪽에서 차단한다 - PG SDK 는 서비스 SDK 라 자체 호스팅이 불가능하므로 manifest 선언 + 주입 직전 호스트 확인이 게이트다. tosspayments SDK URL 은 확장자가 없어 정적 검사에 걸리지 않아 그 런타임 확인이 유일한 게이트다 - 함께 드러난 결함: 동시 loadScript 가 로드 전에 완료 처리되던 문제, 확장 재로드에서 script 기존재가 CSS 까지 건너뛰던 문제, loadCSS 의 in-flight 미공유
30 KiB
G7Core 전역 API 레퍼런스 - 고급
버전: engine-v1.4.0+ 관련 문서: g7core-api.md | state-management.md | components.md
목차
- 다국어 (G7Core.locale, G7Core.t)
- 액션 실행 (G7Core.dispatch)
- 컴포넌트 이벤트 (G7Core.componentEvent)
- 이벤트 생성 헬퍼
- 컴포넌트 렌더링 헬퍼
- 인증 및 API
- 반응형 및 전환 상태
- 타입 정의
다국어 (G7Core.locale, G7Core.t)
로케일 관리 및 번역 API입니다.
G7Core.locale API
| 메서드 | 설명 | 반환값 |
|---|---|---|
current() |
현재 로케일 반환 | string (예: 'ko', 'en') |
supported() |
지원 로케일 목록 | string[] |
change(locale) |
로케일 변경 | Promise<void> |
G7Core.t() 번역 함수
G7Core.t(key: string, params?: Record<string, string | number>): string
| 파라미터 | 타입 | 설명 |
|---|---|---|
key |
string | 번역 키 (예: 'common.confirm') |
params |
object | 번역 파라미터 (선택) |
사용 예시
// 로케일 조회
const currentLocale = G7Core.locale.current(); // 'ko'
const supportedLocales = G7Core.locale.supported(); // ['ko', 'en']
// 로케일 변경
await G7Core.locale.change('en');
// 번역 함수 사용
const text = G7Core.t('common.confirm'); // '확인'
// 파라미터 치환
const message = G7Core.t('admin.users.pagination_info', {
from: 1,
to: 10,
total: 100
});
// "1-10 / 총 100건"
컴포넌트에서 t 함수 선언 패턴
상세 내용: components.md
// 컴포넌트 파일 상단에 선언
const t = (key: string, params?: Record<string, string | number>) =>
(window as any).G7Core?.t?.(key, params) ?? key;
// 사용
<Button>{t('common.confirm')}</Button>
<Span>{t('admin.users.count', { count: 10 })}</Span>
액션 실행 (G7Core.dispatch)
액션을 프로그래밍 방식으로 실행하는 API입니다.
함수 시그니처
G7Core.dispatch(action: ActionConfig): Promise<ActionResult>
ActionConfig 구조
interface ActionConfig {
handler: string; // 핸들러 이름 (예: 'api', 'navigate', 'toast')
params?: Record<string, any>; // 핸들러 파라미터
target?: string; // 대상 ID (모달 등)
}
ActionResult 구조
interface ActionResult {
success: boolean;
data?: any;
error?: Error;
}
사용 예시
// 네비게이션
await G7Core.dispatch({
handler: 'navigate',
params: { path: '/users' }
});
// API 호출
const result = await G7Core.dispatch({
handler: 'api',
params: {
method: 'POST',
endpoint: '/api/users',
body: { name: 'John' }
}
});
// 모달 열기
await G7Core.dispatch({
handler: 'openModal',
target: 'confirm_modal'
});
// 토스트 표시
await G7Core.dispatch({
handler: 'toast',
params: {
message: '저장되었습니다',
type: 'success'
}
});
// 상태 설정
await G7Core.dispatch({
handler: 'setState',
params: {
target: 'global',
sidebarOpen: true
}
});
컴포넌트 이벤트 (G7Core.componentEvent)
컴포넌트 간 통신을 위한 이벤트 시스템입니다.
API 목록
| 메서드 | 설명 | 반환값 |
|---|---|---|
on(eventName, callback) |
이벤트 구독 | 구독 해제 함수 |
emit(eventName, data?) |
이벤트 발생 | Promise<any[]> |
off(eventName) |
특정 이벤트 리스너 제거 | void |
clear() |
모든 리스너 제거 | void |
사용 예시
// 이벤트 구독
const unsubscribe = G7Core.componentEvent.on('fileSelected', (data) => {
console.log('파일 선택됨:', data.file);
});
// 이벤트 발생
await G7Core.componentEvent.emit('fileSelected', { file: selectedFile });
// 이벤트 발생 + 결과 수집
const results = await G7Core.componentEvent.emit('triggerUpload:logo_uploader');
// results: 모든 리스너의 반환값 배열
// 구독 해제
unsubscribe();
// 특정 이벤트의 모든 리스너 제거
G7Core.componentEvent.off('fileSelected');
// 모든 리스너 제거
G7Core.componentEvent.clear();
이벤트 네이밍 규칙
| 패턴 | 설명 | 예시 |
|---|---|---|
{action} |
단순 액션 | fileSelected, dataLoaded |
{action}:{target} |
특정 대상 지정 | triggerUpload:logo_uploader |
{component}.{action} |
컴포넌트별 이벤트 | sidebar.toggle, modal.close |
사용 사례
FileUploader 트리거
// 파일 업로더 컴포넌트에서 이벤트 구독
useEffect(() => {
const unsubscribe = G7Core.componentEvent.on(
`triggerUpload:${uploaderId}`,
async () => {
inputRef.current?.click();
}
);
return unsubscribe;
}, [uploaderId]);
// 다른 컴포넌트에서 업로드 트리거
G7Core.componentEvent.emit('triggerUpload:logo_uploader');
데이터 동기화
// 데이터 변경 알림
G7Core.componentEvent.emit('dataChanged', {
source: 'userForm',
data: updatedUser
});
// 다른 컴포넌트에서 수신
G7Core.componentEvent.on('dataChanged', ({ source, data }) => {
if (source === 'userForm') {
refreshUserList();
}
});
이벤트 생성 헬퍼
ActionDispatcher와 호환되는 이벤트 객체를 생성하는 헬퍼 함수들입니다.
API 목록
| 함수 | 설명 | 반환값 |
|---|---|---|
createChangeEvent(value, name?) |
change 이벤트 생성 | ChangeEvent |
createClickEvent(data?) |
click 이벤트 생성 | ClickEvent |
createSubmitEvent(formData?) |
submit 이벤트 생성 | SubmitEvent |
createKeyboardEvent(key, modifiers?) |
keyboard 이벤트 생성 | KeyboardEvent |
사용 예시
// Change 이벤트 생성
const changeEvent = G7Core.createChangeEvent('newValue', 'fieldName');
// { target: { value: 'newValue', name: 'fieldName' } }
// Click 이벤트 생성
const clickEvent = G7Core.createClickEvent({ itemId: 123 });
// { data: { itemId: 123 } }
// Submit 이벤트 생성
const submitEvent = G7Core.createSubmitEvent({
name: 'John',
email: 'john@example.com'
});
// { formData: { name: 'John', email: 'john@example.com' } }
// Keyboard 이벤트 생성
const keyEvent = G7Core.createKeyboardEvent('Enter', { ctrlKey: true });
// { key: 'Enter', ctrlKey: true, shiftKey: false, altKey: false }
사용 사례
컴포넌트에서 ActionDispatcher 핸들러를 직접 호출할 때 사용합니다:
// 컴포넌트에서 프로그래밍 방식으로 change 이벤트 발생
const handleCustomChange = (newValue: string) => {
const event = G7Core.createChangeEvent(newValue, 'customField');
onChange?.(event); // props로 전달된 onChange 호출
};
// Submit 이벤트와 함께 액션 실행
const handleFormSubmit = async () => {
const submitEvent = G7Core.createSubmitEvent(formData);
await G7Core.dispatch({
handler: 'api',
params: {
method: 'POST',
endpoint: '/api/submit',
body: submitEvent.formData
}
});
};
컴포넌트 렌더링 헬퍼
G7Core.renderItemChildren
반복 렌더링 컴포넌트(CardGrid, DataGrid 등)에서 각 아이템의 자식 컴포넌트를 렌더링하는 헬퍼입니다.
G7Core.renderItemChildren(
children: ComponentDef[],
itemContext: Record<string, any>,
componentMap: Record<string, React.ComponentType>,
keyPrefix?: string,
options?: RenderOptions
): React.ReactNode[]
파라미터
| 파라미터 | 타입 | 설명 |
|---|---|---|
children |
array | 렌더링할 자식 컴포넌트 정의 배열 |
itemContext |
object | 현재 아이템 컨텍스트 (row, item 등) |
componentMap |
object | 컴포넌트 이름 → 컴포넌트 매핑 |
keyPrefix |
string | React key 접두사 (선택) |
options |
object | 추가 옵션 (선택) |
사용 예시
// CardGrid 컴포넌트에서 사용
const CardGrid: React.FC<Props> = ({ items, cellChildren }) => {
const componentMap = G7Core.getComponentMap();
return (
<Div className="grid">
{items.map((item, index) => (
<Div key={item.id || index}>
{G7Core.renderItemChildren(
cellChildren,
{ row: item, index },
componentMap,
`card_${index}`
)}
</Div>
))}
</Div>
);
};
G7Core.getComponentMap
전체 컴포넌트 맵을 반환합니다:
const componentMap = G7Core.getComponentMap();
// { Button: ButtonComponent, Input: InputComponent, ... }
G7Core.renderComponentLayout
engine-v1.12.0+ 추가
component_layout 정의를 받아서 React 요소로 렌더링합니다. RichSelect 등에서 커스텀 아이템 렌더링 시 사용합니다.
G7Core.renderComponentLayout(
layoutDefs: ComponentDef[] | undefined,
itemContext: Record<string, any>,
keyPrefix?: string
): React.ReactNode
파라미터
| 파라미터 | 타입 | 설명 |
|---|---|---|
layoutDefs |
array | component_layout 정의 배열 |
itemContext |
object | 항목별 컨텍스트 (item, index, isSelected 등) |
keyPrefix |
string | React key prefix (선택) |
사용 예시
// RichSelect 컴포넌트에서 사용
const RichSelect: React.FC<Props> = ({ options, __componentLayoutDefs }) => {
return (
<Div>
{options.map((option, index) => (
<Div key={option.value}>
{G7Core.renderComponentLayout(
__componentLayoutDefs?.item,
{ item: option, index, isSelected: selectedValue === option.value },
`item-${index}`
)}
</Div>
))}
</Div>
);
};
G7Core.renderExpandContent
engine-v1.12.0+ 추가
DataGrid, CardGrid 등에서 확장 행(expandChildren) 콘텐츠를 렌더링하는 헬퍼입니다. 60줄 이상의 복잡한 로직을 10줄 이하로 간소화합니다.
G7Core.renderExpandContent(config: {
children: ComponentDef[];
row: any;
expandContext?: Record<string, any>;
componentContext?: { state?: Record<string, any>; setState?: Function };
componentMap?: Record<string, React.ComponentType>;
keyPrefix: string;
}): React.ReactNode
파라미터
| 파라미터 | 타입 | 설명 |
|---|---|---|
children |
array | 렌더링할 expandChildren 컴포넌트 정의 |
row |
object | 현재 행 데이터 |
expandContext |
object | 확장 컨텍스트 (바인딩 표현식 자동 평가) |
componentContext |
object | 부모 컴포넌트 컨텍스트 (state, setState) |
componentMap |
object | 컴포넌트 맵 (없으면 자동 조회) |
keyPrefix |
string | React key prefix |
동작 원리
- 전역 상태 가져오기
componentContext.state를_local에 자동 병합expandContext바인딩 표현식 자동 평가- 최종 컨텍스트 구성 (
row,item,$item,_local,_global,_computed) - 렌더링
사용 예시
// DataGrid에서 확장 행 렌더링 (Before: 60줄+ → After: 10줄)
const renderExpandedContent = useCallback((row: any) => {
if (expandedRowRender) return expandedRowRender(row);
if (expandChildren && expandChildren.length > 0) {
return G7Core.renderExpandContent({
children: expandChildren,
row,
expandContext,
componentContext: __componentContext,
keyPrefix: `expand-${row[idField]}`,
});
}
return null;
}, [expandChildren, expandContext, __componentContext, expandedRowRender, idField]);
인증 및 API
G7Core.AuthManager
인증 상태를 관리하는 클래스입니다.
상세 내용: auth-system.md
// 현재 사용자 정보
const user = G7Core.AuthManager.getUser();
// 로그인 상태 확인
const isLoggedIn = G7Core.AuthManager.isAuthenticated();
// 토큰 조회
const token = G7Core.AuthManager.getToken();
G7Core.api
API 클라이언트 인스턴스입니다.
// GET 요청
const users = await G7Core.api.get('/api/users');
// POST 요청
const result = await G7Core.api.post('/api/users', {
name: 'John',
email: 'john@example.com'
});
// PUT 요청
await G7Core.api.put('/api/users/1', { name: 'Jane' });
// DELETE 요청
await G7Core.api.delete('/api/users/1');
WebSocket (G7Core.websocket)
실시간 데이터 통신을 위한 WebSocket 관리 API입니다.
API 목록
| 메서드 | 설명 | 반환값 |
|---|---|---|
subscribe(channel, event, callback, options?) |
채널/이벤트 구독 | 구독 키 (string) |
unsubscribe(subscriptionKey) |
구독 해제 | void |
leaveChannel(channel) |
채널 떠나기 | void |
disconnect() |
연결 종료 | void |
isInitialized() |
초기화 상태 확인 | boolean |
getSubscriptionCount() |
현재 구독 수 | number |
사용 예시
// 채널 구독
const subscriptionKey = G7Core.websocket.subscribe(
'admin.dashboard', // 채널명
'stats.updated', // 이벤트명
(data) => { // 콜백
console.log('통계 업데이트:', data);
}
);
// 옵션과 함께 구독
const key = G7Core.websocket.subscribe(
'private-user.123',
'notification',
handleNotification,
{ presence: true }
);
// 구독 해제
G7Core.websocket.unsubscribe(subscriptionKey);
// 채널 떠나기 (해당 채널의 모든 구독 해제)
G7Core.websocket.leaveChannel('admin.dashboard');
// 연결 종료
G7Core.websocket.disconnect();
// 상태 확인
if (G7Core.websocket.isInitialized()) {
console.log('구독 수:', G7Core.websocket.getSubscriptionCount());
}
컴포넌트에서 사용 패턴
useEffect(() => {
const key = G7Core.websocket.subscribe(
'admin.orders',
'order.created',
(order) => {
// 새 주문 처리
refreshOrderList();
}
);
return () => {
G7Core.websocket.unsubscribe(key);
};
}, []);
반응형 및 전환 상태
G7Core.ResponsiveManager
반응형 상태를 관리합니다.
상세 내용: responsive-layout.md
// 현재 브레이크포인트 조회
const breakpoint = G7Core.ResponsiveManager.getCurrentBreakpoint();
// 'sm', 'md', 'lg', 'xl', '2xl'
// 모바일 여부
const isMobile = G7Core.ResponsiveManager.isMobile();
G7Core.useResponsive (React Hook)
const { breakpoint, isMobile, isTablet, isDesktop } = G7Core.useResponsive();
G7Core.TransitionManager
페이지 전환 상태를 관리합니다.
// 전환 중 여부
const isPending = G7Core.TransitionManager.getIsPending();
// 상태 변경 구독
const unsubscribe = G7Core.TransitionManager.subscribe((isPending) => {
console.log('전환 상태:', isPending);
});
G7Core.useTransitionState (React Hook)
const { isPending } = G7Core.useTransitionState();
G7Core.updateQueryParams
같은 페이지 내에서 URL 쿼리 파라미터만 변경하면서 컴포넌트 리마운트 없이 데이터소스를 refetch 한다. 일반적으로 navigate 핸들러의 replace: true 옵션 사용 시 엔진이 자동 호출하므로 직접 호출은 드물다.
G7Core.updateQueryParams(
newPath: string,
options?: { transitionOverlayTarget?: string }
): Promise<void>
| 파라미터 | 타입 | 설명 |
|---|---|---|
newPath |
string |
새 경로 (쿼리스트링 포함). 예: /admin/products?status=active&page=2 |
options.transitionOverlayTarget |
string |
transition_overlay.target 동적 override (engine-v1.36.0+). 탭 속 서브 탭 등 부분 영역만 spinner 표시 |
동작:
window.history.replaceState로 URL 갱신 (페이지 리로드 없음)auto_fetch: true인 데이터소스 refetch (websocket 제외)transition_overlay가 정의되어 있고blocking또는wait_for일치 progressive 데이터소스가 1개 이상이면 오버레이 자동 표시 (engine-v1.35.0+)- fetch 완료 시
updateTemplateData호출 +hideTransitionOverlay computed재계산
사용 예시:
// 일반: 쿼리 파라미터 변경 + 데이터 refetch
await G7Core.updateQueryParams('/admin/products?status=active&page=2');
// 서브 탭 spinner target 동적 override
await G7Core.updateQueryParams(
'/admin/settings?tab=notification&channel=email',
{ transitionOverlayTarget: 'notif_channel_content' }
);
관련:
navigate핸들러 (replace: true) → actions-handlers-navigation.mdtransition_overlay.wait_for/overlay_target→ layout-json-components-loading.md
React Hooks
G7Core.useControllableState
engine-v1.12.0+ 추가
Controlled/Uncontrolled 컴포넌트 패턴을 단순화하는 훅입니다. 외부 value prop과 내부 상태를 자동으로 동기화합니다.
const [value, setValue] = G7Core.useControllableState<T>(
controlledValue, // T | undefined - 외부 controlled value (선택)
defaultValue, // T - 초기값 (uncontrolled일 때 사용)
onChange?, // (value: T) => void - 값 변경 시 콜백 (선택)
options? // { isEqual?: (a: T, b: T) => boolean } - 옵션 (선택)
);
파라미터
| 파라미터 | 타입 | 설명 |
|---|---|---|
controlledValue |
T | undefined |
외부 controlled value (있으면 controlled 모드) |
defaultValue |
T |
초기값 (uncontrolled 모드에서 사용) |
onChange |
(value: T) => void |
값 변경 시 호출될 콜백 (선택) |
options |
object |
추가 옵션 (선택) |
options.isEqual |
(a: T, b: T) => boolean |
커스텀 비교 함수 (기본: ===) |
반환값
| 인덱스 | 타입 | 설명 |
|---|---|---|
[0] |
T |
현재 값 (controlled 또는 uncontrolled) |
[1] |
(value: T | ((prev: T) => T)) => void |
값 설정 함수 (함수형 업데이트 지원) |
동작 원리
- Controlled 모드:
controlledValue가undefined가 아니면, 외부 값을 그대로 사용 - Uncontrolled 모드:
controlledValue가undefined면, 내부 상태 사용 - 자동 동기화: 외부
controlledValue가 변경되면 내부 상태도 동기화 - onChange 호출:
setValue호출 시onChange콜백 자동 호출 - 함수형 업데이트:
setValue(prev => newValue)형태 지원
사용 예시
// DataGrid에서 정렬 상태 관리
const DataGrid: React.FC<Props> = ({
sortKey: externalSortKey,
sortOrder: externalSortOrder,
onSort,
...props
}) => {
const [sortKey, setSortKey] = G7Core.useControllableState(
externalSortKey,
null,
(key) => onSort?.(key, sortOrder)
);
const [sortOrder, setSortOrder] = G7Core.useControllableState(
externalSortOrder,
'asc',
(order) => onSort?.(sortKey, order)
);
// 함수형 업데이트 예시
const toggleSortOrder = () => {
setSortOrder(prev => prev === 'asc' ? 'desc' : 'asc');
};
// ...
};
// PermissionTree에서 선택된 권한 관리 (배열 비교 함수 사용)
const PermissionTree: React.FC<Props> = ({ value, onChange }) => {
// 배열 비교 함수 정의
const areArraysEqual = (a: number[], b: number[]): boolean => {
if (a.length !== b.length) return false;
const sortedA = [...a].sort((x, y) => x - y);
const sortedB = [...b].sort((x, y) => x - y);
return sortedA.every((val, idx) => val === sortedB[idx]);
};
const [selectedIds, setSelectedIds] = G7Core.useControllableState<number[]>(
value,
[],
onChange,
{ isEqual: areArraysEqual }
);
// 함수형 업데이트로 선택 토글
const toggleSelection = (id: number) => {
setSelectedIds(prev =>
prev.includes(id)
? prev.filter(x => x !== id)
: [...prev, id]
);
};
// ...
};
G7Core에서 훅 사용 패턴
컴포넌트에서 G7Core의 훅을 사용할 때 타입 안전성을 위한 패턴:
// G7Core 전역 객체에서 훅 가져오기
const G7Core = (window as any).G7Core;
// 타입 캐스팅으로 훅 정의
const useControllableState = G7Core?.useControllableState as
| (<T>(
controlledValue: T | undefined,
defaultValue: T,
onChange?: (value: T) => void,
options?: { isEqual?: (a: T, b: T) => boolean }
) => [T, (value: T | ((prev: T) => T)) => void])
| undefined;
// 폴백 로직과 함께 사용
const [state, setState] = useControllableState
? useControllableState<MyType>(externalValue, defaultValue, handleChange)
: useState<MyType>(defaultValue);
G7Core.shallowArrayEqual
배열의 얕은 비교를 수행합니다. 배열 길이와 각 인덱스의 요소를 ===로 비교합니다.
G7Core.shallowArrayEqual<T>(a: T[], b: T[]): boolean
동작 원리
- 두 배열이 같은 참조면
true반환 - 배열 길이가 다르면
false반환 - 각 인덱스의 요소를
===로 비교 (얕은 비교)
사용 예시
// 기본 타입 배열
G7Core.shallowArrayEqual([1, 2, 3], [1, 2, 3]); // true
G7Core.shallowArrayEqual([1, 2], [1, 2, 3]); // false
// 객체 배열 (참조 비교)
const obj = { id: 1 };
G7Core.shallowArrayEqual([obj], [obj]); // true (같은 참조)
G7Core.shallowArrayEqual([{ id: 1 }], [{ id: 1 }]); // false (다른 참조)
// useControllableState와 함께 사용
const [selectedIds, setSelectedIds] = G7Core.useControllableState<number[]>(
value,
[],
onChange,
{ isEqual: G7Core.shallowArrayEqual }
);
주의: 배열 내 객체는 참조로 비교됩니다. 객체 내용까지 비교하려면 커스텀 비교 함수를 작성하세요.
G7Core.shallowObjectEqual
객체의 얕은 비교를 수행합니다. 1단계 속성만 ===로 비교합니다.
G7Core.shallowObjectEqual<T extends Record<string, any>>(a: T, b: T): boolean
동작 원리
- 두 객체가 같은 참조면
true반환 - 키 개수가 다르면
false반환 - 각 키의 값을
===로 비교 (얕은 비교)
사용 예시
// 기본 속성 비교
G7Core.shallowObjectEqual({ a: 1, b: 2 }, { a: 1, b: 2 }); // true
G7Core.shallowObjectEqual({ a: 1 }, { a: 1, b: 2 }); // false (키 개수 다름)
G7Core.shallowObjectEqual({ a: 1, b: 2 }, { a: 1, b: 3 }); // false (값 다름)
// 중첩 객체 (참조 비교)
const nested = { x: 1 };
G7Core.shallowObjectEqual({ data: nested }, { data: nested }); // true (같은 참조)
G7Core.shallowObjectEqual({ data: { x: 1 } }, { data: { x: 1 } }); // false (다른 참조)
// useControllableState와 함께 사용
const [formData, setFormData] = G7Core.useControllableState<FormData>(
value,
{ name: '', email: '' },
onChange,
{ isEqual: G7Core.shallowObjectEqual }
);
주의: 중첩 객체는 참조로 비교됩니다. 깊은 비교가 필요하면 커스텀 비교 함수를 작성하세요.
타입 정의
전역 타입 선언
declare global {
interface Window {
G7Core: {
// 상태 관리
state: {
get: () => Record<string, any>;
set: (updates: Record<string, any>) => void;
update: (updater: (prev: Record<string, any>) => Record<string, any>) => void;
subscribe: (listener: (state: Record<string, any>) => void) => () => void;
getDataSource: (id: string) => DataSourceValue | undefined;
};
// 토스트
toast: {
show: (message: string, options?: ToastOptions) => void;
success: (message: string, duration?: number) => void;
error: (message: string, duration?: number) => void;
warning: (message: string, duration?: number) => void;
info: (message: string, duration?: number) => void;
};
// 모달
modal: {
open: (modalId: string) => void;
close: (modalId?: string) => void;
closeAll: () => void;
isOpen: (modalId: string) => boolean;
getStack: () => string[];
};
// 네비게이션
navigation: {
isPending: () => boolean;
onComplete: (callback: () => void) => () => void;
};
// 다국어
locale: {
current: () => string;
supported: () => string[];
change: (locale: string) => Promise<void>;
};
t: (key: string, params?: Record<string, string | number>) => string;
// 확장 자산 URL (engine-v1.62.0+)
asset: {
template: (identifier: string, path: string, version?: number | string | null) => string;
templateDir: (identifier: string, path: string) => string;
module: (identifier: string, path: string, version?: number | string | null) => string;
plugin: (identifier: string, path: string, version?: number | string | null) => string;
convertToCurrentMode: (url: string) => string;
// 출처 게이트 적용 (engine-v1.64.0+) — 미신뢰 URL 은 reject
loadScript: (url: string, attrs?: Record<string, string>, options?: Record<string, unknown>) => Promise<void>;
loadStylesheet: (url: string, attrs?: Record<string, string>, options?: Record<string, unknown>) => Promise<void>;
// 같은 판정을 직접 물어보는 seam (engine-v1.64.0+)
isAllowedScriptSrc: (url: string) => boolean;
};
// 자산 실패 안내 (engine-v1.62.0+)
assets: {
notifyFailure: (failure: AssetFailure) => void;
clearFailure: (id: string) => void;
clearAll: () => void;
getFailures: () => AssetFailure[];
retryAll: () => Promise<void>;
};
// 액션
dispatch: (action: ActionConfig) => Promise<ActionResult>;
// 이벤트
componentEvent: {
on: (eventName: string, callback: (data?: any) => void | Promise<any>) => () => void;
emit: (eventName: string, data?: any) => Promise<any[]>;
off: (eventName: string) => void;
clear: () => void;
};
// 이벤트 헬퍼
createChangeEvent: (value: any, name?: string) => any;
createClickEvent: (data?: any) => any;
createSubmitEvent: (formData?: any) => any;
createKeyboardEvent: (key: string, modifiers?: KeyModifiers) => any;
// 렌더링 헬퍼
renderItemChildren: (children: any[], itemContext: Record<string, any>, componentMap: Record<string, any>, keyPrefix?: string, options?: any) => React.ReactNode[];
renderComponentLayout: (layoutDefs: any[] | undefined, itemContext: Record<string, any>, keyPrefix?: string) => React.ReactNode;
renderExpandContent: (config: { children: any[]; row: any; expandContext?: Record<string, any>; componentContext?: any; componentMap?: Record<string, any>; keyPrefix: string }) => React.ReactNode;
getComponentMap: () => Record<string, React.ComponentType>;
// 스타일 헬퍼
style: {
mergeClasses: (base: string, override: string) => string;
conditionalClass: (conditions: Record<string, boolean>) => string;
joinClasses: (...classes: (string | false | undefined)[]) => string;
};
// 플러그인 설정
plugin: {
getSettings: (pluginId: string) => Record<string, any> | undefined;
get: (pluginId: string, key: string, defaultValue?: any) => any;
getAll: () => Record<string, Record<string, any>>;
};
// 모듈 설정
module: {
getSettings: (moduleId: string) => Record<string, any> | undefined;
get: (moduleId: string, key: string, defaultValue?: any) => any;
getAll: () => Record<string, Record<string, any>>;
};
// WebSocket
websocket: {
subscribe: (channel: string, event: string, callback: (data: any) => void, options?: any) => string;
unsubscribe: (subscriptionKey: string) => void;
leaveChannel: (channel: string) => void;
disconnect: () => void;
isInitialized: () => boolean;
getSubscriptionCount: () => number;
};
// React Hooks
useControllableState: <T>(
controlledValue: T | undefined,
defaultValue: T,
onChange?: (value: T) => void,
options?: { isEqual?: (a: T, b: T) => boolean }
) => [T, (value: T | ((prev: T) => T)) => void];
shallowArrayEqual: <T>(a: T[], b: T[]) => boolean;
shallowObjectEqual: <T extends Record<string, any>>(a: T, b: T) => boolean;
useResponsive: () => ResponsiveState;
useTransitionState: () => TransitionState;
useTranslation: () => { t: TranslationFunction };
// 위지윅 편집기
wysiwyg: {
isEditMode: () => boolean;
setEditMode: (layoutName: string, templateId: string) => void;
clearEditMode: () => void;
getCurrentLayoutName: () => string | null;
getCurrentTemplateId: () => string | null;
isEditModeFromUrl: () => boolean;
getEditModeUrl: (route: string, templateId: string) => string;
enterEditMode: (route: string, templateId: string) => void;
exitEditMode: () => void;
getVersion: () => string;
getPhase: () => number;
};
// 기타
AuthManager: AuthManagerType;
api: ApiClientType;
ResponsiveManager: ResponsiveManagerType;
TransitionManager: TransitionManagerType;
};
}
}
관련 문서
- G7Core 기본 API - 상태 관리, 토스트, 모달, 네비게이션, 플러그인/모듈 설정, 확장 자산
- state-management.md - 전역 상태 관리 상세
- components.md - 컴포넌트 개발 규칙
- data-binding.md - 데이터 바인딩 문법
- auth-system.md - 인증 시스템
- responsive-layout.md - 반응형 레이아웃