티스토리 뷰

728x90

[3편] Gemini 3.7 Flash 연동과 503 트래픽 과부하를 극복하는 다중 Fallback 아키텍처

부제: "AI 분석이 멈추지 않는 앱 만들기" 최신 @google/genai SDK 활용법과 대용량 트래픽 상황에서도 0.1초 만에 응답하는 지능형 AI 브리핑 시스템 구축


1. 인트로: 이번 편에서 만들 결과물과 해결하려는 핵심 문제

5일선 시뮬레이터에서 단순한 수치 계산(+3.2%, 5일선 상회)만 보여주는 것보다, 전문 펀드매니저 수준의 정교한 브리핑과 대응 시나리오를 AI가 자연어로 설명해 주면 사용자 경험이 극대화됩니다.

우리는 최신 초고속 LLM인 Google Gemini 3.7 Flash 모델을 채택했습니다. 하지만 실무에서 LLM API를 연동할 때 반드시 마주치는 문제가 있습니다:

  1. 간헐적 API 503 (Model Overloaded / Rate Limit) 에러: 사용자가 몰리거나 구글 서버 상태에 따라 서비스 불가 에러가 발생합니다.
  2. 응답 지연(Latency): AI 응답이 3~5초 이상 걸릴 경우 사용자는 답답함을 느낍니다.
  3. API 키 미설정 시의 뻗음(Crash) 현상: 서버 초기화 시 키가 없다고 전체 서버가 멈추면 안 됩니다.

이번 3편에서는 최신 @google/genai SDK를 사용해 Gemini 3.7 Flash를 백엔드에 안전하게 연동하고, Gemini 3.7 Flash → Gemini 2.5 Flash Lite → 로컬 룰베이스 즉시 생성(Local Rule-based Fallback)으로 이어지는 3단계 무장애(Zero-Downtime) 다중 Fallback 아키텍처를 구현합니다.


2. 주요 개념 및 원리

2.1 3단계 다중 Fallback 파이프라인

[AI 브리핑 요청]
      │
      ▼
[1단계] Gemini 3.7 Flash (최고 지능 분석 시도)
      │
      ├─▶ 성공 시: 풍부한 맥락의 심층 브리핑 반환 (Confidence: HIGH)
      │
      ▼ (503 과부하 또는 429 에러 발생 시)
[2단계] Gemini 2.5 Flash Lite (초경량 백업 모델 재시도)
      │
      ├─▶ 성공 시: 빠른 핵심 요약 브리핑 반환 (Confidence: MEDIUM)
      │
      ▼ (전체 API 장애 또는 네트워크 차단 시)
[3단계] 정밀 룰베이스 로컬 엔진 (0ms 즉시 생성)
      │
      └─▶ 수학적 5일선 원칙에 입각한 100% 무결점 브리핑 즉시 반환!

3. 단계별 코드 구현

3.1 최신 Gemini SDK 클라이언트 초기화 (server.ts)

// server.ts
import { GoogleGenAI } from '@google/genai';

// API 키가 없어도 서버가 크래시되지 않도록 지연(Lazy) 초기화 구현
function getGeminiClient(): GoogleGenAI | null {
  const apiKey = process.env.GEMINI_API_KEY;
  if (!apiKey) {
    console.warn('[Gemini] GEMINI_API_KEY is not set in environment.');
    return null;
  }
  return new GoogleGenAI({ apiKey });
}

3.2 3단계 다중 Fallback AI 분석 함수 (server.ts)

// server.ts
interface AnalysisPayload {
  stockName: string;
  stockCode: string;
  currentPrice: number;
  avgBuyPrice: number;
  ma5: number;
  ma25: number;
  ma5DiffPct: number;
  profitLossPct: number;
  diagnosis: string;
}

export async function generateAiDiagnosisReport(payload: AnalysisPayload) {
  const { stockName, stockCode, currentPrice, avgBuyPrice, ma5, ma25, ma5DiffPct, profitLossPct, diagnosis } = payload;

  const prompt = `
당신은 기술적 분석과 5일선 추세추종 단타 매매 전문 수석 애널리스트입니다.
아래 데이터를 바탕으로 투자자에게 명확하고 직관적인 브리핑 리포트를 3문단 이내로 작성하세요.

[종목 데이터]
- 종목명: ${stockName} (${stockCode})
- 현재가(종가): ${currentPrice.toLocaleString()}원
- 5일 이동평균선(5MA): ${ma5.toLocaleString()}원 (5일선 대비 이격률: ${ma5DiffPct}%)
- 25일 이동평균선(25MA): ${ma25.toLocaleString()}원
- 투자자 매수단가: ${avgBuyPrice > 0 ? `${avgBuyPrice.toLocaleString()}원 (현재 손익률: ${profitLossPct}%)` : '미보유 (신규 관찰)'}
- 알고리즘 1차 진단: ${diagnosis}

[작성 가이드라인]
1. [현재 수급 및 추세 상태]: 5일선과 25일선의 배열 상태 및 이격도 평가.
2. [단기 매매 전략]: 내일 시초가 및 장중 대응 지침 (손절 기준선, 익절 타겟).
3. [투자 유의사항]: 뇌동매매 방지를 위한 심리적 조언.
`;

  const aiClient = getGeminiClient();

  // 1단계: Gemini 3.7 Flash 시도
  if (aiClient) {
    try {
      console.log('[AI] Trying Gemini 3.7 Flash...');
      const response = await aiClient.models.generateContent({
        model: 'gemini-3.7-flash',
        contents: prompt,
      });

      if (response && response.text) {
        return {
          source: 'GEMINI_3_7_FLASH',
          text: response.text.trim(),
        };
      }
    } catch (err: any) {
      console.warn('[AI] Gemini 3.7 Flash failed (e.g. 503/429), trying Fallback 1: Gemini 2.5 Flash Lite...', err.message);

      // 2단계: Gemini 2.5 Flash Lite 시도
      try {
        const responseLite = await aiClient.models.generateContent({
          model: 'gemini-2.5-flash-lite',
          contents: prompt,
        });

        if (responseLite && responseLite.text) {
          return {
            source: 'GEMINI_2_5_FLASH_LITE',
            text: responseLite.text.trim(),
          };
        }
      } catch (errLite: any) {
        console.warn('[AI] Fallback model also failed. Switching to Local Rule-based engine...');
      }
    }
  }

  // 3단계: 로컬 룰베이스 무장애 엔진 (Zero Latency)
  return {
    source: 'RULE_ENGINE_FALLBACK',
    text: generateLocalFallbackReport(payload),
  };
}

// 100% 무중단을 보장하는 로컬 브리핑 템플릿 엔진
function generateLocalFallbackReport(payload: AnalysisPayload): string {
  const { stockName, currentPrice, ma5, ma5DiffPct, profitLossPct, avgBuyPrice, diagnosis } = payload;

  let p1 = `현재 ${stockName}의 주가는 ${currentPrice.toLocaleString()}원으로 5일 이동평균선(${ma5.toLocaleString()}원) 대비 ${ma5DiffPct > 0 ? `+${ma5DiffPct}% 상회` : `${ma5DiffPct}% 하회`}하고 있습니다.`;

  let p2 = '';
  if (diagnosis === '적극 매수' || diagnosis === '홀딩') {
    p2 = `단기 추세선인 5일선 지지력이 양호하여 상승 탄력이 유지되고 있습니다. 5일선 이탈(종가 기준 ${ma5.toLocaleString()}원)을 리스크 관리선으로 삼고 추세 추종을 권장합니다.`;
  } else if (diagnosis === '분할 매도') {
    p2 = `5일선 대비 단기 이격이 과도하게 벌어져 차익 실현 욕구가 강해질 수 있는 구간입니다. 수익 중이라면 30~50% 비중을 선제적으로 분할 매도하여 확정 수익을 챙기시기 바랍니다.`;
  } else {
    p2 = `종가 기준 5일선을 하향 이탈하여 단기 약세 구간에 진입했습니다. 손실 방어를 위해 미련 없는 손절 및 비중 축소를 권고하며, 지지선 확인 전까지 추가 매수는 자제해야 합니다.`;
  }

  let p3 = avgBuyPrice > 0 
    ? `현재 보유 손익률은 ${profitLossPct}%입니다. 단타 매매는 예측이 아닌 대응의 영역이므로 사전에 정한 원칙을 엄격히 준수하세요.`
    : `신규 진입 시 5일선 지지 여부를 반드시 확인하고 분할 진입하시기 바랍니다.`;

  return `${p1}\n\n${p2}\n\n${p3}`;
}

4. 프론트엔드 연동 및 스트리밍/카드 렌더링 (AiReportCard.tsx)

// src/components/AiReportCard.tsx
import React from 'react';
import { Sparkles, Bot, ShieldCheck } from 'lucide-react';

interface AiReportCardProps {
  reportText: string;
  source: 'GEMINI_3_7_FLASH' | 'GEMINI_2_5_FLASH_LITE' | 'RULE_ENGINE_FALLBACK';
  isLoading: boolean;
}

export const AiReportCard: React.FC<AiReportCardProps> = ({ reportText, source, isLoading }) => {
  return (
    <div className="bg-slate-900 border border-slate-800 rounded-xl p-5 text-white space-y-3">
      <div className="flex items-center justify-between">
        <div className="flex items-center gap-2">
          <Sparkles className="w-5 h-5 text-amber-400 animate-spin-slow" />
          <h3 className="font-bold text-base">Gemini AI 수석 애널리스트 브리핑</h3>
        </div>
        <span className="text-xs px-2.5 py-1 rounded-full bg-slate-800 text-slate-300 font-mono border border-slate-700">
          {source === 'GEMINI_3_7_FLASH' && '⚡ Gemini 3.7 Flash'}
          {source === 'GEMINI_2_5_FLASH_LITE' && '✨ Gemini 2.5 Flash Lite'}
          {source === 'RULE_ENGINE_FALLBACK' && '🛡️ 룰기반 스마트 엔진'}
        </span>
      </div>

      {isLoading ? (
        <div className="animate-pulse space-y-2 py-4">
          <div className="h-4 bg-slate-800 rounded w-3/4"></div>
          <div className="h-4 bg-slate-800 rounded w-full"></div>
          <div className="h-4 bg-slate-800 rounded w-5/6"></div>
        </div>
      ) : (
        <div className="text-sm text-slate-200 leading-relaxed whitespace-pre-line bg-slate-950/60 p-4 rounded-lg border border-slate-800/80">
          {reportText}
        </div>
      )}
    </div>
  );
};

5. 동작 검증

  1. 정상 상황: Gemini 3.7 Flash 뱃지와 함께 고품질의 자연어 종목 리포트가 1.5초 만에 출력됨.
  2. 서버 과부하(503) 시뮬레이션: API 호출 실패 시 즉각 룰기반 스마트 엔진으로 전환되어 사용자에게 로딩 멈춤이나 오류 팝업 없이 매끄럽게 분석 리포트가 제공됨.


6. 아웃트로 & 다음 편 예고

이번 3편을 통해 외부 AI API 장애가 발생해도 서비스가 절대 멈추지 않는 3단계 다중 Fallback 아키텍처를 완성했습니다.

다음 [4편]에서는 매일 장 마감 직후인 15:20에 등록된 여러 관심 종목들의 5일선 전략을 한 번에 평가하여 텔레그램으로 자동 발송해 주는 node-cron 스케줄러 봇을 구축해 보겠습니다!

#단타매매 #5일선매매 #알고리즘트레이딩 #주식시뮬레이터 #자동매매 #기술적분석 #뇌동매매극복 #퀀트투자 #투자원칙 #주식앱개발 #웹개발 #타입스크립트 #코딩하는개미 #주식공부

728x90
반응형