---
title: "@vonvon-kit/electron"
description: "메인 프로세스 PKCE 흐름, contextBridge preload, OS keychain 토큰 저장소, loopback 또는 사용자 정의 스킴 콜백 전략이 있는 Electron SDK."
locale: "ko"
---

> Documentation Index
> Fetch the relevant documentation index at: https://vonvon.id/ko/sdks/llms.txt
> Use this file to discover all available pages before exploring further.

# @vonvon-kit/electron

## 상태

Registry 상태: UNPUBLISHED. 이 SDK는 저장소 소스 checkout에서만 설치하고 외부 package registry를 사용하지 마세요.

패키지 상태: **현재 패키지**. 메인 프로세스 앱, contextBridge preload, 렌더러 측 브리지가 구현되었습니다. 프로덕션 인프라에서의 실제 IdP 왕복 테스트는 아직 수동 검증 대기 중입니다.

## 진입점

| 항목 | 목적 |
| --- | --- |
| `@vonvon-kit/electron` | 기본 내보내기: 렌더러 인터페이스 및 타입 |
| `@vonvon-kit/electron/main` | 메인 프로세스 전용: VonvonElectronApp |
| `@vonvon-kit/electron/renderer` | 렌더러 프로세스: getVonvonBridge, VonvonClient |
| `@vonvon-kit/electron/preload` | window.vonvonBridge를 노출하는 기성 preload 스크립트 |

## 메인 프로세스 설정

```ts
// main.ts
import { app, ipcMain } from 'electron'
import { VonvonElectronApp } from '@vonvon-kit/electron/main'

const vonvonApp = new VonvonElectronApp({
  issuer: 'https://vonvon.id',
  clientId: 'client_abc123',
  // callbackStrategy: 'loopback' (default, RFC 8252 s.7.3) | 'custom-scheme'
})

app.whenReady().then(async () => {
  await vonvonApp.init(ipcMain)
  const win = new BrowserWindow({
webPreferences: {
  contextIsolation: true,
  preload: path.join(__dirname, 'preload.js'),
},
  })
  win.on('closed', () => vonvonApp.dispose(ipcMain))
})
```

## Preload 스크립트

```ts
// preload.ts
import '@vonvon-kit/electron/preload'
// Exposes window.vonvonBridge with storage, signIn, signOut,
// getAccessToken, getSession, setTokenStorage
```

## 렌더러 프로세스

```ts
import { getVonvonBridge } from '@vonvon-kit/electron/renderer'

const bridge = getVonvonBridge()

// Opens system browser, waits for loopback callback, exchanges code.
const accessToken = await bridge.signIn()

// Get the current unexpired access token. Expiry requires a new sign-in.
const token = await bridge.getAccessToken() // null when signed out or expired

// Get the current unexpired session (accessToken + expiresAt in epoch seconds).
const session = await bridge.getSession()

// Clear local tokens. No refresh or revoke request is sent.
await bridge.signOut()
```

## 사용자 정의 스킴(loopback의 대안)

```ts
// main.ts
import { app } from 'electron'
import { VonvonElectronApp } from '@vonvon-kit/electron/main'

app.setAsDefaultProtocolClient('myapp')

const vonvonApp = new VonvonElectronApp({
  issuer: 'https://vonvon.id',
  clientId: 'client_abc123',
  callbackStrategy: 'custom-scheme',
  customScheme: 'myapp',  // redirect_uri = myapp://callback
})

vonvonApp.registerDeepLinkHandler(app)
```

## 토큰 저장소

- 토큰은 `safeStorage.encryptString()`(OS keychain)으로 암호화되어 기본적으로 `app.getPath('userData')/vonvon-tokens/`에 바이너리 파일로 저장됩니다.
- `safeStorage.isEncryptionAvailable()`이 `false`(키링이 없는 헤드리스 Linux)를 반환하면, `setItem()`은 평문을 조용히 쓰는 대신 코드 `encryption_unavailable`로 `ElectronStorageError`를 발생시킵니다.
- `VonvonElectronMainOptions`의 `storageDir`으로 저장소 디렉터리를 재정의하세요.

## 공유 네이티브 계약

| 방법 | 설명 |
| --- | --- |
| `signIn(options?)` | 시스템 브라우저를 열고 코드를 교환하며 토큰을 저장합니다 |
| `signOut()` | 로컬 토큰 삭제 |
| `getAccessToken()` | refresh 없이 현재 만료되지 않은 access token을 반환합니다. 만료된 로컬 state를 지우고 null을 반환합니다. DPoP가 구현될 때까지 SDK는 offline\_access를 거부합니다. |
| `getSession()` | 현재 만료되지 않은 accessToken과 expiresAt을 반환하거나 null을 반환합니다. |

Source: https://vonvon.id/ko/sdks/electron/index.mdx
