> ## Documentation Index
> Fetch the complete documentation index at: https://docs.nora.my/llms.txt
> Use this file to discover all available pages before exploring further.

# 클라이언트 옵션

> ClientOptions 전체 레퍼런스 : baseUrl, 타임아웃, 재시도, 로깅, 커스텀 fetch

`createClient(opts)`가 받는 모든 필드입니다. `ClientOptions` TypeScript 타입과 동일합니다.

## 필수

### `tenant: string`

클라이언트가 동작할 워크스페이스 id (`t_…`). 안 넘기면 `NORA_TENANT` env에서 읽습니다.

## 자격증명

### `token?: string`

관리용 호출에 쓸 PAT (`nora_pat_…`). 안 넘기면 `NORA_PAT` env에서 읽습니다. 트리거 시크릿으로 부르는 호출(`flows.run` · `feedback` · `signals.report`)만 쓸 거면 필수는 아닙니다.

### `triggerSecret?: string`

Flow별 실행 시크릿. 안 넘기면 `NORA_TRIGGER_SECRET` env에서 읽습니다. `flows.run`과 `signals.report`에 필수입니다.

### `triggerHeader?: string`

트리거가 시크릿을 기대하는 헤더. Trigger 블록에 설정된 `api_auth_header`와 맞춰야 합니다. 기본 `authorization`, `Bearer <secret>`으로 보냅니다.

트리거가 비표준 헤더를 받도록 설정된 경우에 오버라이드:

```ts theme={null}
createClient({
  // …
  triggerHeader: "x-nora-trigger",   // 트리거 설정이 x-nora-trigger 였을 때
});
```

## 전송

### `baseUrl?: string`

서버 base URL. 기본 `https://platform.nora.my/api/v1` (또는 `NORA_BASE_URL` env).

self-hosted · 스테이징 배포엔 오버라이드:

```ts theme={null}
createClient({
  // …
  baseUrl: "https://staging.platform.example.com/api/v1",
});
```

### `timeoutMs?: number`

호출별 기본 타임아웃 (ms). **기본 `30_000`** (30초).

개별 verb는 자기 옵션 객체에 `timeoutMs`를 넘겨 덮을 수 있습니다 (예: `flows.run(slug, input, { timeoutMs: 60_000 })`).

### `maxRetries?: number`

서버가 retryable로 판정한 실패(`NoraProviderError` · `NoraPlatformError`의 `.retryable === true`)에 대한 재시도 횟수. **기본 `2`**.

모든 시도가 `attempt`와 실패 `code`로 로깅됩니다. 조용한 재시도는 없습니다. [에러 · 로깅](/ko/sdk/errors#로깅) 참고.

SDK 레이어 재시도를 끄려면 `0` (앱 레이어에서 직접 처리):

```ts theme={null}
createClient({
  // …
  maxRetries: 0,
});
```

### `fetchImpl?: typeof fetch`

커스텀 `fetch` 구현. 기본은 `globalThis.fetch`입니다. 이럴 때 씁니다:

* **테스트 seam**. mock 주입:

  ```ts theme={null}
  createClient({ /* … */ fetchImpl: mockFetch });
  ```

* **커스텀 런타임**. `undici` 붙인 Node, 특정 fetch 폴리필의 엣지 워커, HTTP 프록시 shim.

시그니처는 표준 `fetch(input, init?)`와 맞아야 합니다.

## 관측

### `logger?: Logger`

SDK 내부 기록을 받을 구조화 로거. pino · winston · bunyan, 또는 이 인터페이스와 맞는 것 아무거나:

```ts theme={null}
interface Logger {
  debug?(msg: string, meta?: unknown): void;
  info?(msg: string, meta?: unknown): void;
  warn?(msg: string, meta?: unknown): void;
  error?(msg: string, meta?: unknown): void;
}
```

SDK는 **기본으로 레드액션**입니다. 토큰 · 시크릿 · `onBehalfOf` 값 · 요청/응답 본문은 `debug: true` 없이는 로그에 남지 않습니다.

### `debug?: boolean`

`true`면 요청/응답 **본문**이 로그에 실립니다 (지정한 `logger`로). 기본은 off입니다.

사고 재현할 때만 켜세요. 공유 클라이언트가 아니라 스코프드 클론에서 켜는 게 좋습니다. 본문에 PII가 들어 있을 수 있습니다.

### `onLog?: (event: LogEvent) => void`

메트릭용으로 SDK 내부 이벤트 관측 (request · response · retry · error). 이벤트 모양:

```ts theme={null}
interface LogEvent {
  event: "request" | "response" | "retry" | "error";
  traceId?: string;
  flow?: string;      // 이벤트가 특정 Flow에 걸렸을 때 slug
  attempt?: number;   // retry 때 채워짐
  durationMs?: number;// response · error 때 채워짐
  status?: string;    // HTTP 상태 또는 "aborted"
  code?: string;      // error · retry 때의 실패 code
}
```

시크릿·요청 본문은 절대 안 실립니다. 메트릭 싱크 어디에든 그대로 보내도 안전합니다.

메트릭 스택에 꽂기:

```ts theme={null}
createClient({
  // …
  onLog: (e) => {
    switch (e.event) {
      case "retry":              metrics.increment("nora.retry", { code: e.code }); break;
      case "provider.fallback":  metrics.increment("nora.fallback"); break;
    }
  },
});
```

재시도율 · 폴백 빈도 · 서킷 오픈율은 상위 프로바이더가 삐걱대기 시작할 때 가장 먼저 움직이는 지표입니다.

## 전체 예시

```ts theme={null}
import { createClient } from "@conscience-technology/nora-sdk";
import pino from "pino";

const nora = createClient({
  tenant: process.env.NORA_TENANT!,
  token: process.env.NORA_PAT,
  triggerSecret: process.env.NORA_TRIGGER_SECRET,
  baseUrl: process.env.NORA_BASE_URL,     // 안 넘기면 기본 platform.nora.my
  timeoutMs: 30_000,
  maxRetries: 2,
  logger: pino({ level: "info", redact: ["*.token", "*.secret"] }),
  debug: false,
  onLog: (e) => metrics.count("nora." + e.event),
});
```

## 관련

* [Auth](/ko/sdk/auth) 자격증명 + PAT 스코프.
* [에러](/ko/sdk/errors) 전송 실패가 어떻게 드러나는지, 재시도 로직이 어디 적용되는지.
