본문 바로가기

Node.js 문자 발송 API 예제

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

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

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

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

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

단문(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(10000),
  });

  return res.json();
}

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

호출과 결과 판정

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

const result = await sendSms(
  '02-6959-8827',                    // 발신번호
  '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-6959-8827', '010-0000-0001', '주문하신 상품이 출고되었습니다.', {
  smsType:   'LMS',
  title:     '배송 안내',
  reserveYn: 'Y',
  sendDate:  toSendDate(twoHoursLater),
});

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

fs/promises 로 읽은 이미지 BufferBlob 으로 감싸 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 로 문자열로 바꾼 뒤 FormDatakakaoList 항목에 넣습니다. 배열을 그대로 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-6959-8827');
form.append('senderKey', '카카오 채널 발신 프로필 키');
form.append('tmplCd',    '승인된 템플릿 코드');
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(10000),
  });
  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 해석은 발송 실패 원인과 결과코드를 참고하세요.

최종 업데이트 : 2026년 9월 16일 · 「센드엠 API 연동정의서 v3.0」 기준

Node.js 연동은 API 신청부터

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

loading...