페무리 (Femuri)
전국 페스티벌 통합 검색·추천 플랫폼페무리 (Femuri)
전국 페스티벌 통합 검색·추천 플랫폼
- Next.js 16
- React 19
- TypeScript
- Tailwind CSS
- Prisma
- PostgreSQL
- React Query
-
Project페무리 (Femuri)
-
Period2026.05 (약 3주)
-
TypeFullstack / Festival Discovery Platform
-
Role풀스택 개발 (Frontend + Backend + Data Pipeline)
- 프로젝트 유형
- Fullstack / 페스티벌 통합 검색·추천 플랫폼
- Frontend
- Next.js 16 (App Router), React 19, TypeScript, Tailwind CSS, TanStack React Query, Embla Carousel
- Backend
- Next.js API Routes, Prisma 7, PostgreSQL (Supabase)
- 데이터 수집
- KOPIS API + Web Scraping (Cheerio)
- 아티스트 프로필
- 나무위키 HTML 파싱 + Wikidata SPARQL + Wikipedia REST API
- 핵심 성과
- 축제 743·아티스트 3,464·라인업 연결 4,138·활성 232 규모의 멀티소스 멱등 파이프라인 · 자연어 질의 검색 · 규칙 기반 개인화 추천 · PWA
Project
Overview
전국의 페스티벌 정보를 한 곳에서 검색하고 발견할 수 있는 통합 플랫폼입니다.
KOPIS(공연예술통합전산망) API, 커뮤니티 사이트 스크래핑, 티켓 사이트 연동 등 다수의 외부 데이터 소스를 통합해 페스티벌을 자동 수집하고, 상태·월·지역 필터와 페스티벌·아티스트 통합 검색에 더해 자연어 질의 검색(말로 찾기)과 취향 기반 개인화 추천까지 제공하는 발견 플랫폼입니다.
Key
Features
• 말로 찾기 (자연어 질의 검색): 자유 문장에서 아티스트/장르/지역을 추출해 매칭 (키워드 추출 + 퍼지 매칭)
• 취향 기반 개인화 추천: 3단계 온보딩(장르·아티스트·닉네임) → 규칙 기반 겹침 스코어링 + 결정론적 그래프 발견
• Multi-Source Scraping Pipeline: KOPIS API + FestivalLife 스크래핑 + Interpark 티켓 연동
• Hybrid Search: 페스티벌명 + 아티스트명/별칭 통합 검색
• 3단계 필터링: 상태(진행중/예정/종료) × 월별 × 지역별 다중 필터
• 아티스트 프로필 자동 보강: 나무위키 → Wikidata → Wikipedia 순차 탐색
• HOT 페스티벌 캐러셀: 관리자 큐레이션 + Embla Carousel 자동 재생
• PWA 지원: Service Worker 캐시 전략 + Manifest로 모바일 앱 경험
Natural-Language
Search
말로 찾기 — “쏜애플 나오는 축제”, “힙합 좋아하는데 뭐 볼까” 같은 자유 문장을 입력하면 문장에서 아티스트·장르·지역을 추출해 매칭 페스티벌을 찾아줍니다. LLM 없이 키워드 추출 + 퍼지 매칭으로 구현해 결과가 결정론적이고, 매칭된 아티스트는 “🎯 OOO 출연” 태그로 근거를 함께 노출합니다.
Personalized
Onboarding
3단계 취향 온보딩(장르 다중선택 → 인기 아티스트 최대 3명 → 닉네임)으로 취향 프로필을 만들고, 규칙 기반 겹침 스코어링 + 결정론적 그래프 발견으로 페스티벌을 개인화 추천합니다. LLM·추천모델 없이도 설명 가능하고 재현 가능한 추천이 되도록 설계했습니다.
Challenge 1 | 무LLM 결정론 발견 — 개인화 추천 + 자연어 검색
문제: 취향 기반 추천과 자유 문장 검색을 넣어야 했지만, 학습 데이터가 없고 LLM/임베딩을 붙이면 결과가 불투명해 근거 설명·재현·디버깅이 어렵고 외부 호출 비용도 발생할 우려가 있었습니다.
판단: 페스티벌·아티스트·장르는 관계가 명시적인 도메인이라, 모델 없이 규칙 기반 스코어링 + 그래프 탐색만으로 충분하고 오히려 근거 반환·재현성·비용($0)에서 유리하다고 판단했습니다.
해결: 추천은 취향과 라인업의 겹침을 가중 스코어링(아티스트 10·장르 3)하고, 선택 아티스트에서 공동출연·밴드 멤버 그래프(밴드 142팀·평균 3.7명)를 1-hop 결정론 탐색해 롱테일 후보를 보강했습니다. 검색은 자유 문장에서 키워드를 추출해 이름·별칭 매칭(씨엔블루↔CNBLUE 한글·영문)으로 처리하고 TDD로 구현했습니다.
결과: 외부 AI 호출 0 · 비용 $0으로, 같은 입력에 같은 결과(재현 가능)와 매칭 근거 반환(설명 가능)을 갖춘 추천·검색을 확보했습니다.
Challenge 2 | 아티스트 매칭 — N+1 벌크 최적화 + 폴백·노이즈 필터
문제: 대형 라인업(60명+)을 순차 조회하면 라인업당 DB 왕복이 인원수만큼 발생하고, 스크래핑 텍스트에 공연장·JS/CSS/URL·날짜 등 노이즈가 섞여 오염된 아티스트가 생성됐습니다.
판단: 왕복은 벌크 일괄 조회로 없애고, 매칭은 단일 키로는 놓치니 이름→별칭→신규생성 3단계 폴백, 노이즈는 생성 전 규칙 필터로 차단하는 것이 성능·정확도 균형에 맞다고 판단했습니다.
해결: normalizer.ts에 3단계 벌크 매칭 + 15종 규칙 필터(공연장·JS/HTML/CSS·URL·날짜·블로그 UI 등)를 구현하고, 기존 오염 36건을 수동 정리한 뒤 근본 원인(파서)을 개선 태스크로 등록했습니다.
결과: DB 왕복 약 15–20× 감소(60→3~4쿼리), 라인업 오염 유입 차단.
Challenge 3 | 멱등 재스크랩으로 중복 생성 차단
문제: KOPIS·인터파크·페스티벌라이프 3개 소스를 매일 재스크랩하는데, 같은 축제가 소스마다 다른 이름·형식이라 그대로 넣으면 수집할 때마다 중복이 쌓이는 구조였습니다.
판단: 재수집은 피할 수 없으니 삽입 자체를 멱등(같은 걸 다시 넣어도 새로 생성 안 됨)하게 만들어야 한다고 판단하고, kopisId 우선·없으면 이름 매칭으로 upsert하기로 했습니다.
해결: kopisId/이름 매칭 기반 멱등 upsert 파이프라인을 구현해, 재스크랩 시 기존은 갱신하고 신규만 생성하도록 했습니다.
결과: 11,808건 재수집 → 신규 170건(1.5%)만 생성해 중복 축제 생성을 차단했습니다.
What I Learned | 배운 점
• 외부 데이터는 못 믿는다는 전제로 설계 — 스크래핑·오픈 API는 노이즈·중복·누락이 기본이라, ‘수집’보다 검증·정규화·멱등 upsert를 먼저 설계해야 파이프라인이 안정된다는 걸 체감했다.
• 결정론·설명가능을 기본값으로 — 추천·검색에 LLM/임베딩을 붙이는 대신 규칙 기반으로 만들어, 근거를 반환하고 같은 입력에 같은 결과를 보장하는 편이 디버깅·신뢰·비용($0)에서 유리했다.
• precision-over-recall은 의도적 선택 — 커버리지를 높이기보다 오인물 0(검증본만 저장)을 택해, 낮은 커버리지가 오히려 품질 신호가 되게 했다.
• 성능과 데이터 품질은 한 문제 — 아티스트 매칭에서 N+1 제거와 노이즈 오염 차단을 함께 잡으며, 최적화와 정확도가 분리된 과제가 아님을 배웠다.