안녕하세요, 1인 사업가와 개발자 여러분! 펭귄 뮤지엄입니다. 🐧
지난 포스팅까지의 대장정을 통해, 우리는 금영(KY)과 TJ 노래방의 방대한 데이터를 모두 수집하는 데 성공했습니다.
하지만 이 날것의 데이터(Raw Data)를 그대로 데이터베이스에 넣을 수는 없습니다.
지금부터는 프로젝트의 심장을 만드는 과정, 바로 '데이터 정제(Data Cleansing)'를 시작해야 합니다.
솔직히 이 과정은 지루하고, 귀찮고, 끝이 보이지 않는 터널처럼 느껴지기도 합니다.
하지만 이 궂은 작업을 거쳐야만 비로소 우리가 원하는 서비스를 개발할 수 있는 단단한 뼈대가 만들어집니다.
여기까지 함께 달려와 주신 여러분도 같은 마음이시겠죠?
자, 심호흡 한번 하고 꾹 참고 함께 나아가 봅시다!
💪 첫 번째 관문: 파이썬으로 중복 데이터 깔끔하게 정리하기
우리가 수집한 데이터는 말 그대로 '날것'입니다. 여기에는 중복된 값도 있고, 올바르지 않은 데이터도 섞여 있습니다.
이 불순물들을 제거하는 것이 첫 번째 임무입니다.
가장 먼저 손봐야 할 부분은 '중복 데이터'입니다.
저희 서비스에서 '노래방 번호'는 각 곡을 식별하는 유일무이한 값(Unique Key)이 되어야 합니다.
따라서, 노래방 번호를 기준으로 중복된 데이터는 모두 제거하도록 하겠습니다.
이걸 하나하나 눈으로 보고 손으로 지우냐고요? 당연히 아닙니다!
그런 비효율적인 작업은 우리의 소중한 시간과 노력을 좀먹을 뿐입니다. 우리는 똑똑하게 파이썬(Python)*을 활용할 겁니다.
이러한 파이썬 코드는 Gemini에게 데이터 샘플을 보여주며 "이 데이터에서 특정 컬럼(노래방 번호)을 기준으로 중복된 값을 제거하는 코드를 만들어줘"라고 요청하면 아주 훌륭하게 만들어 줍니다.
그것마저 귀찮으신 분들을 위해, 제가 사용한 코드를 아래에 공유해 드립니다.
import json
def deduplicate_songs(input_filename='ky_extract_html.json', output_filename='ky_raw.json'):
"""
ky.json 파일을 읽어 중복된 노래를 제거하고 ky_raw.json으로 저장합니다.
중복 기준: kySongNumber
우선순위: s_value 리스트의 순서가 빠른 것을 남김
"""
# s_value 우선순위 정의 (이전 스크립트와 동일)
s_values_order = [
"あ", "ぁ", "か", "が", "さ", "ざ", "た", "だ", "な", "は", "ば", "ぱ", "ま", "や", "ゃ", "ら", "わ", "ん",
"い", "ぃ", "き", "ぎ", "し", "じ", "ち", "ぢ", "に", "ひ", "び", "ぴ", "み", "り",
"う", "ぅ", "く", "ぐ", "す", "ず", "つ", "づ", "ぬ", "ふ", "ぶ", "ぷ", "む", "ゆ", "ゅ", "る", "っ",
"え", "ぇ", "け", "げ", "せ", "ぜ", "て", "で", "ね", "へ", "べ", "ぺ", "め", "れ",
"お", "ぉ", "こ", "ご", "そ", "ぞ", "と", "ど", "の", "ほ", "ぼ", "ぽ", "も", "よ", "ょ", "ろ", "を",
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V",
"W", "X", "Y", "Z",
"0"
]
try:
with open(input_filename, 'r', encoding='utf-8') as f:
original_data = json.load(f)
print(f"'{input_filename}' 파일을 성공적으로 불러왔습니다.")
except FileNotFoundError:
print(f"오류: '{input_filename}'을 찾을 수 없습니다. 파일이 현재 디렉토리에 있는지 확인해주세요.")
return
except json.JSONDecodeError:
print(f"오류: '{input_filename}' 파일이 올바른 JSON 형식이 아닙니다.")
return
deduplicated_data = {}
seen_song_numbers = set()
original_song_count = 0
# s_value 우선순위 순서대로 처리
for s_value in s_values_order:
if s_value in original_data:
song_list = original_data[s_value]
original_song_count += len(song_list)
unique_songs_in_list = []
for song in song_list:
song_number = song.get("kySongNumber")
# 이미 처리된 곡 번호가 아니면 추가
if song_number not in seen_song_numbers:
unique_songs_in_list.append(song)
seen_song_numbers.add(song_number)
# 해당 s_value에 남은 곡이 있으면 최종 데이터에 추가
if unique_songs_in_list:
deduplicated_data[s_value] = unique_songs_in_list
final_song_count = len(seen_song_numbers)
# 결과를 새 JSON 파일에 저장
try:
with open(output_filename, 'w', encoding='utf-8') as f:
json.dump(deduplicated_data, f, ensure_ascii=False, indent=2)
print("\n--- 작업 완료 ---")
print(f"원본 데이터의 총 노래 수: {original_song_count}개")
print(f"중복 제거 후 총 노래 수: {final_song_count}개")
print(f"제거된 중복 데이터 수: {original_song_count - final_song_count}개")
print(f"결과가 '{output_filename}' 파일에 성공적으로 저장되었습니다. ✅")
except IOError as e:
print(f"오류: '{output_filename}' 파일 저장 중 문제가 발생했습니다: {e}")
if __name__ == '__main__':
deduplicate_songs()
이렇게 각 노래방 데이터의 중복 값을 시원하게 정리했습니다.
이제 우리가 설계했던 ERD에 맞게 데이터를 채워 넣을 차례인데...
여기서 진짜 문제가 발생합니다.
🤯 진짜 문제는 지금부터: '…'으로 잘린 제목과 텅 빈 한국어 필드
금영 노래방 데이터를 자세히 보신 분들이라면 이미 눈치채셨을지도 모릅니다.
문제 1: '...'으로 잘려버린 노래 제목
수집한 데이터 중 일부 노래 제목이 위 사진처럼 ...으로 생략되어 있습니다.
이걸 제가 일일이 구글에 검색해서 원제를 찾아 수정해야 할까요? 전체 JSON 파일이 12만 라인이 넘고, 수만 곡의 데이터가 있다는 사실을 고려하면, 이건 한 사람이 할 수 있는 작업이 아닙니다.
문제 2: 비어있는 한국어 필드
저희가 설계한 스키마를 다시 한번 살펴보시죠.
여기엔 koName(한국어 노래 제목), koArtistName(한국어 아티스트명)이라는 필드가 존재합니다.
하지만 우리가 수집한 데이터는 모두 일본어 원문입니다.
사용자의 편의를 위해 이 필드들을 채워 넣어야 하는데, 이 역시 수동으로 작업하는 것은 불가능에 가깝습니다.
그래서 저는 이 모든 문제를 해결할 열쇠로 'Gemini AI'를 떠올렸습니다.
잘린 제목을 복원하고, 일본어를 한국어로 번역 및 로마자로 변환하는 작업을 요청하면 되겠다고 판단했죠.
하지만, 대용량의 JSON 파일을 Gemini 웹사이트에 통째로 던져주니... AI가 감당하지 못하고 "데이터가 너무 많아서 작업을 처리할 수 없어요"라며 그대로 뻗어버렸습니다.
총체적 난국이었습니다. 이걸 대체 어떻게 해결해야 할까요?
⏳ 시간과의 싸움: 17시간의 노가다 vs 합리적인 투자
Gemini에게 이 문제를 어떻게 해결할지 물어보니, 세 가지 방법을 제안하더군요.
- 데이터를 작은 단위로 쪼개서 보내기
- Gemini API 활용하기
- 구글 번역 API 사용하기
'구글 번역기'는 미묘한 뉘앙스나 고유명사에서 이상한 결과물을 낼 수 있을 것 같아 제외했고, 'API 사용'은 비용이 발생할 것 같아 일단 보류했습니다.
그래서 남은 '데이터 쪼개서 보내기'를 시도해 봤습니다.
한 번에 20곡씩 데이터를 쪼개서 요청하니, 생각보다 훌륭하게 결과물을 만들어주더군요! '아, 그냥 이대로 계속하면 되겠다!' 싶어서 100번 정도 반복 요청을 했는데... 정신을 차려보니 3시간이 훌쩍 지나 있었습니다.
간단한 산수를 해볼까요?
- 전체 노래 데이터: 최소 20,000곡 이상
- 한 번에 처리: 20곡
- 필요한 총 요청 횟수: 1,000번 이상
- 1회 요청 당 소요 시간: 약 1분 (복사, 붙여넣기, 결과 확인 포함)
- 예상 소요 시간: 최소 1,000분 = 약 17시간
17시간 동안 컴퓨터 앞에서 똑같은 작업을 반복해야 한다는 계산이 나오자 아찔해졌습니다.
이건 그 악명 높은 '노가다' 반장님도 손사래를 치며 도망갈 수준의 시간과 노동력입니다.
특히 저희처럼 시간이 금인 1인 사업가와 개발자에게는 절대 선택할 수 없는 길이었죠.
결국, 처음에는 외면했던 'Gemini API'를 진지하게 다시 들여다보게 되었습니다.
돈이 들어간다는 이유로 피하고 있었는데, 17시간의 내 노동력과 비교하니 과연 비싼 게 맞을까 싶더군요.
그래서 Gemini API의 요금표를 찾아봤습니다.
Gemini 2.5 Flash 기준 요금표 (100만 토큰 당)
입력 (Input) | 무료 | $0.35 |
출력 (Output) | 무료 | $0.70 |
Gemini 2.5 Pro 기준 요금표 (100만 토큰 당)
입력 (Input) | 사용 불가 | $2.50 |
출력 (Output) | 사용 불가 | $10.50 |
(참고: 2025년 6월 기준 요금이며, 최신 정보는 공식 홈페이지를 확인하세요.)
생각보다... 훨씬 저렴하지 않나요? 🤯
17시간의 끔찍한 노동과 이 비용을 저울질해보니, 더 이상 고민할 필요가 없었습니다.
API 사용료는 제 시간과 정신 건강을 위한 가장 합리적인 투자였던 겁니다.
그래서 저는, 속 편하게 Gemini API를 사용하기로 마음먹었습니다!
💡 마무리하며: 이제 진짜 시작입니다!
자, 길고 길었던 고민의 시간이 끝나고 드디어 해결의 실마리를 찾았습니다.
그렇다면 과연 저는 이 Gemini API를 활용해서 수만 곡의 데이터를 어떻게 자동으로, 그리고 똑똑하게 정제했을까요?
그 흥미진진한 과정은 다음 포스팅에서 자세히 공개하도록 하겠습니다! 다음 편을 기대해주세요!
'사이드 프로젝트' 카테고리의 다른 글
노래방 일본 노래 검색 서비스 | 제 9부 - 수집한 데이터 정재하기 (3)(TJ와 KY, 분열된 노래 데이터를 하나로 합치기 ) (4) | 2025.06.11 |
---|---|
노래방 일본 노래 검색 서비스 | 제 8부 - 수집한 데이터 정재하기 (2)(Gemini API 데이터 자동 생성 실전편) (5) | 2025.06.10 |
노래방 일본 노래 검색 서비스 | 제 6부 - Octoparse로 노래방 노래 데이터 쉽게 추출하기 (0) | 2025.06.08 |
노래방 일본 노래 검색 서비스 | 제 5부 - 크롤링으로 노래방 노래 데이터 추출하기 (2) | 2025.06.07 |
완벽함 대신 '실행'을 택하다: 노코드 툴로 사이드 프로젝트 빠르게 시작하기 (1) | 2025.06.06 |