[Next.js App Router] 서류 없이 5분 만에 SMS 휴대폰 본인인증 구현하기
2026-05-05T01:02:52.538Z

사이드 프로젝트에 SMS 인증, 왜 이렇게 힘들까?
사이드 프로젝트나 스타트업 MVP를 개발하다 보면 반드시 마주치는 장벽이 있습니다. 바로 **휴대폰 본인인증(SMS 인증)**입니다. 기능 자체는 단순해 보이지만, 막상 기존 벤더사(API)를 찾아보면 사업자등록증, 통신사 발신번호 사전등록, 이용증명원 등 요구하는 서류가 산더미입니다.
"단순히 테스트만 해보고 싶은데 서류부터 내라고?"
이런 고민을 해결하기 위해, 오늘은 서류 제출 없이 단 5분 만에 Next.js 14/15 App Router 환경에서 SMS OTP 인증을 구현하는 방법을 알아보겠습니다. 이를 위해 개발자를 위한 초간단 SMS 인증 API인 [EasyAuth(이지어스)]를 활용합니다.
이 글에서 다룰 내용
- Next.js App Router의 Route Handler를 활용한 서버리스 API 구성
- React Client Component를 이용한 OTP 인증 UI 구현
- 보안과 UX를 고려한 실무 팁 (정규화, 상태 관리)
1단계: API 라우트 설정 (Backend)
Next.js App Router에서는 app/api/.../route.ts 형태로 백엔드 API를 쉽게 만들 수 있습니다. 클라이언트 브라우저에서 직접 외부 API를 호출하면 API 키가 유출될 위험이 있으므로, Next.js 서버를 프록시로 사용합니다. EasyAuth는 단 두 개의 엔드포인트(/send, /verify)만 제공하므로 매우 직관적입니다.
1. 인증번호 발송 API (app/api/auth/send/route.ts)
import { NextResponse } from 'next/server';
export async function POST(request: Request) {
try {
const { phone } = await request.json();
// EasyAuth 발송 API 호출
const response = await fetch('https://api.easyauth.co.kr/send', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.EASYAUTH_API_KEY}`,
},
body: JSON.stringify({ phone }),
});
const data = await response.json();
return NextResponse.json(data, { status: response.status });
} catch (error) {
return NextResponse.json({ message: '발송 실패' }, { status: 500 });
}
}
2. 인증번호 검증 API (app/api/auth/verify/route.ts)
import { NextResponse } from 'next/server';
export async function POST(request: Request) {
try {
const { phone, code } = await request.json();
// EasyAuth 검증 API 호출
const response = await fetch('https://api.easyauth.co.kr/verify', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.EASYAUTH_API_KEY}`,
},
body: JSON.stringify({ phone, code }),
});
const data = await response.json();
return NextResponse.json(data, { status: response.status });
} catch (error) {
return NextResponse.json({ message: '검증 실패' }, { status: 500 });
}
}
2단계: 클라이언트 UI 구현 (Frontend)
이제 사용자가 전화번호를 입력하고 문자로 받은 인증번호 6자리를 입력할 수 있는 UI를 만들어 봅니다.
전체 동작하는 코드 (app/components/SmsVerification.tsx)
'use client';
import { useState } from 'react';
export default function SmsVerification() {
const [phone, setPhone] = useState('');
const [code, setCode] = useState('');
const [step, setStep] = useState(1); // 1: 번호 입력 단계, 2: 인증번호 입력 단계
const [loading, setLoading] = useState(false);
const [message, setMessage] = useState('');
const handleSend = async () => {
setLoading(true);
setMessage('');
try {
const res = await fetch('/api/auth/send', {
method: 'POST',
body: JSON.stringify({ phone: phone.replace(/[^0-9]/g, '') }), // 숫자만 추출
});
if (res.ok) {
setStep(2);
setMessage('인증번호가 발송되었습니다. 문자를 확인해주세요.');
} else {
setMessage('발송에 실패했습니다. 번호를 다시 확인해주세요.');
}
} finally {
setLoading(false);
}
};
const handleVerify = async () => {
setLoading(true);
setMessage('');
try {
const res = await fetch('/api/auth/verify', {
method: 'POST',
body: JSON.stringify({ phone: phone.replace(/[^0-9]/g, ''), code }),
});
if (res.ok) {
setMessage('✅ 인증이 완료되었습니다!');
// TODO: 성공 후 회원가입 프로세스 진행 또는 토큰 발급 로직 연동
} else {
setMessage('❌ 인증번호가 일치하지 않습니다.');
}
} finally {
setLoading(false);
}
};
return (
<div>
<h2>휴대폰 본인인증</h2>
{step === 1 && (
<div>
setPhone(e.target.value)}
className="p-3 border rounded-md focus:ring-2 focus:ring-blue-500 focus:outline-none"
maxLength={13}
/>
{loading ? '발송 중...' : '인증번호 받기'}
</div>
)}
{step === 2 && (
<div>
setCode(e.target.value)}
className="p-3 border rounded-md focus:ring-2 focus:ring-blue-500 focus:outline-none tracking-widest text-center text-lg"
maxLength={6}
/>
{loading ? '확인 중...' : '인증 완료'}
setStep(1)}
className="text-sm text-gray-500 underline mt-2 text-center"
>
번호 다시 입력하기
</div>
)}
{message && (
<p>
{message}
</p>
)}
</div>
);
}
실무 팁과 보안 베스트 프랙티스 (Tips & Best Practices)
안전하고 사용자 친화적인 SMS 인증을 위해 아래 항목들을 챙겨보세요.
- 전화번호 정규화 (Formatting): 사용자는
010-1234-5678,010 1234 5678등 예측 불가능한 형태로 번호를 입력합니다. 위 코드의.replace(/[^0-9]/g, '')처럼 클라이언트와 서버 양측에서 숫자만 추출하도록 처리하는 것이 가장 안전합니다. - 환경 변수 보호 (Security):
EASYAUTH_API_KEY는 반드시.env.local에 보관해야 합니다.NEXT_PUBLIC_접두사를 붙여 클라이언트에 키를 노출하는 실수를 절대 범하지 마세요. Route Handler가 프록시 역할을 하는 이유가 바로 이 때문입니다. - 버튼 연타 방지 (Rate Limiting):
loading상태를 적극 활용하여, API 응답이 오기 전까지 버튼을 비활성화(disabled)해야 합니다. 의도치 않은 중복 API 호출로 인한 문자 발송 비용 낭비를 막을 수 있습니다.
결론: 복잡한 서류 없는 EasyAuth로 5분 만에 시작하세요
지금까지 Next.js App Router 환경에서 간단하게 SMS 휴대폰 본인인증을 구현하는 방법을 완성 코드로 알아보았습니다. 로직 자체는 간단하지만, 그동안 서비스 연동의 가장 큰 허들은 '복잡한 행정 서류'였습니다.
하지만 이커머스, 서비스 플랫폼, 혹은 빠르게 MVP를 검증해야 하는 스타트업이라면 개발에만 집중해야 합니다. 이지어스(EasyAuth)는 개발자의 이런 고충을 해결하기 위해 탄생했습니다.
- 서류 제로: 사업자등록증, 통신사 가입 증명원 제출 없음
- 빠른 시작: 가입 즉시 API 연동이 가능하며, 복잡한 대표번호 사전등록 없이 자동 발신번호가 세팅됩니다.
- 가벼운 가격: 기존 건당 30~50원 수준이던 문자 발송 비용을 15~25원으로 확 낮췄습니다.
- 심플한 API: 복잡한 SDK 없이
POST /send,POST /verify두 번의 호출로 끝납니다.
현재 가입 시 10건의 무료 테스트 크레딧을 바로 제공하고 있습니다. 지금 진행 중인 토이 프로젝트나 사이드 프로젝트에 SMS 인증이 필요하다면, 5분 만에 서류 없이 이지어스로 연동을 마쳐보세요!
Start advertising on Bitbake
Contact Us