---
title: "@vonvon-kit/remix"
description: "React SDK 재내보내기가 포함된 Remix loader/action 서버 헬퍼, cookie 세션 저장소, OAuth 콜백 핸들러."
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/remix

## 상태

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

패키지 상태: **현재 패키지**. Remix loader/action 인증 헬퍼, cookie 세션 통합, OAuth 콜백 핸들러가 구현되었습니다. 프로덕션 인프라에서의 실제 IdP 왕복 테스트는 아직 수동 검증 대기 중입니다.

## 세션 저장소 설정

```ts
// app/sessions.server.ts
import { createVonvonSessionStorage } from '@vonvon-kit/remix'

export const sessionStorage = createVonvonSessionStorage({
  secret: process.env.SESSION_SECRET!, // required: cookie signing secret
  // cookieName: '__vonvon_session', maxAge: 2592000, secure: true
})
```

## loader에서 인증 읽기

`getAuth`는 Bearer 토큰, 명시적인 애플리케이션 JWT cookie, 선택적 동일 출처 Core exchange 또는 설정된 Remix 세션 JWT를 검증한 뒤 `AuthResult`를 반환합니다. `requireAuth`는 미인증 시 `redirectPath`로 302 리디렉션을 발생시킵니다.

```ts
import { getAuth, requireAuth } from '@vonvon-kit/remix'
import { json, redirect } from '@remix-run/node'
import type { LoaderFunctionArgs } from '@remix-run/node'
import { sessionStorage } from '~/sessions.server'

const jwtKey = JSON.parse(process.env.VONVON_JWKS_PUBLIC_KEY!)
const authOptions = {
  jwtKey,
  sessionStorage,
  sessionTokenExchange: { endpoint: '/v1/sessions/token' },
}

// Optional check
export async function loader({ request }: LoaderFunctionArgs) {
  const auth = await getAuth(request, authOptions)
  if (!auth.userId) return redirect('/login')
  return json({ userId: auth.userId, orgId: auth.orgId })
}

// Guard: throws redirect automatically when unauthenticated
export async function protectedLoader({ request }: LoaderFunctionArgs) {
  const auth = await requireAuth(request, authOptions, { redirectPath: '/login' })
  return json({ userId: auth.userId })
}
```

## OAuth 콜백 핸들러

`handleCallback`은 CSRF를 방지하기 위해 `state` 파라미터를 검증하고, 인증 코드를 교환한 후 `Set-Cookie`가 포함된 `Response`를 반환합니다.

```ts
// app/routes/auth.callback.ts
import { handleCallback } from '@vonvon-kit/remix'
import type { ActionFunctionArgs } from '@remix-run/node'
import { sessionStorage } from '~/sessions.server'

export async function action({ request }: ActionFunctionArgs) {
  const result = await handleCallback(request, {
clientId: process.env.VONVON_CLIENT_ID!,
redirectUri: process.env.VONVON_REDIRECT_URI!,
sessionStorage,
defaultReturnTo: '/dashboard',
  })

  if (!result.ok) throw new Response(result.error, { status: 400 })
  return result.response // 302 redirect + Set-Cookie
}
```

## 클라이언트 provider(root.tsx)

```tsx
import { VonvonProvider } from '@vonvon-kit/remix' // re-export from @vonvon-kit/react
import { Outlet } from '@remix-run/react'

export default function App() {
  return (
<VonvonProvider
  mode="oidc"
  issuer="https://vonvon.id"
  clientId="client_abc123"
  redirectUri="https://app.example.com/auth/callback"
>
  <Outlet />
</VonvonProvider>
  )
}
```

## 관리 API 클라이언트

```ts
import { vonvonClient } from '@vonvon-kit/remix'

const client = vonvonClient({ secretKey: process.env.VONVON_SECRET_KEY! })

export async function loader() {
  const result = await client.getUser('user_abc')
  if (!result.ok) throw new Response(result.error.message, { status: result.error.status })
  return json(result.value)
}
```

## 내보내진 API

| 내보내기 | 종류 | 목적 |
| --- | --- | --- |
| `createVonvonSessionStorage` | function | Vonvon 토큰을 위한 Remix cookie 세션 저장소 |
| `getAuth` | function | JWT 또는 세션 토큰을 검증합니다; AuthResult를 반환합니다 |
| `requireAuth` | function | getAuth와 유사하지만 미인증 시 리디렉션 응답을 발생시킵니다 |
| `handleCallback` | function | OAuth 콜백: state를 검증하고 코드를 교환하며 세션 cookie를 설정합니다 |
| `vonvonClient` | function | secret key에 바인딩된 서버 측 관리 API 클라이언트를 반환합니다 |
| `getTokenFromSession, setTokensInSession, clearTokensFromSession` | 함수 | 사용자 정의 세션 처리를 위한 저수준 토큰 헬퍼 |

## 재내보내기

`root.tsx`가 provider와 클라이언트 컴포넌트 모두에 대해 하나의 가져오기만 필요하도록 모든 [@vonvon-kit/react](/ko/sdks/react) 클라이언트 컴포넌트와 hook을 재내보냅니다.

## PKCE 및 보안

- 공개 클라이언트는 PKCE S256을 사용한 Authorization Code를 사용합니다. client secret은 저장되지 않습니다.
- `handleCallback`은 CSRF를 방지하기 위해 세션에 대한 `state` 파라미터를 검증합니다.
- 액세스 토큰은 `HttpOnly` 세션 cookie에 저장되며 `localStorage`에는 기록되지 않습니다.

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