콘텐츠로 이동

@k-msg/provider

k-msg용 provider 구현 패키지입니다. (SendOptions + Result 기반)

Terminal window
npm install @k-msg/provider @k-msg/core
# or
bun add @k-msg/provider @k-msg/core

SOLAPI provider를 사용할 경우, 앱에서 최신 solapi를 별도로 설치하세요. @k-msg/provider는 현재 v6 라인과 이전 v5 peer 범위를 함께 지원합니다:

Terminal window
npm install solapi
# or
bun add solapi
  • SolapiProvider (SOLAPI)
  • IWINVProvider (IWINV 알림톡, SMS v2, RCS 템플릿 중 원하는 조합, src/iwinv/README_ko.md 참고)
  • AligoProvider (Aligo)
  • MockProvider (벤더 API를 호출하지 않는 테스트·로컬 실행용). { id }를 넘기면 인스턴스마다 provider id를 따로 줄 수 있어, 예를 들어 mock 두 개로 routing.byType을 시험할 수 있습니다. 기본 id는 "mock"입니다.

모든 provider는 @k-msg/core의 Provider 인터페이스를 구현합니다:

  • supportedTypes: 지원하는 메시지 type 선언
  • send(options: SendOptions, context?: ProviderRequestContext): Result<SendResult, KMsgError> 반환 (throw 하지 않음)
  • 일부 provider는 선택 capability인 getBalance(query?)를 함께 구현합니다.

ProviderRequestContext로 AbortSignal과 호출 단위 fetch 구현을 전달할 수 있습니다. 기능에 의존하기 전에 provider.transportCapabilities를 확인해야 하며, capability 선언이 없으면 unsupported로 취급합니다.

Provider AbortSignal Injectable fetch 비고
iwinv supported supported send와 getDeliveryStatus의 모든 내부 요청에 context 전달
aligo supported supported 모든 send 채널이 공통 fetch transport 사용
solapi supported unsupported SOLAPI SDK가 signal/fetch를 받지 않으므로 SDK 호출 전마다 signal을 확인하고, abort되면 기다리지 않고 반환합니다. SDK가 이미 보낸 발송 요청은 취소할 수 없어 SOLAPI가 그 메시지를 보낼 수 있으므로, 이 경우는 타임아웃이라도 REQUEST_ABORTED(기본적으로 재시도하지 않음)를 반환합니다
mock supported unsupported 모의 지연은 signal을 따르며 HTTP transport는 사용하지 않음
const controller = new AbortController();
const result = await provider.send(input, {
signal: controller.signal,
fetch: globalThis.fetch,
});

import 경로:

  • @k-msg/provider: 런타임 중립 export (IWINVProvider, AligoProvider, 온보딩 헬퍼, mock)
  • @k-msg/provider/aligo: Aligo provider export
  • @k-msg/provider/solapi: SOLAPI provider export (solapi는 사용자 앱에서 직접 설치)

단일 소스: packages/provider/src/onboarding/specs.ts

Provider 채널 온보딩 템플릿 API plusId 정책 plusId 추론 라이브 테스트 지원
iwinv 수동(콘솔) 가능 optional unsupported supported
aligo API 가능 required_if_no_inference supported supported
solapi 없음(벤더 메타 의존) 미지원 optional unsupported partial
mock API(테스트용) 가능 optional supported none

런타임 접근:

  • 각 built-in provider는 getOnboardingSpec()를 노출합니다.
  • 레지스트리 헬퍼: getProviderOnboardingSpec, listProviderOnboardingSpecs, providerOnboardingSpecs.

해석 기준:

  • 여기서의 채널 온보딩은 vendor prerequisite path(manual, api, none)를 뜻하며, toolkit이 관리하는 approval state를 의미하지 않습니다.
  • CLI가 onboarding.manualChecks를 저장하는 경우도 외부 벤더 절차에 대한 operator evidence/note를 기록하는 용도입니다.

variables는 템플릿의 #{이름} 변수에 이름으로 매칭됩니다:

Provider 전송 방식
iwinv templateParam, 서로 다른 변수 이름마다 값 하나를, 내용과 버튼 링크에서 처음 나오는 순서대로
aligo message_1, 값을 채운 템플릿 본문
solapi kakaoOptions.variables, 템플릿은 SOLAPI가 채움

IWINV와 Aligo는 이를 위해 템플릿 본문이 필요합니다. providerOptions.templateContent에서 읽고, 없으면 템플릿 API(IWINV POST /api/template/, Aligo /akv10/template/list/)를 발송과 같은 request context로 조회해 provider 인스턴스마다 10분간 재사용합니다. variables에 값이 없는 변수(키가 없거나 undefined)가 템플릿에 있으면 아무것도 보내지 않고 INVALID_REQUEST로 실패합니다. IWINV는 variables가 비어 있고 templateContent도 없으면 조회하지 않으며, providerOptions.templateParam은 그대로 보냅니다(src/iwinv/README_ko.md 참고).

@k-msg/messaging의 DeliveryTrackingService는 provider.getDeliveryStatus()를 폴링합니다.

Provider getDeliveryStatus
iwinv 알림톡 전송내역, SMS/LMS/MMS 전송내역은 smsCompanyId 필요, RCS 전송내역은 브랜드·템플릿·수신번호·요청 시각으로 매칭(IWINV 발송 응답에 메시지 키가 없음)
solapi SOLAPI 메시지 목록
aligo 미구현

Aligo에는 결과 조회 API(/akv10/history/detail/, /sms_list/)가 있지만 반환하는 결과 코드를 공개하지 않아 AligoProvider에는 getDeliveryStatus()가 없습니다. 추적 중인 Aligo 메시지는 polling.maxTrackingDurationMs(기본 24시간)가 지나 UNKNOWN이 될 때까지 SENT로 남습니다. 첫 폴링에서 바로 정리하려면 polling.unsupportedProviderStrategy: "unknown"을 설정하세요. 같은 이유로 tracking 기반 API failover는 Aligo 알림톡을 재발송하지 않으며, failover.enabled이면 Aligo가 대체문자를 직접 보냅니다.

ALIMTALK의 failover는 @k-msg/core에서 표준화되어 있지만 provider별 native 매핑은 다릅니다.

Provider Native mapping Warning
iwinv reSend, resendType, resendContent, resendTitle none (native로 처리)
solapi kakao.disableSms, text, subject 발신번호가 없을 때만 FAILOVER_PARTIAL_PROVIDER
aligo failover, fmessage_1, fsubject_1 FAILOVER_PARTIAL_PROVIDER
mock native 매핑 없음 FAILOVER_UNSUPPORTED_PROVIDER

경계:

  • provider 패키지는 벤더 native 필드로 매핑하고 warning 메타데이터를 반환합니다.
  • iwinv는 failover.fallbackContent를 resendContent(resendType: "N")로 보내고, 없으면 IWINV가 알림톡 내용을 대체문자로 보냅니다. SMS/LMS는 내용 길이로 IWINV가 정합니다.
  • tracking 기반 API 레벨 fallback retry(배달 폴링 + SMS/LMS 재발송)는 @k-msg/messaging에서 처리하며, 위 warning을 반환한 발송에만 적용됩니다.
  • solapi는 알림톡에 발신번호(from 또는 defaultFrom)가 있으면 대체문자를 직접 보내므로(kakao.disableSms: false) warning을 반환하지 않습니다. 발신번호가 없으면 SOLAPI가 대체발송할 수 없어 API 레벨 fallback 대상으로 표시됩니다.

RCS_TPL/RCS_ITPL/RCS_LTPL의 failover(@k-msg/core의 RcsFailoverOptions, ALIMTALK과 같은 모양)는 RCS 메시지가 전달되지 않을 때 SMS/LMS 대체 문자를 요청합니다. KMsg는 ALIMTALK과 같이 변수를 채우고 SMS/LMS 크기를 정합니다. tracking 기반 API 레벨 fallback은 RCS에 적용되지 않습니다.

Provider Native mapping Warning
iwinv reSend, resendType, resendContent, resendTitle (RCS_TPL만) none (native로 처리)
solapi 매핑하지 않음, SOLAPI 자체 대체발송은 rcs.disableSms를 따름 FAILOVER_UNSUPPORTED_PROVIDER
import { KMsg } from "@k-msg/messaging";
import { IWINVProvider } from "@k-msg/provider";
import { SolapiProvider } from "@k-msg/provider/solapi";
const kmsg = new KMsg({
providers: [
new SolapiProvider({
apiKey: process.env.SOLAPI_API_KEY!,
apiSecret: process.env.SOLAPI_API_SECRET!,
defaultFrom: "01000000000",
}),
new IWINVProvider({
apiKey: process.env.IWINV_API_KEY!,
smsApiKey: process.env.IWINV_SMS_API_KEY,
smsAuthKey: process.env.IWINV_SMS_AUTH_KEY,
smsSenderNumber: "01000000000",
}),
],
routing: {
defaultProviderId: "solapi",
byType: { ALIMTALK: "iwinv" },
},
});
await kmsg.send({ to: "01012345678", text: "hello" });

새 provider를 추가할 때는 packages/provider/PROVIDER_README_TEMPLATE.md를 시작점으로 사용하고, 벤더 공식 문서 링크를 포함하세요.

provider 코드 구조 표준(파사드 + 도메인 모듈 + shared 유틸 승격 기준)은 아래 문서를 참고하세요.

  • packages/provider/src/PROVIDER_STRUCTURE.md