킹의 개발일지

aws-amplify에서 chat-gpt stream response 받아보기. 본문

aws

aws-amplify에서 chat-gpt stream response 받아보기.

k1ng 2023. 9. 2. 02:27

사건의 발단 (1)

openai의 chat complete api를 요청, stream으로 응답을 받아 한 글자 한 글자 써내려가는 기능을 구현하고 있었다. Next.js 프레임워크를 사용했기에, 프로젝트의 배포를 vercel을 고려하고 있었다.

 

vercel에 배포후 gpt에 요청을 보내면 성공적으로 응답을 가져오는것을 볼 수 있었다. 그런데 여기서 첫 문제가 발생한다. 한 글자 한 글자 써내려 가던도중... 아래 그림과 같이 중간에 멈춰버리는 것이다..

 

 

 

개발자 도구의 network 패널을 열어서 보니 이 응답이 10초를 넘어갈 때마다 멈추는 것이었다.

 

문제를 찾아본 결과, vercel hobby(무료) 버전은 실행 시간이 10초를 넘어가면 알아서 중단시킨다고 한다...

 

 

사건의 발단(2)

때문에 프론트엔드 앱 배포를 도와줄 서비스를 찾다 amplify를 선택하게 됐다. amplify도 vercel과 비슷하게 full stack 배포를 편하게 해준다는 장점이 있기에 기초 셋업을 하고 배포를 했다.

 

또다시 문제가 발생했는데, vercel에서는 중간에 끊기긴 했지만 응답은 잘 받아왔었다...

 

하지만 amplify는 아무 응답도 없다가 한 20초 가량 가만히 있다, 504 에러만 뱉어내는 것이었다.

 

폭풍 검색결과.. 나는 Stream을 반환하기 위해 api route에서 vercel의 ai sdk의 StreamingTextResponse를 사용했는데, 얘가 edge runtime에 동작을 한다는데, (edge에 대해서는 기회가 생기면 따로 다뤄볼 예정이다. 쉽게 말하면 유저와 가까운  network의 '가장자리' 즉, cdn과 비슷한 개념이라고 보면 된다.) amplify는 엣지 API 라우트를 지원하지 않는다고 한다...

 

해결방안

해결 방안을 찾으려고 폭풍 검색을 했고 AWS의 Lambda function을 사용해서 기존 프로젝트에서 gpt api route 따로 함수를 만들어 관리하는 방식을 사용하고자 했다.

 

이를 위해 AWS SAM CLI를 사용했다. Lambda function을 생성후 다음과 같은 코드를 통해서 stream을 반환토록 했다.

 

'use strict';
import { OpenAIStream } from 'ai';

import dotenv from 'dotenv';
dotenv.config();

const apiKey = process.env.OPENAI_API_KEY;
if (!apiKey) {
  throw new Error('Missing OPENAI_API_KEY environment variable');
}

async function fetchData(payload) {
  payload.stream = true;

  const response = await fetch('https://api.openai.com/v1/chat/completions', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      Authorization: `Bearer ${apiKey}`,
    },
    body: JSON.stringify(payload),
  });

  return response;
}

export async function streamOpenAIResponse(responseStream, payload) {
  try {
    const response = await fetchData(payload);
    const convertedStream = OpenAIStream(response);
    for await (const chunk of convertedStream) {
      responseStream.write(chunk);
    }
  } catch (error) {
    console.error('Error in streamOpenAIResponse:', error);
    throw error;
  }
}

 

또 다시 문제.. CORS

lambda를 사용하기 이전에는 Next.js의 api route를 사용했는데, 클라이언트 사이드에서 api route(내 도메인, api route는 디폴트로 same origin이다. )로 요청을 보냈기에 same origin이라 error가 발생하지 않았다.

 

 

로직을 변경하고 api route를 사용하지 않고 클라이언트 사이드에서 요청을 보내기에, 브라우저 보안 정책상 cors error가 발생할 수 밖에 없는것이었다.

 

해결 방안

처음에는 response header에 cors 관련 정보를 담아줬는데도 문제가 계속해서 발생했다.

 

응답 헤더에 cors관련 정보를 기입하더라도 template.yaml 파일에서 FuntionUrlConfig를 설정해주지 않으면 소용이 없었다. 때문에 아래와 같이 template.yaml에 Cors 설정을 넣어주었다.

 

일단 도메인을 구입하지 않았기에, 테스트 용으로 다음과 같이 localhost에서만 접근을 허용했다.

FunctionUrlConfig:
  Cors:
    AllowMethods: ["POST", "OPTIONS"]
    AllowHeaders: [Content-Type, Authorization, authorization]
    AllowOrigins: ['localhost:3000']

 

문제 해결

aws sam cli에서 deploy후 amplify에 내 프로젝트를 배포하고 테스트 해보니 정상적으로 잘 작동했다.

 

결론

하나의 문제를 해결할 때마다 몇가지의 문제가 계속해서 생겨났다. 처음에 이유도 모르고 계속해서 에러가 발생했을 땐 속된말로 때려 치우고 싶었는데, 퍼즐 맞춰가듯 문제 하나하나 해결해 가니 뿌듯함에 계속 문제를 파고 또 팠던것 같다.

 

문제해결 능력도 문제를 만나봐야 생긴다고 계속해서 도전하다 보면 성장해있는 내 자신을 발견하지 않을까 하는 생각이 든다.

'aws' 카테고리의 다른 글

AWS S3 사용해서 이미지 업로드  (0) 2022.08.22
탄력적 IP를 할당해보자  (0) 2022.08.19
AWS 서버를 구축해보자  (0) 2022.08.17