티스토리 뷰
React Native와 NestJS를 활용한 Google Cloud Speech-to-Text 구현하기
YG - 96년생 , 강아지 있음, 개발자 희망 2025. 3. 12. 13:56React Native와 NestJS를 활용한 Google Cloud Speech-to-Text 구현하기
오늘은 모바일 애플리케이션에서 음성 인식 기능을 구현하는 방법에 대해 알아보겠습니다. 특히 React Native로 구현된 프론트엔드와 NestJS로 구현된 백엔드를 통해 Google Cloud Speech-to-Text API를 연동하는 방법을 자세히 설명하겠습니다.
개요
이 글에서는 다음과 같은 내용을 다룹니다:
- Google Cloud Speech-to-Text API 설정
 - NestJS 백엔드 구현
 - React Native 프론트엔드 구현
 - 비용 정보 및 무료 티어 활용 방법
 
1. Google Cloud Speech-to-Text API 설정
1.1 Google Cloud SDK 설치
맥OS에서는 Homebrew를 통해 Google Cloud SDK를 설치합니다:
brew install --cask google-cloud-sdk




설치 후 초기화를 진행합니다:
gcloud init
1.2 서비스 계정 생성 및 인증 키 설정
- Google Cloud Console에 로그인 후 IAM & Admin > Service Accounts로 이동
 - 새 서비스 계정 생성 및 필요한 권한 부여
 - JSON 형식의 키 생성 및 다운로드
 - 서비스 계정 인증 활성화:
 
gcloud auth activate-service-account --key-file=google-credentials.json



1.3 환경 변수 설정
서비스 계정 키를 사용하기 위해 환경 변수를 설정해야 합니다:
방법 1: 터미널에서 설정
export GOOGLE_APPLICATION_CREDENTIALS=~/path/to/google-credentials.json
// 저의 경우 
export GOOGLE_APPLICATION_CREDENTIALS=~/Desktop/interview/interview_app_be/google-credentials.json
방법 2: .env 파일에 추가
GOOGLE_APPLICATION_CREDENTIALS=google-credentials.json
그리고 NestJS 프로젝트의 main.ts에서 dotenv 설정:
import * as dotenv from 'dotenv'; 
dotenv.config();
2. NestJS 백엔드 구현
2.1 필요한 패키지 설치
추후에 ec2로 배포한다면 ec2에도 ffmpeg를 설치해줘야 합니다.
npm install --save @google-cloud/speech
npm install fluent-ffmpeg
brew install ffmpeg  # macOS용 ffmpeg 설치
2.2 Audio 서비스 구현
NestJS에서 음성 인식을 처리하는 서비스를 구현합니다:
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Audio } from './entities/audio.entity';
import { Repository } from 'typeorm';
import {
  UploadSpeechFileInput,
  UploadSpeechFileOutput,
} from './dtos/upload-speech-file.dto';
import { promises as fsPromises } from 'fs';
import { join } from 'path';
import speech, { protos } from '@google-cloud/speech';
import * as ffmpeg from 'fluent-ffmpeg';
@Injectable()
export class AudioService {
  private readonly client = new speech.SpeechClient();
  constructor(
    @InjectRepository(Audio)
    private audioRepository: Repository<Audio>,
  ) {}
  // MP3 파일을 FLAC 형식으로 변환
  private async convertMp3ToFlac(
    inputPath: string,
    outputPath: string,
  ): Promise<void> {
    return new Promise((resolve, reject) => {
      ffmpeg(inputPath)
        .output(outputPath)
        .audioCodec('flac')
        .audioChannels(1)
        .on('end', () => {
          console.log(`파일 변환 완료: ${outputPath}`);
          resolve();
        })
        .on('error', (err) => {
          console.error('파일 변환 실패:', err);
          reject(err);
        })
        .run();
    });
  }
  // 음성 파일 저장 및 처리
  async saveFile({
    file,
  }: UploadSpeechFileInput): Promise<UploadSpeechFileOutput> {
    const base64Data = file.split(';base64,').pop();
    if (!base64Data) {
      throw new Error('잘못된 파일 형식입니다.');
    }
    // 파일 저장
    const fileName = `${Date.now()}.mp3`;
    const directoryPath = join(__dirname, '..', 'uploads');
    await fsPromises.mkdir(directoryPath, { recursive: true });
    const filePath = join(directoryPath, fileName);
    const buffer = Buffer.from(base64Data, 'base64');
    try {
      await fsPromises.writeFile(filePath, buffer);
      console.log('파일 저장 성공:', filePath);
    } catch (err) {
      console.error('파일 저장 실패:', err);
      throw new Error('파일 저장 실패');
    }
    // MP3를 FLAC로 변환
    const outputFilePath = join(directoryPath, `${Date.now()}.flac`);
    await this.convertMp3ToFlac(filePath, outputFilePath);
    // 변환된 FLAC 파일을 사용하여 텍스트 변환
    const transcribedText = await this.transcribeAudio(outputFilePath);
    // 결과 저장
    const audio = this.audioRepository.create({
      filePath: outputFilePath,
      transcribedText,
    });
    await this.audioRepository.save(audio);
    return { ok: true, feedback: '업로드 성공' };
  }
  // Google Cloud Speech-to-Text API를 사용한 음성 인식
  private async transcribeAudio(audioPath: string): Promise<string> {
    const audio = {
      content: await fsPromises.readFile(audioPath, 'base64'),
    };
    const request = {
      audio,
      config: {
        encoding:
          protos.google.cloud.speech.v1.RecognitionConfig.AudioEncoding.FLAC,
        languageCode: 'ko', // 한국어 설정
      },
    };
    try {
      const [response] = await this.client.recognize(request);
      const transcription = response.results
        .map((result) => result.alternatives[0].transcript)
        .join('\n');
      return transcription;
    } catch (error) {
      console.error('음성 인식 실패:', error);
      throw new Error('음성 인식 실패');
    }
  }
}
2.3 주요 구현 내용 설명
- 파일 처리:
- Base64로 인코딩된 음성 파일을 서버에 저장
 - MP3 파일을 Google Cloud가 최적으로 처리할 수 있는 FLAC 형식으로 변환
 
 - 음성 인식:
- Google Cloud Speech-to-Text API를 사용하여 변환된 FLAC 파일에서 텍스트 추출
 - 한국어(ko) 언어 코드 설정
 
 - 결과 저장:
- 변환된 텍스트를 데이터베이스에 저장
 
 

3. React Native 프론트엔드 구현
3.1 음성 녹음 및 업로드 구현
React Native에서는 Expo의 Audio API를 사용하여 음성을 녹음하고, 녹음된 파일을 Base64로 인코딩하여 서버로 전송합니다:
const uploadAudio = async () => {
  if (!recordedUri) {
    Alert.alert('먼저 녹음하세요!');
    return;
  }
  const fileInfo = await FileSystem.getInfoAsync(recordedUri);
  if (!fileInfo.exists) {
    Alert.alert('파일이 존재하지 않습니다.');
    return;
  }
  try {
    // 파일을 Base64로 읽어오기
    const base64 = await FileSystem.readAsStringAsync(recordedUri, {
      encoding: FileSystem.EncodingType.Base64,
    });
    // GraphQL로 파일 전송
    const result = await uploadSpeechFile(base64);
    console.log('업로드 성공:', result);
    Alert.alert('업로드 성공', `URL: ${result?.feedback}`);
  } catch (error) {
    console.error('업로드 실패:', error);
    Alert.alert('업로드 실패', '서버 오류');
  }
};

3.2 기능 구현 시 주의사항
- 파일 크기: 음성 파일은 크기가 클 수 있으므로, 네트워크 상태를 고려하여 사용자에게 적절한 피드백 제공
 - 권한 처리: 마이크 접근 권한을 사용자에게 요청하고 거부 시 적절한 안내 제공
 - 파일 형식: 모바일 기기에서 녹음된 파일이 서버에서 지원하는 형식인지 확인
 
4. Google Cloud Speech-to-Text API 비용 정보
4.1 무료 티어 활용하기
Google Cloud 신규 사용자는 $300 크레딧을 받아 3개월 동안 무료로 서비스를 사용할 수 있습니다. 이는 개발 및 테스트 단계에서 충분히 활용할 수 있는 금액입니다.
4.2 Speech-to-Text V2 API 요금
표준 인식 모델:
- 0분~500,000분: US$0.016/분
 - 500,000분~1,000,000분: US$0.01/분
 - 1,000,000분~2,000,000분: US$0.008/분
 - 2,000,000분 이상: US$0.004/분
 
데이터 로깅 활성화 시:
- 0분~500,000분: US$0.012/분
 - 500,000분~1,000,000분: US$0.0075/분
 - 1,000,000분~2,000,000분: US$0.006/분
 - 2,000,000분 이상: US$0.003/분
 
4.3 Speech-to-Text V1 API 요금
표준 모델:
- 0분~60분: 무료
 - 60분 이상(데이터 로깅 포함): US$0.016/분
 - 60분 이상(데이터 로깅 없음): US$0.024/분
 
의료 모델:
- 0분~60분: 무료
 - 60분 이상: US$0.078/분
 
결론
Google Cloud Speech-to-Text API를 React Native와 NestJS로 연동하는 방법을 살펴보았습니다. 실제 구현 시에는 사용자 경험을 최우선으로 하되, 비용 효율성도 고려해야 합니다.
특히 무료 티어를 활용하여 개발 및 테스트를 하고, 실제 프로덕션 환경에서는 최적화된 설정을 통해 비용을 절감하는 것이 중요합니다.
이 글이 음성 인식 기능을 구현하는 데 도움이 되었기를 바랍니다. 궁금한 점이나 추가적인 정보가 필요하시면 댓글로 남겨주세요.
'react native' 카테고리의 다른 글
| Expo 프로젝트에서 Xcode 인식 문제 해결하기 (0) | 2025.03.11 | 
|---|---|
| Expo에서 'os.availableParallelism is not a function' 오류 해결하기 (0) | 2025.03.11 | 
| React Native 개발 시 Expo 네트워크 연결 문제 해결하기 (0) | 2025.03.11 | 
| React Native 시작하기: 설치부터 expo-router 환경설정까지 (0) | 2025.03.11 | 
| React Native 에서 Admob 사용방법 / 광고 다는 방법 (0) | 2022.02.19 | 
- Total
 
- Today
 
- Yesterday
 
- React
 - TopLayer
 - C언어
 - nextjs
 - nodejs
 - CLASS
 - 노마드코더
 - 원티드
 - javascript
 - 윤성우 열혈C프로그래밍
 - 프론트앤드
 - 우아한테크코스
 - NextRequest
 - NextApiRequest
 - WSL2
 - 프리온보딩
 - electron
 - error
 - jest
 - Storybook
 - 노개북
 - 초보
 - env
 - 북클럽
 - 위코드
 - createPortal
 - 아차산
 - 스토리 북
 - import/order
 
| 일 | 월 | 화 | 수 | 목 | 금 | 토 | 
|---|---|---|---|---|---|---|
| 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 |