createIssueStickerHeadless(config)가 돌려주는 클라이언트가 가진 메서드 전체입니다.
생명주기와 상태 구독
initialize()
initialize(): Promise<void>
클라이언트를 준비 상태(idle)로 만듭니다. API key를 검증하고 프로젝트 정보를 불러옵니다. 다른 메서드들도 필요하면 내부에서 먼저 초기화하므로, 보통 앱을 켤 때 한 번만 명시적으로 호출하면 됩니다.
subscribe(listener)
subscribe(listener: (snapshot: HeadlessSnapshot) => void): () => void
상태가 바뀔 때마다 listener를 호출합니다. 반환된 함수를 호출하면 구독이 해제됩니다. UI는 이 콜백 안에서 다시 그리면 됩니다.
getSnapshot()
getSnapshot(): HeadlessSnapshot
현재 상태를 한 번 동기적으로 읽습니다. 구독 없이 즉시 값이 필요할 때 사용합니다.
phase
readonly phase: HeadlessPhase
현재 phase를 읽는 읽기 전용 속성입니다. getSnapshot().phase의 단축입니다.
destroy()
destroy(): void
클라이언트를 정리합니다. 구독·리스너를 해제하고 진행 중인 녹화·세션 리플레이를 중단하며 내부 상태를 초기화합니다. 컴포넌트를 언마운트하거나 위젯을 내릴 때 호출하세요.
이슈 불러오기
refreshIssues(filters?)
refreshIssues(filters?: HeadlessIssueFilters): Promise<void>
조건에 맞는 이슈 목록을 불러와 snapshot.issues에 채웁니다. idle 또는 viewingIssue에서 호출할 수 있습니다.
// 현재 페이지의 내 이슈만
await client.refreshIssues({
url: window.location.href,
externalUserId: currentUser.id,
});
// 전체 페이지에서 검색·필터
await client.refreshIssues({
url: 'all',
search: 'checkout',
customFieldValues: { [fieldDefinitionId]: ['high'] },
});
식별된 사용자(user 또는 externalUserId)가 없으면 public 모드에서는 목록이 비어 있습니다. 멤버로 로그인하면 프로젝트 전체 이슈를 불러올 수 있습니다.
openIssue(issueId)
openIssue(issueId: string): Promise<void>
이슈 상세를 불러와 snapshot.activeIssue에 담고 phase를 viewingIssue로 바꿉니다.
closeIssue()
closeIssue(): void
상세 보기를 닫고 idle로 돌아갑니다.
refreshFilterSchema()
refreshFilterSchema(): Promise<void>
필터 UI를 그릴 정의(snapshot.filterSchema.groups)를 불러옵니다. 필터는 멤버 모드에서만 제공됩니다.
이슈 만들기
startIssueCapture(options?)
startIssueCapture(options?: HeadlessIssueCaptureOptions): Promise<void>
스크린샷 이슈 만들기를 시작합니다. phase가 selectingTarget이 되고, 사용자가 페이지 요소를 클릭하면 즉시 초안(snapshot.draft)이 만들어지고 draftingIssue로 전환됩니다. 스크린샷은 백그라운드에서 캡처되어 완료되는 대로 snapshot.draft.screenshotPreviewUrls에 채워지므로, 폼을 바로 그리되 프리뷰 영역은 빈 배열일 때 로딩으로 표시하면 됩니다.
제목·설명·동적 필드 값은 앱이 직접 상태로 관리하고, 제출할 때 submitDraft에 넘깁니다.
await client.startIssueCapture();
const form = { title: '', description: '', fields: {} as Record<string, unknown> };
client.subscribe((snapshot) => {
if (snapshot.phase !== 'draftingIssue' || !snapshot.draft) return;
renderIssueForm({
screenshots: snapshot.draft.screenshotPreviewUrls, // []이면 캡처 진행 중
fields: snapshot.formSchema?.fields ?? [], // null이면 스키마 로딩 중
onTitleChange: (title) => (form.title = title),
onFieldChange: (id, value) => (form.fields[id] = value),
onSubmit: () =>
client.submitDraft({
title: form.title,
description: form.description,
dynamicFields: form.fields,
}),
onCancel: () => client.cancelIssueCapture(),
});
});
startVideoCapture()
startVideoCapture(): Promise<void>
화면 녹화를 시작합니다(phase recordingVideo). 브라우저의 화면 공유 권한을 사용합니다.
stopVideoCapture()
stopVideoCapture(): Promise<void>
녹화를 끝냅니다. 녹화 파일이 있으면 요소 선택 단계(selectingTarget)로 이어지고, 사용자가 요소를 클릭하면 녹화 영상이 초안에 연결됩니다. 사용자가 브라우저 공유 UI에서 직접 녹화를 멈춰도 동일하게 처리됩니다.
setSessionReplayEnabled(enabled)
setSessionReplayEnabled(enabled: boolean): void
이슈를 제출할 때 세션 리플레이(직전 조작 다시보기)를 함께 첨부할지 토글합니다. snapshot.sessionReplay의 available·canAttach로 사용 가능 여부를 확인해 스위치를 그리세요.
client.subscribe((snapshot) => {
renderReplayToggle({
available: snapshot.sessionReplay.available,
checked: snapshot.sessionReplay.enabled,
onChange: (on) => client.setSessionReplayEnabled(on),
});
});
cancelIssueCapture()
cancelIssueCapture(): void
진행 중인 캡처·녹화·초안 작성을 취소하고 idle로 되돌립니다.
submitDraft(input)
submitDraft(input: {
title: string;
description: string;
dynamicFields?: Record<string, unknown>;
}): Promise<Issue>
앱이 관리하던 제목·설명·동적 필드 값을 받아 이슈를 생성하고, 생성된 Issue를 반환합니다. 동적 필드의 키는 snapshot.formSchema.fields의 id입니다(DynamicFieldDescriptor 참고). 스크린샷·영상·콘솔/네트워크 로그·세션 리플레이는 자동으로 함께 업로드됩니다.
- 제목·설명이 비어 있으면
VALIDATION_ERROR로 거부됩니다. - 호출 시점에 스크린샷 캡처가 아직 진행 중이면 phase가
submittingIssue로 전환된 채 캡처 완료를 기다렸다가 업로드합니다.submittingIssue동안 제출 버튼 비활성화·스피너 같은 진행 피드백을 phase 기준으로 그리세요.
멤버 로그인
loginAsMember()
loginAsMember(): Promise<void>
워크스페이스 멤버로 로그인합니다. 가능한 경우 조용히 로그인하고, 아니면 로그인 창을 띄웁니다. 멤버로 로그인하면 프로젝트 전체 이슈 조회와 동적 필드·필터를 사용할 수 있습니다. 멤버 자격 증명은 SDK 메모리에만 보관되며 앱이 직접 저장하지 않습니다.
logoutMember()
logoutMember(): Promise<void>
멤버 로그아웃하고 public 모드로 돌아갑니다.