티스토리 뷰
Stripe 구독(Subscription) + 사용자 인증(Auth) + AI 기능 제한까지
실제 SaaS 완성 구조 만들기
(NestJS + Next.js + Stripe + RAG/LLM 권한 제어)
이전 글까지 우리는
- Next.js 프론트
- NestJS 백엔드
- Kubernetes 자동배포
- Terraform IaC
- Cloudflare CDN
- AI 기능(RAG + 이미지 임베딩)
까지 실제 SaaS의 거의 모든 백엔드 기능을 구축했다.
이번 글은 마지막 퍼즐이다.
“실제 돈을 받는 SaaS 구조를 만들려면
반드시 필요한 3가지: 인증 + 결제 + 권한제어”
이번 편에서는 Stripe 기반의 구독 모델을 실제로 NestJS와 Next.js에 붙이고,
구독 플랜에 따라 AI 기능 제한(쿼터/요금제별 기능 제공) 하는 SaaS 구조를 완성한다.
🧭 이번 글에서 완성할 기능
기능 설명
| 🔐 사용자 인증 | JWT 기반 Login · Signup |
| 💳 Stripe 구독 결제 | Basic / Pro / Enterprise 요금제 |
| 🔁 Stripe Webhook | 결제 성공 → DB 반영 |
| 🧱 Role/Plan 시스템 | Free / Pro / Enterprise |
| 🚦 AI 기능 제한 | 하루 사용량 제한, 기능 잠금 |
| 🌐 Next.js UI 통합 | 결제 페이지 + 구독 상태 표시 |
실제 SaaS가 되는 순간이다.
1️⃣ Stripe 기본 설정
① Stripe 설치
백엔드(NestJS)
npm install stripe
프론트(Next.js)
npm install @stripe/stripe-js
② Stripe 대시보드에서 Product / Price 생성
예시 요금제:
Plan Price(월) 제한
| Free | 0원 | AI 요청 하루 5회 |
| Pro | 9,900원 | AI 요청 200회 |
| Enterprise | custom | 무제한 |
각 플랜마다 Price ID를 발급받는다:
price_123_basic
price_456_pro
price_789_enterprise
2️⃣ NestJS 인증(Auth) 구축
DTO
export class SignupDto {
email: string;
password: string;
name: string;
}
Service
async signup(data: SignupDto) {
const hash = await bcrypt.hash(data.password, 10);
return this.prisma.user.create({
data: {
email: data.email,
password: hash,
plan: 'FREE', // 신규 유저 기본 플랜
ai_limit: 5, // 기본 사용량
},
});
}
Login → JWT 발급
async login(email: string, pass: string) {
const user = await this.prisma.user.findUnique({ where: { email }});
const ok = await bcrypt.compare(pass, user.password);
if (!ok) throw new UnauthorizedException();
const payload = { userId: user.id, plan: user.plan };
const token = await this.jwt.signAsync(payload);
return { token };
}
3️⃣ Stripe Checkout Session 생성 (NestJS)
import Stripe from 'stripe';
@Injectable()
export class BillingService {
private stripe = new Stripe(process.env.STRIPE_KEY!, {
apiVersion: '2023-10-16',
});
async createCheckoutSession(userId: number, priceId: string) {
return this.stripe.checkout.sessions.create({
mode: 'subscription',
customer_email: await this.getUserEmail(userId),
line_items: [{ price: priceId, quantity: 1 }],
success_url: `${process.env.FRONT_URL}/billing/success`,
cancel_url: `${process.env.FRONT_URL}/billing/cancel`,
metadata: { userId },
});
}
}
Next.js에서 이 API를 호출하면 Stripe 결제창이 뜬다.
4️⃣ Stripe Webhook으로 구독 상태 업데이트
Stripe Webhook은 SaaS의 핵심이다.
Stripe → 우리 서버 로직이 이벤트 기반으로 자동 반응하도록 만드는 장치이기 때문이다.
NestJS Webhook 엔드포인트
@Post('webhook')
@RawBody() // NestJS 설정 필요
async webhook(@Req() req: Request) {
const sig = req.headers['stripe-signature'];
const event = this.stripe.webhooks.constructEvent(
req.body,
sig,
process.env.STRIPE_WEBHOOK_SECRET
);
switch (event.type) {
case 'customer.subscription.created':
case 'customer.subscription.updated':
await this.handleSubscription(event.data.object);
break;
case 'invoice.payment_failed':
await this.handlePaymentFailed(event.data.object);
break;
}
return { received: true };
}
Webhook 로직 (plan + ai_limit 업데이트)
async handleSubscription(sub: Stripe.Subscription) {
const userId = Number(sub.metadata.userId);
const plan =
sub.items.data[0].price.id === 'price_basic'
? 'PRO'
: 'ENTERPRISE';
await this.prisma.user.update({
where: { id: userId },
data: {
plan,
ai_limit: plan === 'PRO' ? 200 : 99999999,
},
});
}
결제 → 자동으로 DB 업데이트 → 즉시 플랜 활성화.
5️⃣ AI 기능 제한 로직
AI API 요청 시마다 유저의 티어를 확인해야 한다.
NestJS Guard 생성
@Injectable()
export class AiRateLimitGuard implements CanActivate {
constructor(private prisma: PrismaService) {}
async canActivate(ctx: ExecutionContext) {
const req = ctx.switchToHttp().getRequest();
const user = req.user;
const dbUser = await this.prisma.user.findUnique({
where: { id: user.userId },
});
if (dbUser.ai_limit <= 0) {
throw new ForbiddenException("AI 사용량이 모두 소진되었습니다.");
}
// 사용량 감소
await this.prisma.user.update({
where: { id: user.userId },
data: { ai_limit: dbUser.ai_limit - 1 },
});
return true;
}
}
적용
@UseGuards(JwtAuthGuard, AiRateLimitGuard)
@Post('chat')
async chat(@Body('prompt') prompt: string) {
return this.aiService.chat(prompt);
}
무료 유저는 하루 5회, Pro는 200회 제한이 자동 적용된다.
6️⃣ Next.js UI – 요금제 화면
요금제 선택 화면 예시:
export default function Pricing() {
return (
<div>
<h1>요금제</h1>
<div>
<h2>FREE</h2>
<p>AI 5회/일</p>
<button>기본 제공</button>
</div>
<div>
<h2>PRO</h2>
<p>월 9,900원 / AI 200회</p>
<a href="/billing/checkout?price=price_basic">구독하기</a>
</div>
</div>
);
}
클릭하면 Next.js route → NestJS API → Stripe Checkout으로 연결된다.
7️⃣ 최종 전체 아키텍처
[Next.js]
│
▼
/billing/checkout
│
▼
[NestJS Billing API]
create checkout session
│
▼
Stripe UI
│
┌───────────────────┴────────────────────┐
▼ ▼
Stripe Webhook Stripe Dashboard
(subscription created/updated) |
│ |
▼ ▼
Update User Plan in DB (Prisma) Admin 자동화 가능
│
▼
User plan → AI limit 변경
│
▼
AI 요청 시 Guard로 제한 적용
8️⃣ 이번 글 요약
기능 완료
| Stripe Checkout | ✔️ |
| Webhook 처리 | ✔️ |
| 구독 플랜 관리 | ✔️ |
| AI 기능 제한 | ✔️ |
| 프론트 UI 연동 | ✔️ |
| JWT 인증 기반 사용자 시스템 | ✔️ |
이제 만들어진 프로젝트는 진짜로 상용 SaaS 서비스의 구조를 가진다.
Stripe, SaaS, Subscription, NestJS, Next.js, JWT, Webhook, AI요금제, RateLimit, RAG, VectorDB
'study > 백엔드' 카테고리의 다른 글
| 📘 SaaS 프로젝트 기술 문서(Architecture Document) 완성편 (0) | 2025.12.11 |
|---|---|
| SaaS 운영 자동화 최종편 (0) | 2025.12.08 |
| 실서비스급 NestJS + Next.js 프로젝트에 AI 기능 붙이기 (0) | 2025.12.01 |
| Terraform + GitOps(ArgoCD)로 “전체 인프라를 코드로 관리”하기 (0) | 2025.11.25 |
| Next.js + NestJS + Cloudflare 완전 통합 운영 아키텍처 구축 (0) | 2025.11.21 |
- Total
- Today
- Yesterday
- JAX
- CI/CD
- Docker
- JWT
- kotlin
- flax
- PostgreSQL
- Next.js
- ai철학
- node.js
- fastapi
- 개발블로그
- Redis
- Prisma
- REACT
- NestJS
- seo 최적화 10개
- DevOps
- nextJS
- 압박면접
- 딥러닝
- Python
- 쿠버네티스
- 프론트엔드개발
- 백엔드개발
- 웹개발
- Express
- rag
- llm
- SEO최적화
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | ||||
| 4 | 5 | 6 | 7 | 8 | 9 | 10 |
| 11 | 12 | 13 | 14 | 15 | 16 | 17 |
| 18 | 19 | 20 | 21 | 22 | 23 | 24 |
| 25 | 26 | 27 | 28 | 29 | 30 | 31 |

