# @nolraunsoft/appify-sdk

하이브리드 앱 웹뷰 통신을 위한 통합 SDK

[![npm version](https://img.shields.io/npm/v/@nolraunsoft/appify-sdk.svg)](https://www.npmjs.com/package/@nolraunsoft/appify-sdk)
[![license](https://img.shields.io/npm/l/@nolraunsoft/appify-sdk.svg)](https://github.com/nolraunsoft/appify-sdk/blob/main/LICENSE)

## 소개

Appify SDK는 React Native WebView 기반 하이브리드 앱에서 웹과 네이티브 간의 통신을 쉽게 구현할 수 있도록 도와주는 SDK입니다.

## 설치

```bash
npm install @nolraunsoft/appify-sdk
```

## 빠른 시작

```typescript
import { appify } from '@nolraunsoft/appify-sdk';

// 초기화 (앱 시작 시 한 번만)
await appify.initialize({ debug: true });

// 디바이스 정보 가져오기
const info = await appify.device.getInfo();
```

## 싱글턴 패턴

SDK는 싱글턴으로 동작합니다:

- 여러 파일에서 `import { appify }` 해도 **동일한 인스턴스**
- `initialize()` 여러 번 호출해도 **최초 1회만 실행**
- `window.appify`로도 접근 가능

```typescript
import { appify } from '@nolraunsoft/appify-sdk';

console.log(appify === window.appify); // true
```

## React 예시

### Provider 설정 (권장)

```tsx
"use client";

import { ReactNode, useEffect } from "react";
import { appify } from "@nolraunsoft/appify-sdk";

export function AppifyProvider({ children }: { children: ReactNode }) {
  useEffect(() => {
    appify.initialize({ debug: true });
  }, []);
  return <>{children}</>;
}
```

```tsx
import { AppifyProvider } from "./providers/AppifyProvider";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="ko">
      <body>
        <AppifyProvider>{children}</AppifyProvider>
      </body>
    </html>
  );
}
```

### 페이지에서 사용

```tsx
"use client";
import { appify } from "@nolraunsoft/appify-sdk";

export default function Home() {
  const handleClick = async () => {
    if (appify.isWebview) {
      const info = await appify.device.getInfo();
      console.log(info);
    }
  };

  return <button onClick={handleClick}>디바이스 정보</button>;
}
```

## 주요 기능

### 저장소 (Storage)

```typescript
await appify.storage.setItem('user', { name: 'John', age: 30 });
const user = await appify.storage.getItem('user');
await appify.storage.removeItem('user');
await appify.storage.clear();
```

### 디바이스 정보 (Device)

```typescript
const info = await appify.device.getInfo();
// { platform: 'ios' | 'android', appVersion: '1.0.0', uniqueId: '...' }

const adId = await appify.device.getIdfaAdid();
```

### 소셜 로그인 (Auth)

```typescript
const result = await appify.auth.kakao.login();
const result = await appify.auth.naver.login();
const result = await appify.auth.google.login();
const result = await appify.auth.apple.login();

// 로그아웃
await appify.auth.kakao.logout();

// 연결 해제
await appify.auth.kakao.unlink();
```

### 카메라 / 바코드 스캔

```typescript
await appify.camera.openScanner();
const result = await appify.camera.scanOnce();
console.log(result.data);

// 이벤트 구독 방식
const subscription = appify.camera.onScan((result) => {
  console.log(result.data);
});
subscription.unsubscribe();
```

### 위치 정보 (Location)

```typescript
const granted = await appify.location.checkPermission();
const position = await appify.location.getCurrentPosition();
// { latitude, longitude, accuracy, altitude, ... }
```

### 푸시 알림 (Notification)

```typescript
const token = await appify.notification.getToken();

await appify.notification.sendLocalNotification({
  title: '알림 제목',
  body: '알림 내용'
});
```

### 햅틱 피드백 (Haptic)

```typescript
await appify.haptic.trigger('impactMedium');
// 'impactLight' | 'impactMedium' | 'impactHeavy'
// 'notificationSuccess' | 'notificationWarning' | 'notificationError'
// 'selection'
```

### 공유 (Share)

```typescript
await appify.share.systemShare({
  title: '공유 제목',
  message: '공유 메시지',
  url: 'https://example.com'
});

await appify.share.kakaoShare({
  templateId: 12345,
  templateArgs: { key: 'value' }
});
```

### 링크 열기 (Linking)

```typescript
await appify.linking.externalBrowser('https://example.com');
await appify.linking.inappBrowser('https://example.com');
await appify.linking.openSettings();
```

### 파일 다운로드 (Download)

```typescript
await appify.download.file('https://example.com/file.pdf');
await appify.download.image('https://example.com/image.jpg');

// 파일명 지정
await appify.download.fileWithOptions({
  url: 'https://example.com/file.pdf',
  fileName: 'custom-name.pdf'
});
```

### 권한 관리 (Permission)

```typescript
const status = await appify.permission.check('camera');
// 'granted' | 'denied' | 'undetermined'

const results = await appify.permission.checkAll(['camera', 'location', 'notification', 'contacts']);
```

### 앱 상태 이벤트 (Event)

```typescript
const subscription = appify.event.onAppStateChange((state) => {
  console.log(state); // 'active' | 'background' | 'inactive'
});

subscription.unsubscribe();
```

### 클립보드 (Clipboard)

```typescript
await appify.clipboard.setText('복사할 텍스트');
const text = await appify.clipboard.getText();
```

### 연락처 (Contacts)

```typescript
const granted = await appify.contacts.checkPermission();
const contacts = await appify.contacts.getContacts();
```

### 앱 리뷰 (Review)

```typescript
await appify.review.request();
```

### 애널리틱스 (Analytics)

```typescript
await appify.analytics.logEvent('button_click', { screen: 'home' });
```

## 환경 감지

```typescript
appify.isWebview;           // 웹뷰 환경 여부
appify.isWeb;               // 웹 환경 여부
appify.environment.isIOS;   // iOS 여부
appify.environment.isAndroid; // Android 여부
```

## 초기화 옵션

```typescript
await appify.initialize({
  debug: true,                    // 'console' | 'alert' | 'both' | true | false
  serializedPermissions: true,    // 권한 요청 직렬화
  bounces: false,                 // iOS 바운스 효과
  hideScrollbar: true,            // 스크롤바 숨김
  enableRefresh: false,           // Pull-to-refresh
  statusBar: {
    barStyle: 'dark-content',     // 'dark-content' | 'light-content'
    backgroundColor: '#ffffff'
  },
  safeArea: {
    edge: ['top', 'bottom']
  }
});
```

## TypeScript 지원

```typescript
import type { 
  DeviceInfo, 
  Position, 
  AuthResult, 
  AppleAuthResult,
  PermissionStatus,
  PermissionType,
  HapticType,
  AppState
} from '@nolraunsoft/appify-sdk';
```

## 에러 처리

```typescript
import { 
  AppifyError,
  TimeoutError, 
  BridgeNotAvailableError,
  PermissionDeniedError,
  ConcurrentRequestError
} from '@nolraunsoft/appify-sdk';

try {
  await appify.device.getInfo();
} catch (error) {
  if (error instanceof BridgeNotAvailableError) {
    console.log('웹뷰 환경이 아닙니다');
  } else if (error instanceof TimeoutError) {
    console.log('요청 시간 초과');
  }
}
```

## CDN 사용

```html
<script src="https://cdn.jsdelivr.net/npm/@nolraunsoft/appify-sdk@latest/dist/appify-sdk.min.js"></script>
<script>
  appify.initialize({ debug: true });
</script>
```

### ESM

```html
<script type="module">
  import { appify } from 'https://cdn.jsdelivr.net/npm/@nolraunsoft/appify-sdk@latest/dist/appify-sdk.esm.min.js';
  appify.initialize({ debug: true });
</script>
```

### 제공 파일

| 파일 | 용도 |
| :--- | :--- |
| `appify-sdk.min.js` | IIFE 압축 버전 (권장) |
| `appify-sdk.js` | IIFE |
| `appify-sdk.esm.min.js` | ES Module 압축 버전 |
| `appify-sdk.esm.js` | ES Module |
| `appify-sdk.cjs.js` | CommonJS |

## License

ISC
