dj-analyzer.js)로 킥·비트·카메라비트를 구분해 영화 같은 전환을 구현한다. (저장소: XxHuberrr/Mineradio · JavaScript + Electron · v1.1.1 · GPL-3.0 · TrendShift 일간 1위)이 레포가 무엇을 하는 물건인가.
Mineradio는 음악 플레이어이지만 단순 플레이어가 아니다. 오디오 신호를 실시간으로 해부해 킥 드럼이 치는 순간 카메라가 흔들리고, 하이햇이 쏟아질 때 파티클이 폭발한다. 화면이 곧 공연장이다.
기술적으로는 Electron 껍질 안에 Node.js HTTP 서버 + 대규모 단일 HTML 앱이 돌아가는 구조다. UI 전체(1.3MB)가 public/index.html 하나에 담겨 있으며, Three.js 3D 렌더러와 GSAP 애니메이션이 CSS 글래스모피즘(유리 질감) 위에 올라가 있다.
일반 음악 플레이어와 다른 점은 시각 연출이 음악에 종속된다는 것이다. Spotify나 Foobar2000에서는 앨범아트가 정적으로 표시되지만, Mineradio에서는 오디오 파형을 실시간으로 분석해 파티클 밀도·카메라 각도·가사 무대 효과가 비트에 맞춰 변한다. 마치 뮤직비디오를 실시간으로 생성하는 것과 같다.
트렌딩 이유 · 경쟁 제품 대비 장점.
Mineradio가 TrendShift 일간 1위에 오른 이유는 단순히 "예쁜 플레이어"가 아니라, 음악과 시각이 동기화되는 몰입 경험이라는 새로운 카테고리를 제시했기 때문이다. 2024~2025년 사이 생성 AI 도구와 로우코드로 무장한 개인 개발자가 창의적인 데스크탑 앱을 손쉽게 공개하는 트렌드가 이어졌고, Mineradio는 그 흐름 속에서 "공들인 시각 연출"로 차별화했다.
중국어권 커뮤니티에서 먼저 확산된 것도 특징이다. 네이버 클라우드 뮤직 API와 QQ 음악을 직접 지원하기 때문에, 스트리밍 서비스 접근이 까다로운 환경에서 자신의 계정으로 자유롭게 음악을 즐길 수 있는 대안이 된다. 여기에 날씨 연동 라디오 기능이 더해지면서 "오늘 날씨에 어울리는 음악"이라는 감성적 큐레이션 경험을 제공한다.
| 비교 항목 | 일반 플레이어 | Mineradio |
|---|---|---|
| 시각 효과 | 앨범아트 정적 표시 또는 단순 파형 바 | Three.js 파티클이 비트에 실시간 반응 |
| 카메라 연출 | 없음 | 킥·비트 분석 후 영화 컷처럼 카메라 이동 |
| 날씨 연동 | 없음 | Open-Meteo API로 날씨·위치 기반 추천 |
| 플레이리스트 UI | 2D 목록 | 3D 선반(Shelf)에서 마우스로 회전·탐색 |
| 가사 표시 | 싱크 가사 또는 없음 | 가사 무대(Lyric Stage) + 위치·각도 조절 |
| 플랫폼 연동 | 자사 스트리밍만 | 네이버 클라우드 뮤직 + QQ 음악 동시 지원 |
기존 플레이어에서 시각화(Visualizer)는 볼륨 레벨에 반응하는 단순 바(Bar) 그래프가 전부였다. 음악의 구조(킥·스네어·멜로디 라인)를 이해하는 플레이어는 없었고, 따라서 "영화 같은 느낌"은 뮤직비디오를 따로 틀어야만 얻을 수 있었다.
자체 제작한 dj-analyzer.js가 오디오 스트림을 실시간으로 분석해 킥(kick)·일반 비트(beat)·카메라 비트(cameraBeats)를 별도로 추출한다. 이 데이터가 Three.js 렌더러와 GSAP 애니메이션 타임라인에 연결되어, 각 악기 계층마다 다른 시각 이벤트가 발생한다.
프론트엔드 · 렌더링 · 오디오 · 빌드·인프라 상세.
| 레이어 | 기술 / 버전 | 역할 |
|---|---|---|
| 데스크탑 셸 | Electron v42 | BrowserWindow 창 관리, 글로벌 단축키, 단일 인스턴스 잠금 |
| 메인 프로세스 | Node.js (desktop/main.js) | 로컬 HTTP 서버 시작, Chromium 성능 스위치 주입, IPC 처리 |
| 로컬 API 서버 | Node.js HTTP (server.js) | 네이버 클라우드 뮤직 API 래핑, QQ 음악 프록시, Open-Meteo 날씨, 업데이트 관리 |
| 3D 렌더링 | Three.js r128 | 파티클 시각 효과, 3D 플레이리스트 선반, 카메라 무브먼트 |
| 애니메이션 | GSAP v3.15 | 입장·전환 애니메이션, 스플래시 화면, 가사 무대 페이드 |
| 오디오 디코딩 | mpg123-decoder v1.0 | MP3 스트림 WebAssembly 디코딩 (Node.js 환경) |
| 리듬 분석 | music-tempo.js (번들) | 오디오 BPM·비트 타임스탬프 추출 |
| 오디오 분석 엔진 | dj-analyzer.js (자체 제작) | 바이쿼드 필터 + 비트맵 빌더로 킥·비트·카메라비트 분류 |
| 음악 플랫폼 API | NeteaseCloudMusicApi v4.32 | 네이버 클라우드 뮤직 검색·스트리밍·로그인·플레이리스트 |
| 날씨 API | Open-Meteo (무료) | 위치·날씨 기반 라디오 큐레이션 |
| UI 언어 | Vanilla HTML/CSS/JS | 단일 파일(public/index.html 1.3MB)에 모든 UI 집약 |
| 글씨체 | Noto Sans SC, Inter, JetBrains Mono, Cinzel Decorative | 다국어 지원 + 영화 제목 감성 폰트 |
| 빌드 도구 | electron-builder v26 | Windows NSIS 설치 패키지 생성 (dist/) |
| 업데이트 | GitHub Releases + 자체 패치 JSON | 버전 체크, 국내 미러 우선 다운로드, SHA256 검증 |
| 라이선스 | GPL-3.0 | 소스 공개 의무, 상업 배포 제한 |
데이터가 어디서 어디로 흐르는가.
Mineradio의 아키텍처는 크게 세 층으로 나뉜다. 가장 바깥 껍질인 Electron 메인 프로세스가 앱 창과 OS 수준 기능을 담당하고, 그 안쪽 로컬 HTTP 서버(server.js)가 음악 API와 날씨 API를 프록시하며, 가장 안쪽 대형 단일 HTML 앱이 실제 UI와 렌더링을 처리한다.
사용자가 곡을 선택한 순간부터 파티클이 터지기까지 어떻게 데이터가 흐르는지 단계별로 따라가 보자.
렌더러(UI)가 fetch('http://127.0.0.1:{port}/song_url?id=...')를 호출한다. 로컬 서버(server.js)가 이를 받아 NeteaseCloudMusicApi의 song_url_v1()을 호출하고, 저장된 로그인 쿠키를 함께 전송해 인증된 스트리밍 URL을 반환한다.
렌더러의 오디오 요소가 음원 URL을 로드한다. 동시에 music-tempo.min.js가 오디오 버퍼를 분석해 BPM과 비트 타임스탬프 배열을 생성한다. 이 데이터는 로컬 서버의 dj-analyzer.js에도 전달되어 킥 에너지맵과 카메라 비트 타임라인이 계산된다.
분석 결과인 beatmap이 렌더러로 반환된다. UI 루프는 requestAnimationFrame으로 현재 재생 시간을 비트맵과 대조한다. 킥 비트 도달 시 Three.js 파티클 시스템에 폭발 이벤트가 발생하고, 카메라 비트 도달 시 GSAP 타임라인이 카메라 위치를 부드럽게 이동시킨다.
서버에서 가져온 가사 타임스탬프와 오디오 재생 시간을 비교해 현재 가사 라인을 강조한다. 사용자가 설정한 발광 강도, 가사 위치, 각도 값이 실시간으로 CSS 변수와 Three.js 유니폼(uniform)으로 주입된다.
렌더러(UI)는 손님이다. 메뉴를 보고 주문하지만 주방 도구를 직접 만지지 않는다. 로컬 서버는 웨이터다. 손님의 주문을 받아 주방(Node.js API, 음악 플랫폼 API)에 전달하고 결과를 다시 손님에게 가져다준다. Electron 메인 프로세스는 식당 매니저다. 주방과 홀이 잘 돌아가도록 전체를 조율한다.
단, 비유의 한계: 실제 레스토랑 손님은 외부 식당에도 갈 수 있지만, Mineradio 렌더러는 동일한 Electron 프로세스 그룹 내 로컬 서버와만 통신한다. 보안상 외부 직접 접근이 차단된 구조다.
이 과정에서 주목할 설계 패턴이 하나 있다. Electron은 렌더러 프로세스가 Node.js API에 직접 접근하는 것을 보안상 막는다. 그래서 Mineradio는 로컬 HTTP 서버 패턴을 사용한다. 렌더러는 마치 일반 웹 페이지가 백엔드 API를 호출하듯 localhost로 fetch를 보내고, 메인 프로세스가 운영하는 서버가 Node.js API를 대신 호출한다. 보안을 지키면서도 풍부한 기능을 구현하는 영리한 우회로다.
어디에 무엇이 있는가.
| 파일 / 폴더 | 역할 요약 |
|---|---|
| desktop/main.js | 앱의 심장부. BrowserWindow 생성, GPU 가속 스위치 주입, 로컬 서버 포트 탐색 후 시작, IPC 채널 처리 |
| public/index.html | 1.3MB짜리 단일 파일 앱. Three.js 초기화, 오디오 관리, 검색/플레이리스트/Home 화면이 모두 여기에 있다 |
| server.js | 포트 3000에서 시작하는 Node.js HTTP 서버. 40여 개의 네이버 API, QQ 프록시, 날씨, 업데이트 엔드포인트 제공 |
| dj-analyzer.js | 바이쿼드 필터·백분위수 계산·비트 그루핑으로 킥/비트/카메라비트를 구분하는 오디오 엔진 |
| build/installer.nsh | Windows NSIS 설치 스크립트. D~Z 드라이브 우선 설치, 비-Mineradio 디렉터리 덮어쓰기 방지 등 안전 로직 |
| docs/PROJECT_MEMORY.md | AI 협업 핸드오프 메모. 개발자가 새 AI 대화를 열 때마다 컨텍스트를 복원하기 위한 38KB 기록 |
이 레포에서 무엇을 배울 수 있는가.
Electron의 핵심 개념인 메인 프로세스 ↔ 렌더러 프로세스 분리를 실전 코드로 배울 수 있다. desktop/main.js는 app.requestSingleInstanceLock()으로 중복 실행을 방지하고, ipcMain.on()으로 렌더러의 요청을 처리한다. desktop/preload.js는 contextBridge를 사용해 렌더러에 안전하게 API를 노출하는 방법을 보여준다.
// 단일 인스턴스 잠금 패턴 (desktop/main.js)
const gotLock = app.requestSingleInstanceLock();
if (!gotLock) {
app.quit(); // 이미 실행 중이면 즉시 종료
} else {
app.on('second-instance', () => {
mainWindow.focus(); // 기존 창에 포커스
});
}
public/vendor/three.r128.min.js를 사용해 PointCloud (포인트 클라우드) 형태의 파티클 시스템을 구축하는 방법을 배울 수 있다. 핵심은 BufferGeometry에 position 배열을 동적으로 갱신하는 패턴이다. 카메라 무브먼트는 Three.js Camera의 position과 lookAt을 GSAP 트윈으로 보간해 구현한다.
3D 플레이리스트 선반(Shelf) 구현에서는 BoxGeometry·PlaneGeometry와 텍스처 맵핑, 마우스 Raycaster를 활용한 3D 클릭 감지를 배울 수 있다.
dj-analyzer.js의 makeBiquad()와 runBiquad() 함수는 DSP (Digital Signal Processing, 디지털 신호 처리)의 가장 기초적인 필터를 JavaScript로 구현한 예시다. 수학 공식을 코드로 옮기는 과정과, 백분위수(percentile)를 이용해 에너지 임계값을 동적으로 설정하는 방법이 담겨 있다.
// 바이쿼드 하이패스 필터 계수 계산 (dj-analyzer.js)
function makeBiquad(type, freq, q, sr) {
const w0 = 2 * Math.PI * freq / sr; // 각주파수
const alpha = Math.sin(w0) / (2 * q); // Q 팩터 기반 alpha
// b0, b1, b2, a0, a1, a2 계수 계산 후 정규화
return { b0, b1, b2, a1, a2, x1:0, x2:0, y1:0, y2:0 };
}
Mineradio의 UI는 backdrop-filter: blur(34px) saturate(1.34)를 핵심으로 하는 글래스 효과를 광범위하게 사용한다. SVG 필터를 활용한 고성능 글래스 대안도 구현되어 있어, CSS backdrop-filter 지원이 약한 환경에서의 폴백 전략을 배울 수 있다.
CSS 변수(--glass-bg, --glass-border, --glass-shadow)를 시스템처럼 설계해 테마 전환이 변수 값 하나로 전파되는 구조도 주목할 만하다.
server.js는 Electron 앱에서 외부 API를 안전하게 사용하는 패턴의 교과서다. 로그인 쿠키를 파일에 저장하고(.cookie, .qq-cookie), 모든 인증이 필요한 API 호출에 쿠키를 자동으로 주입한다. QQ 음악처럼 직접 접근이 어려운 서비스의 쿠키를 BrowserWindow로 별도 세션(partition)에 로그인해서 추출하는 방법도 배울 수 있다.
스플래시 화면에서 "Mine"과 "radio" 두 글자가 필름 컷처럼 등장하는 애니메이션은 순수 CSS @keyframes로 구현되어 있다. clip-path: inset()으로 글자를 수직 방향으로 슬라이드인시키고, skewX·scaleX로 속도감을 더하는 고급 CSS 기법이 담겨 있다. GSAP은 실제 재생 중 카메라·파티클 인터랙션에 사용되어, CSS 애니메이션과 JS 애니메이션을 역할에 따라 구분하는 설계 전략을 보여준다.
Mineradio를 실행하거나 개발하기 위한 환경.
| 항목 | 요구사항 | 비고 |
|---|---|---|
| 운영체제 | Windows 10 / 11 (64비트) | macOS·Linux 미지원 — 빌드 스크립트가 Windows 전용 |
| Node.js | v18 이상 권장 | Electron v42와 호환되는 버전 필요 |
| GPU | WebGL 지원 GPU | Three.js 3D 렌더링 필수. 내장 그래픽도 가능하나 외장 GPU 권장 |
| RAM | 최소 4GB, 8GB 권장 | Electron + Three.js 파티클 동시 렌더링 시 메모리 사용량 높음 |
| 디스크 | 설치 후 약 300MB | asar 비활성화(asar: false)로 파일 그대로 배포 |
| 인터넷 | 필수 | 음악 스트리밍, 날씨 API, GitHub 업데이트 체크 |
| npm (개발) | electron, electron-builder, gsap, NeteaseCloudMusicApi, mpg123-decoder | npm install 후 npm start로 개발 모드 실행 |
| 빌드 도구 | electron-builder v26 | NSIS 설치 패키지 생성 (npm run build:win) |
| 음악 계정 | 네이버 클라우드 뮤직 또는 QQ 음악 (선택) | QR 코드 스캔으로 로그인, 없어도 검색은 일부 가능 |
Mineradio는 코드 서명(Code Signing) 인증서가 없는 개인 개발 프로젝트다. Windows SmartScreen과 일부 안티바이러스가 경고를 표시할 수 있다. "더 보기 → 실행"으로 우회하거나, 직접 소스에서 빌드해 사용하는 것이 더 안전하다. v1.0.10 이전 설치 패키지는 안전하지 않은 삭제 로직이 있어 공식적으로 격리 권장됨.
보는 것보다 만드는 것이 빠르다.
소스를 클론하고 npm install 후 npm start로 Electron 앱을 띄워보자. 어떤 창이 열리고, 개발자 도구(F12)에서 어떤 오류가 나타나는지 확인한다. desktop/main.js를 열어 GPU 가속 스위치 목록을 읽고, 각 스위치의 역할을 Chromium 공식 문서에서 찾아보는 것도 좋다.
git clone https://github.com/XxHuberrr/Mineradio
cd Mineradio
npm install
npm start
dj-analyzer.js의 makeBiquad()·runBiquad() 함수를 이해하고, 간단한 HTML 페이지에서 Web Audio API의 AnalyserNode와 결합해 실시간 주파수 스펙트럼을 Canvas로 그려보자. 로우패스(저음역)와 하이패스(고음역) 필터를 분리해 각각 다른 색으로 표시하면 DSP 개념이 눈에 보인다.
server.js의 Open-Meteo 날씨 API 코드를 읽고, 날씨 조건(맑음·흐림·비·눈)에 따라 배경 그라디언트 색상이 달라지는 간단한 HTML 페이지를 만들어보자. Mineradio의 날씨 라디오 로직이 어떻게 작동하는지 이해하는 데 도움이 된다.
// Open-Meteo API 호출 예시 (현재 위치 날씨)
const res = await fetch(
'https://api.open-meteo.com/v1/forecast' +
'?latitude=37.5&longitude=127.0' +
'¤t_weather=true'
);
const data = await res.json();
console.log(data.current_weather.weathercode);
Mineradio의 아키텍처를 참고해 최소한의 Electron 앱에 Three.js 파티클을 넣어보자. 목표: (1) main.js에서 BrowserWindow를 열고, (2) index.html에서 Three.js로 1000개 파티클을 띄우고, (3) 마우스 X 좌표에 따라 카메라가 회전하는 인터랙션을 추가한다. Mineradio의 코드 구조를 참고하되, 처음부터 직접 작성해보는 것이 핵심이다.
Web Audio API로 음악 파일을 로드하고, dj-analyzer.js의 바이쿼드 필터 로직을 참고해 저음역 에너지가 임계값을 넘을 때 화면의 배경색이 순간적으로 변하는 페이지를 만들어보자. 이 실습을 마치면 Mineradio의 파티클 폭발 메커니즘을 정확히 이해하게 된다.
Mineradio를 완전히 이해하기 위한 주차별 학습 계획.
| 주차 | 주제 | 학습 자료 및 실습 |
|---|---|---|
| 1주차 | Electron 기초 | Electron 공식 Quick Start, main/renderer 프로세스 분리 이해, preload 스크립트 작성, IPC 통신 실습 |
| 2주차 | Three.js 기초 | Three.js 공식 튜토리얼, Scene·Camera·Renderer 삼위일체 이해, 간단한 큐브 → 파티클 시스템으로 확장 |
| 3주차 | Web Audio API | MDN Web Audio API 가이드, AudioContext·AnalyserNode·BiquadFilterNode 실습, 실시간 스펙트럼 시각화 |
| 4주차 | DSP 기초 (JavaScript) | 바이쿼드 필터 수학 이해, dj-analyzer.js 코드 읽기, 직접 로우패스 필터 구현해 소리 변화 확인 |
| 5주차 | GSAP 애니메이션 | GSAP 공식 문서, Timeline·Tween·ease 함수 실습, Mineradio 스플래시 CSS 애니메이션 분석 및 재현 |
| 6주차 | Node.js HTTP 서버 | Node.js 내장 http 모듈로 REST API 서버 구현, 외부 API 프록시 패턴, 쿠키 파일 영속성 처리 |
| 7주차 | CSS 글래스모피즘 | backdrop-filter 심화, CSS 변수 시스템 설계, SVG 필터 폴백 구현, Mineradio UI 분석 |
| 8주차 | Electron 배포 | electron-builder 설정, NSIS 설치 스크립트, GitHub Releases 자동 업데이트 파이프라인 |
이 레포를 읽다 마주치는 생소한 용어 모음.
| 용어 | 의미 |
|---|---|
| Electron | Chromium + Node.js로 데스크탑 앱을 만드는 프레임워크. 웹 기술로 OS 수준 앱을 배포할 수 있다. |
| BrowserWindow | Electron에서 앱 창 하나를 표현하는 클래스. 창 크기, 투명도, 타이틀바, 프레임리스 등을 설정한다. |
| IPC | Inter-Process Communication. 메인 프로세스와 렌더러 간 메시지 전달 메커니즘. |
| preload.js | 렌더러 프로세스가 로드되기 전에 실행되는 스크립트. contextBridge로 안전한 API만 렌더러에 노출한다. |
| Three.js PointCloud | 수천 개의 점(파티클)을 3D 공간에 배치하는 방식. GPU에서 한 번에 그려져 성능이 높다. |
| BufferGeometry | Three.js에서 정점(vertex) 데이터를 GPU에 직접 전달하는 효율적인 데이터 구조. |
| Raycaster | Three.js에서 마우스 커서에서 3D 공간으로 광선을 쏴 클릭된 3D 객체를 찾는 기법. |
| GSAP Timeline | 여러 애니메이션을 순서대로 또는 동시에 실행하는 GSAP의 시퀀스 컨테이너. |
| Biquad Filter | 2차 무한 임펄스 응답(IIR) 필터. 로우패스·하이패스·밴드패스 등 주파수 대역을 선택적으로 통과시킨다. |
| BPM | Beats Per Minute. 분당 박자 수. 음악의 빠르기를 나타내며, 비트 타임스탬프 계산의 기초. |
| Beatmap | 곡의 킥·비트·카메라 비트 타임스탬프 배열. Mineradio가 오디오 분석으로 생성하는 시각 이벤트 지도. |
| backdrop-filter | 요소 뒤의 배경을 흐리거나 색조 변환하는 CSS 속성. 글래스모피즘 UI의 핵심 기술. |
| 글래스모피즘 | 투명한 유리 효과를 연출하는 UI 디자인 트렌드. 반투명 배경 + blur + 테두리 highlight로 구현. |
| NeteaseCloudMusicApi | 네이버 클라우드 뮤직(网易云音乐)의 비공식 Node.js API 라이브러리. 검색·스트리밍·로그인을 지원한다. |
| Open-Meteo | 무료·오픈 소스 날씨 API. 위도/경도로 현재 날씨 데이터를 반환한다. |
| NSIS | Nullsoft Scriptable Install System. Windows 설치 패키지를 만드는 스크립팅 시스템. |
| asar | Electron의 아카이브 포맷. asar: false면 소스 파일이 압축 없이 그대로 배포된다. |
| SHA256 체크섬 | 파일 무결성을 검증하는 해시값. Mineradio는 업데이트 파일 다운로드 후 반드시 검증해 변조된 파일을 막는다. |
| DSP | Digital Signal Processing. 디지털 신호 처리. 여기서는 오디오 신호를 수학적으로 분석하는 기법 전반을 뜻한다. |
| WebGL | Web Graphics Library. 브라우저에서 GPU를 직접 활용해 3D 그래픽을 렌더링하는 저수준 API. Three.js가 WebGL 위에서 동작한다. |
더 깊이 파고들고 싶다면.