PROJECT INFO
소개
유튜브 임베딩을 활용해 별도의 음원 라이선스 없이 음악을 스트리밍할 수 있는 웹 서비스.
11일이라는 짧은 기간 안에 기획부터 배포까지 완료한 개인 프로젝트.
기능
Naver VIBE 차트·신곡·검색 데이터 제공, 유튜브 자동 매핑 재생, 개인 재생목록 저장.
→ 별도 라이선스 없이 일반 음악 스트리밍 서비스와 동일한 UX 제공.
주요 기술
2개의 서버(메인, 유튜브 스크래핑)를 역할별로 분리 설계.
브릿지 역할의 FastAPI 서버를 통해 유튜브 스크래핑 결과를 웹으로 전달.
CONTRIBUTIONS
react-player 기반 음악 플레이어 구현
유튜브 영상을 오디오처럼 재생할 수 있는 커스텀 뮤직 플레이어 구현
react-player 라이브러리를 활용하여 재생, 정지, 볼륨 조절, 재생 바 탐색 등 음악 플레이어에 필요한 컨트롤 기능을 구현하였습니다.
영상 UI는 숨기고 오디오만 출력되도록 처리하여 일반 음악 스트리밍 앱과 동일한 청취 경험을 제공하였습니다.
재생이 끝나면 재생목록의 다음 곡으로 자동 전환되는 로직을 구현하여 연속 재생 흐름을 완성하였습니다.
Express 서버 MVC 패턴 도입
기존 app.js 단일 파일 방식에서 Model / Controller / Service 3계층 구조로 리팩토링
Mongoose Schema로 모델을 정의하고, express.Router()로 라우터를 분리하였습니다.
핸들러 함수를 서비스 레이어로 분리하여 비즈니스 로직과 라우팅을 분리하였습니다.
유지보수, 공통 로직 재사용 등의 효율성을 향상시킬 수 있었습니다.
MongoDB Atlas 기반 유저 재생목록 데이터 모델 설계
RDB에서 여러 테이블과 조인이 필요한 구조를 중첩 객체로 단일 컬렉션에 모델링
아티스트 목록, videoId 목록 등 일대다 관계를 별도 테이블 없이 직관적으로 표현하였습니다.
쿼리 복잡도를 낮추고, MongoDB Atlas를 통해 로컬 서버 없이 원격 DB를 운용하는 방식을 도입하였습니다.
JSON과 동일한 구조로 DB를 다룰 수 있어 웹 개발자의 관점에서 직관적으로 스키마를 설계할 수 있었습니다.
로컬 DB 서버 없이 Atlas 원격 클러스터에 연결하여 인프라 부담 없이 DB를 운용하는 방식을 처음으로 도입하였습니다.
API Key 없는 유튜브 검색 중계 서버 구현
Google API Key 없이 스크래핑만으로 유튜브 검색이 가능한 innertube 모듈 사용
외부 유료 API 없이도 유튜브 스크래핑 기능을 안정적으로 서비스에 통합할 수 있었습니다.
곡 재생 요청이 들어오면 제목과 아티스트명을 받아 유튜브 스크래핑을 수행하고, videoId 목록을 클라이언트로 반환하는 흐름을 설계하였습니다.
소규모 서버임에도 Model/Service 디렉터리를 분리하고 컨트롤러는 main.py에 두는 구조를 유지하여 코드 일관성을 지켰습니다.
TROUBLE SHOOTINGS
유튜브 임베딩 차단 영상 재생 실패 문제 해결
임베딩 실패 시 후보 videoId를 사용하도록 구현하여 문제를 해결하였습니다.
일부 영상이 외부 임베딩이 차단되어 react-player가 영상을 로드하지 못하는 문제가 발생하였습니다.
공식 문서와 직접 실험을 통해 onError 콜백이 임베딩 차단 시 에러 코드 150을 반환함을 확인하였습니다.
150 수신 시 다음 videoId로 자동 전환하는 폴백 로직을 구현하여 대부분의 곡을 끊김없이 재생할 수 있도록 처리하였습니다.
RapidAPI 무료 크레딧 한계로 인한 외부 API 전면 교체
직접 Youtube 데이터 스크래핑 서버를 구축하여 문제를 해결하였습니다.
초기에는 써드파티 API에 의존 했으나, 무료 크레딧 소진으로 서비스가 간헐적으로 중단되는 문제가 있었습니다.
유료 전환 대신 VIBE API 직접 연동과 innertube 기반 유튜브 스크래핑 서버를 직접 구현하는 방향으로 전환하였습니다.
외부 API 비용을 완전히 제거하면서도 동일한 기능을 안정적으로 운용할 수 있게 되었습니다.
innertube 비정형 응답 데이터 파싱 오류 해결
리스트 컴프리헨션을 사용하여 필요한 데이터를 구분하도록 구현하여 문제를 해결하였습니다.
innertube 검색 결과는 영상 / 채널 / 광고 등 여러 타입이 혼재된 배열로 반환되어 videoId 추출 시 간혹 KeyError가 발생하였습니다.
리스트 컴프리헨션으로 videoRenderer 필드가 있는 영상 객체만 필터링하는 로직을 작성하여 어떤 검색어에도 안정적으로 videoId를 추출할 수 있도록 개선하였습니다.
