티스토리 뷰
Edge Function이라는 기술이 있다.
이 기술은 서버에 무언가 데이터를 저장할 필요가 없을 때, API콜을 프론트엔드와 가까운 쪽으로 만들어서 바로 실행하여 레이턴시를 줄이는 기술이다.
현재 STT - Backend - TTS의 프로젝트를 진행하는데, ChatGPT TTS를 실행할 일이 생겼다.
그러나 만약 백엔드에서 TTS를 실행하면, 프론트엔드에 넘겨 줄 때 텍스트 파일이 아닌 오디오 파일로 변환해야 하며, 이는 그만큼의 오버헤드와 레이턴시를 증가시키게 된다.
얼마만큼 차이가 나냐고?
그래서 한번 실험해봤다. 결론부터 먼저 보고 가자.
Edge Function 적용
전체 처리 시간: 1068.966064453125 ms
전체 처리 시간: 1832.30712890625 ms
전체 처리 시간: 2110.337890625 ms
전체 처리 시간: 1516.05615234375 ms
전체 처리 시간: 1492.2529296875 ms
전체 처리 시간: 1642.301025390625 ms
전체 처리 시간: 1680.763916015625 ms
전체 처리 시간: 1531.09814453125 ms
전체 처리 시간: 1927.72509765625 ms
평균 시간 = 1644.65ms
정말 빠를 때는 1083.5751953125 ms
Latency: 1.7840192317962646 seconds
(index):144 전체 처리 시간: 1788.551025390625 ms
(index):138 Latency: 2.0312352180480957 seconds
(index):144 전체 처리 시간: 2036.31396484375 ms
(index):138 Latency: 2.412046432495117 seconds
(index):144 전체 처리 시간: 2415.839111328125 ms
(index):138 Latency: 2.0091772079467773 seconds
(index):144 전체 처리 시간: 2013.64599609375 ms
(index):138 Latency: 1.9517569541931152 seconds
(index):144 전체 처리 시간: 1956.001953125 ms
(index):138 Latency: 1.6881499290466309 seconds
(index):144 전체 처리 시간: 1692.40380859375 ms
(index):138 Latency: 1.684964895248413 seconds
(index):144 전체 처리 시간: 1690.360107421875 ms
(index):138 Latency: 2.3809704780578613 seconds
(index):144 전체 처리 시간: 2385.91796875 ms
(index):138 Latency: 1.991988182067871 seconds
(index):144 전체 처리 시간: 1996.52783203125 ms
(index):138 Latency: 1.9736440181732178 seconds
(index):144 전체 처리 시간: 1978.381103515625 ms
(index):138 Latency: 2.0834221839904785 seconds
(index):144 전체 처리 시간: 2088.0009765625 ms
전체 Latency 평균 : 1.999초
전체 처리 시간의 평균 : 2003.81ms
정말 느릴 때는 3447.5849609375 ms
평균 차이 : 359.17ms
최대 차이 : 2364.01ms
얼마 안되어 보이지만 서비스 측면에서는 정말 크다.
이게 현재 비동기 상태에서 서버에 부하가 걸리지 않은 상태라는 게 더 중요하다.
이 상태에서 360ms~2364.01ms나 차이가 난다면, 서버에 부하가 있을 때 훨씬 많은 레이턴시가 발생한다.
따라서 이 기술은 TTS 측면에서 다음과 같은 장점이 있다.
1. 백엔드 -> 프론트 전송 과정에서 오디오 파일이 깨질 위험이 없고, API가 나오는 그대로의 음성 품질을 전달 가능
2. 서버 부하를 줄일 수 있다. 또한 서비스 안정성을 늘릴 수 있다.
3. 중간 서버의 개념으로 받아들이는 방식을 더 유연하게 할 수 있음(보안 강화)
실제로 실험했을 때 백엔드 -> 프론트엔드 API를 보내는 동안 파일이 깨져서 이상하게 말하는 경우가 많았다.
다만 단점이라면, API 키 때문에 .env 파일을 만들어서 관리해야 한다는 정도이며, 음성 파일의 저장은 웹서버 쪽에 해야 한다는 것 정도.
이제 이 Edge Function을 구현한 과정을 보도록 하자
Express.js 설치
백엔드는 어느걸 써도 상관 없으므로, 다음과 같이 Express로 가장 간단하게 진행한다.
일단 node.js는 깔려 있다는 전제하에 진행한다.
npm init -y
npm install express
그리고 폴더를 만든 후에, 다음과 같이 index.js를 만들어준다.
tts-api/index.js
// local-edge-server.js
const express = require('express');
const cors = require('cors');
require('dotenv').config();
const app = express();
app.use(cors());
app.use(express.json());
// local-edge-server.js
app.post('/tts', async (req, res) => {
console.log('Received request at /tts'); // 요청 로그 추가
try {
const { input } = req.body;
console.log('Received text:', input); // 받은 텍스트 로그 추가
const response = await fetch('https://api.openai.com/v1/audio/speech', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.OPENAI_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: 'tts-1-hd',
input: input,
voice: 'nova'
})
});
console.log('response:', response)
const audioBuffer = await response.arrayBuffer();
res.set('Content-Type', 'audio/mpeg');
res.send(Buffer.from(audioBuffer));
} catch (error) {
console.error('Error in /tts:', error.message);
res.status(500).json({ error: error.message });
}
});
app.listen(3000, () => {
console.log('Local Edge Function server running on port 3000');
});
이제 이 서버를 실행해보자.
node index.js
그러면 로컬호스트 3000에서 진행될 것이다.
이제 이 서버는 프론트엔드에서 돌아가는 api 서버의 개념이 되었다.
다만 같은 폴더에 .env 파일을 만들어서 openai api key를 넣어줘야 보안 위협이 없이 돌아갈 수 있다.
꼭 명심하자. 프론트엔드는 고객에게 그대로 노출되기 때문에, 절대로 api key를 하드코딩해서 넣어서는 안된다!
이제 TTS를 다음과 같이 html에서 java script로 간단하게 불러올 수 있다.
async function speakResponse(text) {
if (isSpeaking) {
stopSpeaking(); // Stop any ongoing speech
}
// console.time('tts 처리 시간');
// 2. 이 텍스트를 Express 서버로 전송 (TTS 변환 요청)
const ttsResponse = await fetch('http://localhost:3000/tts', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCookie('csrftoken')
},
body: JSON.stringify({ input: text }) // 'input'으로 전송
});
// 3. TTS 변환된 음성 파일 받기
const audioBlob = await ttsResponse.blob();
const audioUrl = URL.createObjectURL(audioBlob);
// 4. 프론트엔드에서 오디오 재생
const audio = new Audio(audioUrl);
audio.play();
// console.timeEnd('tts 처리 시간');
}
아주 간단하게 Edge Function이 완성되었다.
이 기술로 저장이 필요없는 각종 API의 레이턴시를 줄이고, 서비스를 효율화해보자.
+backend는 django로 , 코드는 다음과 같다.
@method_decorator(csrf_exempt, name='dispatch')
class TextView(View):
def get(self, request):
text_data = {
'message': '이것은 테스트입니다.',
'status': 'success'
}
return JsonResponse(text_data)
def post(self, request):
# POST 요청으로 받은 텍스트를 다시 전송
try:
import json
data = json.loads(request.body)
received_text = data.get('text', '')
response_data = {
'received_message': received_text,
'status': 'success'
}
return JsonResponse(response_data)
except json.JSONDecodeError:
return JsonResponse({
'error': '잘못된 JSON 형식입니다.',
'status': 'error'
}, status=400)