본문 바로가기

Node.js 문자 발송 API 예제

Node.js 18 이상에 내장된 fetch 로 센드엠 문자 발송 API를 호출하는 예제입니다. 추가 패키지 설치가 필요 없습니다.

별도 SDK 없이 표준 HTTP 호출만으로 동작합니다.

적용 버전 「센드엠 API 연동정의서 v3.1」 · 작성 센드엠 고객지원팀 · 작성일 2026년 9월 16일 · 최종 수정 2026년 10월 21일

Node.js에서 문자를 어떻게 보내나요?

Node.js 18 부터는 별도 패키지 없이 전역 fetch 로 https://api.sendm.co.kr/v1/sms/send 에 요청을 보내면 됩니다. 인증은 헤더 user-id·api-key 로 하고, 응답 code 가 문자열 "0" 이면 접수된 것입니다.

아래 예제는 CommonJS(require) 모듈 sendm.js 하나로 구성했습니다. 아이디와 인증키는 소스에 쓰지 않고 process.env 에서 읽으므로, Express 라우트나 배치 스크립트에서 require('./sendm') 로 불러 그대로 쓸 수 있습니다.

브라우저에서 호출하지 마세요. Next.js를 쓴다면 클라이언트 컴포넌트가 아니라 서버 액션·라우트 핸들러에서 호출해야 인증키가 노출되지 않습니다.

실행 환경

Node.js 예제 실행 환경
구분사용검증
런타임Node.js 18 이상 (전역 fetch·FormData·Blob)2026-09-16 실행 확인
타임아웃AbortSignal.timeout()2026-09-16 실행 확인
모듈CommonJS(require)-
Node.js 18 미만에는 전역 fetch 가 없어 이 예제가 그대로 동작하지 않습니다. CommonJS 파일 맨 위에서는 await 를 쓸 수 없으니 async 함수 안에서 호출하세요.

문자 요청 예제 단문(SMS) 발송

예시 값을 실제 발신번호·수신번호·인증키로 바꿔 고객님 서버에서 실행하세요.

sendm.js

const BASE_URL = 'https://api.sendm.co.kr';

const HEADERS = {
  'user-id': process.env.SENDM_USER_ID,   // 센드엠 아이디
  'api-key': process.env.SENDM_API_KEY,   // API 신청 후 발급받은 키
};

/**
 * 문자 발송
 * @param {string} callerNo   등록·승인된 발신번호
 * @param {string} receiveNos 수신번호. 여러 건은 '#' 로 구분
 * @param {string} message    본문
 */
async function sendSms(callerNo, receiveNos, message, options = {}) {
  const body = {
    callerNo,
    receiveNos,
    message,
    smsType: options.smsType || 'SMS',   // 'SMS' 단문 / 'LMS' 장문
    adYn:    options.adYn    || 'N',     // 'N' 일반 / 'Y' 광고 / 'E' 선거
  };
  if (options.title)     body.title     = options.title;      // 장문 제목
  if (options.reserveYn) body.reserveYn = options.reserveYn;  // 'Y' 예약
  if (options.sendDate)  body.sendDate  = options.sendDate;   // YYYYMMDDHHMM

  const res = await fetch(BASE_URL + '/v1/sms/send', {
    method: 'POST',
    headers: Object.assign({ 'Content-Type': 'application/json' }, HEADERS),
    body: JSON.stringify(body),
    signal: AbortSignal.timeout(30000),
  });

  return res.json();
}

module.exports = { sendSms, BASE_URL, HEADERS };

호출과 결과 판정

const { sendSms } = require('./sendm');

const result = await sendSms(
  '02-0000-0000',                    // 발신번호
  '010-0000-0001#010-0000-0002',     // 수신번호 (# 구분)
  '주문이 접수되었습니다.'
);

// code 는 숫자가 아니라 문자열 '0' 이다
if (result.code === '0') {
  console.log('발송 접수 완료. msgId =', result.data.msgId);
} else {
  console.error('발송 실패 [' + result.code + '] ' + result.message);
}

장문(LMS) 예약 발송 reserveYn: 'Y'

function toSendDate(date) {
  const p = (n) => String(n).padStart(2, '0');
  return date.getFullYear()
       + p(date.getMonth() + 1)
       + p(date.getDate())
       + p(date.getHours())
       + p(date.getMinutes());          // YYYYMMDDHHMM (24시간제)
}

const twoHoursLater = new Date(Date.now() + 2 * 60 * 60 * 1000);

await sendSms('02-0000-0000', '010-0000-0001', '주문하신 상품이 출고되었습니다.', {
  smsType:   'LMS',
  title:     '배송 안내',
  reserveYn: 'Y',
  sendDate:  toSendDate(twoHoursLater),
});

FormData 로 포토문자(MMS) 보내기

fs/promises 로 읽은 이미지 Buffer 를 Blob 으로 감싸 attachFile01~03 에 붙입니다.

const fs = require('node:fs/promises');
const path = require('node:path');

async function sendMms(callerNo, receiveNos, message, imagePaths, title) {
  const form = new FormData();
  form.append('callerNo', callerNo);
  form.append('receiveNos', receiveNos);
  form.append('message', message);       // MMS 는 본문이 없어도 된다
  form.append('smsType', 'MMS');
  if (title) form.append('title', title);

  // 최대 3장 : attachFile01 ~ attachFile03
  for (let i = 0; i < Math.min(imagePaths.length, 3); i++) {
    const buf = await fs.readFile(imagePaths[i]);
    const name = 'attachFile' + String(i + 1).padStart(2, '0');
    form.append(name, new Blob([buf]), path.basename(imagePaths[i]));
  }

  const res = await fetch(BASE_URL + '/v1/mms/send', {
    method: 'POST',
    headers: HEADERS,                    // Content-Type 은 fetch 가 자동 생성
    body: form,
    signal: AbortSignal.timeout(30000),
  });

  return res.json();
}

요청 headers 에는 인증 헤더만 두세요. Content-Type 을 직접 지정하면 fetch 가 자동으로 붙이는 boundary 값이 사라져 서버가 첨부파일을 읽지 못합니다. 첨부 가능한 이미지 규격은 API 연동 방법에 정리돼 있습니다.

알림톡 발송

수신자 배열을 JSON.stringify 로 문자열로 바꾼 뒤 FormData 의 kakaoList 항목에 넣습니다. 배열을 그대로 append 하면 [object Object] 형태의 문자열로 바뀌어 전송됩니다.

const kakaoList = [
  {
    receive: '010-0000-0001',
    message: '홍길동님, 주문번호 A1234 상품이 출고되었습니다.',
    reYn:    'Y',              // 알림톡 실패 시 문자로 대체
    reType:  'LMS',
    reTitle: '배송 안내',
    reMsg:   '홍길동님, 주문번호 A1234 상품이 출고되었습니다.',
  },
];

const form = new FormData();
form.append('callerNo',  '02-0000-0000');
form.append('senderKey', 'YOUR_SENDER_KEY');
form.append('tmplCd',    'YOUR_TEMPLATE_CODE');
form.append('kakaoList', JSON.stringify(kakaoList));

const res = await fetch(BASE_URL + '/v2/kakao/sendAt', {
  method: 'POST',
  headers: HEADERS,
  body: form,
  signal: AbortSignal.timeout(30000),
});

console.log(await res.json());

알림톡 message 에는 승인받은 템플릿 문구를 그대로 넣어야 합니다. 템플릿 승인 과정은 카카오 알림톡 시작하기에서 설명합니다.

fetch 로 발송 결과·잔여 건수 조회

async function get(pathname) {
  const res = await fetch(BASE_URL + pathname, {
    headers: HEADERS,
    signal: AbortSignal.timeout(30000),
  });
  return res.json();
}

// 잔여 코인·발송 가능 건수
const info = await get('/v2/info/coinInfo');
console.log(info.data.smsCnt + '건 발송 가능');

// 발송 상세 결과 (수신자별 resultCd)
const detail = await get('/v1/sms/sendList/' + msgId);
console.log('성공 ' + detail.data.successCnt
          + ' / 실패 ' + detail.data.failCnt
          + ' / 대기 ' + detail.data.waitCnt);

detail.list.forEach(function (row) {
  console.log(row.receiveNo, row.resultCd, row.resultCdNm);
});

// 예약 발송 취소
await get('/v1/sms/cancel/' + msgId);
발송 직후 조회하면 대부분 대기 상태입니다. 확정까지는 통신사 리포트가 도착해야 하고, 그 시간은 최대 48시간입니다. resultCd 해석은 발송 실패 원인과 결과코드를 참고하세요.

제한과 주의

시간초과가 나도 바로 다시 보내지 마세요. 요청이 이미 접수됐을 수 있습니다. 발송 목록(GET /v1/sms/sendList)으로 접수 여부를 먼저 확인하고, 접수되지 않은 건만 다시 보냅니다.
  • 응답 code 가 "0" 이면 접수입니다. 수신 성공은 결과 조회로 따로 확인합니다.
  • 예약 발송의 sendDate 는 호출 시점 기준으로 계산하세요. 예시 날짜를 그대로 복사하면 지난 시각이 됩니다.
  • 인증키는 서버 환경변수에서 읽고 저장소에 커밋하지 마세요.
  • 광고문자는 adYn=Y 로 보내며 야간(21시~08시) 발송 제한이 적용됩니다.

자주 막히는 문제와 해결

Node.js 연동 시 흔한 문제와 해결
증상원인해결
MMS 첨부가 안 됨headers 에 Content-Type 을 직접 지정해 boundary 가 사라짐인증 헤더만 넣고 Content-Type 은 fetch 에 맡기기
kakaoList 형식 오류배열을 그대로 append 해 [object Object] 로 전송됨JSON.stringify 로 문자열로 바꿔 넣기
TimeoutErrorAbortSignal.timeout 시간 안에 응답이 없음바로 다시 보내지 말고 발송 목록으로 접수 여부 확인
권한 없음으로 차단호출 서버 IP가 미등록API 설정에서 발송 IP 등록. 서버 증설·이전 시 다시 등록
발신번호 오류callerNo 가 미등록·미승인 번호발신번호 등록 후 사용
야간에만 실패야간 발송 제한(21시~08시)reserveYn=Y 와 sendDate 로 예약 전환

이 글에서 답한 질문

Node.js에서 별도 패키지가 필요한가요?

Node.js 18부터는 별도 패키지 없이 전역 fetch로 요청을 보내면 됩니다. 이 글의 예제도 Node.js 18 이상을 기준으로 합니다.

Next.js에서는 어디에서 호출하나요?

클라이언트 컴포넌트가 아니라 서버 액션이나 라우트 핸들러에서 호출해야 인증키가 노출되지 않습니다.

아이디와 인증키는 어떻게 넣나요?

소스에 쓰지 않고 process.env에서 읽습니다. 예제 모듈 sendm.js를 Express 라우트나 배치 스크립트에서 require('./sendm')로 불러 그대로 쓸 수 있습니다.

Node.js 연동은 API 신청부터

승인되면 아이디를 SENDM_USER_ID, 발급된 인증키를 SENDM_API_KEY 환경변수로 지정하고 위 sendm.js 를 불러오면 됩니다.

loading...