테이블링 백엔드 개발자가 GPT-4 Turbo 발표 직후 사이드 프로젝트로 리뷰 기반 맛집 추천 서비스를 만들어 본 기록이다. 23만 건의 리뷰를 ada v2 임베딩으로 바꿔 Elasticsearch 코사인 유사도로 검색했고, 초기 설계의 실패 네 가지를 하나씩 고쳐 나간 과정과 최종 API 비용 1.31달러까지 공개한다.
핵심 포인트- 최근 6개월·별점 4점 이상 리뷰 23만 건을 `text-embedding-ada-002` 로 1536차원 벡터로 만들어 매장 정보와 함께 Elasticsearch 에 넣었다.
- 매장별 리뷰를 평균 임베딩 하나로 압축한 게 결정적 실수였다. 수백~수천 건을 평균 내니 키워드와 무관하게 늘 같은 매장이 나왔다.
- 음식 1개 + 특징 2개 키워드의 평균 벡터로 찾으니 "초밥+친절한+맛있는" 에 숯불갈비집이 섞였고, 음식에 가중치 5배를 줘도 부족해 결국 음식과 키워드를 나눠 두 번 조회했다.
- 리뷰만으로 추천하면 "냉면" 검색에 고깃집이 뜨고 "애완 동반" 결과 20곳 중 실제 가능한 곳이 9곳뿐인 한계가 그대로 드러난다.
- 총 278,221회 호출·1,311만 토큰을 쓰고 비용은 1.31달러였다. ada v2 가 1k 토큰당 0.0001달러로 가장 싼 API 라 부담 없이 실험할 수 있었다.
상세 정리- 구성: 데이터 가공은 Django·Python·OpenAI API, 백엔드는 Node.js·TypeScript, 프론트는 React, 저장소는 Elasticsearch 와 MongoDB, 호스팅은 AWS EC2·Route53·ACM 을 썼다.
- Elasticsearch 를 고른 이유는 vector 타입 필드에 `cosineSimilarity` 검색이 가능하고 업무상 익숙했기 때문이다.
- 임베딩 변환은 `openai.embeddings.create(input=..., model='text-embedding-ada-002')` 결과를 `async_bulk` 로 색인하는 단순한 파이프라인이다.
- 사전 계산: 음식·특징 키워드를 각각 20여 개 준비해 임베딩으로 바꿔 MongoDB 에 넣고, 가능한 모든 키워드 조합의 조회 결과를 미리 저장해 호출마다 계산하지 않게 했다.
- 프론트엔드는 GPT-4 의 이미지 입력을 활용해 그리고 싶은 화면을 그림으로 넘겨 React 골격을 받고, "키워드는 최대 3개까지만 클릭 가능하게" 같은 요구를 덧붙여 다듬었다. GPT-3.5 때보다 에러가 확연히 적었다고 평한다.
- 문제 ①: 키워드 3개의 평균 벡터가 "맛있는" 쪽으로 끌려가 음식 종류가 무시됐다. 가중치 조정으로는 해결되지 않아 조회를 분리했다.
- 문제 ②: 매장별 평균 임베딩을 버리고 전체 리뷰 벡터를 그대로 검색하도록 바꾸자 결과의 다양성과 정확도가 눈에 띄게 올라갔다.
- 문제 ③: "애완 동반" 검색에 "강아지도 데려오고 싶어요" 같은 반대 의미 리뷰가 걸렸다. 매장 DB 에 동반 가능 여부 필드가 없어 근본 해결이 불가능해, 해당 277개 조합에는 매장 문의 안내 alert 를 띄우는 임시방편을 썼다.
- 문제 ④: 냉면 리뷰가 많은 고깃집이 상위에 오는 것은 리뷰 100% 기반 기획의 자연스러운 결과라 보고 그대로 뒀다. 메뉴 등 매장 데이터를 함께 쓰면 개선될 것으로 봤다.
- 회고: 리뷰가 악의적이거나 사실과 다르면 그대로 추천 품질에 반영되므로, 메뉴·특징을 종합해야 실서비스급이 된다고 정리한다.
- 디자인은 DALL·E 3 로 로고와 메인 이미지를 뽑았는데, 한국어 간판 텍스트 생성은 지원되지 않는다는 답변을 받았다.
- 비용 비교: GPT-4 Turbo 는 1k 토큰당 0.01달러로 ada 의 100배라 리뷰 종합 요약은 다음으로 미뤘고, GPT-3.5 Turbo(1k 토큰당 0.001달러)를 대안으로 제시한다.
왜 읽나임베딩 검색을 처음 붙여 보는 개발자에게 "평균 벡터로 뭉치면 안 된다" 는 실패와 실제 비용 감각을 동시에 준다.