수료·수상 시 배지 발급

수료·수상·승인 결과가 확정되면 발급 작업을 만들고, 발급 작업의 처리 상태가 COMPLETED가 될 때까지 확인합니다. 아래는 Node.js 작업 큐 구현 예시입니다.

시작 전 준비
  • badgeId 발급할 배지
  • programId 연결할 프로그램
  • 수신자 이름과 이메일 또는 전화번호
  • sourcedId로 사용할 외부 시스템 수신자 ID
  • 수료·수상·승인 결과의 내부 고유 ID
01
발급 조건

발급 조건이 확정된 결과를 백그라운드 작업으로 넘깁니다.

화면 요청을 처리하는 동안 발급 작업이 끝날 때까지 기다리지 마세요. 수료 확정, 수상자 등록, 승인 완료 시점에 작업 큐로 내부 결과 ID와 수신자 정보를 전달합니다.

발급 조건 확정작업 큐써티 발급 요청
수료일을 발급일로 지정하기선택값 issuedAt에 UTC 또는 offset이 있는 과거·현재 시각을 전달할 수 있습니다. 같은 수료 건을 재시도할 때는 발급일도 같은 값으로 유지하세요. 생략/null이면 최초 요청에서 서버 현재 시각으로 확정합니다.
02
중복 방지

같은 내부 결과 건에는 같은 멱등성 키를 사용합니다.

같은 결과를 다시 처리해도 중복 발급되지 않도록 내부 결과 ID에서 같은 Idempotency-Key를 만듭니다. V3에서는 같은 수신자를 다시 조회할 수 있도록 외부 시스템의 사용자 ID를 sourcedId에도 보냅니다.

Node.js 작업 예시jobs/issue-completion-badge.js
jobs/issue-completion-badge.js
// jobs/issue-completion-badge.js — 수료 처리 후 실행되는 서버 작업
import { createHash } from "node:crypto";
import { certi } from "@/lib/certi";

export async function issueCompletion({ completionId, badgeId, programId, learner, issuedAt }) {
  // 같은 수료 건에는 같은 키를 사용해 중복 발급을 막습니다.
  const key = "completion:" + createHash("sha256").update(completionId).digest("hex");

  return certi(`/badges/${badgeId}/issuances`, {
    method: "POST",
    headers: { "Idempotency-Key": key },
    body: JSON.stringify({
      programId,
      ...(issuedAt == null ? {} : { issuedAt }),
      recipients: [{
        name: learner.name,
        email: learner.email,
        sourcedId: learner.id,
        receiveType: "EMAIL",
      }],
    }),
  });
}
03
상태 확인

발급 작업 ID를 저장하고 상태를 다시 확인합니다.

직접 발급 응답의 data.id를 내부 결과 건에 저장합니다. PROCESSING이면 같은 ID를 다시 조회하고, COMPLETED 후에는 발급된 배지 목록의 stateCode와 errorCode를 확인합니다. 완료 상태는 모든 수신자의 성공을 보장하지 않습니다.

Node.js 작업 예시jobs/check-issuance.js
jobs/check-issuance.js
// jobs/check-issuance.js — 백그라운드 작업에서 주기적으로 확인
import { certi } from "@/lib/certi";

export async function checkIssuance(issuanceId) {
  const issuance = await certi(`/issuances/${issuanceId}`);

  if (issuance.processingStatus === "PROCESSING") {
    return { done: false };
  }
  if (issuance.processingStatus === "FAILED") {
    throw new Error("발급 작업이 실패했습니다. issuanceId로 발급된 배지 목록과 errorCode를 확인하세요.");
  }

  const page = await certi(
    `/assertions?${new URLSearchParams({ issuanceId, page: "1", size: "100" })}`,
  );
  return { done: true, assertions: page.items };
}
PROCESSING정책에 따라 재확인
COMPLETED발급된 배지 조회
RECEIVED수신자별 성공 확인
재시도할 때네트워크 오류에는 같은 멱등성 키와 같은 본문을 사용합니다. 다른 발급 요청에는 새 키를 사용합니다.