딥페이크 AI 음성 응답: 사용자 목소리와 합성 목소리를 분리한 구조
2022년 말 국내 시중은행의 AI 상담원 프로젝트에 참여했습니다. 사용자가 앱에 질문하면 화면 속 가상 은행원이 딥페이크 영상과 AI 합성 음성으로 답하는 서비스였습니다. 저는 사용자의 음성을 실시간으로 서버에 보내고, 서버에서 돌아온 영상 응답을 앱에서 재생하는 구간을 맡았습니다.
이 프로젝트를 설명할 때 가장 쉽게 섞이는 것이 두 목소리입니다. 서버로 올라가는 사용자의 원래 목소리와 서버에서 만들어져 돌아오는 AI 목소리는 출발점도, 기술도 달랐습니다.
한 번의 대화에 두 개의 음성 경로가 있었습니다
전체 흐름을 음성 기준으로 나누면 다음과 같습니다.
[사용자 목소리]
마이크
-> 16비트, 16 kHz, 모노 PCM
-> Speex 인코딩
-> 소켓 스트리밍
-> 서버의 질문 처리
[AI 목소리]
답변 데이터
-> 합성 음성 생성
-> 딥페이크 얼굴 영상과 결합
-> WebRTC 미디어 응답
-> 앱 화면에서 영상과 함께 재생첫 번째 경로는 입력입니다. 제가 iOS 앱에서 마이크 데이터를 수집하고 전송한 구간입니다. 두 번째 경로는 출력입니다. 서버에서 생성된 목소리가 딥페이크 영상과 함께 돌아오고, 앱이 이를 대화 순서에 맞춰 재생했습니다.
이 구분을 하지 않으면 Speex가 AI 음성을 만드는 기술이거나 WebRTC가 딥페이크를 생성하는 기술처럼 설명하게 됩니다. 둘 다 사실과 다릅니다.
Speex는 목소리를 복제하지 않았습니다
Speex는 음성 합성 모델이 아니라 음성 코덱입니다. 마이크에서 받은 PCM 샘플을 네트워크로 보내기 좋은 크기로 압축하는 역할을 했습니다.
이 프로젝트의 입력은 16 kHz 모노 PCM이었습니다. Speex 공식 설명에서 16 kHz는 광대역 음성 모드에 해당합니다. Speex는 패킷 네트워크와 VoIP 용도의 음성 압축을 목표로 설계된 코덱이며, 특정 사람의 음색이나 말투를 만들어내지 않습니다.
따라서 정확한 표현은 다음과 같습니다.
- 사용자 음성: PCM으로 수집한 뒤 Speex로 압축해 전송했습니다.
- AI 음성: 서버에서 합성되어 딥페이크 영상 응답에 포함됐습니다.
- WebRTC: 생성된 영상과 음성을 앱까지 전달하고 재생하는 데 사용했습니다.
Speex 기반 AI 음성 생성이라고 쓰면 입력 압축과 출력 합성을 한 기술로 합쳐버립니다. 당시 구조를 설명하는 제목으로는 정확하지 않습니다.
딥페이크 AI 음성은 서버에서 만들어졌습니다
사용자가 들은 상담원의 목소리는 사람 상담원이 실시간으로 말한 것이 아니었습니다. 서버가 질문을 처리한 뒤 만든 합성 음성이었습니다. 이 음성은 화면 속 가상 은행원의 입 모양과 함께 딥페이크 영상 응답으로 돌아왔습니다.
제가 맡은 범위는 음성 모델 학습이나 딥페이크 생성 엔진이 아니었습니다. 서버에서 만들어진 응답을 받아 앱에서 재생하는 구간이었습니다. 그래서 이 글도 어떤 모델을 사용했는지, 어떤 데이터로 학습했는지를 추측하지 않습니다. 현재 남은 공개 가능한 기록에는 그 정보가 없습니다.
Voice Cloning이라는 표현도 조심해야 합니다. 합성 음성이 특정 실존 인물의 음색을 복제했다는 근거가 있을 때 사용할 수 있는 말입니다. 이 프로젝트가 AI 합성 음성을 사용했다는 점은 확인되지만, 공개 기록만으로 특정인의 목소리를 복제했다고 단정할 수는 없습니다. 제목에는 사용자가 이해하기 쉬운 딥페이크 AI 음성을 쓰되, 본문에서는 확인된 범위인 합성 음성으로 설명하는 이유입니다.
얼굴과 목소리는 같은 시간에 움직여야 했습니다
딥페이크 영상에서는 화질만큼 입 모양과 목소리의 동기화가 중요합니다. 영상이 먼저 움직이고 음성이 늦게 나오거나, 음성이 시작됐는데 첫 영상 프레임이 아직 준비되지 않으면 가상 상담원이 말한다는 느낌이 바로 깨집니다.
WebRTC는 미디어 트랙의 시간 정보를 이용해 오디오와 비디오를 재생합니다. W3C WebRTC 명세는 RTCPeerConnection을 통해 로컬 트랙을 보내고 원격 트랙을 받는 API를 정의합니다. 이 프로젝트에서는 생성 결과를 실시간 미디어 응답으로 받고 앱에서 재생하는 데 이 경로를 사용했습니다.
앱 쪽에서 중요했던 것은 다음 세 시점이었습니다.
- 사용자의 발화가 끝났다고 판단하는 시점
- 서버의 응답 미디어를 받을 준비가 된 시점
- 합성 음성과 영상의 첫 프레임을 실제로 재생하는 시점
세 시점 사이의 공백이 길면 사용자는 AI가 생각하는 중인지 연결이 끊긴 것인지 알 수 없습니다. 반대로 첫 패킷만 보고 너무 빨리 재생하면 네트워크 변화에 따라 중간에 멈출 수 있습니다. 실시간 응답은 지연을 무조건 0으로 만드는 문제가 아니라, 재생을 시작할 만큼 준비됐는지 판단하는 문제였습니다.
사용자가 느끼는 지연은 모델 시간만이 아니었습니다
AI 상담원이 늦게 답하면 생성 모델부터 의심하기 쉽습니다. 실제 대기 시간은 여러 구간의 합입니다.
발화 수집
+ Speex 인코딩
+ 소켓 전송
+ 질문 인식과 답변 생성
+ 합성 음성 생성
+ 딥페이크 영상 생성
+ WebRTC 전달
+ 앱 디코딩과 첫 프레임 재생당시 프로젝트의 세부 측정값은 현재 기록에 남아 있지 않습니다. 어느 구간이 몇 ms였는지 숫자를 만들어낼 수는 없습니다. 다만 제가 담당한 앞쪽의 음성 전송과 뒤쪽의 영상 재생만 줄여도, 서버 모델을 바꾸지 않고 사용자가 느끼는 침묵을 줄일 수 있었습니다.
이 때문에 실시간 AI 서비스에서는 전체 응답 시간 하나만 기록하는 것보다 각 경계에 시간을 남기는 편이 좋습니다. 지금 다시 만든다면 음성 첫 샘플, 발화 종료, 서버 수신 완료, 응답 생성 완료, 첫 미디어 패킷, 첫 프레임 재생 시점을 같은 요청 ID로 연결하겠습니다. 이것은 당시 구현했다고 주장하는 기능이 아니라, 남은 기록을 다시 보며 분명해진 개선점입니다.
WebRTC 음성 코덱과 Speex도 구분해야 합니다
WebRTC와 Speex가 같은 프로젝트에 있었다고 해서 WebRTC가 Speex를 표준 음성 코덱으로 사용한 것은 아닙니다. RFC 7874는 WebRTC 엔드포인트가 구현해야 하는 기본 음성 코덱으로 Opus와 G.711의 PCMA, PCMU를 규정합니다.
이 프로젝트에서 Speex는 별도의 소켓 경로로 올라가는 사용자 음성에 사용됐습니다. 돌아오는 딥페이크 영상과 합성 음성은 WebRTC 미디어 경로였습니다. 같은 대화 안에서도 입력과 출력은 서로 다른 전송 조건을 가질 수 있습니다.
지금 공개할 때 지키는 선
금융 서비스의 합성 얼굴과 목소리는 기술 설명만으로 끝나지 않습니다. 지금 같은 기능을 다시 만든다면 사용자가 AI라는 사실을 알 수 있는 표시, 목소리와 얼굴 원본의 사용 동의, 생성물의 저장 기간, 오용 방지와 사람 상담원으로 전환하는 경로를 요구사항에 먼저 넣겠습니다.
이 글은 2022년 프로젝트에 그런 장치가 모두 있었다고 주장하지 않습니다. 당시 공개 가능한 기록으로 확인할 수 없는 부분은 성과처럼 덧붙이지 않았습니다. 제가 확실히 말할 수 있는 범위는 사용자의 음성을 PCM과 Speex로 실시간 전송했고, 서버가 만든 딥페이크 영상과 합성 음성을 WebRTC로 받아 대화 흐름에 맞춰 재생했다는 것입니다.
화면 속 얼굴을 만든 모델은 가장 눈에 띄는 기술이었습니다. 제가 맡은 일은 그 모델의 앞과 뒤를 연결하는 일이었습니다. 사용자의 목소리가 제때 올라가고 생성된 목소리가 얼굴과 함께 돌아와야, 비로소 한 번의 응답이 됐습니다.
함께 읽기
- 에이전트 RAG를 작은 모델로 짜는 법: 도구 선택은 코드가, 답은 모델이에이전트 RAG라는 말을 처음 들었을 때 떠올린 그림은, 모델이 스스로 판단해 도구를 부르는 거였습니다. "이 질문은 검색을, 이 질문은 DB 조회를" 모델이 결정하고, 그 결과를 받아 다시 답을 쓰는 구조. 그런데 막상 작은 로컬 모델로 이걸 짜려니 한 가지가 걸렸습니다. 도구를 고르는 결정을 모델에게 맡길 수가 없었…
- RAG 운영: 전체 재색인을 피하고 캐시는 본 요청을 막지 않는 법처음 RAG를 짤 때는 문서를 고칠 때마다 전체를 다시 색인했습니다. 문서가 백 개쯤 될 때는 그래도 됐습니다. 잠깐 기다리면 끝났거든요. 그런데 문서가 천 개를 넘고, 거기에 재고 아이템까지 색인에 들어가니 얘기가 달라졌습니다. 전체를 다시 임베딩하면 수천 번 모델을 부르는 셈이고, 그 사이에 질문이 들어오면 답이 늦…
- 한국어 RAG에서 벡터만 쓰면 안 되는 이유: 하이브리드 검색 설계처음 RAG를 짤 때는 벡터 검색만 썼습니다. 코사인 유사도 하나로 충분할 줄 알았죠. 그런데 한국어 질문에서 이상한 일이 잦았습니다. 분명 문서에 있는 단어를 물었는데 검색이 안 됩니다. 비슷한 의미의 다른 문서만 계속 위에 뜹니다. 원인을 찾다 보니, 벡터 검색이 "의미가 비슷한가"는 잘 재는데 "그 단어가 실제로 …
- RAG 답변 품질을 메트릭 없이 지키는 법: 출처·임계값·방어 코드RAG 답변 품질을 재는 정량 프레임워크가 있습니다. RAGAS, TruLens 같은 도구가 답변의 충실도(faithfulness), 관련성, 맥락 적합성을 점수로 냅니다. 처음엔 이런 걸 달아야 하나 고민했습니다. 그런데 막상 혼자 쓰는 RAG를 짜고 보니, 정량 점수보다 답이 거짓말을 안 하게 만드는 "코드"가 먼저…
- 멀티모달 RAG의 현실: 모달을 섞지 않고 텍스트로 환원하는 이유멀티모달 RAG라는 말을 처음 들었을 때, 이미지를 임베딩해서 문서 텍스트와 같은 벡터 공간에 올리는 걸 상상했습니다. CLIP 같은 모델로 그림과 글을 하나의 공간에 두면, "이런 느낌의 다이어그램 찾아줘" 같은 질문도 답이 나올 것 같았죠.