Twilio를 이용한 SMS 인증 구현-Twilio 연동

Odyssey·2025년 6월 27일
0

Next.js_study

목록 보기
48/58
post-thumbnail

2025.6.28 토요일의 공부기록

이번 글에서는 Twilio를 사용하여 SMS 인증번호를 발송하는 방법을 Next.js 기반의 실제 코드로 상세히 알아본다. 추가적으로, Twilio 무료 계정의 한계점과 이를 극복할 수 있는 대안(Vonage)에 대해서도 다룬다.


Twilio란?

Twilio는 SMS, 전화, 이메일 등의 다양한 통신 서비스를 제공하는 클라우드 기반 플랫폼이다. 특히 SMS 발송을 위한 API가 직관적이고 강력해서 사용자 인증 등의 목적으로 널리 사용된다.

🔗 Twilio 공식 사이트


Twilio 설치 및 기본 설정법

설치 방법

npm i twilio

기본 사용법 예시 코드

import twilio from 'twilio';

const client = twilio(process.env.TWILIO_SID, process.env.TWILIO_TOKEN);

client.messages.create({
  body: 'Hello from Twilio!',
  to: '+821012345678', // 실제 수신자의 전화번호
  from: '+12025550123', // Twilio에서 제공하는 번호
}).then((message) => console.log(message.sid));
  • Account SID, Auth Token, Twilio Phone Number는 Twilio 콘솔에서 확인할 수 있다.
  • .env 파일에서 안전하게 관리해야 한다.

🔗 Twilio 콘솔 바로가기


Next.js에서 SMS 인증번호 보내기 (실습 코드)

📍 SMS 인증번호 발송 및 검증 (actions.ts)

다음은 실제 Next.js 환경에서 구현된 SMS 인증번호 발송 및 검증 코드이다.

"use server";

import { z } from "zod";
import validator from "validator";
import db from "@/lib/db";
import crypto from "crypto";
import { getSession } from "@/lib/session";
import { redirect } from "next/navigation";
import twilio from "twilio";

// 전화번호 유효성 검증
const phoneSchema = z
  .string()
  .trim()
  .refine(
    (phone) => validator.isMobilePhone(phone, "ko-KR"),
    "Wrong phone number format"
  );

// 토큰 존재 여부 검증 함수
async function tokenExists(token: number) {
  const exists = await db.sMSToken.findUnique({
    where: { token: token.toString() },
    select: { id: true },
  });
  return Boolean(exists);
}

// 토큰 유효성 검사
const tokenSchema = z.coerce
  .number()
  .min(100000)
  .max(999999)
  .refine(tokenExists, "This token does not exist");

interface ActionState {
  token: boolean;
  error?: object;
}

// 랜덤한 SMS 토큰 생성
async function getToken() {
  const token = crypto.randomInt(100000, 999999).toString();
  const existingToken = await db.sMSToken.findUnique({
    where: { token },
    select: { id: true },
  });
  if (existingToken) return getToken();
  return token;
}

// SMS 로그인 액션
export async function smsLogIn(prevState: ActionState, formData: FormData) {
  const phone = formData.get("phone");
  const token = formData.get("token");

  if (!prevState.token) {
    const result = phoneSchema.safeParse(phone);
    if (!result.success) {
      return { token: false, error: result.error.flatten() };
    }

    // 이전 토큰 삭제 후 새로운 토큰 생성
    await db.sMSToken.deleteMany({ where: { user: { phone: result.data } } });
    const newToken = await getToken();

    await db.sMSToken.create({
      data: {
        token: newToken,
        user: {
          connectOrCreate: {
            where: { phone: result.data },
            create: {
              phone: result.data,
              username: crypto.randomBytes(10).toString("hex"),
            },
          },
        },
      },
    });

    // Twilio로 SMS 전송
    const client = twilio(process.env.TWILIO_SID, process.env.TWILIO_TOKEN);
    await client.messages.create({
      body: `Your Karrot verification code is ${newToken}`,
      from: process.env.TWILIO_PHONE_NUMBER!,
      to: process.env.MY_PHONE_NUMBER!, // 실제 사용자 번호로 교체 필요
    });

    return { token: true };
  } else {
    const result = await tokenSchema.safeParseAsync(token);
    if (!result.success) {
      return { token: true, error: result.error.flatten() };
    }

    // 인증 성공 시 세션 생성 후 로그인
    const validToken = await db.sMSToken.findUnique({
      where: { token: result.data.toString() },
      select: { id: true, user: true },
    });

    const session = await getSession();
    session.userId = validToken!.user.id;
    await session.save();

    await db.sMSToken.delete({ where: { id: validToken!.id } });

    redirect("/profile");
  }
}

Twilio 무료 계정의 한계점

Twilio의 무료 계정에서는 인증되지 않은 번호로 SMS를 보낼 수 없다. 즉, Verified Number로 등록된 번호로만 발송 가능하다. 만약 실서비스에서 제대로 활용하려면 다음 중 하나를 선택해야 한다:

  • Twilio 유료 계정으로 전환
  • 다른 서비스로 변경 (예: Vonage)

🔗 Vonage (Nexmo) 공식 사이트


Twilio의 대안: Vonage 소개

Vonage (구 Nexmo)는 Twilio와 유사한 SMS 발송 서비스를 제공한다. Vonage는 무료 계정으로도 특정 국가로 메시지 전송이 가능해 Twilio의 무료 계정의 한계를 극복할 수 있는 대안으로 적합하다.

Vonage 설치 방법

npm install @vonage/server-sdk
npm install @vonage/auth

Vonage SMS 예시 코드

import { Vonage } from "@vonage/server-sdk";
import { Auth } from "@vonage/auth";

const credentials = new Auth({
apiKey: process.env.VONAGE_API_KEY,
apiSecret: process.env.VONAGE_API_SECRET,
});

const vonage = new Vonage(credentials);
await vonage.sms
.send({
to: process.env.MY_PHONE_NUMBER!,
//to: result.data,
from: process.env.VONAGE_SMS_FROM!,
text: `Your Karrot verification code is: ${token}`,
})

🔗 Vonage 공식 문서

대안을 활용하여 원활히 서비스를 운영해 보자.

0개의 댓글