프로젝트를 진행 중 OPEN AI의 API를 활용하여 GPT 기능을 넣어 보았다. 처음에는 비용적인 부분이 걱정이 되었지만 OPENAI의 가격정책을 보면 input, ouput이 1K token 라는 가정하에 0.0035$ 정도라 현재 유저가 많은 서비스가 아닌 만큼 크게 부담이 되지 않았다. OPEN AI의 API를 사용하여 GPT 기능을 구현하는 방법은 공식문서 이외에도 많은 글들이 존재해 이번에는 CI 환경에서 빌드 중 발생한 에러 해결과정 및 배운점을 적어 보려고 한다.
로컬 환경에서는 에러 없이 잘 작동 하였는데 git action으로 build 테스트시에 다음과 같은 에러가 발생했다.
응? OPEN_API_KEY가 없다고? KEY값을 어디서 사용하더라...
에러 해결을 위해 사용되는 코드 부분을 찾아 보았다.
//open AI의 api 사용을 위한 전처리 파일 입니다.
import OpenAI from 'openai';
const openai = new OpenAI({
apiKey: process.env.NEXT_PUBLIC_OPEN_API,
dangerouslyAllowBrowser: true,
});
async function getCodeReview({
code,
question,
}: {
code: string;
question: string;
}) {
const completion = await openai.chat.completions.create({
messages: [{ role: 'user', content: `${code} \n ${question} ` }],
model: 'gpt-3.5-turbo',
max_tokens: 1024,
});
return completion.choices[0].message.content;
}
export default getCodeReview;
아! 현재 API 키는 env.development에 저장되어 있고 gitignore에 env.development 파일이 설정되어 있기 때문에 빌드시에는 OPEN_API 값을 받아올 수 없기에 에러가 떴구나😅
그럼 빌드 전에 env 파일을 넣어주면 되지 않을까?
이러한 고민을 하여 yml 파일에 Generate Environment Variables File for Production
이름을 가진 코드를 추가하여 빌드 전 env 파일에 NEXT_PUBLIC_OPEN_API
값을 넣어주었다.
name: 'test-lint-build'
on:
push:
pull_request:
jobs:
test:
name: Test lint, build
runs-on: ubuntu-latest
env:
working-directory: ./client
steps:
- uses: actions/checkout@v2
- name: Use Node.js
uses: actions/setup-node@v2
with:
node-version: '18'
- name: Cache node modules
uses: actions/cache@v2
id: cache
with:
path: node_modules
key: npm-packages-${{ hashFiles('**/package-lock.json') }}
- name: Generate Environment Variables File for Production
run: echo "NEXT_PUBLIC_OPEN_API=${{ secrets.NEXT_PUBLIC_OPEN_API }}" >> .env
- name: Install Dependencies
run: npm install
working-directory: ${{ env.working-directory }}
- run: npm run lint
if: ${{ always() }}
working-directory: ${{ env.working-directory }}
- run: npm run build
if: ${{ always() }}
working-directory: ${{ env.working-directory }}
해결될것 같았지만 여전히 같은 에러가 발생하였다. 무엇이 문제일까? 고민하다 두가지 의문점이 들었다.
NEXT_PUBLIC
이 의미하는게 뭘까?.env.development
를 사용하고 있었는데 .env, .env.development 차이가 뭘까?Non-NEXTPUBLIC environment variables are only available in the Node.js environment, meaning they aren't accessible to the browser (the client runs in a different environment).
In order to make the value of an environment variable accessible in the browser, Next.js can "inline" a value, at build time, into the js bundle that is delivered to the client, replacing all references to process.env.[variable] with a hard-coded value. To tell it to do this, you just have to prefix the variable with NEXTPUBLIC. NEXTJS
요약하면, 일반적으로 env 변수는 NodeJS 환경에서만 사용할 수 있는데, 만약 브라우저 환경에서도 사용하고 싶으면 환경변수 앞에 접두사로 NEXT_PUBLIC_
을 붙여야 한다.
결론적으로 나는 브라우저 환경에서 GPT 기능을 사용하기 위해 NEXT_PUBLIC_
을 추가적으로 붙여주었었고 이는 NODEJS 환경에서도 잘 작동하니 문제의 원인이 아니였다.
참고로 process.env.NODE_ENV
는 현재 실행 환경이 development,test,production 인지 구분해주는 내장 환경 변수이다. 이를 활용하여 환경에 따라 의도적으로 다른 작업을 할 수도 있을 것 같다.
(예를들면 개발환경, 배포환경에서 서버에 요청하는 URL를 다르게 한다던지???)
매번 프로젝트 할떄마다 env 파일을 사용하지만 여러 env 종류와 의미를 알지못해 학습해 보았다.
.env: 기본 파일.
.env.local: .env를 덮어쓰는 파일. Test를 제외한 모든 환경에서 로딩.env.development: 개발자 환경에서 로딩
.env.test: 테스트 환경에서 로딩
.env.production: 프로덕션 환경에서 로딩.env.development.local, .env.test.local, .env.production.local: 각각 env.* 를 덮어쓰는 파일 참고
npm start: .env.development.local > .env.development > .env.local > .env
npm run build: .env.production.local > .env.production > .env.local, .env
npm test: .env.test.local > .env.test > .env (note .env.local is missing)
정확히 알고 사용한 것은 아니였지만 개발환경에서는 .env.development 파일로, git action CI 빌드 환경에서는 .env 파일로 사용하여 이 또한 문제가 아니였다. (만약 빌드 환경에서 .env.development로 만들었다면 npm run build 실행 시 파일을 읽지 못했을 것이고 이를 원인이라고 생각했을 수도 있겠다.)
env 설정은 잘해주었는데 왜 OPEN_API 값을 못찾을까?? 도대체 어떤 방식으로 코드를 짰길래...
이러한 생각으로 OPEN_AI의 에러 처리 방식을 라이브러리를 뜯어보며 찾아보았다.
open-ai/src/index.ts
//open-ai/src/index.ts
...
constructor({
apiKey = Core.readEnv('OPENAI_API_KEY'),
organization = Core.readEnv('OPENAI_ORG_ID') ?? null,
...opts
}: ClientOptions = {}) {
if (apiKey === undefined) {
throw new Errors.OpenAIError(
"The OPENAI_API_KEY environment variable is missing or empty; either provide it, or instantiate the OpenAI client with an apiKey option, like new OpenAI({ apiKey: 'My API Key' }).",
);
}
...
//open-ai/src/core.ts
...
export const readEnv = (env: string): string | undefined => {
if (typeof process !== 'undefined') {
return process.env?.[env] ?? undefined;
}
if (typeof Deno !== 'undefined') {
return Deno.env?.get?.(env);
}
return undefined;
};
...
이 코드를 보면서 OEPN AI의 코드 구현 방식을 생각해 보았다.
나는 현재 아래와과 같이 NEXT_PUBLIC_OPEN_API(open ai에서 탐색하는 OEPNAI_API_KEY가 아님을 주의) 값을 설정해 놨었고 2번으로 분기 되지 않겠다는 것을 생각했다. 그럼 결론적으로 빌드 환경에서 OPENAI의 인자로 apiKey값이 들어가기는 할텐데 3번이(에러) 발생한다는 것은 apiKey가 undefined 라는 의미이고, 결국 아직도 build 환경에서는 env를 못찾는구나.
//open AI의 api 사용을 위한 전처리 파일 입니다.
...
const openai = new OpenAI({
apiKey: process.env.NEXT_PUBLIC_OPEN_API,
dangerouslyAllowBrowser: true,
});
...
너무나도 허무하게 문제는 env 파일 생성 경로를 잘못 지정해 주었기 때문이다.(그러니 계속해서 env 파일을 못찾았지...😭) 이 프로젝트 폴더 구조는 root-client 구조로 되어있어 프로젝트를 실행하기 위해서는 clinet 폴더로 변경하여 run을 실행시켜야했다. 하지만 Generate Environment Variables File for Production
에서는 working-directory
를 변경하지 않아 env 파일은 root 폴더에 npm build는 client에서 실행되어 문제가 발생했던 것이다.
그래서 다음과 같이 working-directory
를 추가한 yml 파일로 CI build 테스팅을 성공시킬 수 있었다.
name: 'test-lint-build'
on:
push:
pull_request:
jobs:
test:
name: Test lint, build
runs-on: ubuntu-latest
env:
working-directory: ./client
steps:
- uses: actions/checkout@v2
- name: Use Node.js
uses: actions/setup-node@v2
with:
node-version: '18'
- name: Cache node modules
uses: actions/cache@v2
id: cache
with:
path: node_modules
key: npm-packages-${{ hashFiles('**/package-lock.json') }}
- name: Generate Environment Variables File for Production
run: echo "NEXT_PUBLIC_OPEN_API=${{ secrets.NEXT_PUBLIC_OPEN_API }}" >> .env
working-directory: ${{ env.working-directory }}
- name: Install Dependencies
run: npm install
working-directory: ${{ env.working-directory }}
- run: npm run lint
if: ${{ always() }}
working-directory: ${{ env.working-directory }}
- run: npm run build
if: ${{ always() }}
working-directory: ${{ env.working-directory }}
이번 에러를 통해 개발자의 성장은 에러를 해결하는 과정에서 가장 크다고 느꼈다. 만약 working-directory 설정을 안해줬다는것을 바로 알아차렸다면 해결은 빠르게 했을지 몰라도 NEXT_PUBLIC의 의미, ENV 종류와 특징, OPEN_AI가 API를 받아오는 과정을 학습할 수 없었을 것이다. 삽질하는 과정을 너무 부정적으로만 생각하지 말고 성장의 영양제라고 생각하자!😊