본문 바로가기

사이드 프로젝트

노래방 일본 노래 검색 서비스 | 제 8부 - 수집한 데이터 정재하기 (2)(Gemini API 데이터 자동 생성 실전편)

 

안녕하세요, 1인 사업가와 개발자 여러분! 펭귄 뮤지엄입니다. 🐧

지난 포스팅에서 우리는 데이터베이스 스키마를 성공적으로 구축했습니다.

하지만 데이터가 없다면 잘 차려진 진수성찬도 그림의 떡이겠죠?

 

특히 제가 수집한 초기 데이터에는 생략된 가수 이름과 노래 제목, 그리고 새롭게 채워 넣어야 할 한국어 제목(koName)과 한국어 아티스트명(koArtistName) 필드라는 큰 숙제가 남아있었습니다.

수천, 수만 개의 데이터를 일일이 검색해서 채워 넣는 것, 상상만 해도 아찔합니다.

이 지루하고 반복적인 작업을 해결하기 위해, 우리는 똑똑한 조력자 Gemini API를 활용하기로 했습니다.

 

자, 그럼 지금부터 Gemini API를 이용해 어떻게 데이터를 자동으로 생성하고 풍성하게 만들었는지, 그 여정을 함께 떠나보실까요?

 


 

⚙️ Gemini API, 실전 사용을 위한 첫걸음

Gemini API를 사용하는 방법은 생각보다 간단합니다. 차근차근 따라 해 보세요!

 

1. Google Cloud 프로젝트 생성

먼저 Google Cloud Console에 접속하여 새 프로젝트를 만들어 줍니다.

프로젝트 이름(예: Gemini API)을 입력하고, 다른 설정은 기본값으로 두어도 무방합니다.

 

 

2. Gemini API 키 발급받기

 

다음으로 API 키를 발급받기 위해 Google AI Studio 사이트로 이동합니다.

방금 생성한 Google Cloud 프로젝트를 선택하면, 여러분만의 고유한 API 키가 생성됩니다.

 

⚠️ API 키, 절대 외부에 노출하지 마세요! 이 API 키는 여러분의 집 열쇠와 같습니다. 외부에 유출될 경우 다른 사람이 여러분의 사용량과 비용을 소진시킬 수 있으니, 안전한 곳에 복사하여 보관하고 코드에 직접 하드코딩하는 일이 없도록 각별히 주의해야 합니다. .env 파일을 활용하는 것을 적극 추천합니다!

 

꿀팁!🍯 Google Cloud를 처음 사용하는 경우, 90일 동안 사용 가능한 300달러 상당의 무료 크레딧이 제공되니 부담 없이 마음껏 테스트해볼 수 있습니다.

 

3. 파이썬(Python) 환경 설정

이제 API를 호출할 환경을 만들어 봅시다. 터미널을 열고 아래 명령어로 필요한 패키지를 설치합니다.

pip install google-genai

 

설치가 완료되었다면, 간단한 코드로 사용 준비가 되었는지 확인해 볼까요?

아래 파이썬 코드를 실행하여 현재 사용 가능한 Gemini 모델 리스트를 확인해 보세요.

import google.generativeai as genai
import os

# API 키 설정 (환경 변수 또는 직접 입력)
# 실제 프로젝트에서는 os.getenv("GOOGLE_API_KEY")와 같이 환경 변수를 사용하는 것이 안전합니다.
API_KEY = "여기에_발급받은_API_키를_입력하세요"
genai.configure(api_key=API_KEY)

# 콘텐츠 생성을 지원하는 모델 목록 출력
print("✅ 사용 가능한 Gemini 모델 목록:")
for m in genai.list_models():
  if 'generateContent' in m.supported_generation_methods:
    print(m.name)

 

코드를 실행하면 models/gemini-1.5-flash-latest, models/gemini-1.5-pro-latest 등 다양한 모델명이 출력될 겁니다.

저는 이번 작업에서 빠르고 효율적인 gemini-1.5-flash-latest 모델을 사용하기로 결정했습니다.

 


 

✍️ AI의 능력을 100% 끌어내는 '프롬프트' 설계하기

준비는 끝났습니다! 이제 가장 중요한 단계, 바로 AI에게 작업을 시킬 '명령어(프롬프트)'를 설계하는 일입니다.

프롬프트가 얼마나 구체적이고 명확한지에 따라 결과물의 품질이 하늘과 땅 차이로 달라집니다.

저는 Gemini 웹사이트에서 여러 번의 테스트를 거쳐, 다음과 같이 가장 좋은 결과를 보여준 프롬프트를 완성했습니다.

 

JS 코드로 된 배열 노래 데이터를 제공해드리겠습니다. 데이터 형식은 다음과 같습니다.

export const song_list = [
  {
    "kySongNumber": 75951, // 노래방 번호
    "jpName": "#君と僕とが出逢った日", //노래 제목 (일본어)
    "jpArtistName": "舟津真翔" // 가수 이름 (일본어)
  },
  ...
]

여기서 jpName과 jpArtistName 데이터가 중요합니다.
위 두 데이터를 가지고 분석하여 아래와 같은 형식의 완전한 데이터로 만들어주세요.

export const song_list = [
  {
    "kySongNumber": 75951, // 노래방 번호
    "jpName": "#君と僕とが出逢った日", // 노래 제목 (일본어)
    "koName": "#너와 내가 만난 날", // 노래 제목 (한국어)
    "jpArtistName": "舟津真翔", // 가수 이름 (일본어)
    "koArtistName": "후나츠 마나토" // 가수 이름 (한국어)
  },
  ...
];

만약 .. 으로 생략되어 있는 노래 제목이나 가수 이름이 있다면, 반드시 웹 검색을 통해 모든 데이터를 빠짐없이 채워주세요.
그리고, 아래 표기 원칙을 철저히 지켜주세요.

- koArtistName 데이터 작성 시 요구사항
  - 먼저 한국 내 음원 서비스 플랫폼의 한국어 표기(한국어 데이터 표기)를 우선 사용합니다.
  - 만약 음원 서비스 플랫폼의 한국어 표기가 없다면 유튜브의 해당 음원 한국어 표기를 사용합니다.
  - 만약 유튜브에 적합한 데이터를 찾을 수 없는 경우 공식 한국어 표기(정식 번역, 국내 공식 사이트, 방송·출판물 표기 등)를 사용합니다.
  - 공식 번역이 없는 경우, 국립국어원 외래어 표기법, 위키백과, 주요 미디어의 통용 표기를 참고합니다.
- koName 데이터 작성 시 요구사항
  - 먼저 한국 내 음원 서비스 플랫폼의 한국어 표기(한국어 데이터 표기)를 우선 사용합니다.
  - 작품명이 있을 경우 국내 외 OTT 서비스(왓챠, 넷플릭스, 티빙 등)의 한국어 표기를 사용합니다.
  - 만약 음원 서비스 플랫폼이나 국내 외 OTT 서비스에 한국어 표기가 없다면 유튜브의 해당 음원 한국어 표기를 사용합니다.
  - 만약 유튜브에 적합한 데이터를 찾을 수 없는 경우 공식 한국어 표기(정식 번역, 국내 공식 사이트, 방송·출판물 표기 등)를 사용합니다.
  - 공식 번역이 없는 경우, 국립국어원 외래어 표기법, 위키백과, 주요 미디어의 통용 표기를 참고합니다.
- 일본어 데이터 작성 시(jpName, jpArtistName)
  - 먼저 한국 내 음원 서비스 플랫폼의 일본어 표기(일본어 데이터 표기)를 우선 사용합니다.
  - 만약 음원 서비스 플랫폼의 일본어 표기가 없다면 유튜브의 해당 음원 일본어 표기를 사용합니다.
  - 만약 유튜브에 적합한 데이터를 찾을 수 없는 경우 일본 공식 표기(원어 표기, 일본 현지 공식 사이트, 음반/애니/방송/출판물의 공식 표기 등)를 사용합니다
  - 일본어 인명, 곡명, 작품명 등은 일본 현지에서 쓰는 표기를 그대로 따릅니다.

이제부터 JS 코드로 된 배열 노래 데이터를 제공해드리겠습니다.

---

<여기에 실제 노래 배열 데이터 입력>

 

 

 

이 프롬프트의 핵심은 '기대하는 결과물의 명확한 형식 제시''데이터를 채우기 위한 구체적인 규칙과 우선순위 명시'입니다.

이렇게 해야 AI가 헤매지 않고 우리가 원하는 결과물을 정확하게 만들어낼 수 있습니다.

 


 

🐍 파이썬 코드로 데이터 생성 자동화 시스템 구축하기

이제 완성된 프롬프트를 파이썬 코드에 녹여내어, 수많은 데이터 파일을 자동으로 처리하는 시스템을 만들어 봅시다.

1. 대용량 데이터, 잘게 쪼개기 (Chunking)

한 번에 너무 많은 데이터를 AI에게 요청하면 API 제한에 걸리거나 응답이 오지 않을 수 있습니다.

그래서 저는 전체 데이터를 20개씩 작은 묶음(Chunk)으로 쪼개는 전략을 사용했습니다.

아래는 JSON 파일을 읽어 20개씩 쪼개어 여러 개의 JS 파일로 만들어주는 헬퍼(helper) 스크립트입니다.

# 파일명: 1_chunk_data.py
import json
import os

def create_js_song_files(input_filename="ky_songs.json", output_folder="input_chunks", chunk_size=20):
    if not os.path.exists(output_folder):
        os.makedirs(output_folder)
        print(f"📂 '{output_folder}' 폴더를 새로 생성했습니다.")

    with open(input_filename, 'r', encoding='utf-8') as f:
        all_songs = json.load(f)
        print(f"✅ 총 {len(all_songs)}개의 노래 데이터를 읽었습니다.")

    file_count = 0
    for i in range(0, len(all_songs), chunk_size):
        chunk = all_songs[i:i + chunk_size]
        file_count += 1
        js_filename = os.path.join(output_folder, f"chunk_{file_count}.js")
        
        # JavaScript 배열 형식으로 포맷팅
        js_content = f"export const song_list = {json.dumps(chunk, indent=2, ensure_ascii=False)};"
        
        with open(js_filename, 'w', encoding='utf-8') as f:
            f.write(js_content)
            
    print(f"✨ 총 {file_count}개의 JS 파일이 '{output_folder}' 폴더에 성공적으로 생성되었습니다.")

if __name__ == '__main__':
    create_js_song_files()

 

2. Gemini API 호출하여 데이터 변환하기

이제 잘게 쪼개진 파일들을 하나씩 읽어 Gemini에게 보내고, 변환된 결과를 다시 파일로 저장하는 메인 스크립트를 작성합니다.

# 파일명: 2_process_with_gemini.py
import google.generativeai as genai
import os
import re
from tqdm import tqdm # 진행 상황을 예쁘게 보여주는 라이브러리

# ... (API 키 설정 및 모델 선택 부분은 위와 동일) ...

def request_song_data_to_gemini(js_file_content: str):
    # 위에서 설계한 프롬프트를 여기에 그대로 넣습니다.
    # f-string을 사용하여 파일 내용을 프롬프트에 동적으로 삽입합니다.
    prompt = f"""
    (앞서 설계한 긴 프롬프트를 여기에 붙여넣기)

    ---
    {js_file_content}
    ---
    """
    
    try:
        model = genai.GenerativeModel('gemini-1.5-flash-latest')
        response = model.generate_content(prompt)
        
        # AI 응답에서 JavaScript 코드 블록만 깔끔하게 추출
        match = re.search(r'```javascript\n(.*)\n```', response.text, re.DOTALL)
        if match:
            return match.group(1).strip()
        else:
            return response.text.strip()
    except Exception as e:
        print(f"🚨 Gemini API 요청 중 오류 발생: {e}")
        return None

if __name__ == '__main__':
    input_directory = "input_chunks"
    output_directory = "output_results"
    
    if not os.path.exists(output_directory):
        os.makedirs(output_directory)

    # 입력 폴더의 모든 파일을 순회하며 작업 수행
    for filename in tqdm(os.listdir(input_directory), desc="전체 진행률"):
        if filename.endswith(".js"):
            filepath = os.path.join(input_directory, filename)
            
            with open(filepath, 'r', encoding='utf-8') as f:
                content = f.read()

            processed_content = request_song_data_to_gemini(content)
            
            if processed_content:
                output_filepath = os.path.join(output_directory, filename)
                with open(output_filepath, 'w', encoding='utf-8') as f:
                    f.write(processed_content)
                tqdm.write(f"  ✅ 성공: '{filename}' 처리 완료!")
            else:
                tqdm.write(f"  🚨 실패: '{filename}' 처리 중 오류 발생.")
                
    print("\n\n✨ 모든 작업 완료! ✨")

 

이렇게 스크립트를 실행하면, tqdm 라이브러리 덕분에 전체 진행 상황을 한눈에 보며 커피 한잔의 여유를 즐길 수 있습니다. ☕️

 

 

 

 

 


 

✨ 결과 확인 및 다음 과제

모든 과정이 끝나고 생성된 데이터 파일을 열어보면, 비록 100% 완벽하다고 할 순 없지만 놀랍도록 정확하게 데이터가 채워진 것을 볼 수 있습니다.

일부 어색한 번역이나 잘못된 정보는 사람의 손으로 최종 검수하는 과정이 필요하지만, 수작업에 비하면 그야말로 어마어마한 시간과 노력을 절약한 셈이죠!

 

이로써 우리는 생략된 이름과 제목, 그리고 비어있던 한국어 필드 문제를 성공적으로 해결했습니다.

"이제 정말 끝인가요?" 하고 물으신다면... 아쉽지만 아직입니다. 😅

현재 데이터는 TJ미디어와 KY(금영) 노래방 데이터가 분리된 상태입니다.

같은 노래라도 두 개의 데이터로 존재하고 있는 것이죠.

서비스의 완성도를 높이려면, 이 두 데이터를 하나로 합치는 작업이 반드시 필요합니다.

그럼 이 흩어진 데이터를 어떻게 지혜롭게 합칠 수 있을까요? 이 이야기는 다음 포스팅에서 자세히 다루도록 하겠습니다.

다음 편, 중복 데이터 병합(Merging) 편도 많이 기대해주세요!