
어두운 방 안, 모니터에서 뿜어져 나오는 푸르스름한 빛이 책상 위 커피 잔에 반사되어 일렁였다. 터미널 창에는 ‘Invalid Monster Attribute’라는 붉은색 에러 메시지가 세 줄이나 반복해서 깜빡이고 있었다. 정교하게 설계했다고 믿었던 몬스터 데이터 구조가 LLM의 예측 범위를 벗어났을 때 느껴지는 그 묘한 정적과 당혹감이 공기 중에 무겁게 내려앉았다.
단순한 표를 넘어선 AI 데이터 생성의 실험
처음 시작은 아주 소박했다. 이름, 설명, 체력(Health), 공격력(Attack), 방어력(Defense)이라는 다섯 가지 속성을 가진 몬스터 목록을 보여주는 간단한 웹 애플리케이션을 만드는 것이 목표였다. 하지만 단순히 고정된 JSON 파일을 불러오는 방식은 지루했다. 나는 LLM을 활용해 매번 새로운 특성과 능력을 갖춘 ‘랜덤 몬스터’를 생성하고, 이를 통해 AI가 어떻게 가상의 생태계를 구축하는지 관찰하고 싶었다.
단순히 “몬스터를 만들어줘”라고 요청하는 것만으로는 부족했다. AI가 일관성 있는 수치를 제시하게 하려면 구체적인 제약 조건이 필요했다. 예를 들어, 공격력이 높으면 방어력이 낮아지는 반비례 관계를 설정하거나, 특정 속성(불, 물, 바람)에 따라 체력의 기본 가중치를 다르게 부여하는 식의 프롬프트 엔지니어링이 필요했다. 이 과정에서 나는 AI가 데이터를 생성하는 방식이 인간이 상상하는 ‘창의성’보다는, 기존에 학습된 수많은 게임 데이터의 ‘평균값’을 추론하는 과정에 가깝다는 것을 깨달았다.
구현 과정: LLM 기반 몬스터 제너레이터 구축하기
이 프로젝트를 구현하기 위해 나는 Node.js 환경에서 OpenAI API를 연결한 간단한 서버를 구축했다. 몬스터의 속성을 정의하는 스키마를 명확히 하고, AI가 반환하는 응답이 항상 유효한 JSON 형식이 되도록 강제하는 것이 핵심이었다. 만약 AI가 설명 문구에 따옴표를 잘못 넣거나 쉼표를 빠뜨리면, 프론트엔드에서 JSON.parse() 에러가 발생하며 화면이 하얗게 변하는 ‘화이트아웃’ 현상을 겪어야 했다.
실제로 프로젝트를 세팅하고 실행하기 위해 사용한 기본적인 단계는 다음과 같다.
- 프로젝트 폴더를 생성하고
npm init -y를 통해 환경을 초기화한다. npm install openai dotenv express명령어로 필요한 라이브러리를 설치한다..env파일에OPENAI_API_KEY=your_api_key_here를 입력하여 인증 키를 설정한다.- AI에게 몬스터의 스탯 범위를 지정한 시스템 프롬프트를 전달하는 스크립트를 작성하고 실행한다.
서버를 구동하고 AI에게 몬스터 생성을 요청하는 핵심 로직은 다음과 같은 형태였다.
const response = await openai.chat.completions.create({
model: "gpt-4o",
messages: [
{ role: "system", content: "You are a monster generator. Return ONLY a JSON object with keys: name, description, health, attack, defense." },
{ role: "user", content: "Create a lava-based monster with high attack but low defense." }
],
response_format: { type: "json_object" }
});
console.log(JSON.parse(response.choices[0].message.content));
여기서 response_format: { type: "json_object" } 옵션을 사용하지 않았을 때, AI는 종종 “Here is your monster: { … }”와 같은 서술형 문장을 덧붙여 파싱 에러를 유발했다. 이 작은 옵션 하나가 프로그램의 안정성을 결정짓는 결정적인 차이를 만들었다.
AI의 진단과 인간 추론의 괴리
프로젝트의 하이라이트는 생성된 몬스터의 스탯을 보고 AI가 해당 몬스터의 ‘약점’과 ‘공략법’을 진단하게 만드는 것이었다. 예를 들어 체력이 10인데 공격력이 100인 몬스터를 보고 AI는 “유리 대포(Glass Cannon)형 몬스터이므로 빠른 선제공격이 필요하다”라고 정확히 진단했다. 하지만 흥미로운 점은, AI가 때때로 인간이 보기에 매우 불합리한 수치를 ‘균형 잡혔다’고 판단하는 순간들이 있었다는 것이다.
인간은 몬스터의 스탯을 볼 때 단순히 숫자의 합산이 아니라, 그 몬스터가 가진 ‘이미지’와 ‘맥락’을 함께 고려한다. 거대한 바위 괴물이 공격력이 1밖에 안 된다면 우리는 그것을 ‘오류’라고 생각하지만, AI는 주어진 수치 범위 내에서 논리적 모순이 없다면 이를 정상적인 개체로 받아들였다. 이는 AI가 지식을 습득하는 방식이 경험적 공감이 아니라 확률적 분포에 기반하고 있음을 보여주는 대목이었다.
결국 AI에게 몬스터를 진단하는 법을 가르치는 과정은, 역설적으로 내가 당연하게 생각했던 ‘상식’이라는 것이 얼마나 많은 암묵적 전제와 경험적 데이터의 결합인지를 깨닫게 해주었다. AI는 모르는 상태가 어떤 것인지 알지 못하기 때문에, 인간이 어디서 혼란을 느끼는지 공감하지 못한다. 다만 주어진 데이터의 패턴을 최적화할 뿐이다.
데이터의 무작위성이 가르쳐준 프로그래밍의 본질
이번 실험을 통해 배운 가장 큰 교훈은 AI를 단순한 ‘답변 기계’가 아니라 ‘랜덤 데이터 생성기’이자 ‘가설 검증 도구’로 활용했을 때의 효용성이다. 수동으로 100마리의 몬스터 데이터를 입력하는 대신, LLM을 통해 수천 가지의 변칙적인 케이스를 생성하고 이를 내 애플리케이션이 얼마나 견고하게 처리하는지 테스트할 수 있었다. 이는 일종의 Fuzz Testing과 유사한 효과를 내어, 예상치 못한 엣지 케이스(Edge Case)를 발견하는 데 큰 도움이 되었다.
다음에 도전해보고 싶은 것은 AI에게 단순한 스탯 진단을 넘어, 몬스터 간의 ‘상성 관계’를 스스로 정의하게 하고 이를 그래프 데이터베이스로 시각화하는 작업이다. AI가 정의한 생태계의 논리가 인간의 직관과 얼마나 일치하는지, 혹은 얼마나 기발하게 빗나가는지를 확인하는 과정은 분명 또 다른 지적 즐거움을 줄 것이다.
만약 여러분이 지금 진행 중인 프로젝트에서 데이터 부족으로 고민하고 있다면, AI에게 단순한 정답을 요구하는 대신 ‘창의적인 오답’이나 ‘변칙적인 데이터’를 만들어달라고 요청해 보는 것은 어떨까? 때로는 완벽한 정답보다 예측 불가능한 데이터가 시스템의 빈틈을 찾아내고 우리의 사고를 확장하는 더 좋은 도구가 될지도 모른다.