Files
music_bot_v2/page
tkrmagid 5f8dc184b4 feat(page): 대시보드 UI/UX 전면 개선 — 모바일 지원, 터치 조작, 접근성
실제 화면을 캡처해 확인한 문제:
- 모바일(390px)에서 3열 고정 레이아웃(사이드바 240 + 대기열 320)이 본문을 ~50px 로 눌러
  "로그인이 필요합니다"가 한 글자씩 세로로 표시되는 등 사실상 사용 불가였다.
- 대기열의 삭제/드래그가 hover 에서만 노출돼, hover 가 없는 터치 기기에서는 접근 자체가 불가능.
  순서 변경도 HTML5 드래그앤드롭뿐이라 모바일에서 동작하지 않았다.
- 조작 요소가 터치 기준(44px)에 한참 못 미쳤다(재생 32px, 삭제 ~24px, 진행바 높이 1px).
- 로그아웃 상태에서도 사이드바·대기열·플레이어바가 자리를 차지했고, 본문은 "로그인이 필요합니다"
  라고만 적어둔 채 정작 버튼은 우상단에만 있어 동선이 끊겼다.
- 아이콘 버튼에 접근성 이름이 없고(title 만), 검색창 안내가 로그인 전에도 "서버를 먼저 선택"이었다.

변경:
- 레이아웃: 반응형으로 재구성. 좁은 화면은 단일 컬럼 + 대기열을 슬라이드 패널(FAB/플레이어바에서 토글),
  넓은 화면은 기존 3열 유지. 로그인 전·서버 미선택 시에는 재생/대기열 영역을 아예 렌더링하지 않는다.
- 대기열: 조작 버튼 상시 노출, 좁은 화면에는 위/아래 이동 버튼 제공(드래그 대체), 넓은 화면은 드래그 유지.
  번호·곡수 표시 추가, 실패 시 이전 순서로 롤백.
- 플레이어바: 버튼을 44~48px 로 키우고 모바일은 진행바를 상단 전체 폭으로 분리. 볼륨은 좁은 화면에서 숨김.
- 검색 결과/서버 카드: 카드 전체를 버튼으로 만들어 hover 없이도 누를 수 있게 하고 재생 아이콘 상시 노출.
- 로그인 화면에 실제 로그인 버튼 배치, 서버 미선택/결과 없음 등 빈 상태 문구 정리.
- 좌측 사이드바를 안내문 전용 죽은 공간에서 "조작 중인 서버 + 서버 전환 + 사용법"으로 교체.
- 아이콘 버튼 aria-label, range input aria-label, 장식 이미지 alt="" 정리. 프로필 이미지 없을 때 이니셜 대체.

검증: tsc 통과, lint 에러 0. 실제 렌더링을 데스크톱(1440x900)·모바일(390x844)에서 캡처해
로그인 전/서버 목록/서버 상세/검색 결과/대기열 패널 화면을 모두 확인했다.
2026-09-29 13:47:26 +09:00
..
2026-04-08 12:59:45 +09:00
2026-04-08 12:59:45 +09:00
2026-04-08 12:59:45 +09:00
2026-04-08 12:59:45 +09:00
2026-04-08 12:59:45 +09:00

music_bot_v2 — 웹 대시보드(page/)

디스코드 음악봇(../bot)을 브라우저에서 제어하는 웹 대시보드. 디스코드 로그인 후 내가 속한 서버를 골라 검색·재생·대기열 관리·볼륨/탐색 등을 조작하고, 현재 재생 상태를 실시간으로 본다.

  • 스택: Next.js 16 (App Router) · React 19 · TypeScript · Tailwind CSS 4 · NextAuth 4(Discord OAuth) · ioredis
  • 봇과의 통신: 직접 호출이 아니라 Redis Pub/Sub + Key로 봇과 주고받는다(아래 3장).
  • 리포 전체 개요는 ../README.md, 봇 본체는 ../bot/README.md 참고.

1. 빠른 시작 (개발)

cd page
npm install
# .env.local 작성 (2장 참고)
npm run dev        # http://localhost:3000

빌드/구동: npm run build → npm run start. 린트: npm run lint.

Redis(봇과 공유)와 디스코드 OAuth 앱이 있어야 실제 동작한다. Redis에 연결되지 않으면 부팅 시 에러를 던진다(src/lib/Redis.ts).


2. 환경변수 (.env.local)

변수 설명
REDIS_HOST / REDIS_PORT 봇과 공유하는 Redis. 봇 .env의 값과 동일해야 한다(미설정 시 부팅 에러).
DISCORD_CLIENT_ID / DISCORD_CLIENT_SECRET 디스코드 OAuth 앱 자격증명(로그인·서버목록 scope).
NEXTAUTH_SECRET NextAuth 세션 서명 키.
NEXTAUTH_URL 배포 URL(예: https://music.example.com). 개발은 http://localhost:3000.
  • 로그인 scope는 identify email guilds(유저 정보 + 서버 목록). src/app/api/auth/[...nextauth]/route.ts.
  • 디스코드 개발자 포털의 OAuth2 Redirect에 <NEXTAUTH_URL>/api/auth/callback/discord를 등록해야 한다.

3. 봇과의 통신 구조 (Redis)

대시보드는 봇을 직접 호출하지 않고 Redis를 메시지 버스로 쓴다. 봇 쪽 구현은 ../bot/src/classes/RedisClient.ts.

3-1. 명령 (site → bot, 요청/응답 RPC) — src/lib/api.ts의 botRpc

site: PUBLISH "site-bot"  {action, serverId, userId, requestId, …}
bot : 처리 후  SETEX "<action-key>:<requestId>"  (예: player:play:<reqId>)  에 결과 저장
site: 그 키를 짧게 폴링(100ms→최대400ms 백오프, 기본 3초 타임아웃)해 결과 수신
  • requestId는 CSPRNG(randomUUID). 결과 키는 읽은 즉시 삭제 + TTL 보험.
  • action: search, player_play, player_playlist, player_now, player_pause, player_skip, player_seek, player_volume, queue_list, queue_set, queue_remove.

3-2. 실시간 상태 (bot → site, SSE) — src/lib/sse.ts의 botEventStream

bot : PUBLISH "bot-site"  {event:"player_update"|"queue_update", guildId}
site: "bot-site" 구독 → serverId 일치하는 것만 EventSource(SSE)로 브라우저에 전달(30초 keep-alive)
  • 브라우저는 /api/player/events?serverId=…, /api/queue/events?serverId=…(SSE)를 구독해 상태 변경 알림을 받고 now/queue/list를 다시 당겨온다.

4. API 라우트 (src/app/api)

모든 라우트는 세션 가드(requireSession)를 거치고 { success, … } 형태로 응답한다.

  • player: play, pause, skip, seek, volume, playlist, now, events(SSE)
  • queue: list, set, remove, events(SSE)
  • servers: 로그인 유저의 디스코드 서버 목록(봇이 있는 서버로 필터링)
  • search: 검색(봇의 search action → Spotify/YT Music/YT Video 결과)
  • auth: [...nextauth](Discord OAuth)

공용 헬퍼(src/lib/api.ts): requireSession, botRpc, readJsonBody, requireString/Number/Boolean, errorResponse.


5. UI 구성 (src/)

  • app/layout.tsx, app/page.tsx — 루트 레이아웃/페이지. components/Providers.tsx(세션 등), ToastProvider.tsx.
  • components/layout/ — TopNav(상단/서버선택·로그인), LeftSidebar(탐색).
  • components/player/ — PlayerBar(재생바/볼륨/탐색), MainContent(검색·결과), QueueSidebar(대기열).
  • types/music.ts — DiscordServer, Track/TrackInfo, SearchTrack, SearchResults 등 공용 도메인 타입.
  • next.config.ts — 보안 헤더(CSP: self + discord/spotify/ytimg/google 이미지 CDN만 허용, frame-ancestors 'none' 등).

6. 배포 메모

  • Next.js 16 standalone/next start로 구동. 봇과 같은 Redis(.7)에 접근 가능해야 한다.
  • NEXTAUTH_URL/디스코드 Redirect URI를 실제 도메인으로 맞춘다.
  • 이 디렉터리는 Next가 제공하는 에이전트 규칙 파일(AGENTS.md, CLAUDE.md)을 둔다 — 코드 수정 전 참고.