본문바로가기
페무리 (Femuri)
전국 페스티벌 통합 검색·추천 플랫폼
페무리 (Femuri)
전국 페스티벌 통합 검색·추천 플랫폼
페무리 (Femuri)
페무리 (Femuri)
전국 페스티벌 통합 검색·추천 플랫폼
  • Next.js 16
  • React 19
  • TypeScript
  • Tailwind CSS
  • Prisma
  • PostgreSQL
  • React Query
  • Project
    페무리 (Femuri)
  • Period
    2026.05 (약 3주)
  • Type
    Fullstack / 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 제거와 노이즈 오염 차단을 함께 잡으며, 최적화와 정확도가 분리된 과제가 아님을 배웠다.