TypeScript / Node SDK

@junyul/core (runtime-agnostic) + 런타임별 얇은 어댑터. 노드 서버·Next.js· 브라우저·엣지 런타임에서 같은 이벤트 모델을 공유합니다.

릴리스 채널. 공개 npm 배포 전에는 도입 고객에게 제공되는 고객 전용 registry 또는 검증된 tarball로 설치합니다. 아래 명령은 공식 registry 또는 고객 registry가 연결된 환경 기준이며, 접근 권한은 support@junyul.com에서 확인합니다.

패키지 구조

패키지런타임비고
@junyul/coreuniversal (fetch + crypto.subtle)타입 + UUIDv7 + CircuitBreaker + MemoryOutbox
@junyul/nodeNode 20+Express/Fastify 미들웨어, NestJS 모듈, SqliteOutbox
@junyul/browser브라우저고지 배너 + 원격 로그 (제한적)
@junyul/reactReact 18+훅: useTrack, useDisclosure
@junyul/vueVue 3Composable: useJunyul

설치

# 공개 npm 릴리스 또는 고객 전용 registry 연결 후
pnpm add @junyul/node

# Edge, Worker, 라이브러리처럼 런타임 중립 코어만 필요한 경우
pnpm add @junyul/core

브라우저 전용:

pnpm add @junyul/browser
pnpm add @junyul/react   # React 앱
pnpm add @junyul/vue     # Vue 앱

초기화

import { init, recordDecision } from '@junyul/node';

init({
  apiKey: process.env.JUNYUL_API_KEY!,
  tenantSecret: process.env.JUNYUL_TENANT_SECRET!, // HMAC anonymization
  environment: 'production',
});

recordDecision('automated_decision_made', {
  assetId: 'credit_scoring_v3',
  legal_bases: ['ai_basic_law_kr', 'credit_info_36_2_kr'],
  risk_tier: 'high',
});

Express 통합

import express from 'express';
import { complyMiddleware, attachDisclosure } from '@junyul/node';

const app = express();
app.use(complyMiddleware({ defaultAssetId: 'chatbot_v1', locale: 'ko' }));
app.get('/chat', attachDisclosure('chatbot_v1'), handler);

Next.js (App Router) 통합

// app/api/ai/route.ts
import { track } from '@junyul/core';

export async function POST(req: Request) {
  return track('chatbot_v1', async () => {
    const body = await req.json();
    return Response.json(await callLLM(body.message));
  });
}

성능 & 신뢰성

  • 32 event types discriminated union · Zod 런타임 검증
  • UUIDv7 (time-sortable) · UUIDv5(idempotencyKey) 결정적 event_id
  • CircuitBreaker (5 실패 → OPEN 30s cooldown → HALF_OPEN)
  • Outbox: 브라우저/엣지 Memory, Node는 @junyul/nodeSqliteOutbox(better-sqlite3) 선택 가능
  • HMAC-SHA256 subject_hash: 원본 user_id는 절대 전송되지 않음

다음