Post

생성형 AI 미니 프로젝트(2)-1: 실시간 STT (웹소켓, 쓰레드 사용)

Skala 과정에서 생성형 AI 대해 새롭게 알게 되었습니다.
생성형 AI 수업 중 진행하게 된 미니 프로젝트 구현 과정을 기술합니다.


저번 글에서 SSE 방식으로 챗지피티의 응답을 클라이언트에 실시간으로 전송하였습니다!

이번 글에서는 음성과 STT 변환결과까지 실시간으로 클라이언트와 주고 받기 위해서
웹소켓과 쓰레드를 활용해보았습니다 ~.~
다음 글에서 자세한 구현 방법에 대해 설명하고, 이번 글에서는 사용한 기술에 대해 공부해보겠습니다!

시간이 된다면 사용자끼리도 웹소켓으로 연결해보고 싶네욤,.,.

웹소켓이란?

웹소켓은 클라이언트와 서버를 소켓으로 연결하여 실시간으로 응답을 주고 받을 수 있도록 하는 기술입니다.

항목설명
정의웹 브라우저와 서버 간에 양방향 통신을 가능하게 해주는 프로토콜
프로토콜ws:// 또는 https로 연결 시 wss://
기반HTTP로 핸드셰이크 후, 전용 TCP 커넥션으로 업그레이드됨
주요 특징- 실시간 통신 가능
- 양방향 통신
- 낮은 오버헤드
- 지속 연결 유지
비교: HTTP- HTTP는 요청/응답 기반 (클라이언트가 요청해야 서버가 응답)
- WebSocket은 연결 이후 서버도 자유롭게 데이터 전송 가능
사용 예시채팅 앱, 실시간 알림, 온라인 게임, 실시간 주식 정보 등
브라우저 지원대부분의 최신 브라우저에서 지원 (Chrome, Firefox, Safari 등)
메시지 형식텍스트 또는 바이너리 데이터

이 기술을 사용해서 다음과 같은 로직으로 서비스를 구현할 예정입니다.

  1. 클라이언트에서 웹소켓을 통해 실시간으로 음성 토큰을 보내면,
  2. 서버에서 Google Speech-To-Text API로 클라이언트로부터 전달받은 음성 토큰을 전송하고
  3. API의 응답으로 오는 실시간 STT 변환 정보를 웹소켓을 통해 클라이언트로 전달합니다.

이 과정에서 저는 쓰레드와 코루틴이라는 개념도 함께 사용했습니다

쓰레드란?

항목설명
정의하나의 프로세스 내에서 독립적으로 실행되는 흐름(작업 단위)
구성 관계- 하나의 프로세스는 여러 개의 쓰레드를 가질 수 있음
공유 자원같은 프로세스 내의 메모리 공간(Heap, Data 영역 등)을 공유
고유 자원각 쓰레드는 스택(Stack) 을 별도로 가짐
장점- 경량 실행 단위
- 빠른 컨텍스트 스위칭
- 자원 공유로 효율성 증가
단점- 동기화 문제
- 교착 상태(Deadlock) 발생 가능
비교: 프로세스- 프로세스는 독립적 메모리 사용
- 쓰레드는 메모리 공유
사용 예시멀티태스킹(채팅 + 파일 업로드), 게임 엔진, 백그라운드 작업 등

왜 쓰레드를 사용했나요?

Google Speech-To-Text API는 동기식으로 동작하는 API였습니다.
동기식으로 동작하는 구글 API를 사용하면 사용자의 음성을 전달 받거나, API 호출 결과를 실시간으로 웹소켓으로 전달하는 것이
실시간이 아니게 되어버리는 것 이었습니다..

음성 데이터를 전달하거나 API 호출 결과를 받아올 때 까지 사용자의 음성을 받지 못하고,
받아 온 결과를 웹소켓으로 보낼 수 없게 됩니다.
(동기식으로 동작하는 함수는 현재 실행 중인 함수 혹은 이벤트가 끝날 때까지 멈춰있습니다.)

제가 작성한 Fast API 서버는 데이터베이스까지 비동기식으로 동작하기 때문에
비동기식으로 API를 호출하고 싶었지만 구글에서 비동기 API는 지원하지 않았기 때문에(gRPC를 지원했지만..쓰기 어려워서..) 쓰레드를 사용하기로 했습니다

또한, 웹소켓을 통해 전달된 음성을 계속해서 저장하는 동작, API로부터 전달 받은 결과를 웹소켓으로 계속해서 전송하는 동작도 안정적으로 수행되도록 하기 위해서는 코루틴 을 사용해야 했습니다.


코루틴이란?

항목설명
정의함수의 실행을 일시 중단(suspend) 하고, 필요 시 다시 재개(resume) 할 수 있는 함수
언어 지원Python (async/await), Kotlin, C#, JavaScript 등
키워드 (Python)async def, await
특징- 비동기 작업 처리에 유리
- 이벤트 루프 기반으로 실행됨
장점- 쓰레드보다 가볍고 효율적
- 컨텍스트 스위칭 비용이 적음
비교: 쓰레드- 쓰레드는 OS 스케줄러가 관리, 코루틴은 개발자가 흐름을 제어
사용 예시- 네트워크 요청 처리
- 웹 크롤러
- 실시간 채팅 서버 등
Python 예시python<br>async def foo():<br> await bar()<br>
실행 방식- 코루틴 객체는 실행만으론 동작 안 함
- 이벤트 루프(asyncio.run) 필요
코루틴 vs 일반 함수일반 함수는 호출 → 끝날 때까지 실행됨
코루틴은 중단/재개 가능

위에서 설명하고 있는 코루틴은 마치 라운드 로빈처럼 함수들이 동작하는 것처럼 보입니다.
지피티에게 코루틴과 라운드로빈의 유사성을 물어봤습니닷

Q. 그러면 코루틴은 마치 하나의 쓰레드 안에서 함수들이 라운드 로빈처럼 실행되는거야?(실행 시간이 정해져 있는 것은 아니지만 await을 만나면 잠시 멈추니깐)

A. 🎯 와… 딱이야.
정확히 그 개념이 맞아!
코루틴은 마치 하나의 쓰레드 안에서 여러 함수가 라운드로빈 방식처럼 번갈아 실행되는 것과 유사해.
.
.
.
✅ 결론
🎯 코루틴 = 하나의 쓰레드 안에서 순서를 양보하며 돌아가는 비동기 함수들 await은 마치 “다른 함수 먼저 실행해!”라고 말하는 자발적 양보

그러니까, 코루틴이란 쓰레드 내에서 함수 수준의 문맥교환이 일어나는 것입니다!(await을 만나면 잠시 중단되고, await한 함수가 완료되면 다시 실행)

왜 코루틴을 사용했나요?

음성이 실시간으로 텍스트로 변환되게 하기 위해

  1. While 문 안에서 웹소켓을 통해 음성을 지속적으로 받아오고
  2. 받아온 음성을 지속적으로 API에 보내고
  3. API의 결과를 지속적으로 웹소켓으로 전달해야했습니다.

위 3가지 일이 각각의 일에 방해받지 않고 돌아가게 하기 위해
API를 보내는 것은 새 쓰레드를 생성하여 방해받지 않게 하도록 했고

STT 변환 결과를 지속적으로 웹소켓으로 전달하기 위해 코루틴을 사용했습니다.


마치며

다음 글에서는 웹소켓, 쓰레드, 코루틴을 활용해 실시간으로 음성을 STT로 변환하는 코드에 대해 살펴보겠습니다!

🤓



참고 자료

하나, ,

This post is licensed under CC BY 4.0 by the author.