티스토리 뷰

반응형

SaaS 운영 자동화 최종편

사용량 기반 빌링 · 에러 모니터링 · Sentry · Slack 알림 · Observability 구축하기
(NestJS + Next.js + K8s + Cloudflare + Terraform 기반 실서비스 운영편)


이제 우리는 개발·배포·AI 기능·결제·구독 모델까지
“상품으로서의 SaaS” 를 모두 만들었다.

그러나 진짜 운영은 지금부터다.

서버는 절대 우리가 보는 동안만 문제가 생기지 않는다.
사용자는 새벽 3시에 에러를 터뜨리고,
Stripe 결제 실패는 주말에 몰려오며,
로그는 알아서 쌓이지 않는다.

이번 글은 실제 스타트업에서 반드시 구축해야 하는 운영 자동화 체계를 정리한다:


🧭 이번 글에서 완성되는 운영 체계

기능 목적

📡 Sentry 에러 모니터링 실시간 서버/프론트 에러 감지
🔔 Slack 알림 장애 즉시 알림
📊 Prometheus + Grafana CPU, 메모리, RPS, Latency 모니터링
📝 구조적 로그(JSON Logging) 오류 상황 분석
💰 사용량 기반 Billing AI 호출량 기반 결제 기능
🧹 Cron 기반 유지작업 매일 사용량 리셋, 오래된 로그 삭제
🚦 API Rate-Limit 악의적 트래픽 방지
🔁 Circuit Breaker 외부 API 문제 시 서버 전체 장애 방지

이 글을 끝내고 나면, 당신의 서비스는
“혼자서도 회사급 운영 시스템” 을 가지게 된다.


1️⃣ Sentry 도입 — 백엔드/프론트 에러를 실시간 수집

반응형

Sentry는 스타트업 99%가 쓰는 운영 필수 툴이다.
에러가 발생하면 자동으로 Slack과 메일로 날아온다.


NestJS Sentry 설치

npm install @sentry/node @sentry/tracing

초기화

main.ts

import * as Sentry from '@sentry/node';
import { nodeProfilingIntegration } from "@sentry/profiling-node";

Sentry.init({
  dsn: process.env.SENTRY_DSN,
  environment: process.env.NODE_ENV,
  integrations: [
    nodeProfilingIntegration(),
  ],
  tracesSampleRate: 1.0,
  profilesSampleRate: 1.0,
});

Next.js Sentry 설정

npm install @sentry/nextjs

프로젝트 루트:

npx @sentry/wizard

자동으로:

  • pages/app 에러 추적
  • API route 에러 캡처
  • sourcemap 업로드
    가 적용된다.

2️⃣ Slack 알림 — Sentry + 서버 장애 알림

Slack Incoming Webhook 생성

환경변수:

SLACK_WEBHOOK_URL=https://hooks.slack.com/services/xxxx

NestJS Slack 서비스

@Injectable()
export class SlackService {
  async notify(message: string) {
    await fetch(process.env.SLACK_WEBHOOK_URL!, {
      method: 'POST',
      body: JSON.stringify({ text: message }),
    });
  }
}

장애 발생 시 호출

errorHandler.ts

if (statusCode >= 500) {
  slackService.notify(`🚨 서버 오류 발생: ${err.message}`);
}

3️⃣ Prometheus + Grafana 로 Observability 구축

운영하려면 최소한 알아야 한다:

  • CPU / 메모리 / 디스크
  • API 요청 수
  • 평균 응답시간
  • 에러율
  • RPS(Requests per second)

Kubernetes 환경에서 보통 아래 조합을 사용한다:

구성 설명

Prometheus 메트릭 수집
Grafana 대시보드 시각화
Prometheus Operator 설치 자동화

NestJS에서 Prometheus 메트릭 제공

npm install prom-client
import { Injectable } from '@nestjs/common';
import { Counter, Registry } from 'prom-client';

@Injectable()
export class MetricsService {
  private registry = new Registry();
  private requestCount = new Counter({
    name: 'http_requests_total',
    help: '전체 API 요청 수',
  });

  getMetrics() {
    return this.registry.metrics();
  }

  increment() {
    this.requestCount.inc();
  }
}

Metrics 엔드포인트

@Get('/metrics')
getMetrics() {
  return this.metricsService.getMetrics();
}

Prometheus가 자동으로 긁어간다.


4️⃣ 구조적 로그(JSON Logging) — 로그를 데이터처럼 다루기

개발자들 대부분이 초반에 하루 종일 삽질하는 부분: 로그가 흐트러짐.

운영 로그는 무조건 JSON이어야 한다.


NestJS Logger 패치

npm install pino pino-pretty nestjs-pino

main.ts

import { Logger } from 'nestjs-pino';

app.useLogger(app.get(Logger));

모든 요청·응답·에러가 JSON 형태로 기록된다:

{
  "msg": "Request completed",
  "method": "GET",
  "path": "/posts",
  "status": 200,
  "responseTime": 34
}

이제 Loki/Grafana/CloudWatch로 전송 가능.


5️⃣ 사용량 기반 Billing (AI 호출량 기반 결제)

우리는 이미 Stripe 구독 모델을 만들어두었다.
이제 여기에 사용량 기반 결제를 추가해볼 수 있다.

예시:
AI 요청 초과 → 추가 과금


AI 호출 기록 테이블

model AiUsage {
  id        Int      @id @default(autoincrement())
  userId    Int
  count     Int      @default(1)
  createdAt DateTime @default(now())
}

요청 시마다 기록 남기기

AiRateLimitGuard에서 추가:

await this.prisma.aiUsage.create({
  data: { userId: user.id },
});

Stripe 사용량 리포트 업로드

await stripe.subscriptionItems.createUsageRecord(
  subscriptionItemId,
  {
    quantity: 1,
    action: "increment",
  }
);

이제 Stripe가 자동으로 과금한다.


6️⃣ Cron 작업 — 매일 00시 AI 사용량 초기화

NestJS Schedule 패키지

npm install @nestjs/schedule

app.module.ts

imports: [ScheduleModule.forRoot()]

Reset Service

@Cron('0 0 * * *') // 매일 00:00
async resetAiLimits() {
  await this.prisma.user.updateMany({
    data: { ai_limit: 5 },
    where: { plan: 'FREE' },
  });

  await this.prisma.user.updateMany({
    data: { ai_limit: 200 },
    where: { plan: 'PRO' },
  });
}

7️⃣ API Rate Limit — 악성 요청 방어

npm install express-rate-limit

main.ts

app.use(
  rateLimit({
    windowMs: 60 * 1000,
    max: 100, // IP당 1분 100요청 제한
  })
);

8️⃣ Circuit Breaker — 외부 API 장애로 전체 서버가 죽는 걸 방지

LLM API 오류가 전체 API를 다운시키지 않도록.

npm install opossum
const breaker = new CircuitBreaker(
  () => openai.chat.completions.create(...),
  { timeout: 5000, errorThresholdPercentage: 50 }
);

OpenAI 문제가 생기면 자동으로 fallback 실행.


🔥 최종 운영 아키텍처

┌──────────────────────────────┐
│       Next.js Frontend       │
│  (Cloudflare Pages + Sentry) │
└───────────────┬──────────────┘
                │
                ▼
┌──────────────────────────────┐
│        NestJS Backend         │
│ JWT Auth / Stripe Billing     │
│ AI / RAG / VectorDB           │
│ Prometheus Metrics / Logging  │
│ Sentry Error Tracking         │
└───────────────┬──────────────┘
                │
                ▼
┌──────────────────────────────┐
│       Kubernetes Cluster      │
│ Auto Restart / Autoscaling    │
│ Grafana / Prometheus / Loki   │
└──────────────────────────────┘

✅ 이번 편 요약

기능 구축 여부

Sentry 에러 모니터링 ✔️
Slack 장애 알림 ✔️
Prometheus 메트릭 ✔️
Grafana 대시보드 ✔️
JSON 구조 로그 ✔️
AI 사용량 Billing ✔️
AI RateLimit ✔️
Cron 스케줄러 ✔️
Circuit Breaker ✔️

이제 단순한 프로젝트가 아니라
실서비스급 운영 능력을 갖춘 SaaS가 완성되었다.


🔮 다음 글 예고

이제 모든 흐름이 완성됐으니,
다음 편에서는 “전체 프로젝트 구조를 아키텍처 문서로 정리하기” 를 다룬다.

실제 회사에서 “기술 문서 / 아키텍처 문서 / 운영 문서 / 장애 대응 문서”를 어떻게 작성하는지
샘플과 함께 정리한다.

즉,
프로젝트의 모든 구성요소를 문서화하는 실무 편 으로 넘어간다.


 

SaaS운영, Stripe사용량과금, Prometheus, Grafana, Loki, Sentry, Slack알림, NestJS, Next.js, 운영자동화, Observability, DevOps

※ 이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2026/01   »
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
글 보관함
반응형