옛날 옛날 한 옛날에 아주 게으른 날먹 개발자가 살았더래요. 이 날먹 개발자는 어느 날 AI를 사용해 YouTube 동영상 제작을 자동화하고, 떼돈을 벌겠다는 발칙한 생각을 했습니다. 그렇게 행복한 상상을 하며 자신만만하게 도전했지만, 날먹 개발자는 결국 떼돈은커녕 실패하고 말았답니다. 이후 행복하게 살았다는 것 같기도 하고... 끝.
(그렇다. 제목은 어그로일 뿐이었다.)
이 글은 “무엇을 했는가”와 “어떻게 했는가”로 나뉜다. "무엇을"에서는 정말 무엇을 했는지만 나온다. 진짜 어떻게 했고 어떤 문제가 있었는지는 "어떻게"에서 얘기한다. “어떻게”에 대한 얘기는 기술 용어를 그대로 사용할 것이니, 기술적인 부분에 관심이 없는 사람들은 “무엇을”에 대한 내용만 읽어도 충분하다.
3줄 요약
1. AI를 이용해 유튜브 동영상 제작을 자동화하려 했고, 실제로 어느 정도 성공했다!
2. 그러나 자동화의 한계로 인해 퀴즈 문제의 품질 관리와 TTS의 오류로 사람이 매번 개입해야 했다.
3. 결국 시즌 1은 여기서 마무리. 다음 시도는 과연 자동화의 꿈을 이룰 수 있을까?
위에서 들려준 동화는 전혀 놀랍지 않게도 실화다. 동기는 아주 단순했다. 점심시간에 옆자리 동료 개발자가 유튜브를 보고 있었는데 대충 “이거 다 알면 당신도 상식왕!”라는 제목의 동영상에서 퀴즈 문제가 나오고 정답을 맞히는 아주 단순한 구성의 영상이었다. 지금 유튜브에 “상식퀴즈” 같은 키워드로 검색해 보면 비슷비슷한 형태의 동영상 수백 개가 나올 것이다. 중요한 것은 이거다. “저런 걸 왜 보고 있지?”가 아니라 “저런 걸 생각보다 많은 사람들이 보고 있다.”는 사실이다.
실제로 내가 운영한 유튜브 채널 링크이다. 87편을 마지막으로 더 이상 새로운 동영상이 올라오고 있지 않다.
그 사실을 알고 나서 ’저거 그냥 ChatGPT API로 자동으로 문제랑 TTS 만들고 프리미어 프로도 자동화해서 동영상을 뽑아내면 되는 것 아닌가?’ 같은 생각을 했다. 꽤 그럴싸하지 않은가?! 이전에 동영상도 제작하는 디자이너와 일했을 때도 고정된 형식과 그 형식을 채울 수 있는 내용만 있다면 일일이 노가다하지 않고 웬만한 거는 다 자동으로 할 수 있을 거라 생각하곤 했다. 물론 실제로 시도하지는 않았지만 말이다.
그냥 해 보고 싶었다. 할 수 있을 것 같았고, 성공하면 돈도 벌 수 있을 것 같았다. 즉시 POC(Proof Of Concept, 개념증명)를 진행하기로 했다. 이를 위해서 검증해야 할 항목들은 아래와 같다.
누구나 그럴싸한 계획을 가지고 있다. 쳐 맞기 전까지는 말이다. 그래서 완전히 실패한 것은 아니고, 나름대로 작동했다. 위에서 설명한 것처럼 각 기능이 모두 작동하긴 했다.
그런데 “동작한다”가 다다. “우리가 기대하는 수준의 자동화”를 하지 못할 뿐이다. 자동화의 핵심은 양질의 문제를 생성하고 TTS의 매끄러운 출력을 보장하는 것이지만, 이 부분이 제대로 이루어지지 않았다. 영상을 자동으로 만들더라도, 재료가 부실하다면 의미가 없다.
나의 프롬프트 엔지니어링의 한계 때문에 “양질의 무한한 문제”를 만들어내지 못했다. 지금 당장 아무 LLM 챗봇 서비스에 가서 “퀴즈 문제 3개 만들어줘”라고 물어보면 그럴싸하게 퀴즈를 만들어 줄 것이다. 그러나 이것이 반복되면 더 이상 그럴싸하지 않게 된다.
한편당 3개의 문제가 필요했고, 이를 반복적으로 생성하되 "중복되지 않고, 너무 쉽지도 어렵지도 않으며, 보편적인 지식이고 흥미를 유발할 수 있는 문제"를 무한히 만들어내야 했다. 그러나 중복 문제를 걸러내는 것은 큰 도전이었다. 프롬프트 엔지니어링을 통해 지시사항을 구체적으로 명시하더라도 문제는 해결되지 않았다. 반복적인 시도에도 불구하고 모델이 유사한 문제를 지속적으로 생성하는 경향이 있어, 이것이 큰 걸림돌이 되었다.
또한, "상식 문제"라는 주제를 명확히 정의하는 것 역시 쉽지 않았다. 상식의 범위는 사람마다 다를 수 있기 때문에 이를 특정 짓는 것 자체가 모호했고, 적절성을 판단하는 것도 매우 까다로웠다. 특히 수준 이하의 문제를 만들지 말라는 지시사항을 프롬프트에 명시적으로 포함했음에도 불구하고, 결과는 만족스럽지 못했다. 모델은 여전히 지나치게 기본적이거나 적절치 않은 문제들을 생성하는 경향이 있었다. 생성된 문제와 선택지, 정답은 인간의 검토가 필요했다.
결론적으로, 이 자동화의 꿈은 여러 난관에 부딪혔다. 문제의 생성, 중복성 검토, 그리고 최종적인 품질 확인 등 모든 과정에서 자동화가 충분하지 않았고, 매번 사람이 확인하고 수정해야만 했다.
원래는 문제 풀(DB)을 만들어서 관리하는 시스템을 구축하는 것도 고려하였다. 그러나 LLM만으로 해결이 되지 않는다면, 결국 각 문제의 내용을 분석하는 체계를 만들어야만 했는데, 이 접근은 현 프로젝트의 범위를 벗어나 완전히 다른 프로젝트가 될 수 있기에 시도하지 않았다. 예를 들어, "세계에서 가장 큰 사막은?"과 "사막 중에 가장 큰 사막은?"은 같은 문제지만, 이를 자동으로 동일한 문제로 인식하고 중복을 처리하는 것은 매우 어려운 일이다. 이러한 시스템적 접근은 LLM의 한계와 맞물려, 궁극적으로 이번 프로젝트에서 다루기에는 너무 방대한 작업이었다.
[
{
"question": "산업 혁명이 시작된 나라는?",
"choices": ["프랑스", "독일", "미국", "영국"],
"answer": 4,
"explanation": "산업 혁명은 영국에서 시작되었습니다."
},
{
"question": "일본의 수도는?",
"choices": ["쿄토", "오사카", "도쿄", "나고야"],
"answer": 3,
"explanation": "일본의 수도는 도쿄입니다."
},
{
"question": "빛의 삼원색이 아닌 것은?",
"choices": ["빨강", "노랑", "파랑", "초록"],
"answer": 2,
"explanation": "빛의 삼원색은 빨강, 초록, 파랑입니다."
}
]
결국 생성된 결과를 매번 확인하고 교정해야 했다. 여기서부터 완전 자동화의 꿈은 이미 멀어진 셈이다. 문제를 자동으로 생성했지만, 중복된 것을 걸러내는 일이 가장 큰 과제였다. 문제를 만들면 만들수록 사용한 문제, 그것과 비슷한 문제의 수가 계속해서 늘어나는데 이것을 자동으로 걸러낼 수 없다면 결국 사람이 이 모든 것을 기억하고 있어야만 된다는 말이 된다. 현실적으로 불가능하다.
문제 생성도 어려웠지만 TTS 또한 난관이었다. 고전적인 음성 합성 방식의 TTS를 사용하는 것이 아닌 머신러닝 기반이 TTS를 사용하는 것이다 보니 글자를 이상하게 읽게 된다. 갑자기 소리를 지르거나 말을 중간에 끊는 등 이상한 현상이 발생했다. 외계어를 하지 않나 숫자를 이상하게 읽지 않나 문제가 많다. 생성하고 들어보고 이상하면 다시 만들고 들어보고를 반복해야 한다.
비용을 더 들여 다른 TTS를 사용하면 해결될 수 있었지만, POC에 그렇게 많은 돈을 들일 수는 없었다.
동영상 제작은 자동화했지만, 문제 생성과 음성 합성은 결국 사람의 검토가 필수적이었다. 그러면 동영상 생성을 자동화하는 것이 더 이상 의미가 없다. 이를 위해서 툴을 만들기 위해서 노력을 했으나 배보다 배꼽이 더 크게 되는 것을 발견했다.
결국 일일이 검사를 해야 했고, 그로 인해 유튜브 채널 운영을 포기하게 되었다. 완전한 자동화를 위해 사람의 개입을 없애거나 최소화해야 했지만, 이번 프로젝트에서는 매번 개입이 필요했다. 이는 자동화라고 할 수 없었다.
이것이 AI로 YouTube 자동화하려는 날먹 개발자 썰!
에서 내가 하였던 무언가이다. 무엇을 배웠고 그런 얘기는 하고 싶지 않다. 그냥 그렇다는 것이다. 이제 어떻게 이것들을 해왔는지 않아보자.
실제 소스 코드를 깃허브에 올려놓았다. 궁금한 사람들은 가서 보시면 된다.
.env
를 채우면 일부 코드는 동작할 것이다. 동영상의 경우는 템플릿을 내가 만든 것이 아니어서 제거하였기에 동작하지 않을 것이다. 그전에 우선 프리미어 프로가 반드시 필요하다.
실제 내가 한 것들은 아래와 같다.
왜 프리미어 프로인가? 별 이유는 없다. 그냥 내가 이전에 다뤘던 툴이 프리미어라서 그렇다. 동영상을 만들기 위한 소스가 있어도 그 소스를 가지고 동영상 제작 자체를 자동화하지 못하면 아무 소용이 없다. 이제 방법을 찾아봐야 했다. 그냥 막연히 시작하기 전에 ‘당연히 되겠지’라고 생각했고 역시나 당연히 있었다.
Welcome to The Javascript Tools Guide — JavaScript Tools Guide CC 0.0.1 documentation Adobe 사의 프로그램들을 프로그래밍으로 제어하기 위해서는 ExtendScript를 사용해야 한다. 무려 ES3 구현체이다. 구글 스프레드시트도 그렇고 이런 툴 제어를 위한 자바스크립트는 대부분 ES3인가 싶다. ExtendScript에서 당장 알아야 할 것은 크게 없다. 나의 경우는 File system에 대한 것만 조금 사용하였을 뿐이다.
ExtendScript Debugger - Visual Studio Marketplace 이제 이걸 실행하고 디버그 할 환경이 필요한데 이전에는 The ExtendScript Toolkit
라는 것을 사용한 것 같은데 다행히 VSCode용 익스텐션이 제공되고 있었다. 그러나 Apple Silicon은 지원하지 않는다. 그렇기에 x86 VSCode를 따로 받아서 사용해야만 한다. 굳이 x86용을 받아서 사용한 적이 없으니 두 개의 서로 다른 VSC가 같은 설정을 공유해서 동작하길래 Portable Mode로 설정해서 x86 설정을 분리시켰다.
ExtendScript는 runtime일 뿐이고 각 App(포토샵, 프리미어 같은) 마다 다른 API를 제공한다. Welcome to the Premiere Pro Scripting Guide! — Premiere Pro Scripting Guide 22.5 documentation 문서를 보면서 프리미어의 각 기능이 어떤 객체에 연결되는지를 파악해야 한다. 사실 프리미어를 통해서 동영상을 만드는 기술만 익혀서 써봤을 뿐이지 이것을 객체의 관점에서 생각해 본 적이 없다 보니 처음에는 많이 헤매게 되었다. 진짜 이 문서만 일주일 보면서 하나씩 실행해 보면서 각 기능과 개념을 매칭시켰었다.
처음 코드를 작성할 때는 그냥 JavaScript로 작성했었다. 좀 규모가 커지다 보니 TypeScript를 찾을 수밖에 없게 되었다. 타입을 만들어야 하나 고민했으나 다행히! ExtendScript의 타입 오픈소스가 있었다. docsforadobe/Types-for-Adobe: TypeScript types for Adobe: AfterEffects, Animate, Audition, Illustrator, InDesign, Photoshop, Premiere, ScriptUI. Runtime이 ES3다 보니 모듈이 없고 Global Scope에 모든 것을 다 해야 하다 보니 Triple-Slash Directives를 사용해야 하지만 이것만으로 감지덕지 아니겠는가.
처음에는 텍스트 배치 같은 것들을 쉽게 할 수 있을 것이라 생각했는데 이게 그렇게 만만하지 않았다. 내용을 직접 배치하는 것이 어렵기에 뭔가 동영상을 템플릿화 하고 변수를 바꾸면 내용이 바뀌게 만들고 싶었는데 다행히 MOGRT(Motion Graphics Template)라는 것을 지원했고 같이 프로젝트를 진행하는 디자이너에게 템플릿을 만들어 달라 부탁했다. 이런저런 시도 끝에 동영상이 자동제작과 인코딩 작업까지 자동화하였다.
이 ExtendScript가 ES3 기반이기에 실행 전에 반드시 컴파일해줘야만 한다. 또한 ExtendScript를 실행시키는 것은 Debugger이다. 아래와 같이 .vscode/launch.json
에 디버거를 설정해 주거 실행 전에 build script가 돌게 설정해 주었다.
{
"version": "0.2.0",
"configurations": [
{
"name": "ppro-short",
"type": "extendscript-debug",
"request": "launch",
"preLaunchTask": "npm: build",
"script": "${workspaceFolder}/apps/ppro/dist/script-short.js",
"hostAppSpecifier": "premierepro-24.0",
"bringToFront": true
}
]
}
이게 진짜 아쉬운 게 SourceMap을 사용할 수 없다. 그래서 매번 디버깅하고 코드를 수정할 때, 컴파일된 js 파일을 수정해 놓고 “아니! 왜 안 되는데!” 하며 헛수고를 한 적이 한두 번이 아니다.
코드 규모가 커지다 보니 모듈화를 하고 싶었다. 스크로로로로롤을 너무 많이 하다 보니 도대체 내가 어디를 보고 있는지를 모르겠었다. 그리고 이 Namespace pattern도 보기 싫었다. 처음에는 webpack을 사용하려 했는데 아뿔싸. webpack5의 bootstrap code가 ES5이지 뭐람. 이건 답이 없다. 더 낮은 버전의 webpack을 사용하면 해결될 수도 있겠으나 그냥 rollup을 사용하니 해결되었다. 출력 구조도 몹시 간단하게 나와서 꽤 맘에 들었다.
마지막으로 코드가 어느 정도 완성되었으니 디버거가 아닌 다른 방법으로 해당 코드를 실행하려 했었다. 그런데 방법이 없다. 놀랍게도 정말 없다. 이걸 Adobe 앱 안에 삽입해서 플러그인처럼 사용하는 방법은 있었지만, 외부에서 실행하는 방법은 없었다. 어려가지 방법들이 웹상에 떠돌아다녔지만 내 환경에서는 되는 게 없었다. 시작부터 글러 먹은 건가 싶어 좌절하였으나 어떻게 방법이 없는 것은 아니었다.
Debug Launcher - Visual Studio Marketplace 디버거 런처라는 놀라운 익스텐션이 있지 않겠는가. 디버거를 실행시킬 수 있는 익스텐션이다. 외부에서 디버거 런처를 실행시키고 옵션으로 ExtendScript Debugger를 실행시켜 달라 하면 되는 것이다. ExtendScript를 단독으로 앱에 실행시키는 게 아니라 간접적으로 어쨌든! 실행시킨 것이다. 참 환장하겠다.
여차저차 어찌하였든 결국 내가 원하는 수준의 프리미어 프로 자동화 POC에 성공하였다.
이제 OpenAI의 API 통해서 문제를 만들어보자. 이번 프로젝트를 진행하면서 처음으로 LLM 관련 API를 만져봤는데 “진짜 이게 맞나?” 싶은 순간이 많았다. 이런 형식의 API는 처음 만져봤다.
Chat API를 사용하면 우리가 ChatGPT app에서 대화하는 것과 같은 기능을 사용할 수 있다. 물론 chat이라고 써놨지만 사실 chat이 아니다.
curl https://api.openai.com/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
"model": "gpt-4o",
"messages": [
{
"role": "system",
"content": "You are a helpful assistant."
},
{
"role": "user",
"content": "Hello!"
}
]
}'
request body로 여러 가지 model에 관련된 설정을 할 수 있고 중요한 것은 messages
항목이다. 여기에 간단하게는 role과 content로 구성된 객체의 배열을 넘겨주면 되는데 role에는 크게 system, assistant, user가 있다. 더 있는데 크게 중요하지는 않다. system으로 우리가 흔히 말하는 “프롬프트 엔지니어링” 지시사항을 알려줄 수 있다. 기법이야 여러 가지가 있겠지만 그냥 답변을 어떤 식으로 해라식의 지시사항을 써 놓으면 된다. 그리고 진짜 내가 할 말을 user로 작성하면 된다. 그러면 아래와 같이 응답이 온다.
{
"id": "chatcmpl-123",
"object": "chat.completion",
"created": 1677652288,
"model": "gpt-4o-mini",
"system_fingerprint": "fp_44709d6fcb",
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": "\n\nHello there, how may I assist you today?",
},
"logprobs": null,
"finish_reason": "stop"
}],
"usage": {
"prompt_tokens": 9,
"completion_tokens": 12,
"total_tokens": 21,
"completion_tokens_details": {
"reasoning_tokens": 0,
"accepted_prediction_tokens": 0,
"rejected_prediction_tokens": 0
}
}
}
choices 필드로 assistant role의 메세지 객체가 담겨 오는데 요청 시 n의 설정을 조정하면 n에 맞게 대답 선택지가 담겨오게 된다. 기본값이 1이니 1개가 온 것이다. 아무튼 이 choices에 담긴 메세지가 내가 요청한 user에 대한 답변이다. 이게 다다. 엄청나지 않은가? 사실 처음에 이걸 보고 ‘이게 도대체 뭐지…’ 한참 생각을 했었다. 그래서 이제 이걸 어떻게 써야 하나 멍 때렸던 것 같다. 그런데 놀랍게도 많은 사람들이 사용하는 ChatGPT API라는 게 이거다. 이거로부터 시작하는 거다.
그러면 대화는 어떻게 할까? 응답으로 받은 assistant meesage를 이전 응답의 message 끝에 추가하고 이어서 물어 대답하고 싶은 user message를 넣어 보내면 그게 채팅이다. 진짜다. 처음에는 단순한 명령어 체계도 만들어서 loop를 도는 console app을 만들어 채팅형식으로 어떻게 돌아가나 보기도 했고
export enum CommandOrWord {
CREATE = "생성",
RECREATE = "재생성",
CHECK_DUPLICATION = "중복확인",
REMOVE_DUPLICATION = "중복제거",
SAVE_TEXT = "저장",
CHECK_QUANTITY = "검토",
YES_DUPLICATION = "있음",
NO_DUPLICATION = "없음",
CORRECT = "적절함",
INCORRECT = "부적절함",
}
feed back loop를 만들어서 지시한 수준의 문제가 나올 때까지 알아서 자문자답하게도 해보았다. 물론 딱히 내가 원하는 그런 게 나오지는 않았다. 아래가 최근까지 내가 문제를 뽑아낼 때 사용한 요청/응답 내용이다.
[
{
"role": "system",
"content": "당신은 퀴즈 생성 프로그램입니다. 사용자에게 간단하고 흥미로운 퀴즈를 한국어로 작성하여 제공해야 합니다. 다음의 규칙을 반드시 준수하세요:\n\n1. 퀴즈는 한국어로 작성되어야 하며, 가능한 한 영어 단어는 사용하지 마세요.\n2. 퀴즈는 객관적으로 정답이 하나인 문제로 만들어야 하며, 주관적인 의견이나 다수의 정답이 가능한 문제는 만들 수 없습니다.\n3. 퀴즈 내용은 시간이 지나도 정답이 변하지 않는 영구적인 내용을 포함해야 합니다. 즉, 정답이 과학적 사실, 역사적 사실, 수학적 원리 등과 같이 변하지 않는 내용이어야 합니다.\n4. 사용자가 제공한 '이미 사용한 문제 목록'과 '수준 이하 문제 목록'에 포함된 문제와 중복되거나 비슷한 내용을 가진 문제는 생성하지 않습니다. 문제를 생성할 때는 이미 사용된 문제와의 유사성을 확인하고 새로운 문제를 만들어야 합니다. 문제의 내용, 주제, 형식 등을 구체적으로 검토하여 중복을 방지하세요.\n5. 주제와 관련하여 시간이 지남에 따라 변할 수 있는 내용, 예를 들어 소비 트렌드, 인기 순위, 통계 수치 등은 퀴즈 문제로 출제하지 않습니다. 정답이 주기적으로 변할 가능성이 있는 문제는 배제해야 합니다.\n6. 문제는 명확하고 구체적으로 표현되어야 하며, 모호하거나 애매한 표현은 피해야 합니다. 모든 문제는 정답이 명확하게 결정될 수 있도록 해야 합니다.\n7. 문제의 답변은 명확하고 정확해야 하며, 정답을 추측하기 어렵게 만드는 표현이나 혼동을 일으킬 수 있는 문구는 피해야 합니다.\n8. 사용자가 별도로 제공하는 '수준 이하 문제 목록'에 포함된 문제와 유사한 문제는 생성하지 않도록 합니다. 수준 이하 문제의 예시는 너무 기본적이거나 쉽게 정답을 유추할 수 있는 문제로, 이러한 문제를 피하기 위해 적절한 난이도의 문제를 생성해야 합니다.\n9. 퀴즈는 특정 대상에게 유용하고 흥미로운 내용이어야 합니다. 특히 상식 문제지만 유용하거나 흥미로운 정보를 제공하여 사용자에게 새로운 지식을 학습할 수 있도록 해야 합니다.\n10. 생성한 문제는 간단하고 명확하게 표현되어야 하며, 혼동을 일으킬 수 있는 모호한 표현을 피해야 합니다.\n11. 첫 번째 문제는 사용자들의 흥미와 호기심을 유발할 수 있는 내용을 포함해야 합니다. 흥미로운 사실이나 예상치 못한 정보를 활용하여 주의를 끌어야 합니다. 단, 이 문제도 시간이 지나도 정답이 변하지 않는 내용이어야 합니다.\n12. 모든 문제의 길이는 가능하면 30자를 넘지 않도록 합니다.\n13. 요청된 문제의 수를 정확히 생성하고, 각 문제는 줄바꿈 기호(\"\n\")로 구분하여 출력합니다.\n14. 문제를 생성할 때, 이미 사용된 문제 목록과의 유사성을 확인하여 중복되지 않도록 주의합니다. 기존 문제의 초점과 세부사항을 고려하여 새로운 문제를 제안합니다.\n15. 정답이 언제나 하나로만 특정될 수 있는 문제를 생성하도록 합니다. 즉, 다수의 답변이 가능한 문제는 피해야 합니다.\n16. 문제는 사용자가 모를 수 있는 정보도 포함될 수 있으며, 다양한 주제를 다루어 사용자에게 새로운 정보를 학습할 수 있는 기회를 제공해야 합니다. 특히 상식 문제이지만 유용하거나 흥미로운 정보를 포함하도록 합니다."
},
{
"role": "user",
"content": "\"이미 사용한 문제 목록\"은 다음과 같습니다:\n- 다보탑이 위치한 사찰은?\n- 백제의 마지막 왕은 누구인가요?\n- 축구 경기에서 한 팀의 선수 수는?\n- 대한민국 대통령 후보가 되기 위한 최소 나이는?\n- 축구의 연장전은 몇 분 동안 진행되나요?\n- 제2차 세계대전이 끝난 해는?\n- 대한민국의 대표적인 귀족 제도는?\n- 대한민국의 최북단 섬은 어디인가요?\n- 조선 후기 근대화를 위한 최초의 개혁은?\n- 최근 한국의 원자력 발전 비중은?\n- 세계에서 가장 많이 재배되는 커피 품종은?\n- 물질의 상태 중 고체, 액체, 기체 외에 무엇이 있나요?\n- 대한민국은 세계 표준 시간보다 몇 시간이 빠른가요?\n ….. 지금까지 나왔던 엄청나게 많은~~ 긴~~ 문제 들 ..… \n\n이 목록을 참고하여 수준 이하 문제를 피하고, 새로운 퀴즈 문제를 10개 만들어 주세요. 대상은 '한국의 평범한 고등학교를 졸업한 사람'이고, 주제는 '정치, 경제, 사회, 지리, 역사, 과학, 문화, 예술, 스포츠에 관한 상식'입니다.”
},
{
"role": "assistant",
"content": "1. 한반도의 면적은 약 몇 제곱킬로미터인가요? \n2. 로마 제국이 가장 번영했던 시기를 무엇이라고 하나요? \n3. 빛의 속도가 공기 중에서 약 몇 km/s인가요? \n4. 경제에서 'GDP'는 무엇의 약자인가요? \n5. '19세기'는 몇 년부터 몇 년까지를 의미하나요? \n6. 우리나라에서 가장 많은 면적을 차지하는 산맥은 무엇인가요? \n7. 인류 최초의 문명이 자리잡았던 지역은 어디인가요? \n8. 클림트가 그린 유명한 작품 '키스'는 어떤 예술 장르에 속하나요? \n9. 스페인에서 유래된 전통 춤 중 하나는 무엇인가요? \n10. 제2차 세계대전에서 중요한 역할을 한 공격 작전의 이름은 무엇인가요? ",
"refusal": null
}
]
규칙을 줄줄줄 넣고 이미 나온 문제도 다~~~ 알려줘도 중복 문제가 나오고 만들지 말라는 문제 만들고 하였다. 물론! 내가 내가 잘 몰라서 그런 거려니 한다. 서점에 가서 “ChatGPT API로 나만의 챗봇 어플 만들기” 같은 비슷한 책이 있길래 봤었는데 내가 아무것도 모르고 시도했던 것들과 꽤 비슷한 걸 하지 않겠는가. ‘아. 이걸 프롬프트 엔지니어링이라 하는 건가?’ 처음 생각하게 되었다. 물론 거기서는 더 많은 기법들(대화가 길어지는 것을 방지하기 위해 요약을 하고 외부 DB에 저장했다가 새로 시작하는 대화 앞에 넣는다던가, user message 뒤에 힌트 메세지를 끼워 넣는다던가 식의 등)을 사용하긴 했지만 말이다.
여차저차해서 랜덤 하게 10개의 문제를 만들고 원하는 것을 선택하고 다시 거기서 선택지를 만드는 식의 콘솔앱을 만들어서 문제 선별작업을 진행하였다. 물론 문제가 다 나오고 사실 검증을 하는 것 또한 나의 일이었다. 실제로 문제의 정답을 검토했는데도 오답을 영상으로 만든 적도 있다.
? 문제를 선택해주세요 (Press <space> to select, <a> to toggle all, <i> to invert selection, and <enter> to proceed)
❯◯ 1. 한반도의 면적은 약 몇 제곱킬로미터인가요?
◯ 2. 로마 제국이 가장 번영했던 시기를 무엇이라고 하나요?
◯ 3. 빛의 속도가 공기 중에서 약 몇 km/s인가요?
◯ 4. 경제에서 'GDP'는 무엇의 약자인가요?
◯ 5. '19세기'는 몇 년부터 몇 년까지를 의미하나요?
◯ 6. 우리나라에서 가장 많은 면적을 차지하는 산맥은 무엇인가요?
◯ 7. 인류 최초의 문명이 자리잡았던 지역은 어디인가요?
사용한 문제가 많아지면 많아질수록 사람이 해야 할 일의 난의도가 높아진다. 이전과 비슷한 문제를 선별해야 하고 특히 중복을 걸러내야 한다. 나도 다 기억 못 한단 말이다! 어찌 됐든 뭔가 이상하긴 하지만 돌아는 가게끔 뭔가 만들어 냈기에 영상을 만들 수 있었다.
Audio API는 chat 보다 더 직관적이다.
curl https://api.openai.com/v1/audio/speech \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "tts-1",
"input": "The quick brown fox jumped over the lazy dog.",
"voice": "alloy"
}' \
--output speech.mp3
모델과 음성을 선택하고 읽어낼 문자열을 주기만 하면 된다. 응답으로 주는 mp3를 저장하기만 하면 된다. 물론 그렇게만 되면 참 좋겠으나 그렇지 않다. 대부분은 입력한 대로 음성 데이터가 잘 나오지만 이게 딥러닝 기반이라 그런지 가끔씩 위에서 보여준 것과 같이 이상한 데이터를 만들어 낸다.
그렇기에 만들고 들어보고 다시 만들고를 반복할 수밖에 없다. 심지어 모델이 tts-1
과 tts-1-hd
가 있는데 hd 모델은 1분에 3개씩 밖에 못 만든다. 3개 만들고 기다리고 3개 만들고 해야 한다. 이 문제를 해결하려면 전문 TTS API를 사용해야 하지만, 비용이 너무 높았다.
영상 템플릿 만들고, 코드 짜고, 운영하고를 3명이서 나눠서 하기로 한 프로젝트였다. 운영하는 사람이 위에서 설명한 것처럼 코드 돌려가면서 할 수는 없는 노릇이다. 그렇기에 이것을 돌릴 수 있는 툴이 필요했다. 그렇기에 슬랙봇을 만들기로 생각했다. 그러나 이 슬랙봇이 내가 생각한 그런 것이 아니었다.
슬랙봇을 통해 구현하려는 기능은 아래와 같다.
/new_quiz
슬래시 명령을 보내면 문제를 만들어서 보내준다.얼추 동작하게 만들기는 했었다. 그러나 그게 잘~ 제대로라고는 말 못 하겠다. 우선 진짜 한 땀 한 땀 노가다가 장난이 아니었다. 이게 완전히 고전적인 웹 MVC를 만드는 느낌이다. 요청을 받으면 슬랙 블록이라는 형식의 View를 만들어서 응답으로 보내주면 해당 메세지가 슬랙에 나오는 방식이다. 선택을 위한 모달을 만들어야 하는데 이것도 기존 메세지에 버튼을 누르면 요청이 보내지고 어떤 메세지를 통해서 들어왔는지 context를 파악해 modal view를 보내주면 된다. 이 모달이란 방법을 선택한 것도 그냥 메세지에 input 영역을 만들면 change가 발생하면 요청이 즉시 나가기에 한 번에 보낼 수 없어 찾다가 선택한 방법이다. 계속 핑퐁핑퐁이다. 단건 메세지를 보내는 거면 단순할 수 있으나 위에서 설명한 것도 같은 흐름이 있는 동작을 만들기 위해서는 영속성 데이터 스토어가 필수로 들어가게 되고 상태를 계속 추적해야만 한다. 그냥 노출만 슬랙으로 했을 뿐이지 그냥 App 만드는 것과 아니, 더 어려운 수준이다.
심지어 음성데이터를 내가 원하는 것처럼 이쁘게 보낼 수도 없다. 파일 업로드가 동시에 되지 않기 때문이다. 파일이 특정 메세지의 thread 형식으로 하나씩 올라가게 되는데 처음 보면 이게 도대체 뭐지 싶게 된다. 어떻게든 영상 제작까지 해서 YouTube에 업로드하는 것까지 자동화하긴 했다. 이걸 운영에 쓰기에는 현실적인 어려움이 있었다.
전용 앱을 개발하려 했지만, 이미 이전 단계에서 에너지를 많이 소모했고, 이 작업이 단순히 사람의 노동을 줄이는 수준에 그칠 것이라 판단하여 제작에 이르지 못했다. 이러한 이유로 전용 앱을 만드는 의욕을 잃게 되었다.
“운영! 내가 네가 쓸 툴 만들어 줄게! 기다려!”라고 약속한 뒤, 직접 콘솔을 통해 87개의 에피소드를 하나씩 제작하여 올리는 방식으로 진행하게 되었다. 그렇게 시즌 1은 마무리되었다.
처음엔 정말로 "자동화면 다 해결되겠지"라고 생각했었다. 하지만, 세상은 역시 내 생각처럼 쉽게 굴러가지는 않더라. 개발자라면 누구나 한 번쯤 "자동화가 가능해!"라고 외치지만, 막상 직접 해보면 그게 그렇게 간단한 일이 아니라는 걸 깨닫게 된다. 나도 그랬다. 물론 내가 부족한 점도 많았겠지만, 이 프로젝트는 생각만큼 쉽지 않았다.
함께했던 디자이너 친구가 프로젝트를 이어가겠다고 했지만, 결국 모든 과정을 보고는 "다른 방법을 찾아보자"고 하더라. 요즘 프롬프트 엔지니어링에 관심이 많다니, 그걸 공부해서 다음엔 더 나은 방식으로 다시 도전해보겠다는 말로 이번 시즌은 마무리되었다.
이렇게 길고 긴 글을 쓰고 보니, 나름대로 재미있는 여정이었다. 혹시 이 글을 읽고 궁금한 점이 생기신다면, 댓글로 남겨주시면 언제든지 답변해드리겠다.
날로먹는다기엔 과정이 심상치 않네요. 대단하십니다 👍