지난 번에 구현한 Dify를 배포하고, Google AI Studio로 웹앱을 만드는 것이 2주차에 소개되었다.

 

Google AI Studio를 열고 Build를 선택하면 다음과 같은 창이 보이게 된다.

 

Dify API 가이드를 Prompt로 제공하기 위해서 Dify의 "Access API Reference"에 접근한다.

 

 

여기에 나오는 모든 내용을 텍스트 에디터에 넣고 마크다운 코드로 만든다. (``` ... ```)

Dify의 아래 메뉴를 통해서 API key를 획득한다.

 

이미지도 AI Studio에 전달하기 위해서 아래 화면 처럼 캡춰한다.

 

Build의 대화 창에 API Guide, 아래 Prompt와 API Key 그리고 캡춰한 이미지를 넣고 실행한다.

 

아래와 같이 테스트도 가능하고, 실재로 Google Cloud에 배포도 가능하다.

 Voice Cloning + TTS API 시스템 PRD

목표:
사용자가 제공한 음성으로 개인화된 TTS 서비스를 제공하는 오픈소스 기반 API 시스템을 Google Cloud 위에 구축한다.

 

 핵심 기능 요약

기능 설명
음성 클로닝 사용자가 업로드한 음성(3~5초)을 기반으로 음색 임베딩 생성
텍스트 → 음성 합성 사용자가 입력한 텍스트와 임베딩을 사용하여 음성 생성
REST API 음성 업로드, 합성 요청, 결과 조회 API 제공
저장 Cloud Storage에 사용자 음성 및 합성 결과 저장
GPU 기반 추론 Vertex AI Custom Job을 활용한 TTS 처리 (필요 시 GCE로 확장)

 

 구성 요소 및 기술 스택

구성 요소 기술 설명
음성 클로닝 모델 xtts-v2, YourTTS, SV2TTS 음성 임베딩 생성 및 다국어 음성 합성 지원
TTS 엔진 xtts-v2 (by Coqui) 텍스트 + 임베딩 → 오디오 생성
API 서버 FastAPI + Docker + Cloud Run /upload, /synthesize REST API 제공
음성 저장소 Cloud Storage 원본 및 합성 음성 저장 (gs://.../voices/, .../results/)
TTS 실행 환경 Vertex AI Custom Job (GPU) 비용 효율적 배치 처리용
향후 확장 가능 환경 GCE GPU 인스턴스 + FastAPI 실시간 추론 서비스 필요 시 확장
임베딩 관리 Firestore 또는 Redis (옵션) user_id별 speaker embedding 저장
배포 자동화 Cloud Build + Artifact Registry + YAML CI/CD 구성
인증 및 보안 Firebase Auth / Cloud IAM / API Key 사용자 인증, 호출 제한
모니터링 Cloud Logging, Error Reporting 상태 추적 및 장애 감지

 

 아키텍처

graph TD
  A[사용자] -->|음성 업로드| B[Cloud Run API 서버]
  B --> C[Cloud Storage (원본 음성 저장)]
  B --> D[Vertex AI Custom Job (XTTS 추론)]
  D --> E[Cloud Storage (합성 결과 저장)]
  B -->|합성 음성 URL 반환| A

 

 폴더 및 Docker 구조

voice-clone-backend/
├── cloudrun-api/                        # Cloud Run용 경량 API 서버
│   ├── app/
│   │   ├── main.py                      # /synthesize 요청 수신
│   │   ├── trigger_job.py              # Vertex Job 실행용 wrapper
│   │   ├── voice_encoder.py            # 사용자 음성 임베딩
│   │   ├── storage.py                  # GCS 입출력
│   │   └── config.py                   # 환경 설정
│   ├── requirements.txt                # ⚠️ TTS 제거
│   ├── Dockerfile                      # Cloud Run용
│   └── deploy/
│       └── cloudrun.yaml               # 배포 스크립트
└── vertex-job/                         # Vertex AI Custom Job 실행 코드
    ├── job/
    │   ├── run_batch.py                # text + voice_id → 음성 생성
    │   ├── tts_engine.py              # XTTS 호출 wrapper
    │   ├── voice_encoder.py           # 동일하지만 Vertex 버전
    │   ├── storage.py
    │   └── config.py
    ├── requirements.txt                # ⚠️ TTS 포함 (TTS, torch 등)
    ├── Dockerfile                      # Vertex Job용
    └── deploy/
        └── xtts-job.yaml              # Custom Job 정의 YAML

 

처리 흐름

1. 사용자 음성 업로드

  • 클라이언트 → /upload-voice 호출 (파일 포함)
  • Cloud Run → GCS /voices/{user_id}/sample.wav 저장
  • (선택) speaker embedding 생성 후 Firestore/캐시에 저장

2. 텍스트 → 음성 합성

  • 클라이언트 → /synthesize 호출 (text, user_id)
  • Cloud Run → Vertex AI Custom Job 실행 요청
  • XTTS 모델 실행: sample.wav + text.wav 결과 생성
  • 결과 파일 gs://.../results/{user_id}/{uuid}.wav 저장
  • Cloud Run → 결과 URL 반환

 

 비용 전략

실행 방식 장점 월 예상 비용 (T4 기준)
Vertex AI Custom Job 사용한 시간만큼만 과금 (비용 효율) $30$50/월 (1일 12시간 기준)
GCE GPU 인스턴스 실시간 API 서버 운영 가능 $390~400/월 (상시 실행 기준)

 

 

구현 내용

https://github.com/blcktgr73/VoiceCloning

👋 개요

앞에서 GCP에 적용하기로 한 뉴스 요약 앱을 만들기 위한 여정을 두 단계로 나누어 정리한다.

  • 1단계는 Google Cloud 기반의 백엔드 구축과 자동화에 중점을 두었고,
  • 2단계는 Firebase 인증과 React Native Expo 앱을 개발하여 실제 사용자 경험을 다듬은 단계입니다.

✅ 1단계: Cloud 기반 백엔드 + 뉴스 요약 자동화

🎯 목표

  • Google News에서 원하는 키워드로 뉴스 수집
  • Gemini API를 활용한 요약 처리
  • 사용자별 요약 결과 저장
  • 자동 실행 및 확장 가능한 구조로 구축

🧱 주요 기술 스택

구성 요소 기술/서비스 설명
뉴스 수집 Python (requests, bs4) Google News 크롤링
요약 처리 Gemini Flash API 텍스트 요약 모델 호출
API 서버 FastAPI + Cloud Run RESTful API 제공
데이터 저장 Firestore (NoSQL) 사용자별 키워드 및 요약 결과 저장
인증 Firebase Authentication Google 로그인 기반 사용자 인증
예약 실행 Cloud Scheduler + Functions 키워드별 뉴스 수집/요약 자동 실행
배포 GitHub Actions + Cloud Build CI/CD 파이프라인

🔄 동작 흐름 요약

  1. 사용자가 관심 키워드를 등록
  2. Cloud Scheduler가 주기적으로 트리거 발행
  3. Cloud Function이 사용자별 Keyword를 취합하고 Pub/Sub을 통해서 FastAPI의 /summary API 호출
  4. 결과를 Firestore에 사용자별로 저장

📁 예시 Firestore 스키마

{
  "users": {
    "userId123": {
      "keywords": {
        "AI": {
          "2025-07-06": {
            "title": "OpenAI launches GPT-5",
            "summary": "OpenAI unveiled GPT-5 today with multimodal capabilities..."
          }
        }
      }
    }
  }
}

특이 사항

이 때, 특이 사항으로는 사용자 Id에서도 document를 만들지 않으면 접근이 되지 않는 부분이 있어서 debugging에 어려움이 있었다. 문제는 아래의 코드에서 를 통해서 keywords_ref = db.collection("users").document(uid).collection("keywords")를 통해서 Firebase에 접근하려고 할 때, 데이터가 읽히지 않는 것이었다. 이는 uid수준에 document가 없기 때문이었다.

def fetch_all_user_keywords():
    db = firestore.Client()
    users = db.collection("users").stream()
    users_list = list(users)

    result = []
    for user in users_list:
        uid = user.id
        keywords_ref = db.collection("users").document(uid).collection("keywords")
        keywords = [doc.to_dict()["keyword"] for doc in keywords_ref.stream()]
        if keywords:
            result.append({"user_id": uid, "keywords": keywords})

    print(f"result {result}")
    return result

여러가지 실험들이 있었고, 그 실험을 통해서 문제점에 대해서 이해하고 Database에 keyword를 추가할 때, 상위 user document가 없다면 빈 document를 만드는 코드를 추가하여, 문제가 해결되는 것을 확인하였다.

def add_keyword(user_id: str, keyword: str) -> str:
    # ✅ 상위 user 문서가 없다면 빈 문서라도 생성 (merge=True)
    db.collection("users").document(user_id).set({}, merge=True)

    keyword_ref = db.collection("users").document(user_id).collection("keywords")
    existing = keyword_ref.where("keyword", "==", keyword).limit(1).stream()
    if any(existing):
        raise ValueError("Keyword already exists")

    doc_ref = keyword_ref.document()
    doc_ref.set({
        "keyword": keyword,
        "created_at": datetime.utcnow().isoformat()
    })
    return doc_ref.id

✅ 2단계: React Native 앱으로 사용자 경험 구현

🎯 목표

  • 사용자 로그인
  • 키워드 등록 및 요약 리스트 보기
  • 앱 내에서 요약 결과 확인
  • 다크/라이트 모드 지원

🧱 주요 기술 스택

구성 요소 기술/서비스 설명
프레임워크 React Native (Expo) 크로스 플랫폼 앱 개발
인증 Firebase Authentication Google 로그인 연동
데이터 통신 REST API (FastAPI) 백엔드와 데이터 연동
앱 배포 EAS Build + Expo Go Android/iOS 앱 테스트 및 배포
디자인 React Native Paper 테마 기반 다크/라이트 모드 지원

📱 주요 UI 기능

  • 🔑 로그인 화면: Firebase 인증 기반
  • 📝 키워드 입력: 관심 주제 등록
  • 📄 요약 리스트: 날짜별 요약 보기
  • 🎨 다크/라이트 모드: 시스템 설정 연동
  • 🚫 백버튼 제어: 앱 종료 버튼으로 대체

💡 화면 예시 흐름

  1. 로그인 화면
  2. → 요약 리스트 화면
  3. → 키워드 등록 화면

특이 사항

Frontend 개발 시 특이 사항은 React Native Expo였다. Web 기반 React를 다룰 때도 그랬지만, 가이드가 최신 버전과 충돌이 있는 부분이 있었다. Web에서 Tailwindcss의 초기 설정이 달라져서 적절한 설정을 찾는데 어려움이 있었던 것과 같이 Expo의 SDK 최신이 53이었는데 Firebase 연동에 문제가 있다는 것을 알았다(1). 그래서, SDK 버전을 52로 낮추고 Expo Go 앱도 낮은 버전 찾아서 설치하면서 문제를 해결했다.

React Native Expo의 중간 테스트는 Expo Go라는 앱으로 로컬에서 실행하는 내용을 가져와서 테스트하기 때문에 로컬 서버가 항상 동작하고 있어야했다. 이것은 중간 테스트하는 결과물이고 최종 결과물은 Android 혹은 iOS에서 동작하는 결과물을 만들어야 하는 것이다. 이 결과물을 만드는 과정을 EAS(Expo Application Service) build라고 불렀다. 실재로 코드가 github에 있어야했고, 서버에서 빌드하는 과정이 필요하다 그리고, Free 모델에서는 Queue에 2시간 대기 하고 나서 빌드하는데 Fail이 발생하면 결과를 최소 2시간 이후에 알 수 있어서 동작하는 버전을 처음 얻는데 까지 이틀 가까이 걸렸다.

🔚 마무리: 확장 가능성과 다음 단계

이 프로젝트는 다음과 같은 방향으로 확장 가능하고 고민해보고 있다:

  • 🔐 다른 사용자 인증 지원: Google account와 같은 OAuth 2.0 연동. 이를 통한 가입의 유연성 증대
    • 📈 요약 품질 개선: Selenium으로 News를 가져오고 Gemini Pro 또는 LLM fine-tuning 도입
  • 🔊 TTS 기능: 요약 내용을 음성으로 읽어주는 기능
  • 📡 푸시 알림: 새로운 뉴스 요약이 도착했을 때 알림 제공

✨ 느낀 점

Cloud 기반 자동화와 모바일 내이티브 앱을 한 프로젝트 안에서 다뤄본 덕분에, 서비스 기획부터 운영까지 전 과정을 경험할 수 있었다. 특히 Pub/Sub, Cloud Function, Firestore를 연결하는 구조가 매우 유연하면서도 확장성 있게 느껴졌다. 나에게도 유용한 기능이어서 나만의 서비스로 활용하고 있다. 조금 더 확장하면 실재로 타인들에게도 공개해서 피드백 받을 수 있는 기능으로 확장도 가능하겠다.

참고 문헌

[1] https://www.inflearn.com/community/questions/1581981/expo-%EA%B0%80-%EC%97%85%EB%8D%B0%EC%9D%B4%ED%8A%B8-%EB%90%98%EB%A9%B4%EC%84%9C-%EB%AC%B8%EC%A0%9C%EA%B0%80-%EB%B0%9C%EC%83%9D%ED%95%9C%EA%B1%B0%EA%B0%99%EC%8A%B5%EB%8B%88%EB%8B%A4?srsltid=AfmBOopaM9vHkoH-TpxDm6dFG6jVK585QCe4zMDbJ_LatRew-t_iiVP1
[2] https://github.com/blcktgr73/GCPNewsPortal

✅ 프로젝트 개요

  • 목표: Google News 기반 뉴스 수집 및 요약 앱
  • 핵심 가치 (PCA 관점):
    • 고가용성 설계
    • CI/CD 파이프라인 구축 (GitHub Actions + Cloud Build)
    • 비용 효율적인 서버리스 아키텍처

🏗️ 아키텍처 구성도

+--------------------+           +--------------------+
|  User Frontend     | <-------> | Firebase Hosting   |
|  (React Native)    |           | + Firebase Auth    |
+--------------------+           +--------------------+
                                        |
                                        v
                                +------------------+
                                | Firestore (DB)   |
                                +------------------+
                                        ^
                                        |
+----------------------+       +---------------------------+
| Cloud Scheduler      | ----> | Cloud Functions           |
| (매일 크롤링 트리거)  |       | (뉴스 크롤링/요약 실행)     |
+----------------------+       +---------------------------+
                                        |
                                        v
                            +---------------------------+
                            | Cloud Run (LLM API 연동) |
                            +---------------------------+

⚙️ 각 컴포넌트 설명 및 역할

컴포넌트 역할 PCA 고려 포인트
Firebase Hosting 프론트엔드 정적 호스팅 글로벌 CDN 활용으로 고가용성 확보
Firebase Authentication 로그인 및 인증 처리 OAuth2, Anonymous 등 다양한 인증 방식 간편 지원
Firestore 사용자별 뉴스/요약 저장 글로벌 분산 NoSQL, Auto-scaling 및 고가용성 내장
Cloud Functions 뉴스 크롤링 및 요약 자동화 서버리스 처리로 이벤트 기반 트리거 가능 (Cloud Scheduler or HTTP)
Cloud Scheduler 주기적 크롤링 트리거 시간 기반 이벤트, 관리 비용 없음
Cloud Run 뉴스 수집/요약 요청 처리 및 LLM API 호출 서버 (FastAPI 기반) 필요 시만 실행되어 비용 최적화 가능
Cloud Build + GitHub Actions CI/CD 파이프라인 구성 Build/Deploy 자동화, 롤백도 가능

💰 비용 효율 아키텍처 전략

전략 설명
서버리스 기반 Cloud Run, Functions, Firestore 모두 사용량 기반 과금 (idle 시 비용 없음)
Cloud Scheduler + Functions 분리 주기적 트리거만 필요하므로 Compute Engine 등 상시 서버보다 훨씬 저렴
LLM 연동은 Cloud Run에서 제한적으로 사용 필요 시만 호출, API 요금 절감
CI/CD 자동화로 개발 효율 극대화 GitHub Actions → Cloud Build로 배포까지 자동 처리

📌 개발 단계

  1. 뉴스 수집 및 요약 기능
    • FastAPI + Cloud Run
    • Gemini API 연동 + JSON 반환
    • 수동 테스트용 엔드포인트 우선 개발
  2. 사용자 인증 및 저장/조회 기능
    • React Native + Firebase Auth
    • Firestore에 요약 저장, 조회 UI 구현
  3. 자동화 및 고도화
    • Cloud Scheduler + Functions로 주기적 호출
    • Slack 또는 Email 연동 통한 실패 알림 추가 가능

🧱 공통 포함 사항

  • Firestore 스키마 설계
users/
  {userId}/
    summaries/
      {summaryId}: {
        "url": "https://news.example.com",
        "title": "기사 제목",
        "summary": "요약된 본문",
        "createdAt": "2025-06-21T07:30:00Z",
        "source": "Google",
        "category": "Technology",
        "keywords": ["AI", "Google", "ChatGPT"],
        "summaryTokens": 156
      }
  • 필드 설명
필드명 타입 설명
url string 요약한 뉴스의 원본 링크
title string 뉴스 제목
summary string 요약된 본문 내용
createdAt timestamp Firestore 서버 시간 기반 생성일
source string 뉴스 출처 (예: Google, Naver, Daum)
category string 뉴스 카테고리 (예: Technology, Politics, Health)
keywords array Gemini 또는 기타 LLM이 추출한 키워드 리스트
summaryTokens number 요약된 텍스트의 토큰 수 (LLM 비용 추적용)
  • 기술 요약 테이블
항목 기술/서비스 설명
뉴스 수집 Python (requests, BeautifulSoup) 주요 뉴스 사이트 크롤링
요약 처리 Gemini Free API(2.5 Flash) 텍스트 요약 모델 호출
API 서버 Cloud Run (FastAPI) REST API 형태로 제공
인증 Firebase Authentication Google, 이메일 등 연동
저장소 Firestore 요약 결과 사용자별 저장
모바일 앱 React Native (Expo) iOS/Android 앱 구현
스케줄링 Cloud Scheduler + Functions 뉴스 자동 수집/요약 트리거
CI/CD GitHub Actions + Cloud Build 자동 테스트/배포 파이프라인

마무리

이 작업은 요즘 인기 있는 Firebase Only로 고민해 보는 것도 좋겠지만, 처음에 적었 듯 Google Cloud Platform으로 서비스를 구성해보는 것이 목표였다.

뉴스 요약을 시작으로 한 것도 기존에 NAS에서 Google News에서 검색하고 GPT로 요약한 것을 Obisidan에 연동하는 기능이 이미 구현된 것이 있어서 Cloud와 React Native, 특히 모바일에 집중해서 학습이 가능할 것으로 보였다.

이 후에는 Bacekend에 API 설계 구현하고 News Summary를 저장하는 것을 먼저 하게 될 것 같다. 좀 더 익숙하기도 하고, 이 후에는 React Native에 집중해서 학습 할 수도 있을 것 같고 좀 더 편할 것 같기도 하다.

 

참고 링크

https://github.com/blcktgr73/GCPNewsPortal

 

+ Recent posts