티스토리 뷰

반응형

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)
반응형