node.js 서버로 SMS 전송 기능 구현하기

ssumniee·2021년 12월 20일
2
post-thumbnail

왜 필요한가?

테니스장 회원 관리 시스템 개발 중,
비밀번호 재설정 기능 구현에 필요한 본인인증 코드 전송에 SMS 전송 기능을 사용하기로 했다.

꼭 SMS 전송이어야 하나?

비밀번호 재설정을 위해서만이라면, 이메일 전송을 사용하는 편이 더 쉬운 선택지였다.
과금될 걱정도 없고 이전에 사용해 본 방법이라 더 쉽게 느껴지기도 했다.

하지만 지금 문자 전송 기능을 구현해두면,
회원들의 수강 횟수가 다 소진된 후 재결제 문자 알림을 보내는 기능을 이후에 추가할 때
구현해 둔 기능을 활용할 수 있을 것 같아 SMS 전송을 이용하는 방식을 선택했다.

사전 작업

네이버 클라우드 플랫폼의 Simple & Easy Notification Service를 사용하기로 했다.

인증키 발급 🔑

  1. 네이버 클라우드 플랫폼 회원가입 후 아래 링크에서 서비스 이용 신청하기
    🔗 www.ncloud.com/product/applicationService/sens

  2. 우측 상단 마이페이지 > 계정 관리

    비밀번호 입력 후 인증키 관리 탭 진입 > 신규 API 인증키 생성 > key Access Key IDSecret Key 복사해두기

  3. 콘솔 > Simple & Easy Notification Service > SMS 메뉴에서 프로젝트 생성하기

  4. 생성된 프로젝트의 서비스 ID 복사해두기

  5. Simple & Easy Notification Service > SMS > Calling Number 메뉴에서 발송할 때 사용할 번호 등록

    번호 등록 방법에는 서류 등록과 본인인증 등록 2가지 방법이 있다.
    일단 테스트를 위해 사용중인 개인 번호로 설정해두었다.

코드 작성 💻

사전 작업 과정에서 따로 적어둔
Access Key ID Secret Key 서비스 ID를 이용해 코드를 작성해보았다.

환경변수 세팅

서버 쪽 환경변수로 Access Key ID Secret Key 서비스 ID 기록 및 사용 가능하도록 세팅

server/.env

server/config/config.js

const dotenv = require("dotenv");

dotenv.config();

function required(key, defaultValue = undefined) {
  const value = process.env[key] || defaultValue;
  if (value == null) {
    throw new Error(`Key ${key} is undefined`);
  }
  return value;
}

module.exports = {

  // ...

  sens: {
    accessKey: required("NCP_SENS_ACCESS"),
    secretKey: required("NCP_SENS_SECRET"),
    serviceId: required("NCP_SENS_ID"),
    callNumber: required("NCP_SENS_NUMBER"),
  },
};

server/controllers/.../util.js

const { sens } = require("../config");

module.exports = {
  sendVerificationSMS: async (req, res) => { 
    // 환경 변수
    const sens_service_id = sens.serviceId;
    const sens_access_key = sens.accessKey;
    const sens_secret_key = sens.secretKey;
    const sens_call_number = sens.callNumber;

    // ...
    
  },
};

암호화를 위한 CryptoJS 모듈 설치

$ npm install crypto-js

server/controllers/.../util.js

const CryptoJS = require("crypto-js");

// ...

메시지 전송 함수 작성

server/controllers/.../util.js

const { createRandomNumber } = require("./functions/utility");
const { sens } = require("../config");
const CryptoJS = require("crypto-js");
const axios = require("axios");

module.exports = {
  sendVerificationSMS: async (req, res) => {
    try {
      const { tel } = req.body;
      const user_phone_number = tel.split("-").join(""); // SMS를 수신할 전화번호
      const verificationCode = createRandomNumber(6); // 인증 코드 (6자리 숫자)
      const date = Date.now().toString(); // 날짜 string

      // 환경 변수
      const sens_service_id = sens.serviceId;
      const sens_access_key = sens.accessKey;
      const sens_secret_key = sens.secretKey;
      const sens_call_number = sens.callNumber;

      // url 관련 변수 선언
      const method = "POST";
      const space = " ";
      const newLine = "\n";
      const url = `https://sens.apigw.ntruss.com/sms/v2/services/${sens_service_id}/messages`;
      const url2 = `/sms/v2/services/${sens_service_id}/messages`;

      // signature 작성 : crypto-js 모듈을 이용하여 암호화
      console.log(1);
      const hmac = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA256, sens_secret_key);
      console.log(2);
      hmac.update(method);
      hmac.update(space);
      hmac.update(url2);
      hmac.update(newLine);
      hmac.update(date);
      hmac.update(newLine);
      console.log(sens_access_key);
      hmac.update(sens_access_key);
      const hash = hmac.finalize();
      console.log(4);
      const signature = hash.toString(CryptoJS.enc.Base64);
      console.log(5);

      // sens 서버로 요청 전송
      const smsRes = await axios({
        method: method,
        url: url,
        headers: {
          "Contenc-type": "application/json; charset=utf-8",
          "x-ncp-iam-access-key": sens_access_key,
          "x-ncp-apigw-timestamp": date
          ,
          "x-ncp-apigw-signature-v2": signature,
        },
        data: {
          type: "SMS",
          countryCode: "82",
          from: sens_call_number,
          content: `인증번호는 [${verificationCode}] 입니다.`,
          messages: [{ to: `${user_phone_number}` }],
        },
      });
      console.log("response", smsRes.data);
      return res.status(200).json({ message: "SMS sent" });
    } catch (err) {
      console.log(err);
      return res.status(404).json({ message: "SMS not sent" });
    }
  },
};

라우터 설정

server/router/util.js

const express = require("express");
const router = express.Router();
const { isAuth } = require("../middlewares");
const { sendVerificationSMS } = require("../controllers/util");

router.get("/message/code", isAuth, sendVerificationSMS);

module.exports = router;

테스트

request body의 tel 프로퍼티에 수신할 전화번호를 담아,
설정한 라우터의 엔드포인트로 POST 요청을 보냈더니 문자가 잘 수신되는 것을 확인할 수 있었다.


참고한 글
[Node.js] NCP SENS 문자 발송 API
[Node.js] 네이버 문자 전송 API 사용하기(feat. axios)

profile
개발에 뛰어든 UX/UI 디자이너

0개의 댓글