들어가며
최근에 처음으로 React 라이브러리 하나를 직접 만들어서 배포했습니다.
이름은 react-youtube-jukebox이고, React 앱에 YouTube 기반
플로팅 주크박스를 붙일 수 있게 만든 컴포넌트입니다.
처음부터 “라이브러리를 만들어야지” 하고 시작한 건 아니었습니다. 원래는 이 블로그 안에서 쓰던 플레이어 UI였는데, 계속 손보다 보니까 점점 특정 화면에 묶인 코드라기보다 따로 떼서 재사용할 수 있는 단위에 더 가깝다는 생각이 들었습니다.
그래서 이번에는 컴포넌트 하나를 그냥 옮기는 수준이 아니라, 앱 안에 있던 구현을 라이브러리로 다시 설계하는 과정을 처음부터 끝까지 해보게 됐습니다. 이 글에서는 왜 이걸 분리하게 됐는지, 분리하면서 어떤 걸 고민했는지, 그리고 첫 라이브러리를 만들면서 뭘 배웠는지 정리해보려고 합니다.
원래는 그냥 서비스 안의 컴포넌트였다
시작은 되게 단순했습니다. 페이지 한쪽에 붙어서 음악을 재생하고, 곡 정보를 보여주고, 필요하면 확장 패널을 열 수 있는 플레이어 컴포넌트가 하나 있었어요.
그런데 막상 뜯어보면 이 컴포넌트가 생각보다 많은 일을 하고 있었습니다.
- YouTube iframe API를 불러와서 플레이어를 붙여야 했고
- 재생/일시정지, 음소거, 볼륨 상태를 관리해야 했고
- 트랙이 끝나면 다음 곡으로 넘겨야 했고
- 화면 위치, 포털 렌더링, 확장 UI까지 같이 다뤄야 했습니다
처음에는 그냥 “블로그 안에서 잘 돌아가면 됐지” 정도로 생각했는데, 기능이 하나둘 붙고 나서 오히려 반대로 보이더라고요. 이건 앱 안에만 묶어두기엔 좀 아깝다는 생각이 들었습니다.
왜 굳이 라이브러리로 뺐나
제일 큰 이유는 재사용성이었습니다. 물론 이런 건 그냥 복붙해서 다른 프로젝트에 가져다 써도 됩니다. 당장은 그게 제일 빠를 수도 있고요.
근데 그렇게 가면 결국 프로젝트마다 조금씩 다른 버전이 생기고, 수정도 따로 들어가고, 나중에는 어느 쪽이 최신인지도 애매해집니다. 한번은 편한데 오래 가져가기는 별로 좋은 방식이 아니죠.
반대로 라이브러리로 분리하면 “이 컴포넌트는 뭘 받고 어떻게 동작하는가”를 공개된 API 기준으로 다시 정리하게 됩니다. 저한테는 그 과정이 꽤 중요했습니다. 단순히 파일 위치를 옮기는 게 아니라, 서비스 맥락에 묻어 있던 구현을 범용적인 인터페이스로 다시 만드는 일이었기 때문입니다.
첫 라이브러리라서 더 많이 배웠다
이번이 첫 라이브러리 작업이라 그런지, 막상 해보니까 생각보다 신경 쓸 게 많았습니다. 앱 안에서만 쓸 때는 그냥 넘어갔던 것들이, 패키지로 분리하는 순간 바로 고민거리가 되더라고요.
예를 들면 이런 것들이요.
- 어디까지를
props로 받고 어디까지를 내부 구현으로 숨길지 - 기본 스타일을 얼마나 제공할지
- 커스터마이징은 어느 정도까지 열어둘지
- 빈 배열이나 단일 트랙 같은 예외를 누가 책임질지
- 문서를 어느 정도까지 써야 처음 보는 사람도 바로 쓸 수 있을지
앱 내부 코드였으면 적당히 넘어갈 수도 있었던 부분인데, 라이브러리는 이런 게 그대로 사용성으로 이어졌습니다. 결국 좋은 라이브러리는 “기능이 된다”에서 끝나는 게 아니라, 처음 보는 사람도 이해하고 바로 붙일 수 있어야 한다는 걸 많이 느꼈습니다.
제일 먼저 정리한 건 책임이었다
분리하면서 가장 먼저 한 일은 책임을 나누는 거였습니다. 바깥에서 알아야 하는 것과 라이브러리 안에 숨겨야 하는 걸 구분해야 했습니다.
바깥으로 드러낸 건 최대한 단순하게 가져갔습니다.
- 트랙 목록
- 자동 재생 여부
- 플레이어 위치와 오프셋
- 테마와 크롬 프리셋
- 커스텀 expanded 패널 렌더링
반대로 플레이어 인스턴스 관리, 현재 곡 계산, 곡 종료 시 다음 곡 전환, 음소거/볼륨 동기화 같은 건 내부 책임으로 넣었습니다.
제가 원했던 사용 경험은 복잡한 게 아니었습니다. 쓰는 쪽에서는 그냥 트랙 넘기고
Jukebox 렌더링하면 되고, 그 아래에서 일어나는 상태 전이는
라이브러리가 맡는 구조를 만들고 싶었습니다.
기본 사용은 단순하게, 확장은 필요한 만큼만
라이브러리를 만들면서 제일 많이 고민한 건 “어디까지 열어둘까”였습니다. 너무 닫아두면 서비스에 맞게 쓰기 어렵고, 너무 열어두면 라이브러리라기보다 조립 키트처럼 느껴지거든요.
그래서 기본 사용은 최대한 단순하게 두고, 꼭 필요한 확장 지점만 열어두는 쪽으로 정리했습니다.
import { Jukebox } from "@react-youtube-jukebox/core";
const tracks = [
{ videoId: "yTg4v2Cnfyo", title: "Soul Below", artist: "Ljones" },
{ videoId: "s4MQku9Mkwc", title: "Something About Us", artist: "Daft Punk" },
];
export function Page() {
return <Jukebox tracks={tracks} position="bottom-center" />;
}
기본은 이 정도로 바로 쓸 수 있게 두고, 더 커스터마이징이 필요할 때만 expanded 패널을 교체할 수 있게 했습니다. 개인적으로는 이 정도가 제일 균형이 좋았습니다. 기능은 공유하되 표현은 어느 정도 열어둘 수 있는 구조였으니까요.
문서와 예제를 따로 만든 이유
이번 작업하면서 꽤 크게 느낀 것 중 하나는, 라이브러리는 코드만 있다고 끝나는 게 아니라는 점이었습니다. 오히려 배포하고 나서부터가 시작에 가까운 느낌이었습니다.
설치 방법, 기본 사용법, 테마 변경, 위치 프리셋, 커스텀 expanded 패널, 빈 트랙 같은 예외 케이스까지 직접 보여줘야 다른 사람도 실제로 써볼 수 있습니다.
그래서 docs 앱도 같이 붙였습니다. 저한테는 이 문서 작업이 단순한 부가 작업이 아니라, “이 패키지가 진짜 사용 가능한 상태인가?”를 확인하는 과정이기도 했습니다.
직접 예제를 만들다 보니까 부족한 API도 보였고, 설명이 어색한 부분도 눈에 들어왔고, 기본값이 정말 괜찮은지도 다시 보게 되더라고요. 결과적으로 문서를 쓰는 과정 자체가 라이브러리 품질을 올리는 데 꽤 도움이 됐습니다.
조금 소개해보면
이번에 만든 react-youtube-jukebox는 React 앱에 도킹형 YouTube
플레이어를 붙이고 싶을 때 바로 가져다 쓸 수 있도록 만든 라이브러리입니다.
- 플로팅 주크박스 형태로 렌더링할 수 있고
- 위치 프리셋과 오프셋을 조절할 수 있고
- 테마와 크롬 프리셋으로 분위기를 바꿀 수 있고
- 필요하면 expanded 패널도 직접 커스텀할 수 있습니다
“유튜브 음악 플레이어를 앱 안에 조금 더 제품처럼 붙이고 싶다”는 상황이라면 꽤 잘 맞을 거라고 생각합니다. iframe을 직접 붙이고 상태를 다루는 것보다 훨씬 덜 번거롭게 시작할 수 있게 만드는 게 목표였습니다.
이번 작업에서 배운 점
- 앱 안에서 잘 돌아가는 컴포넌트가 바로 좋은 라이브러리가 되는 건 아니다
- 라이브러리화의 핵심은 코드 이동보다 경계 재설계에 가깝다
- 기본 사용성이 좋아야 커스터마이징 포인트도 의미가 생긴다
- 예외 케이스를 패키지 안에서 잘 처리할수록 쓰는 사람 입장에서 편하다
- 문서를 쓰는 과정 자체가 API를 다시 다듬게 만든다
마무리
첫 라이브러리를 만들면서 제일 크게 느낀 건, 코드를 “공개 가능한 형태”로 정리하는 일이 생각보다 훨씬 많은 판단을 필요로 한다는 점이었습니다. 그냥 돌아가는 코드를 넘어서, 남이 써도 되는 코드로 바꾸는 과정이 따로 필요했습니다.
그래도 이 과정을 한번 겪고 나니까 서비스 안에서 만들던 컴포넌트를 보는 시선도 조금 달라졌습니다. 이게 정말 여기서만 쓰이는 코드인지, 아니면 하나의 제품 단위로 분리할 수 있는지 예전보다 더 자주 생각하게 됐습니다.
혹시 React 앱에 붙일 수 있는 YouTube 주크박스 컴포넌트를 찾고 있다면, 이번에
만든 react-youtube-jukebox도 한번 봐주시면 좋겠습니다. 저한테는
첫 라이브러리라 유난히 많이 배운 작업이었고, 그래서 더 애착이 가는 결과물이기도
합니다.