API 레퍼런스

Custom Field SDK 전체 API 레퍼런스

API 레퍼런스

@wallaform/custom-field-sdk 패키지의 전체 레퍼런스입니다.

WallaField

커스텀 필드를 만들기 위한 메인 클래스입니다. 연결 설정, RPC 세션 관리, 호스트 폼과의 통신을 처리합니다.

생성자

import { WallaField } from '@wallaform/custom-field-sdk';

const field = new WallaField(options?: WallaFieldOptions);
파라미터타입기본값설명
options.debugbooleanfalse모든 RPC 메시지를 브라우저 콘솔에 출력

생성자가 자동으로 수행하는 작업:

  • URL 쿼리 파라미터에서 fieldId 추출 (?fieldId=xxx)
  • 호스트의 MessagePort 전달 대기
  • 포트 수신 시 Cap'n Web RPC 세션 수립

WallaField는 모듈 최상위에서 생성하세요. 호스트는 iframe의 load 이벤트에 맞춰 포트를 전달합니다. postMessage는 재배달되지 않습니다. useEffect나 onMounted 같은 마운트 훅 안에서 생성하면 포트를 놓칠 수 있습니다 (라이프사이클 & 통신 → 핸드셰이크 계약 참조). 생성자는 브라우저 전용입니다. SSR 프레임워크를 쓴다면 이 모듈을 서버 렌더에서 제외하세요. typeof window 가드는 쓰지 마세요.

콜백

호스트 이벤트에 응답하는 콜백을 등록합니다. 모든 콜백은 선택사항이며, 동일한 메서드를 다시 호출하면 이전 콜백이 교체됩니다.

onInit

field.onInit(callback: (payload: FieldInitPayload) => void): void

호스트가 필드를 초기화할 때 호출됩니다. 필드 UI를 설정하는 주요 진입점입니다.

field.onInit(({ properties, value, theme, locale, fieldInfo, formContext, mode }) => {
  document.body.style.fontFamily = theme.fontFamily;
  if (value) restoreState(value);
  field.setHeight(document.body.scrollHeight);
});
  • SDK가 이 콜백을 동기로 호출하며, 콜백이 반환하면 호스트에 init ack가 전달됩니다
  • 콜백 안의 느린 동기 작업은 ack를 지연시킵니다. 20초 ready 예산이 지나면 호스트가 로딩 오류를 표시합니다. 이후에 ack가 도착하면 필드가 복구됩니다. 비동기 작업은 문제되지 않습니다.

onValidate

field.onValidate(callback: ValidateCallback): void

호스트가 유효성 검사를 요청할 때 호출됩니다 (폼 제출 또는 페이지 이동 시). WallaFieldValidationResult를 동기 또는 Promise로 반환합니다.

field.onValidate((submitType) => {
  if (!currentValue) {
    return { valid: false, errors: [{ message: 'Required' }] };
  }
  return { valid: true };
});
  • submitType: 'next' (페이지 이동) 또는 'submit' (최종 제출)
  • 콜백이 등록되지 않으면 자동으로 { valid: true }가 반환됩니다
  • 콜백에서 예외가 발생하면 { valid: false, errors: [{ message: String(error) }] }로 처리됩니다
  • 호스트가 10초 타임아웃을 적용합니다

onValueSet

field.onValueSet(callback: (value: unknown) => void): void

호스트가 외부에서 값을 주입할 때 호출됩니다 (예: 사전 입력, 페이지 이동 후 복원).

field.onValueSet((value) => {
  applyValue(value);
});

onDestroy

field.onDestroy(callback: () => void): void

필드가 폼에서 제거될 때 호출됩니다. 타이머, 이벤트 리스너, 네트워크 연결 등의 리소스를 정리하세요.

field.onDestroy(() => {
  clearInterval(myTimer);
  myWebSocket.close();
});

onFocus

field.onFocus(callback: () => void): void

호스트가 이 필드에 포커스를 요청할 때 호출됩니다 (예: 페이지 이동 후 또는 유효성 검사 실패 시).

field.onFocus(() => {
  document.getElementById('main-input').focus();
});

onFormValuesChanged

field.onFormValuesChanged(callback: (values: Record<string, unknown>) => void): void

관찰 중인 다른 필드의 값이 변경될 때 호출됩니다. 커스텀 필드 버전의 observeFields 정책이 all 또는 configured로 설정되어 있어야 동작합니다.

field.onFormValuesChanged((values) => {
  const total = Object.values(values)
    .filter(v => typeof v === 'number')
    .reduce((sum, v) => sum + v, 0);
  field.setValue(total);
});

액션 메서드

호스트 폼과 통신하기 위해 호출하는 메서드들입니다. RPC 연결이 수립되기 전에 호출해도 안전합니다 — 디버그 모드에서 경고만 출력하고 조용히 반환됩니다.

setValue

field.setValue(value: unknown): void

현재 필드 값을 호스트에 보고합니다. 호스트가 매 호출마다 자동으로 저장합니다.

  • value는 JSON 직렬화 가능해야 합니다
  • 최대 크기: 64 KB (JSON 직렬화 기준)
  • 디바운스: 50ms 이내의 연속 호출은 마지막 값으로 배치 처리됩니다
field.setValue('#FF6B6B');
field.setValue({ hex: '#FF6B6B', opacity: 0.8 });
field.setValue(['option1', 'option2']);

setHeight

field.setHeight(height: number): void

호스트에 iframe 높이를 지정된 픽셀로 변경하도록 요청합니다.

  • 범위: 0–50000 px (범위를 벗어나는 값은 제한됩니다)
  • 스로틀: 100ms 이내의 연속 호출은 스로틀됩니다
field.setHeight(document.body.scrollHeight);

uploadBlob

field.uploadBlob(
  data: ArrayBuffer,
  mimeType: string,
  filename?: string
): Promise<{ url: string }>

호스트를 통해 바이너리 데이터를 업로드합니다. 업로드된 파일의 URL을 포함하는 Promise를 반환합니다.

uploadBlob은 현재 호스트에 미구현 상태입니다

uploadBlob()은 아직 호출하지 마세요. 호출하면 런타임 에러가 발생합니다. 아래 예제는 의도된 사용법을 보여줍니다.

const canvas = document.getElementById('signature');
const blob = await new Promise(resolve => canvas.toBlob(resolve, 'image/png'));
const buffer = await blob.arrayBuffer();

const { url } = await field.uploadBlob(buffer, 'image/png', 'signature.png');

RPC 연결이 수립되지 않은 상태에서 호출하면 에러가 발생합니다.

reportError

field.reportError(error: { message: string; recoverable: boolean }): void

호스트에 에러를 보고하여 폼 UI에 표시합니다.

  • message: 에러 설명 (호스트 측에서 500자로 잘림)
  • recoverable: true이면 사용자가 재시도 가능, false이면 영구적 실패
field.reportError({
  message: 'Failed to load external data source',
  recoverable: true,
});

recoverable: false는 되돌릴 수 없습니다

호스트가 필드를 화면에서 제거하고 다시 살아나지 않습니다 — iframe 안에서 나중에 복구되더라도 그 결과는 응답자에게 표시되지 않습니다. 이 응답자에게 정말로 계속할 수 없을 때만 쓰세요. 재시도 가능한 문제(조회 실패, 일시적 네트워크 오류)는 recoverable: true로 보내면 필드 위에 안내만 뜨고 필드는 계속 동작합니다.

destroy

field.destroy(): void

SDK 인스턴스를 수동으로 소멸합니다. MessagePort 리스너를 제거하고 RPC 세션을 종료합니다.

field.destroy();

상태 조회

필드의 현재 상태를 읽습니다. 마지막 init() 호출에서 수신한 값을 반환합니다.

getProperties

field.getProperties<T = Record<string, unknown>>(): T

현재 필드 속성을 반환합니다. 타입 안전성을 위해 제네릭 파라미터를 사용합니다:

interface MyProperties {
  placeholder: string;
  maxLength: number;
}
const props = field.getProperties<MyProperties>();

getTheme

field.getTheme(): WallaFieldTheme

현재 폼 테마를 반환합니다. init()이 호출되기 전에는 기본 테마를 반환합니다.

getLocale

field.getLocale(): string

현재 로케일 문자열을 반환합니다 (예: 'en', 'ko', 'ja').

getFieldInfo

field.getFieldInfo(): WallaFieldInfo

이 필드 인스턴스의 메타데이터를 반환합니다.

getFormContext

field.getFormContext(): { fields: FieldSummary[]; values: Record<string, unknown> }

현재 폼 컨텍스트(다른 필드의 메타데이터와 값)를 반환합니다. 내용은 observeFields 정책에 따라 달라집니다.

getObservedFields

field.getObservedFields(): ObservedFieldRef[]

폼 작성자가 이 커스텀 필드 인스턴스에 대해 선택한 참조 목록을 반환합니다. 버전의 observeFields 모드가 'configured'가 아니거나 선택된 참조가 없으면 빈 배열을 반환합니다. 순서는 작성자가 고른 그대로 유지되며, diff처럼 순서에 의존하는 연산자에서 의미가 있습니다.

field.onInit(({ observedFields, formContext }) => {
  for (const ref of observedFields) {
    if (ref.kind === 'field') {
      const value = formContext.values[ref.id];
      // ...
    } else {
      const summary = formContext.fields.find(f => f.label === ref.label);
      // ...
    }
  }
});

isConnected

field.isConnected(): boolean

호스트와 RPC 채널이 수립되었으면 true를 반환합니다.

getVersion

field.getVersion(): string

SDK 버전 문자열을 반환합니다 (예: '0.2.0').

타입

WallaFieldOptions

interface WallaFieldOptions {
  debug?: boolean;
}

FieldInitPayload

interface FieldInitPayload {
  protocolVersion: number;
  mode: 'live' | 'preview';
  fieldInfo: WallaFieldInfo;
  properties: Record<string, unknown>;
  observedFields: ObservedFieldRef[];
  value: unknown | null;
  theme: WallaFieldTheme;
  locale: string;
  formContext: {
    fields: FieldSummary[];
    values: Record<string, unknown>;
  };
}

WallaFieldTheme

interface WallaFieldTheme {
  background: string;        // Form background color
  foreground: string;        // Primary text color
  primary: string;           // Accent/brand color
  primaryForeground: string; // Text color on primary background
  field: string;             // Input field background color
  fieldForeground: string;   // Input placeholder color
  border: string;            // Border color
  accent: string;            // Secondary accent color
  fontFamily: string;        // CSS font-family value
  borderRadius: string;      // CSS border-radius value
  direction: 'ltr' | 'rtl'; // Text direction
}

WallaFieldInfo

interface WallaFieldInfo {
  fieldId: string;
  formId: string;
  label: string;
}

WallaFieldValidationResult

interface WallaFieldValidationResult {
  valid: boolean;
  errors?: WallaFieldValidationError[];
}

WallaFieldValidationError

interface WallaFieldValidationError {
  message: string;
  itemId?: string;
}

FieldSummary

폼 컨텍스트를 통해 제공되는 다른 필드의 정보를 나타냅니다.

interface FieldSummary {
  id: string;
  fieldType: string;
  label: string;
  description?: string;
  isRequired: boolean;
  properties: Record<string, unknown>;
  customField?: {
    typeId: string;
    typeName: string;
    outputSchema: Record<string, unknown>;
  };
}

customField 속성은 fieldType이 'CUSTOM'인 경우에만 존재합니다.

ObservedFieldRef

버전의 observeFields 모드가 'configured'일 때 폼 작성자가 선택한 다른 필드에 대한, 타입이 지정된 참조입니다. payload.observedFields로 전달되며 field.getObservedFields()로도 조회할 수 있습니다.

type ObservedFieldRef =
  | { kind: 'field';  id: string }     // 일반 폼 필드, id로 식별
  | { kind: 'hidden'; label: string }; // Hidden 필드 (URL 쿼리 파라미터), 작성자가 정의한 label로 식별
종류식별자해석 방법
fieldid (uuid)formContext.values[id]
hiddenlabel (예: utm_discount)formContext.fields.find(f => f.label === label)

저장 시 순서가 보존됩니다 — diff 연산자처럼 순서를 사용하는 곳에서 의미가 있습니다.

ValidateCallback

type ValidateCallback = (
  submitType: 'next' | 'submit',
) => WallaFieldValidationResult | Promise<WallaFieldValidationResult>;

ObserveFieldsMode

type ObserveFieldsMode = 'none' | 'all' | 'configured';

커스텀 필드에 제공되는 폼 컨텍스트 데이터를 제어합니다:

모드설명
none폼 컨텍스트 미제공 — fields와 values가 빈 상태
all모든 필드와 값 제공 (민감 타입은 항상 제외)
configured관리자가 선택한 필드만 제공

FieldRpcApi

커스텀 필드가 노출하는 메서드 (호스트 → iframe 방향)입니다. SDK가 내부적으로 구현합니다.

interface FieldRpcApi {
  init(payload: FieldInitPayload): void;
  destroy(): void;
  focus(): void;
  setValue(value: unknown): void;
  onFormValuesChanged(values: Record<string, unknown>): void;
  validate(submitType: 'next' | 'submit'): Promise<WallaFieldValidationResult>;
}

HostRpcApi

호스트가 노출하는 메서드 (iframe → 호스트 방향)입니다. SDK 액션 메서드가 호출합니다.

interface HostRpcApi {
  setValue(value: unknown): void;
  setHeight(height: number): void;
  uploadBlob(data: ArrayBuffer, mimeType: string, filename?: string): Promise<{ url: string }>;
  reportError(error: { message: string; recoverable: boolean }): void;
}

호스트 측 제한

동작제한설명
setValue 크기64 KB최대 JSON 직렬화 값 크기
setValue 디바운스50 ms정리 시와 RPC 검증 전후에 대기 중인 값 반영
setHeight 범위0–50000 px범위를 벗어나면 제한됨
setHeight 스로틀100 ms연속 호출은 스로틀
reportError 메시지500자초과 시 잘림
validate 타임아웃10초응답 없으면 타임아웃
포트 재전송 간격5초최대 4회 제안, ready 마감에 재시도 중단, 응답 전까지 전달한 포트 유지
필드 ready 예산20초iframe 로드 시작부터 init ack까지

내보내는 멤버

export {
  WallaField,
  type WallaFieldTheme,
  type WallaFieldInfo,
  type WallaFieldOptions,
  type WallaFieldValidationError,
  type WallaFieldValidationResult,
  type ValidateCallback,
  type FieldInitPayload,
  type FieldRpcApi,
  type HostRpcApi,
  type FieldSummary,
  type WallaPortInitMessage,
  type ObserveFieldsMode,
} from '@wallaform/custom-field-sdk';

목차