# G7Core 전역 API 레퍼런스 - 고급

> **버전**: engine-v1.4.0+
> **관련 문서**: [g7core-api.md](g7core-api.md) | [state-management.md](state-management.md) | [components.md](components.md)

---

## 목차

1. [다국어 (G7Core.locale, G7Core.t)](#다국어-g7corelocale-g7coret)
2. [액션 실행 (G7Core.dispatch)](#액션-실행-g7coredispatch)
3. [컴포넌트 이벤트 (G7Core.componentEvent)](#컴포넌트-이벤트-g7corecomponentevent)
4. [이벤트 생성 헬퍼](#이벤트-생성-헬퍼)
5. [컴포넌트 렌더링 헬퍼](#컴포넌트-렌더링-헬퍼)
6. [인증 및 API](#인증-및-api)
7. [반응형 및 전환 상태](#반응형-및-전환-상태)
8. [타입 정의](#타입-정의)

---

## 다국어 (G7Core.locale, G7Core.t)

로케일 관리 및 번역 API입니다.

### G7Core.locale API

| 메서드 | 설명 | 반환값 |
|--------|------|--------|
| `current()` | 현재 로케일 반환 | `string` (예: 'ko', 'en') |
| `supported()` | 지원 로케일 목록 | `string[]` |
| `change(locale)` | 로케일 변경 | `Promise<void>` |

### G7Core.t() 번역 함수

```typescript
G7Core.t(key: string, params?: Record<string, string | number>): string
```

| 파라미터 | 타입 | 설명 |
|----------|------|------|
| `key` | string | 번역 키 (예: 'common.confirm') |
| `params` | object | 번역 파라미터 (선택) |

### 사용 예시

```typescript
// 로케일 조회
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](components.md#6-다국어-번역-g7coret)

```tsx
// 컴포넌트 파일 상단에 선언
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입니다.

### 함수 시그니처

```typescript
G7Core.dispatch(action: ActionConfig): Promise<ActionResult>
```

### ActionConfig 구조

```typescript
interface ActionConfig {
  handler: string;           // 핸들러 이름 (예: 'api', 'navigate', 'toast')
  params?: Record<string, any>;  // 핸들러 파라미터
  target?: string;           // 대상 ID (모달 등)
}
```

### ActionResult 구조

```typescript
interface ActionResult {
  success: boolean;
  data?: any;
  error?: Error;
}
```

### 사용 예시

```typescript
// 네비게이션
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` |

### 사용 예시

```typescript
// 이벤트 구독
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 트리거

```typescript
// 파일 업로더 컴포넌트에서 이벤트 구독
useEffect(() => {
  const unsubscribe = G7Core.componentEvent.on(
    `triggerUpload:${uploaderId}`,
    async () => {
      inputRef.current?.click();
    }
  );
  return unsubscribe;
}, [uploaderId]);

// 다른 컴포넌트에서 업로드 트리거
G7Core.componentEvent.emit('triggerUpload:logo_uploader');
```

#### 데이터 동기화

```typescript
// 데이터 변경 알림
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` |

### 사용 예시

```typescript
// 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 핸들러를 직접 호출할 때 사용합니다:

```typescript
// 컴포넌트에서 프로그래밍 방식으로 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 등)에서 각 아이템의 자식 컴포넌트를 렌더링하는 헬퍼입니다.

```typescript
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 | 추가 옵션 (선택) |

### 사용 예시

```tsx
// 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

전체 컴포넌트 맵을 반환합니다:

```typescript
const componentMap = G7Core.getComponentMap();
// { Button: ButtonComponent, Input: InputComponent, ... }
```

### G7Core.renderComponentLayout

> **engine-v1.12.0+** 추가

`component_layout` 정의를 받아서 React 요소로 렌더링합니다. RichSelect 등에서 커스텀 아이템 렌더링 시 사용합니다.

```typescript
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 (선택) |

#### 사용 예시

```tsx
// 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줄 이하로 간소화합니다.

```typescript
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 |

#### 동작 원리

1. 전역 상태 가져오기
2. `componentContext.state`를 `_local`에 자동 병합
3. `expandContext` 바인딩 표현식 자동 평가
4. 최종 컨텍스트 구성 (`row`, `item`, `$item`, `_local`, `_global`, `_computed`)
5. 렌더링

#### 사용 예시

```tsx
// 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](auth-system.md)

```typescript
// 현재 사용자 정보
const user = G7Core.AuthManager.getUser();

// 로그인 상태 확인
const isLoggedIn = G7Core.AuthManager.isAuthenticated();

// 토큰 조회
const token = G7Core.AuthManager.getToken();
```

### G7Core.api

API 클라이언트 인스턴스입니다.

```typescript
// 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` |

### 사용 예시

```typescript
// 채널 구독
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());
}
```

### 컴포넌트에서 사용 패턴

```tsx
useEffect(() => {
  const key = G7Core.websocket.subscribe(
    'admin.orders',
    'order.created',
    (order) => {
      // 새 주문 처리
      refreshOrderList();
    }
  );

  return () => {
    G7Core.websocket.unsubscribe(key);
  };
}, []);
```

---

## 반응형 및 전환 상태

### G7Core.ResponsiveManager

반응형 상태를 관리합니다.

> 상세 내용: [responsive-layout.md](responsive-layout.md)

```typescript
// 현재 브레이크포인트 조회
const breakpoint = G7Core.ResponsiveManager.getCurrentBreakpoint();
// 'sm', 'md', 'lg', 'xl', '2xl'

// 모바일 여부
const isMobile = G7Core.ResponsiveManager.isMobile();
```

### G7Core.useResponsive (React Hook)

```tsx
const { breakpoint, isMobile, isTablet, isDesktop } = G7Core.useResponsive();
```

### G7Core.TransitionManager

페이지 전환 상태를 관리합니다.

```typescript
// 전환 중 여부
const isPending = G7Core.TransitionManager.getIsPending();

// 상태 변경 구독
const unsubscribe = G7Core.TransitionManager.subscribe((isPending) => {
  console.log('전환 상태:', isPending);
});
```

### G7Core.useTransitionState (React Hook)

```tsx
const { isPending } = G7Core.useTransitionState();
```

### G7Core.updateQueryParams

같은 페이지 내에서 URL 쿼리 파라미터만 변경하면서 컴포넌트 리마운트 없이 데이터소스를 refetch 한다. 일반적으로 `navigate` 핸들러의 `replace: true` 옵션 사용 시 엔진이 자동 호출하므로 직접 호출은 드물다.

```typescript
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 표시 |

**동작**:

1. `window.history.replaceState` 로 URL 갱신 (페이지 리로드 없음)
2. `auto_fetch: true` 인 데이터소스 refetch (websocket 제외)
3. `transition_overlay` 가 정의되어 있고 `blocking` 또는 `wait_for` 일치 progressive 데이터소스가 1개 이상이면 오버레이 자동 표시 (engine-v1.35.0+)
4. fetch 완료 시 `updateTemplateData` 호출 + `hideTransitionOverlay`
5. `computed` 재계산

**사용 예시**:

```typescript
// 일반: 쿼리 파라미터 변경 + 데이터 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.md](actions-handlers-navigation.md)
- `transition_overlay.wait_for` / `overlay_target` → [layout-json-components-loading.md](layout-json-components-loading.md)

---

## React Hooks

### G7Core.useControllableState

> **engine-v1.12.0+** 추가

Controlled/Uncontrolled 컴포넌트 패턴을 단순화하는 훅입니다. 외부 value prop과 내부 상태를 자동으로 동기화합니다.

```typescript
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)` 형태 지원

#### 사용 예시

```tsx
// 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');
  };

  // ...
};
```

```tsx
// 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의 훅을 사용할 때 타입 안전성을 위한 패턴:

```tsx
// 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

배열의 얕은 비교를 수행합니다. 배열 길이와 각 인덱스의 요소를 `===`로 비교합니다.

```typescript
G7Core.shallowArrayEqual<T>(a: T[], b: T[]): boolean
```

#### 동작 원리

1. 두 배열이 같은 참조면 `true` 반환
2. 배열 길이가 다르면 `false` 반환
3. 각 인덱스의 요소를 `===`로 비교 (얕은 비교)

#### 사용 예시

```typescript
// 기본 타입 배열
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단계 속성만 `===`로 비교합니다.

```typescript
G7Core.shallowObjectEqual<T extends Record<string, any>>(a: T, b: T): boolean
```

#### 동작 원리

1. 두 객체가 같은 참조면 `true` 반환
2. 키 개수가 다르면 `false` 반환
3. 각 키의 값을 `===`로 비교 (얕은 비교)

#### 사용 예시

```typescript
// 기본 속성 비교
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 }
);
```

> **주의**: 중첩 객체는 참조로 비교됩니다. 깊은 비교가 필요하면 커스텀 비교 함수를 작성하세요.

---

## 타입 정의

### 전역 타입 선언

```typescript
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;

      // 액션
      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](g7core-api.md) - 상태 관리, 토스트, 모달, 네비게이션, 플러그인/모듈 설정
- [state-management.md](state-management.md) - 전역 상태 관리 상세
- [components.md](components.md) - 컴포넌트 개발 규칙
- [data-binding.md](data-binding.md) - 데이터 바인딩 문법
- [auth-system.md](auth-system.md) - 인증 시스템
- [responsive-layout.md](responsive-layout.md) - 반응형 레이아웃
