Next.js App Router에서 5분 만에 SMS 본인인증 구현하기 (서류 불필요)
2026-04-23T01:02:00.940Z
1. 사이드 프로젝트에 SMS 인증을 붙일 때 겪는 문제
새로운 웹 서비스나 앱을 개발할 때, 사용자 검증을 위한 **SMS 본인인증(OTP)**은 필수적인 기능 중 하나입니다. 특히 이커머스, 소셜 플랫폼, 혹은 스팸 가입을 막아야 하는 B2C 서비스에서는 빠질 수 없는 기능이죠.
하지만 토이 프로젝트, 사이드 프로젝트를 진행하는 1인 개발자나 이제 막 MVP를 개발하는 스타트업에게 기존의 SMS 인증 서비스들은 진입 장벽이 너무 높습니다. 기존 API 제공업체(NHN Cloud, CoolSMS 등)를 이용하려면 다음과 같은 복잡한 과정을 거쳐야 합니다:
- 사업자등록증 제출: 아직 사업자를 내지 않은 개인 개발자는 가입조차 불가능한 경우가 많습니다.
- 통신서비스 이용증명원: 발신번호 사전등록을 위해 통신사에서 서류를 발급받아 제출해야 합니다.
- 긴 심사 시간: 서류를 제출하고 승인받기까지 며칠씩 소요됩니다.
- 높은 단가: 건당 30원에서 많게는 50원 이상의 높은 비용이 발생합니다.
개발자는 그저 POST /send API 하나를 호출하고 싶을 뿐인데, 행정적인 절차 때문에 개발 흐름이 뚝 끊기게 됩니다.
2. 해결책: 서류 없이 시작하는 EasyAuth(이지어스) API
이러한 개발자들의 고충을 완벽하게 해결해주는 서비스가 바로 EasyAuth(이지어스) 입니다. EasyAuth는 개발자 친화적인 초간단 SMS 인증 API로, 복잡한 절차를 모두 없앴습니다.
EasyAuth의 핵심 장점
- 서류 불필요: 사업자등록증, 이용증명원 등 어떠한 서류 제출도 필요 없습니다.
- 즉시 시작: 가입 후 발급받은 API 키로 5분 안에 연동을 완료할 수 있습니다.
- 자동 발신번호: 대표번호를 사전 등록할 필요 없이 시스템에서 자동으로 처리됩니다.
- 합리적 가격: 건당 15~25원으로 기존 업체 대비 절반 가까이 저렴합니다.
- 무료 체험 제공: 가입 즉시 테스트용 10건이 무료로 제공됩니다.
이번 포스팅에서는 최신 Next.js App Router 환경에서 EasyAuth를 활용하여 단 5분 만에 SMS 본인인증 기능을 완벽하게 구현하는 방법을 단계별로 알아보겠습니다.
3. Next.js App Router 환경의 보안과 아키텍처
Next.js App Router에서 외부 API를 연동할 때 가장 중요한 것은 보안입니다. 클라이언트(브라우저)에서 직접 EasyAuth API를 호출하게 되면 브라우저 네트워크 탭에 API Key가 노출되어 누군가 내 크레딧을 무단으로 사용할 위험이 있습니다.
따라서 다음과 같은 아키텍처로 구현해야 합니다:
- Client Component: 사용자로부터 전화번호와 인증번호를 입력받아 Next.js 내부 API로 요청을 보냅니다.
- Route Handlers (Server): 클라이언트의 요청을 받아, 안전한 서버 환경에서 환경변수(
process.env)에 저장된 API Key를 사용하여 EasyAuth API와 통신합니다.
4. 백엔드 구현: Route Handlers 만들기
이제 본격적으로 코드를 작성해보겠습니다. EasyAuth는 직관적으로 Send와 Verify 단 두 개의 엔드포인트만 제공합니다.
Step 1: 환경 변수 설정
프로젝트 루트의 .env.local 파일에 EasyAuth 대시보드에서 발급받은 API Key를 추가합니다.
# .env.local
EASYAUTH_API_KEY=ea_live_xxxxxxxxxxxxxxxxx
Step 2: 인증번호 발송 API (Send)
사용자가 전화번호를 입력하고 '인증번호 받기'를 클릭했을 때 호출될 Next.js 라우트를 생성합니다. app/api/sms/send/route.ts 파일을 생성합니다.
// app/api/sms/send/route.ts
import { NextResponse } from 'next/server';
export async function POST(request: Request) {
try {
const { phone } = await request.json();
if (!phone) {
return NextResponse.json(
{ error: '전화번호가 필요합니다.' },
{ status: 400 }
);
}
// 서버 환경에서 EasyAuth API 호출 (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();
if (!response.ok) {
throw new Error(data.message || '발송 실패');
}
return NextResponse.json({ success: true, message: '인증번호가 발송되었습니다.' });
} catch (error: any) {
return NextResponse.json(
{ success: false, error: error.message },
{ status: 500 }
);
}
}
Step 3: 인증번호 검증 API (Verify)
사용자가 문자로 받은 6자리 코드를 입력했을 때 검증하는 라우트를 만듭니다. app/api/sms/verify/route.ts 파일을 생성합니다.
// app/api/sms/verify/route.ts
import { NextResponse } from 'next/server';
export async function POST(request: Request) {
try {
const { phone, code } = await request.json();
if (!phone || !code) {
return NextResponse.json(
{ error: '전화번호와 인증 코드가 필요합니다.' },
{ status: 400 }
);
}
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();
if (!response.ok) {
throw new Error(data.message || '검증 실패');
}
return NextResponse.json({ success: true, message: '인증이 완료되었습니다.' });
} catch (error: any) {
return NextResponse.json(
{ success: false, error: error.message },
{ status: 400 }
);
}
}
5. 프론트엔드 완성 코드 (Client Component)
이제 사용자가 상호작용할 UI를 만듭니다. Tailwind CSS를 사용하여 깔끔한 인증 폼을 구성해보겠습니다. app/page.tsx에 작성합니다.
// app/page.tsx
'use client';
import { useState } from 'react';
export default function SMSAuth() {
const [phone, setPhone] = useState('');
const [code, setCode] = useState('');
const [step, setStep] = useState<1 | 2>(1);
const [isLoading, setIsLoading] = useState(false);
const [message, setMessage] = useState('');
// 1단계: 인증번호 발송 처리
const handleSend = async (e: React.FormEvent) => {
e.preventDefault();
setIsLoading(true);
setMessage('');
try {
const res = await fetch('/api/sms/send', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ phone: phone.replace(/-/g, '') }),
});
const data = await res.json();
if (res.ok) {
setStep(2);
setMessage('인증번호가 발송되었습니다. 문자를 확인해주세요.');
} else {
setMessage(data.error || '발송에 실패했습니다.');
}
} catch (error) {
setMessage('네트워크 오류가 발생했습니다.');
} finally {
setIsLoading(false);
}
};
// 2단계: 인증번호 검증 처리
const handleVerify = async (e: React.FormEvent) => {
e.preventDefault();
setIsLoading(true);
setMessage('');
try {
const res = await fetch('/api/sms/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ phone: phone.replace(/-/g, ''), code }),
});
const data = await res.json();
if (res.ok) {
alert('본인인증이 완료되었습니다! 🎉');
// TODO: 회원가입 다음 단계로 라우팅 혹은 전역 상태 업데이트
} else {
setMessage(data.error || '잘못된 인증번호입니다.');
}
} catch (error) {
setMessage('네트워크 오류가 발생했습니다.');
} finally {
setIsLoading(false);
}
};
return (
<div>
<div>
<h1>
휴대폰 본인인증
</h1>
{step === 1 ? (
<div>
휴대폰 번호
setPhone(e.target.value)}
placeholder="01012345678"
className="w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500 outline-none"
required
/>
</div>
{isLoading ? '발송 중...' : '인증번호 받기'}
) : (
<div>
인증번호 6자리
setCode(e.target.value)}
placeholder="123456"
maxLength={6}
className="w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500 outline-none"
required
/>
</div>
{isLoading ? '확인 중...' : '인증 완료하기'}
setStep(1)}
className="w-full text-sm text-gray-500 hover:text-gray-800 mt-2"
>
전화번호 다시 입력하기
)}
{message && (
<p>
{message}
</p>
)}
</div>
</div>
);
}
6. 실무 적용 팁과 보안 모범 사례
MVP 단계라도 실제 프로덕션 환경에 앱을 배포할 때는 몇 가지 고려해야 할 점이 있습니다.
- Rate Limiting (요청 제한): 악의적인 사용자가 무단으로 여러 번
/api/sms/send를 호출하면 금전적 피해(크레딧 소모)가 발생할 수 있습니다. Upstash Redis 등을 사용하여 동일 IP에서 1분에 3회 이상 발송하지 못하도록 제한하는 것이 좋습니다. - 전화번호 정규화: 프론트엔드에서 사용자가
010-1234-5678처럼 하이픈(-)을 넣어 입력하더라도replace(/-/g, '')를 통해 숫자만 백엔드로 넘기도록 처리했습니다. UX 측면에서 매우 중요합니다. - 에러 핸들링: 통신사 장애, 번호 오기입 등 다양한 변수가 존재합니다. EasyAuth API는 명확한 에러 메시지를 반환하므로, 이를 클라이언트에게 적절하게 보여주어 혼란을 방지해야 합니다.
7. 마무리
지금까지 Next.js App Router와 EasyAuth(이지어스)를 결합하여 SMS 본인인증을 빠르게 구현하는 방법을 살펴보았습니다.
과거에는 번거로운 서류 작업과 통신사 심사 때문에 스타트업 초기 단계나 사이드 프로젝트에 SMS 인증을 붙이는 것을 망설이곤 했습니다. 하지만 EasyAuth를 사용하면 서류 없이, 사전 대표번호 등록 없이 단 5분 만에 즉시 API를 연동할 수 있습니다. 거기에 건당 15~25원의 경제적인 요금제까지 갖추어 1인 개발자나 토이 프로젝트에도 전혀 부담이 없습니다.
새로운 프로젝트를 기획 중이라면, 가입 시 제공되는 10건의 무료 크레딧을 활용해 지금 바로 EasyAuth를 테스트해보세요!
Tags: Next.js, SMS Authentication, EasyAuth, 이지어스, Web Development, API Integration, App Router Excerpt: 서류 제출 없이 5분 만에 Next.js App Router 환경에서 SMS 본인인증을 구현하는 방법을 단계별로 알아봅니다. 스타트업과 사이드 프로젝트에 최적화된 EasyAuth를 활용해보세요.
Start advertising on Bitbake
Contact Us