Roadmap구현 현황

구현 현황

기준 BE v0.140.0 / FE v0.197.0 / SPEC #001~#180 (#009 결번 · #098 F1 highlight 포함) + 점장 플레이어 감사(2026-07) SPEC A~G 전량(A·B·C·D·E·#11 ack outcome·F BE 무결성·G 문서 정합) + 재감사 후속(N1·R1·D1) + 시간대별 라이브러리 스케줄링(#171 · 무음/갭 0) + 매장 재생 가시성(#172) + 본사 하위 매장 CS(#173) + CM송 파일 업로드(#174) + 재생 일/주/월 리포트(#175) + 신탁 재생 로그 뷰어(#176) + 대용량 파일 직접 업로드(#177) + 점장 시간표 빈-본사기본 커스텀 시작·편집 대상 PL 표시 + 매장 멀티 기기(#178) + 음원 전송 캐시(#179) + 본사 즉시방송 SSE 구독(#180) + 매장 생성 통합 — 이메일 1회·등록=계정·본사 발급 권한(#184) — 2026-08-19

최근 릴리스(2026-08-13) — BE v0.140.0 / FE v0.197.0 — 매장 생성 통합: 이메일 1회 · 등록=계정 · 본사 발급 권한 (SPEC #184): 사용자 실사용 피드백(「매장 발급 시 이메일과 로그인 이메일이 다르다」)에서 출발했다. 매장 등록의 managerEmailStore연락처 컬럼일 뿐이고 로그인 ID 는 발급 다이얼로그에서 다시 타이핑한 값이라, 두 값이 갈려도 프리필·대조·경고가 0건이었다 — 오타가 나면 점장은 로그인을 못 하고 운영자용 정정 경로도 없어 데드엔드였고, CSV 는 「담당자이메일」 50개를 이미 받아놓고 계정을 0개 만들었다. 본사 온보딩(조직+계정 한 트랜잭션) 선례에 매장을 맞춘다. BE: 매장 생성 4경로(운영사 독립/본사소속 · 본사 위임 단건 · CSV)가 같은 트랜잭션에서 점장 계정까지 만든다 · 이메일 미입력이면 계정 없이 매장만(「점장 미정」) · 이메일 중복이면 매장 생성도 롤백(반쪽 상태 금지, CSV 는 행 단위 격리) · 본사에 자기 산하 매장 발급 권한(생성 시점 + 사후 POST /hq/stores/{storeId}/managers, PrincipalScopeGuard 로 매장 귀속 재검증 · 타 본사·미존재 모두 403 은닉 · HQ_STORE_MANAGER_ISSUED 감사) · setup 메일을 트랜잭션 밖으로(CSV 는 행마다 외부 ACS 왕복이 트랜잭션 안이면 1000행에서 프록시 타임아웃 + 무보고 부분커밋 → 재업로드 시 매장은 다 생긴 채 전 행 DUPLICATE_EMAIL; 단건은 afterCommit 으로 롤백된 트랜잭션의 메일 유출 차단) · 임시비번 서버 생성·미노출(생성 경로 한정 — setup 메일이 유일한 진입 경로가 되므로 발송 성공 여부를 응답에 싣는다) · rate limit STORE_PROVISION 20/분 · BULK_PROVISION 3/분(요청 1건 = 최대 1000통) · DUPLICATE_EMAIL 에서 입력 이메일 제거(본사에게 열리면서 409/201 로 플랫폼 전체 계정 존재를 열거할 수 있게 됐다) · hasManagerAccountWITHDRAWN 제외(회수한 매장이 「발급됨」으로 집계돼 재발급 진입점이 사라졌다) + 상세는 비-회수 우선. FE: 「점장 연락처」 → 「점장 계정」 섹션 승격(이메일 = 로그인 ID 명시) · [점장 미정으로 등록] 토글(리듀서가 이메일을 비워 stale 값으로 계정이 생기는 경로를 구조적으로 차단) · 소속 기본 선택(INDEPENDENT) 제거(안 건드리면 본사 산하 매장이 가상 본사로 영구 고정되고 되돌릴 경로가 없었다 — 가장 쉽게 도달하는 영구 무음 경로) · 등록 완료 화면에서 매장 상세로 연결(종전에는 storeId 를 버리고 대시보드로 튕겼다) · 본사 사후 발급 다이얼로그 · 운영사·본사 목록 [점장 계정] 필터(폐점 매장은 발급 진입점 제거 + 종착 사유 노출) · 409 는 매장도 롤백된 상태라 4경로 전부 폼에 머물러 이메일 필드 에러만 띄운다(성공 화면·상세 이동·성공 토스트 0 — 「매장은 만들어졌겠지」로 중복 생성하는 경로 제거) · 429 는 한도·재시도 시점을 문구에(CSV 분당 3회는 파일 문제로 오인하기 쉽다) · setupEmailSent=false 는 성공으로 뭉개지 않고 복구 액션까지(운영사=재발송, 본사=재발송 권한이 없어 운영사 문의). 기존 managerEmail 은 백필하지 않는다 — 운영 중 매장은 이 값이 연락처라 계정 이메일과 다를 수 있고 정합시키면 멀쩡한 계정의 로그인 ID 가 바뀐다. 새 env 0 · 마이그레이션 0 · player 무접촉. 근거: docs/audit/persona-flow-audit-2026-08.md 부록 A·B. 잔여: 운영자용 이메일 정정 · Store admin PATCH · 사후 소속 이동 · 스크린샷 재캡처.

릴리스(2026-08-12) — BE v0.139.0 / FE v0.196.0 — 본사 즉시방송 SSE 구독 (SPEC #180): BE 는 #178 에서 통지 채널을 만들었지만 FE 가 구독하지 않아 (1) 본사 즉시방송이 최대 20초 늦고(기획 차별점 「본사 실시간 대응」의 체감 지점) (2) pending 20초 폴링이 매장 API 호출(일 ~8,640/기기)의 지배 항목이며(전부 Vercel BFF 경유 = 비용 직결) (3) 즉시방송 리드타임 실측이 불가능했다. BE: 스트림 티켓 발급(POST /store/announcements/stream-ticketUPLOAD_TICKET_SECRET 재사용 + type=stream-ticket claim, 새 env 0 · DB 0) · 스트림 인증을 ?ticket= 로 전환(fail-closed 401) · 25초 keepalive : ping · 구독 직후 connected 이벤트 · 통지 지점 보강(예약 도래 SCHEDULED→PENDING · 원격 중단 revoke — 없으면 폴링 완화가 예약 정시성·중단 반응을 20→60초로 회귀시킨다) · ack 계측 3필드 + announcement_dispatch 컬럼 4개(V68 · 전부 nullable·백필 없음). FE: use-announcement-stream.ts — 티켓 mint → 브라우저 → 백엔드 직접 EventSource(BFF 스트리밍 프록시는 Vercel maxDuration 강제 재연결 + in-flight 스트림 상시 과금이라 기각) → connected 로 연결 확정 → announcement 이면 pending·scheduled invalidate. 폴링은 안전망으로 유지 — 연결이 연속 90초 유지된 뒤에만 60초로 완화(히스테리시스)하고 끊기면 즉시 20초 복귀. react-query 는 refetchInterval 값이 바뀔 때마다 타이머를 0 부터 재무장하므로, 완화를 즉시 적용하면 플래핑 회선에서 폴링이 한 번도 발화하지 못한다(무인 매장 방송 소실). 더해 5초 주기 절대 신선도 바닥이 「의도한 주기 + 10초」를 넘긴 pending 을 강제 재조회하고, 재연결 직후 1회 resync invalidate(BE 통지는 재전송이 없어 단절 창의 신호가 유실된다) · 선제 재연결은 make-before-break(새 커넥션 확정 후 옛 것 close — 10분마다의 미연결 플립 제거)다. EventSource 내장 재연결은 만료 티켓 URL 을 재사용하므로 끄고 티켓 재발급 + 지수 backoff·jitter로 다시 붙는다(연결 확정 워치독 15초 · 티켓 mint 상한 10초(apiFetch 에 timeout 이 없어 hang 하면 재시도 경로가 통째로 사라진다) · 영속 실패 상한 5분(발급 503 + connected 를 한 번도 못 받은 실패 5회 초과 — CORS 오리진 누락이 대표. 60초 상한이면 기기당 하루 1,440회 mint) · 선제 재연결 10분 — BE keepalive 는 SSE comment 라 브라우저가 JS 로 노출하지 않아 「무신호」를 이벤트로 잴 수 없다). fail-open 불변식: 티켓 401/429/503·CORS·EventSource 미지원·회선 단절이 전부 조용히 미연결로 수렴하고, SSE 의 어떤 실패도 (1) 재생을 막지 않고 (2) 폴링을 20초보다 늦추지 않으며 (3) 기존 due/ack 파이프라인을 우회하지 않는다. 계측: ack 에 signalSource(SSE/POLLING)·receivedAt·startedAt(서버 시각 보정) → 서버가 signal_latency_ms 확정 저장(리드타임 튜닝 근거·SSE 커버리지). 목표: 즉시방송 체감 지연 ≤ 리드타임+α(~3초) · 매장 API 호출 일 ~8,640 → ~2,880 + 티켓 48(약 66% ↓) · SSE 장애 시 기능 손실 0·방송 소실 0. 새 env·시크릿 없음 · 마이그레이션 V68(컬럼 4개 + partial index 1개). 잔여: 실측 1주(signal_latency_ms p95·SSE 커버리지·Vercel invocation 감소) → 그 결과로 리드타임 튜닝(별도).

직전 릴리스(2026-08-11) — BE v0.138.0 / FE v0.195.0 — 음원 전송 캐시 (SPEC #179): 음원 대역폭이 원가의 지배 항목(12h 매장 월 ~21GB ≈ 3,500원 = AI 플랜 매출의 70% · 멀티기기 4대 ~84GB = 3배 적자)이고 매장은 같은 곡을 반복 재생하므로 캐시 적중률이 원가의 자릿수를 결정한다. BE: 음원·CM 교체를 같은 key 덮어쓰기 → 새 UUID key 발급으로 전환(장기 캐시의 전제 — 이 전환 없이 immutable 을 걸면 “파일을 바꿨는데 옛 소리가 난다” 는 stale 고착). 업로드에 Cache-Control 부여 — 음원·CM = public, max-age=1y, immutable / TTS·점장 녹음 = no-store(같은 key 덮어쓰기 계약·1회성 재생) / 티켓 첨부 = 무관(프록시 경유). report cacheHit 계측(V67) — 적중률을 추정이 아닌 실측으로. FE: 서비스워커 없이 앱 레벨 캐시 — Cache Storage 적재 + objectURL 재생, 렌더 시점 동기 확정(warm 레지스트리에 있으면 objectURL·없으면 원격 즉시)이라 캐시 때문에 재생이 늦어지는 경로가 구조적으로 부재. 다음 곡 1곡 프리페치 + 백그라운드 사후 적재. fail-open 불변식 — fetch 실패·quota·Cache Storage 부재·CORS 미설정 전부 원격 폴백(캐시가 재생을 인질로 잡지 않는다). objectURL 수명 = 슬롯 소유(3지점 revoke 수렴 — 24/7 단말 메모리 증가 차단), 손상 캐시 evict + 원격 자가 복구, 3GB LRU. 운영 액션 잔여: scripts/audio-cache/(BE 레포) — ① cors(Azure CORS 룰 — FE 캐시 fetch 의 전제) ② backfill(기존 blob Cache-Control 일괄 부여 — BE 배포 확인 후 실행, 순서가 안전성). 미실행 상태에서도 재생은 종전과 동일(캐시만 미동작). 목표: 매장(기기)당 월 egress ≤3GB(85%+ 절감) · 적중률 ≥80%. 새 env·시크릿 없음 · 마이그레이션 V67(컬럼 1개).

최근 릴리스(2026-08-06) — FE v0.191.0 — 시간표 화면 폴리시(고객사 제보 3건): (1) 플레이리스트를 바꿔도 시간표에 반영되지 않아 새로고침해야 했다/store/playlist 의 활성 PL 변경 콜백이 재생 큐·me 만 무효화하고 시간표 쿼리를 빠뜨려 옛 PL 시간표가 캐시에 남았다(시간표는 활성 PL 단위). getGetStoreScheduleQueryKey() 무효화 추가. (2) 시간표 화면에서 PL 전환 드롭다운 — 후보가 2개 이상이면 모드 카드의 이름 표시가 드롭다운이 되어 이 화면에서 바로 갈아탄다(저장 안 한 편집이 있으면 확인 후 전환·성공 시 시간표/큐 무효화 + 로컬 편집 모드 해제). 후보 1개면 종전처럼 이름만. (3) 블록을 옮길 때 커서가 옮기는 모양이 아니었다 — 블록 본체가 pointerEvents:none(트랙이 드래그를 받는 구조)이라 블록에 건 cursor:grab애초에 적용 불가였고 아래 슬롯의 crosshair 가 보였다. 슬롯이 data-lib 로 자기 위 블록 유무를 알고 있으므로 슬롯이 커서를 표시(블록 위=grab·빈칸=crosshair)하고, 드래그 중에는 트랙이 [&_*] 로 자식 커서까지 덮어써 포인터가 블록 밖으로 나가도 유지된다(move=grabbing·top/bot=ns-resize). BE 변경 없음 · 마이그레이션 없음 · 새 env 없음 · 재생 파이프라인 무변경.

직전 릴리스(BE v0.134.0 / FE v0.190.0 · 2026-08-06) — 점장 시간표 사용성 수정(고객사 제보): (1) 본사 기본 시간표가 비어 있으면 [눌러서 커스텀 시작하기]가 동작하지 않던 것 수정 — BE customize 는 본사 기본을 매장 override 로 복사하는 연산이라 복사 원본이 없으면 200 이어도 override 행이 0 → 응답 isOverride=false(계약상 정상)인데, 화면이 isOverride 하나로 편집 잠금을 판정해 버튼을 눌러도 읽기 모드 그대로였다. 본사가 시간표를 한 칸도 넣지 않으면 점장이 시간표를 만들 수 없었다. 편집 경로 자체는 열려 있어(PUT 은 override 행 없이도 upsert 생성) 잠금 판정만 editing(= isOverride || localCustomizing)으로 전환. 저장 전 상태에서는 서버에 만든 게 없으므로 [되돌리기] 대신 [취소](로컬 리셋·요청 0). (2) 편집 대상 플레이리스트 표시 — 시간표는 활성 PL 단위라 PL 이 바뀌면 편집 대상도 바뀌는데 화면만 보고는 알 수 없었다. BE 응답에 playlistName(additive·nullable) 추가 후 모드 카드에 노출. 마이그레이션 없음 · 새 env 없음 · 재생 파이프라인 무변경(재생 중 클라이언트 무영향). 함께: 로드맵 3종 코드 기준 팩트체크(known-gaps 전면 재작성 — 이미 구현된 16건을 gap 에서 제거 · pending-features 를 잔여만 남겨 61kB→6.6kB) · progress 본사 note 의 “PL 시간표 읽기 전용” stale 정정(코드는 본사도 편집 가능) · guide store/playlist 에 점장 시간표 안내 신설.

직전 릴리스(BE v0.133.0 / FE v0.189.0 · 2026-08-03) — SPEC #177 대용량 파일 직접 업로드: 음원·CM송 파일 업로드가 브라우저 → Vercel 서버리스 BFF proxy 의 요청 본문 4.5MB 하드 리밋(FUNCTION_PAYLOAD_TOO_LARGE)에 막혀 5MB+ MP3 가 413 으로 차단되던 것을, 파일 본문만 백엔드로 직접(cross-origin) 전송해 우회한다. 인증은 백엔드 발급 단기 업로드 티켓(POST /api/v1/uploads/ticketsuploadUrl·X-Upload-TicketPOST /api/v1/uploads/files). FE 는 공용 uploadViaTicket 헬퍼(@linkmusic/api-client)로 4 호출부(음원 등록·음원 파일 교체·CM 등록·CM 파일 교체) 통일 + 진행률(%) 표시. 점장 store player 무영향(CM 관리 화면 전용). 새 시크릿 UPLOAD_TICKET_SECRET(BE·Render, fail-lazy) — main 배포 전 설정 필요. FE 신규 env 없음. 후속 핫픽스(FE v0.189.0): 운영사가 본사를 임퍼소네이션한 상태의 CM 등록이 403 나던 것 수정 — POST /uploads/tickets 를 apps/space BFF proxy 의 impersonation-aware 화이트리스트에 추가(HQ_MANAGER 토큰으로 발급).

직전 릴리스(BE v0.132.0 / FE v0.187.0) · 프로덕션 핫픽스 2건: (1) CM송 업로드 “파일 용량 에러” 근본 수정(FE)apps/space catch-all BFF proxy 의 middleware proactive refresh 가 NextResponse.next({ request }) 로 요청을 rewrite 하면서 POST multipart body 를 드롭해 정상 MP3 도 빈 파일로 도착 → 서버 빈 파일 검증에 걸리던 것을, GET/HEAD 외 메서드는 request rewrite 를 건너뛰도록 수정(멱등 조회만 proactive refresh, 상태 변경은 proxy 반응형 401 경로에 위임). (2) 신탁 로그 CSV “서버 에러” 근본 수정(FE)downloadCsvFromBackend 호출부가 v1/...(→/api/backend/v1/...) 로 api/ prefix 를 빠뜨려 backend 미매핑 경로 500 으로 떨어지던 것을 api/v1/... 로 정정(신탁 로그 + audit CSV 7개 호출부 전부). + BE 방어(BE) — 미매핑 경로가 catch-all Exception→500 으로 오분류되던 것을 NoResourceFoundException→404 NOT_FOUND 전용 핸들러로 매핑(monitoring·retry 정책 5xx 민감·경로는 DEBUG 로그만). 마이그레이션 없음·새 env 없음.

그 이전(BE v0.131.0 / FE v0.186.0): 신탁 재생 로그 뷰어(SPEC #176·#172 FU-3) — play_log 전량 조회·CSV. GET /api/v1/admin|hq/playback-logs(list) + /export(서버 CSV·상한 5만·injection 방어) — 운영사(플랫폼 전체)·본사(hqId 격리). 전량+신탁 필터(기본 신탁). 운영사 설정 신탁 재생 로그 placeholder 실기능화 + 본사 /admin/playback/logs. (KOMCA 공식 신고 포맷은 외부 규격 후속.) + 본사 대시보드 정산 placeholder 실데이터화(구독·결제 제거·활성 CM송/미해결 매장 문의 수)·재생 리포트 4탭(일·주·월 단일 + 커스텀)·재생 상태 드릴다운 전체 매장 노출. 마이그레이션 없음(play_log 조회)·새 env 없음.

직전 릴리스(BE v0.130.0 / FE v0.185.0): 본사 재생 일/주/월 리포트(#175·#172 FU-2 — hq/playback/summary 집계·SVG 추이 차트).

최근 하이라이트매장 생성 통합(#184 — 매장 등록 폼의 이메일이 곧 점장 로그인 ID·등록과 계정이 한 트랜잭션·“점장 미정” 건너뛰기·본사 사후 발급 권한·목록 “계정 미발급” 필터) · 본사 CS 티켓(목록·작성·상세·댓글 + 상태/우선순위 변경) · 본사 산하 매장 등록·인라인 편집·CSV 일괄 등록·상세 조회 · 본사 CM송 관리 + 점장 player 사이클 재생(오버레이 동시재생)·빈도 설정·매장 override·라운드로빈 · 본사 송출 이력 고도화(actor·재송출·취소·예약·그룹핑·일괄 액션·매장명 검색) · 점장 player(Classic) + 긴급 인터럽트·예약방송(목록·취소·페이지네이션·본사 점유 시각 차단) · 점장 CS 티켓 + 운영자 store ticket 식별(end-to-end) · 점장 액션 audit 백본(store_audit_log) · 본사·점장 비밀번호 변경·프로필 편집 · 점장 공개 랜딩·온보딩 풀 플로우(design_9·design_10 시안 정합) · 앱-내 unread 배지(운영자 CS 카운트·본사/점장 새 답변 dot·본사 오늘 미도달 카드) · 빈/에러 dead-end CTA·본사 bootstrap 체크리스트·계정 전달 안내문 복사 · 점장 CS 확인 종료(/close)·MISMATCH 큐 plan 필터·운영사 본사명 편집 · **송출 이력 매장 그룹 일괄 액션·운영 통계 확장(송출·audit 지표)·운영자 설정 8탭 스캐폴딩·협약 동의 이력·상태 조회·점장 활성 PL 선택·예약 송출 미리듣기·운영자 설정 내 프로필 · 운영자 설정 장르/무드 옵션 CRUD·운영 통계 CSV 내보내기(지표·30일 추이) · TTS 프로덕션 활성화(Typecast 응답계약 실토큰 검증 완료·운영자 [TTS 연결 확인] smoke-test) · 점장 모드 전체 디자인 시안 정합(design_14 묶음5 — player 홈(커버는 음악 전용 2상태 music/none — 방송 표기는 커버 아래 방송 카드가 담당, SPEC #141)·CS·예약·프로필·활성PL + StoreSubHeader, 시각만 교체·동작 보존) · 송출/재생 제어(더킹 — 본사 default·매장 per-field override 설정 + 점장 player 2트랙 음악 감쇠/복원, 긴급 제외 · 점장 멘트 즉시중단 [중단] · 5분 슬롯 그리드 예약 등록, 본인/본사 점유 비활성, design_15 시안 정합) · 풀스택 E2E harness(docker-compose pg→backend→frontend + Playwright 크로스-페르소나 happy-path, 논블로킹 CI) · 운영사 백오피스 UX 폴리시(음원 다중 파일 업로드 + 진행률·일괄 소스 선택 · 하단 고정 커스텀 플레이어(fill 재생/일시정지·seek) · 음원 담기 서버측 라이브러리 제외·정렬(최신/오래된/제목/길이)·페이지네이션·Shift 범위선택·전체선택/해제 · 본사 ONBOARDING→ACTIVE 활성화 액션(BE transition + 운영자 액션 + audit HQ_ACTIVATED) · 사이드바 고정 셸·대시보드 분포 카드 정렬·정책 발효시각 검증) · 운영사→본사 임퍼소네이션 cross-app 핸드오프(admin → 진짜 본사 앱 apps/space로 exchange-token 전달 → space가 자기 sealed 세션 발급·본사 셸 임퍼소네이션 배너/60분 카운트다운/돌아가기, 단일 소스 유지 · 신규 env NEXT_PUBLIC_SPACE_ORIGIN) · 점장 셸 로그아웃 버튼 · 본사 반복 송출 예약 관리 UI(/admin/announcements/schedules — DAILY/WEEKLY·5분 슬롯·대상 ALL/지정매장·기간 등록·조회·취소, 안내방송 서브탭) · Linkmusic 브랜딩 적용(로고 BrandMark·favicon.ico/svg·PWA manifest 전 앱 + 브랜드 컬러 teal 전환) · 점장 플레이어 폴리시(기본 앨범커버·재생상태 한 줄·다음 방송 미리보기·비인터랙티브 진행바·헤더 로고/고정·트랜스포트/큐 fill·큐 듀얼 스크롤) · 본사 지정 방송 스케줄링 강화(SPEC #139 — 반복 송출 주기 시각형 HOURLY/EVEN_HOURS/ODD_HOURS + 운영시간 startHour/endHour 발생 전개 일반화 · 등록 다이얼로그 주기 확장 · 시각×방송 타임테이블 시각화 · TTS 발화속도 tempo 방송별 파라미터(0.5~2.0, 기본 1.0)) · 점장 플레이어 곡 크로스페이드(큐 내부 3초)+더킹 곱 램프(인터럽트/긴급 위치보존 불변) · 점장 플레이어 보조 플로우 모달화(SPEC #140 — 즉시방송·자주쓰는방송·예약방송·플레이리스트·다음방송 미리보기를 페이지→Radix 모달로 → player 마운트 유지·오디오 끊김 0. 자주쓰는방송/예약방송 stub→broadcast/now 실기능(templates 탭·예약 모드) 연결, embedded prop으로 모달/페이지 공유) · 점장 플레이어 방송 동시재생 안정화(SPEC #141 — 안내방송 무한 반복 수정: dispatch 1회만 재생 보장·차단 집합 prune · 음악/방송 표기 분리: 우측 컨트롤 컬럼·커버=음악 전용, 방송 표기는 커버 아래 방송 카드로 이동 · 음악 진행바 상시 노출(방송 중에도 음악 진행 그대로) · 방송 카드 단일화(현재/다음 분기, 클릭 시 예약/대기 목록 모달) · TTS 방송 음소거 토글(소리만 0, 재생·ack 진행)) · 점장 플레이어 방송 카드 UX 정리(TTS 음소거 토글을 볼륨 옆→방송 카드 우상단 형제 버튼으로 이동(중첩 인터랙티브 회피·모달 비오픈) · [다음 방송] 모달 목록을 “이번 영업시간(오늘 KST 자정)까지 남은 예약”만 표시(scope="today" 필터), 별도 페이지는 전체 유지) · 점장 플레이어 daiso식 방송 동시재생 완성(SPEC #141/#142 — 안내방송을 음악 더킹+위에 동시재생(곡 정지 없음, daiso 오버레이 모델)·부드러운 더킹 램프(30ms ease-in-out)·곡 끝 대기 없이 즉시·긴급 preempt·resume/full-stop 제거 · 자동재생 시작 게이트(시작하기 dim+자동안내방송 토글)+진입 시 밀린 방송 비우기 · 곡 변경 시 간헐 무음 수정 · 뮤트 버튼 제목 왼쪽·활성 teal · TTS 시안 남성 정정·기본 음성 애란 · 예약 목록 [미리듣기]→[즉시 방송](원본 예약과 독립, BE broadcast-now endpoint)+오늘남은/전체 탭) · 베타 무블로커 배치(SPEC #143/#144 등 — 도래 후 미재생 PENDING 자동 만료 MISSED(스케줄러·운영통계 missed 반영) · 타임테이블 전체 페이지 집계 · 본사 원격 즉시중단 revoke(점장 player가 폴링 revokedDispatchIds 수신해 재생 중 즉시 정지) · rate limit HQ_CONTROL 그룹(cancel·revoke·예약) · 송출/예약 캘린더 view(본사·점장 월 그리드) · REGION 모드(매장 시/도 17 region 편집 + 송출 target REGION fan-out, 즉시·반복)) · 계정·인증 이메일(SPEC #145 — Azure ACS 이메일 인프라(EmailSender·NoOp fallback) + 토큰 링크 방식(SHA-256 해시·일회용·만료) · 계정 setup-link 활성화·비밀번호 재설정(존재 은닉)·이메일 변경 인증(새 주소 확인+기존 주소 알림) · 공개 페이지 양 앱·BFF 무세션 allowlist) · 계정·인증 이메일 디자인 템플릿 적용(BE v0.110.0 — 핸드오프 16 정합: 계정 활성화·비밀번호 재설정·이메일 변경 인증·기존 주소 알림 4종을 이메일-세이프 HTML 템플릿으로 교체 · 로고 EMAIL_LOGO_URL 옵션 env) · 공개 랜딩을 본사·점장 설득 마케팅 랜딩으로 재구성(FE v0.144.0 — design_17 시안 정합: 단일 공개 랜딩이 본사 잠재고객+점장+기존 로그인 셋을 대상 · 주 CTA=도입 문의 mailto·보조=점장 로그인 · 실제 제품 캡처 5종 라이트/다크 스왑 · 자체완결 teal 팔레트·다크 토글 · 인증자 redirect·public 라우팅 보존) · 정적자산 미들웨어 회귀 수정 + 랜딩 기본 다크(FE v0.145.0 — 미들웨어 matcher 가 /public 정적자산(/landing/*.png·아이콘·매니페스트)을 prod 에서 /login 으로 redirect 하던 회귀 차단(확장자 경로 제외) · 랜딩 기본 테마 다크(라이트 폴리시 전까지) · 히어로 제품샷 우선로드) · 운영사 이메일 템플릿 미리보기·테스트 발송(SPEC #146 — 계정·인증 이메일 4종을 운영사 백오피스 /settings/email-templates 에서 미리보기(iframe srcDoc + .force-dark 라이트/다크 토글)·테스트 발송(기본 대상 me.email·Azure 미설정 시 emailConfigured=false 미발송 안내·rate limit 5회/분), 실제 발송과 동일 EmailTemplates 재사용·read-only) · 로그인 세션 안정화 + 모바일 사이드바(FE v0.147.0/BE v0.112.0 — 로그인 풀림 근본 수정: RSC 에서 토큰 refresh 시 쿠키 저장 불가(ReadonlyRequestCookiesError)로 회전된 옛 refresh token 만 남아 15~30분마다 로그아웃되던 버그를 Next.js 공식 패턴(middleware proactive refresh + request/response 양쪽 쿠키 forward·RSC 읽기전용 가드)으로 차단 · 세션 토큰 수명 access 30분/refresh 14일 통일(임퍼소네이션 60분)·iron-session ttl 14일 정합 · 이메일 미리보기 preview/list rate limit · 모바일에서 사이드바 햄버거→drawer 접기, 데스크탑 무변경) · refresh token grace period(BE v0.113.0 — 멀티탭 동시 refresh race 해소: revoked 토큰이 grace 60초 내·만료 전이면 re-revoke 없이 새 토큰 재발급(impersonatedBy 보존)·grace 밖 401·상태검증→만료→grace 순서 · logout/change-password 는 hard-delete 로 grace 부활 방지) · 약관 동의 발급→계정 설정(첫 활성화) 이전(SPEC #147 — BE v0.115.0/FE v0.148.0: 본사·매장 발급 request 에서 consent 제거(LegalConsentPart 삭제)로 발급 마법사 step 감소(본사 5→4·매장 3→2) · setup/verify 응답에 활성 약관 버전·setup/complete 에 termsVersion/privacyPolicyVersion/signerName 추가 · setup 페이지(admin·space 양 앱)에 약관 동의 UI(항목별 체크·전체 동의·public 본문 “보기”·동의자 이름, atom-grounded) · OPERATOR·미게시 시 동의 단계 생략 · consent INSERT 가 발급이 아니라 계정 주인 본인의 setup 트랜잭션으로 이동 → 점장 계정도 본인 동의 단위) · 사용설명서 앱 apps/guide(FE v0.149.0 — 엔드유저 매뉴얼 운영사·본사·점장 29페이지·Playwright 자동 캡처·factcheck 동기화) + 운영사 앱 URL admin.space.linkmusic.io 정정 · 점장·본사 셸 공용 계정 메뉴(FE v0.150.0 — admin식 드롭다운 아바타→프로필/설정·로그아웃을 점장 player·모든 서브페이지·본사 topbar 에 일관 도입 · 본사는 이전까지 로그아웃 자체가 없었음 · 임퍼소네이션 sealed 세션에선 계정 메뉴 숨김(돌아가기가 종료) · me=generic useGetMe · StoreLogoutButton 삭제) · 보안 패치 2건(SPEC #149/#150 — BE v0.116.0/FE v0.151.0: 로그인 rate limit(POST /api/v1/auth/login 초과 시 429 RATE_LIMITED+Retry-After, BFF 가 자격증명 401 분기보다 먼저 그대로 surface·FE 폼 전용 안내 분기·과거 “5회 실패 15분 잠금” 거짓 문구 정정) · 운영사 앱(apps/admin) OPERATOR 전용화(잔존 본사 모드 placeholder 셸 /admin·components/hq-shell/ 제거, HQ_MANAGER 로그인 시 space.linkmusic.io 안내·차단, protected layout OPERATOR 외 전부 /login fail-closed) · 임퍼소네이션 민감 작업(비번·이메일 변경) 403 IMPERSONATION_FORBIDDEN_ACTION 차단. 임퍼소네이션 cross-app 핸드오프는 무영향) · rate limit IP 신뢰 모델 하드닝(SPEC #151 — BE v0.117.0: X-Forwarded-For 위조로 rate limit 우회 가능했던 것을 client-ip-strategy 플래그로 분리 — 기본 LEFTMOST(현행·배포 무변경) + RIGHTMOST_PUBLIC(오른쪽부터 내부 프록시 IP skip하고 첫 public IP, DNS-free IPv4/IPv6 파서·CGNAT/ULA 처리, 스푸핑 저항) · 로그인 본문 64KB 가드 · 운영 XFF 검증 후 env로 활성화·즉시 롤백) · 안내방송 삭제 cascade(SPEC #152 — BE v0.118.0: 안내방송 삭제 시 연결된 ACTIVE 반복예약·미래 예약 dispatch 를 함께 CANCELED 처리해 orphan 송출 차단(D7 버그 수정)·이미 도래한 PENDING 보존·스케줄러 deletedAt 가드 · 목록/상세 DTO 에 activeScheduleCount 추가 → FE 삭제 다이얼로그가 “반복예약 N건도 함께 취소됩니다” 경고(추가 fetch 없음)) · 에러/404 dead-end 바운더리(SPEC #153 — FE v0.153.0: 양 앱(apps/admin·apps/space)에 App Router error.tsx(렌더 예외 복구 [다시 시도]reset+홈 링크·role="alert"·error 상세 console-only 보안) · global-error.tsx(루트 layout 예외 최후 폴백, 자체 <html><body>·토큰 미의존 인라인) · 루트 not-found.tsx(전역 잘못된 URL) 신설. 기존 흰 화면 dead-end 차단 · admin (protected)/not-found.tsx(셸 유지 404)와 역할 분리·과배치 회피 · /audit 루트 → /audit/impersonation redirect(L4)) · 강제 비밀번호 변경 관문 탈출구(감사 H4 · FE v0.154.0 — /onboarding/change-password 강제 관문에서 임시 비번 분실·다른 계정 로그인 시 갇히던 dead-end 해소: [임시 비밀번호를 잊으셨나요?]→공개 /forgot-password, [다른 계정으로 로그인]→로그아웃 후 /login, 양 앱. 강제 변경 자체는 유지) · 공용 ErrorState atom + 에러 dead-end 일괄 해소(SPEC #154 · FE v0.155.0 — packages/ui 에 재시도형(onRetry=refetch/router.refresh)·복귀 링크형(actionHref) atom 신설(role=alert·semantic 토큰) · 목록 6화면(티켓·음원·라이브러리·플레이리스트·본사 매장·점장 CS)·본사 CS 목록/상세·점장 CS 상세·본사 대시보드·본사 매장 상세의 Banner(danger) dead-end 를 재시도 CTA 로 교체 · 본사 목록(hq-list)은 에러 시 툴바·테이블 숨겨 “0개” 빈상태 오동시노출 제거(매장 목록 패턴 통일)·점장 CS 부제 isError 분기 · 토큰 화면(setup·reset·email-change verify, 양 앱)에 만료/무효 시 로그인 복귀+비밀번호 재발급 링크 · 서버 컴포넌트용 RetryErrorState client 아일랜드) · 본사 발급 자격증명 전달 + 점장 프로필 이름 정정(SPEC #155 · FE v0.156.0 — 본사 온보딩 발급 성공 시 홈으로 즉시 이탈하던 dead-end 해소: 점장·운영자 발급과 동일한 AccountShareSection(임시 비번 복사 + [설정 이메일 보내기] managerAccountId + [본사 상세]/[완료·목록]) 단계로 전환 · 발급 마법사 step3/4 “등록과 동시에 메일 자동 발송·환영 메일” 문구를 실제 동작(수동 공유 + 설정 메일)에 맞게 정정 · 점장 프로필 이름 편집 초기값을 현재 매니저 이름 useGetMe().name 으로 seed + 저장 시 getMe·getStoreMe 둘 다 invalidate → 계정 메뉴 이름 즉시 반영(이전 “me 에 매니저 이름 없음” 주석은 stale 정정)) · 점장 첫 진입 약관·온보딩 게이트(SPEC #156 · FE v0.157.0 — 임시비번 직접 공유 경로(setup 메일 미경유)가 약관 동의·온보딩을 모두 건너뛰던 갭 해소: apps/space/onboarding/reagree 신설(admin 미러·role별 복귀 /store·/admin) + store·admin 셸 layout 이 pmc 통과 후 termsRequiresAgreement || privacyRequiresAgreement 게이트(C2 — HQ_MANAGER-in-space 재동의 갭도 해소, 임퍼소네이션 sealed 세션 제외) · /store 진입 얇은 client 게이트가 온보딩 완료 플래그(localStorage done) 미완료 시 /store/onboarding 으로 유도. BE 무변경(me 플래그·agree endpoint·훅 재사용), 온보딩 BE 백드는 후속) · 임퍼소네이션 만료 종착 통일(SPEC #157 · FE v0.158.0 — 감사 H6: 운영자 임퍼소네이션 60분 만료 후 /admin/* 서브페이지 이동·새로고침 시 서브페이지의 401→/login(운영자 자격 없는 space 로그인)에 갇히던 dead-end 해소: middleware/admin/* 요청에서 임퍼소네이션 쿠키를 unseal 해 refreshExpiresAt 경과면 전용 라우트 /impersonation-expired 로 앞단 redirect(+sealed 쿠키 삭제) → admin page 서버 컴포넌트가 아예 실행되지 않아 /login 낙착 원천 차단. App Router 는 page(children)를 client 조건 분기와 무관하게 서버 병렬 렌더하므로 layout·셸 단 차단으론 부족 → middleware 앞단 차단이 유일 견고. 정적 카운트다운 onExpire(idle)와 동일한 단일 만료 화면(ImpersonationExpiredScreen)으로 수렴 · 진입 시 sealed 쿠키 impersonation-exit 재정리(stale 방지) · “이 탭 닫기” close-fail 폴백(운영사 복귀 안내·NEXT_PUBLIC_ADMIN_ORIGIN 옵션 링크) · 배너 카운트다운과 동일 무마진 만료 정책(L1) · BE 무변경·backend 호출 0·실 HQ_MANAGER /login 회귀 없음) · 본사 관리자 계정 관리(SPEC #158 · BE v0.119.0/FE v0.159.0 — 지금까지 읽기전용이던 /hq/:id 계정 탭에 점장 계정 관리(#029)를 미러한 HqManagerManageDialog 도입: 행 [관리] → 비밀번호 재설정(resetHqManagerPassword → 임시 비번 발행·활성 세션 무효화, 성공 시 발급 흐름의 AccountShareSection 재사용해 복사·[설정 이메일 보내기])·정지·복구·회수·설정메일 재발송(resendHqManagerSetup) · ACTIVE/SUSPENDED 만 관리(WITHDRAWN terminal) · 정지/회수 2-step 사유 확인 · 에러 매핑 409 ACCOUNT_INVALID_STATUS_TRANSITION·404 HQ_MANAGER_NOT_FOUND·403 IMPERSONATION_FORBIDDEN_ACTION(#148, OPERATOR 전용) · 운영자 audit HQ_MANAGER_PASSWORD_RESET/_SUSPENDED/_REACTIVATED/_REVOKED 신규 · 온보딩 step3 “분실 시 재발급” 문구를 실재 경로(계정 탭 [관리])로 정정) · docs 드리프트 정합(SPEC #159 · FE v0.160.0 — 코드 동작 무변경, 문서·주석·라벨만: 이력 페이지(changelog·fe/be-releases)를 GitHub Releases 단일 소스 포인터로 대체(수기 이력 드리프트 재발 방지) · 토큰 수명 오기 정정(access 30분·refresh 14일·임퍼소네이션 60분 — auth-flow·refresh·login·change-password·glossary·overview·dtos·binding-constraints + apps/space/lib/refresh.ts 주석) · 존재하지 않던 임퍼소네이션 에러코드 IMPERSONATE_INVALID_TARGET → 실제 INDEPENDENT_HQ_IMPERSONATION_FORBIDDEN·AUTH_HQ_SUSPENDED·EXCHANGE_TOKEN_* (auth-flow·error-codes·hq list/impersonation) · 운영자 설정 “7서브”→“8탭”(email-templates 반영) · 대시보드 활동 피드 주석(audit 구현 반영) · /music 화면 제목 “음원 라이브러리”→“음원 카탈로그”(별도 라이브러리 메뉴와 혼동 제거)) · 송출/예약 신뢰성 4건(SPEC #160 · FE v0.161.0 — 감사 H9·H13·D3·D4, apps/space FE-only·BE 무변경: 본사 송출 다이얼로그 미리듣기 audio 로드 실패(blob 404·네트워크) 시 <audio onError>→media 에러 배너 + 다시 시도로 dead-end(영구 disabled·무안내) 해소, 게이트 유지(H9) · 점장 모달 예약 등록 “조용한 성공” 수정 — 즉시 송출은 소리 신호로 기존대로 닫되 예약은 즉시 닫지 않고 성공 배너 + [확인]으로 닫아 무신호·중복 등록 방지(예약/즉시 분기, H13) · 점장 점유 사전조회 size:100size:50(BE coerceIn(1,50) clamp 일치, 50건 초과 점유 슬롯 누락 방지, D3) · 본사 점유 409 후 선택 슬롯 scheduledTime=null 리셋으로 재-409 dead loop 차단 + 슬롯 그리드 stateClass 가 점유(hq/own/past)를 selected 보다 우선 평가(점유 슬롯이 “선택됨”으로 안 보이게·aria 일치, D4)) · 송출/예약 UX 정합 6건(SPEC #161 · FE v0.162.0 — 감사 D1·D2·D5·D6·D8·D9, apps/space FE-only·BE 무변경: 본사 예약 송출 time 입력 step=300(5분) + 5분 정렬·과거/임박 제출 차단(점장 그리드 규칙 정합)·자동 채움 기본 시각도 다음 5분 경계로 올림(자기모순 미정렬 에러 방지, D1) · 송출 대상 매장 total>로드 100이면 잘림 경고 + INACTIVE/SUSPENDED/폐점 매장 선택 비활성(사유 aria)·[전체선택] ACTIVE만·refetch로 정지된 stale 선택 자동 prune(송출·반복예약 두 다이얼로그 공용 store-dispatch-target.ts 단일화, D2) · REGION 송출 0건 문구를 ‘선택한 지역에 매장 없음’으로(전체 매장 부재와 구분, D5) · 반복예약 주기×운영시간 발생 0건 조합 제출 차단(D6) · 반복예약 목록 제목 폴백 통일·마지막 페이지 취소 후 빈목록 이전복귀·등록 배너 자동 dismiss·타임테이블 에러 ErrorState(D8) · 레거시 broadcast 라우트 {schedule,templates}/store?modal= 딥링크 모달 오픈(빈 홈 dead-end 제거, D9)) · 본사 오늘 미도달 매장 카드 드릴다운(SPEC #162 · BE v0.120.0/FE v0.163.0 — 감사 H11: 본사 대시보드 ‘오늘 송출 미도달 매장 N곳’ danger 카드가 /admin/audit(delivery 필터 없음)로 보내 어느 매장인지 볼 수 없던 dead-end 해소 · BE 신규 GET /api/v1/hq/dashboard/undelivered-today/stores(오늘 KST PENDING dispatch의 distinct 매장 목록·JOIN Store 이중 격리·매장명 정렬·페이지네이션, count 쿼리에도 동일 JOIN 추가로 count/list envelope 정합·#119 과다집계 해소) · FE 카드 count>0=버튼→미도달 매장 다이얼로그(목록·[매장 상세] 링크·ErrorState 재시도·빈상태·페이지네이션), count0=비링크(false affordance 제거)) · space 토스트 호스트 통일(SPEC #163 · FE v0.164.0 — 감사 B2: 운영사만 useToast 쓰고 본사·점장(apps/space)은 ToastHost 미마운트라 액션 피드백이 인라인뿐이던 비대칭 해소 · ToastHostProvider/useToastpackages/ui로 승격(단일 소스, admin 로컬은 재-export로 축소·동작 불변) + space 두 인증 셸(store·admin layout)에 마운트 + 일회성 성공 피드백(프로필·플레이리스트·본사 설정/매장/CM/CS 저장·상태변경 등)을 토스트로 전환 · 에러/ErrorState dead-end·폼 검증·H13 예약 배너·지속 상태는 인라인 유지) · 발급 이탈·역할차단 링크·제목 오버플로(SPEC #164 · FE v0.165.0 — 감사 C4·C5·E3: 운영사 발급 마법사(본사·매장)에 [목록으로] 이탈 경로 추가(입력 있으면 유실 확인 다이얼로그·빈 단계 즉시 이동, 발급 성공 화면 제외) · space 로그인 OPERATOR 차단 안내에 운영사 백오피스 링크(NEXT_PUBLIC_ADMIN_ORIGIN, admin↔space 대칭) · 점장 StoreSubHeader 긴 제목 truncate+title(계정 메뉴 밀림·가로 스크롤 방지)) · 운영사 비기능 placeholder 정리(SPEC #165 · FE v0.166.0 — 감사 B6·B7: 정직성 원칙으로 “준비 중” toast 만 띄우던 비기능 UI 제거 — 사이드바 ‘정산’ nav 항목 + /billing ComingSoon 페이지(billing/PG 확정 스코프 제외) · 운영사 topbar ⌘K 전역검색·장애벨(전역검색·incident 도메인 부재) 제거. 도메인 도착 시 후속 SPEC 재도입) · 셸 일관성 3건(SPEC #166 · FE v0.167.0 — 감사 B1·B8·B3: 점장 셸에 공용 ThemeToggle 추가(본사 hq-topbar 미러 — player 헤더·StoreSubHeader 계정 메뉴 옆, 점장도 라이트/다크 전환 가능, B1) · 운영사 대시보드 6 stat 카드를 관련 목록 드릴다운 링크로(활성/신규/미납 본사→/hq·전체 매장→/stores·임퍼소네이션→/audit/impersonation·미해결 티켓→/tickets?status=OPEN, 키보드·aria, B8) · 본사 hq-sidebar 구독·결제 disabled span 제거(billing 확정 스코프 제외·B6 를 본사 셸로 확장, LLM 자동멘트는 스코프 안·미구현이라 disabled 유지, B3)) · 운영사 티켓 category 설정·변경(SPEC #167 · BE v0.121.0/FE v0.168.0 — 감사 E1: 운영사 티켓 목록엔 category 필터·컬럼·배지가 있고 점장 제출 티켓은 category를 갖는데 운영자 생성 폼엔 category가 없고 상세에 변경 컨트롤도 없어 운영자 티켓이 영원히 ’—‘였던 것 해소 · BE TicketCreateRequest.category 옵션 + PATCH /api/v1/admin/tickets/{id}/category(changeStatus 동형·audit TICKET_CATEGORY_CHANGED·마이그레이션 없음) · FE 생성 다이얼로그 분류 select(옵셔널·미분류 기본) + 상세 분류 변경 ActionCard(미분류=null 해제·현재값 no-op·refresh)) · 점장 플레이어 임시 단순화(SPEC #168 · FE v0.169.0 — 사용자 요청, apps/space FE-only: 하단 재생 큐 목록·곡 수·곡 앞뒤 이동(이전/다음) 버튼을 렌더 토글 SHOW_HIDDEN_PLAYER_SECTIONS로 임시 숨김(코드·로직 유지·즉시 복원) — 단 재생할 게 없을 때 dead-end 안내+CTA(SPEC #118)는 || emptyState로 유지 · 볼륨 슬라이더 좌측 음량 아이콘을 음악 음소거 토글 버튼으로(VolumeX/Volume2·border·<audio muted> 슬라이더/더킹 독립) · 플레이리스트 선택 버튼·모달 유지(모달 내 곡 수만 숨김) · 하단 큐 여백은 세로 중앙정렬(my-auto+overflow, 넉넉하면 중앙·부족하면 스크롤) + 배경 브랜드 teal radial glow(장식·blur)) · 매장 재생 모델 정리(SPEC #169·#170 — 사용자 확정: (169·BE) 플랜 게이팅 정확일치→포함(계층) — TRUST plan(상위)은 TRUST+AI 모두 재생·AI plan은 AI만·plan null은 전곡(StorePlaybackQueueService 필터 계층화) · (170·FE) 운영사 매장별 활성 PL 지정(적용/해제) 제거 — 본사는 기본 PL만 정함(모든 매장 통합)·매장 재생 PL 선택은 점장 player(#129)에서만·본사 매장 상세 읽기전용 활성 PL 표시는 유지. 운영사 active-playlist BE 엔드포인트는 orphan(후속 cleanup)) · 점장 플레이어 무음 근절(점장 플레이어 감사 2026-07 SPEC A · FE-only·BE 무변경 — 고객사 제보 “한 번 재생되고 더 안 된다”의 근본 원인 3종 수정: (1) 1곡 큐 영구 무음(감사 #7) — 재생 재개 트리거가 musicSrc 문자열 변화뿐이라 큐 소진 refetch 후 0번 곡 URL 이 직전과 같으면 play() 가 영영 재호출되지 않고 <audio> 가 ended 로 주차되던 것을, queueEpoch 세대 state 를 재생 effect deps 에 넣고 “세대만 바뀌고 URL 은 동일”이면 되감아 재생 재개(N≥2 동작 불변·회귀 테스트 보장) · (2) 깨진 트랙 1개로 영구 사망(감사 #1) — 메인 <audio>onError 신설(실패 musicId 마킹 후 다음 곡 스킵, 연속 실패가 큐 길이 도달 시 스킵 중단 + danger 배너 + 지수 backoff(15s→30s→60s·cap 5분) refetch 로 무한 루프 차단(고정 간격이면 큐 전체가 깨진 매장 한 대가 무기한 수십 rps), 정상 로드 시 자가복구 리셋) + play() reject 원인 분기(NotAllowedError=자동재생 정책 → 시작 오버레이 재노출·사유 문구·[계속 재생], AbortError 등 그 play() 가 pause()/새 load() 로 대체된 경우 → 무시(곡 전환 직후 [일시정지]·[다음] 연타는 트랙 결함이 아니다), MediaError 가 실제로 있는 경우만 → 실패 마킹·스킵. 종전엔 전부 setPlaying(false) 로 삼켜 화면 무표시. 판정은 메인·크로스페이드 incoming 이 공유하는 classifyPlayRejection 단일 함수 + 늦게 도착한 stale rejection 을 거르는 재생 세대 토큰 + onError·reject 이중 발화 dedupe 로 오분류/이중 카운트 차단) · (3) 보조 액션 모달화 누락분(감사 #5) — 헤더 [고객지원]·계정 메뉴 [프로필]만 라우트 이동이라 player 언마운트로 음악이 끊기던 것을 기존 모달 3종과 동일 패턴으로 전환(고객지원은 모달 안에서 목록↔상세↔작성 뷰 전환·ShellAccountMenuonProfileSelect prop 주입 방식이라 본사·서브페이지 사용처 무영향) · [다음] 버튼 복원(토글 없이 상시 노출 — stall 시 새로고침 말고 탈출구가 없던 악화 요인 제거. [이전]·큐 목록은 계속 숨김) · 밀린 방송 drain 을 pending 첫 성공 응답 시점으로 이동(로딩 중 [시작하기] 시 no-op drain 으로 가드가 닫혀 영구히 drain 불가가 되던 창 제거 · drain 완료 전 방송 재생 차단 게이트) · 모달 안 티켓 작성 후 목록 캐시 무효화(staleTime 30초로 방금 만든 티켓이 안 보이던 것) · 계정 메뉴 [프로필] 선택 시 드롭다운 정상 닫힘(모달만 여는 주입 경로에서 preventDefault() 제거 — 모달 뒤 focus trap 중첩 해소) · 테스트 픽스처 makeItem/makeAnnouncement/makeCommercialaudioUrl 을 id 파생으로 바꿔 “곡 전환 단언이 전부 항등식”이던 구조적 은폐 제거 · 세션·프록시 정합(점장 플레이어 감사 SPEC B · FE v0.173.0 — apps/space·apps/admin FE-only·BE 무변경: (1) catch-all BFF proxy 가 대상 backend 경로로 세션 스코프를 명시 선택(getActiveSession({ scope }) — 기본값 없는 필수 인자) → api/v1/store/** 는 임퍼소네이션 cookie 를 읽지 않고 실 로그인 세션만 forward. 같은 브라우저에 운영사 임퍼소네이션 세션이 공존하면 점장 호출이 본사 토큰으로 나가 backend hasRole("STORE_MANAGER") 가드에 403 → 60분 창 내내 자가복구 불가(그 사이 PENDING 방송이 grace 초과로 MISSED 영구 소실)하던 P0 차단 · (2) refresh 실패를 invalid(refresh 401/403) vs transient(5xx·네트워크)로 분류하는 RefreshResult 도입 — transient 는 세션을 죽이지 않고 502 BACKEND_ERROR 로 surface(무인 매장 기기가 일시 장애로 /login 에 갇히던 것 차단). 판정 정책은 middleware tryRefresh 단일 소스, proxy·/api/auth/me·/api/auth/change-password·RSC 로더 전부 일치 · (3) /api/backend/* proactive refresh 를 middleware 에 추가(점장 player 는 모달 전환이라 페이지 네비게이션이 없어 XHR 이 유일한 갱신 기회 — 실패해도 세션·쿠키는 건드리지 않고 proxy 반응형 401 에 위임) · (4) catch-all proxy 에 CSRF 동일 출처 검사(GET/HEAD 외 전 메서드 assertSameOrigin, /api/auth/* 와 동일 정책·심층 방어) · (5) 보안 리뷰 후속 하드닝 — 스코프 판정을 화이트리스트 반전으로 전환(impersonation-awareapi/v1/hq/**·api/v1/auth/me 만, 나머지 전부 real-login). 점장 셸 신원 출처를 실 세션 고정 BFF /api/auth/me(lib/use-me.ts)로 이전해 임퍼소네이션 신원 노출·cross-identity 이름 저장 차단, api/v1/legal/** 실 세션 고정으로 약관 개정 직후 점장 재동의 403 영구 차단 해소, catch-all 경로 dot-segment 400 INVALID_PATH 거부(양 앱), refresh 쿠키 재봉인 실패를 transient 대신 즉시 destroy + invalid 로 fail-closed(reuse guard 계정 전체 revoke 차단), 운영사 보호 page 9곳의 transient 정책을 재시도 배너로 통일). · 방송 파이프라인 정합(감사 SPEC C — FE분: CM 사이클 카운트/임계치 검사를 곡 종료 공용 콜백 onSongCompleted 으로 통합해 크로스페이드 swap 경로에서도 발화(큐 1랩당 1회 → 곡당 1회, 최대 100배 결손 해소) · 방송 폴링 refetchIntervalInBackground: true + 탭 복귀 즉시 invalidate(탭 hidden 10분 → BE MISSED 영구 소실 차단) · 방송 오버레이 워치독(재생 진행 정지 25초 = stall 만 강제 종료 — 더킹 영구 고정·이후 방송 불통 해소. durationSeconds 상한은 정상 재생을 자르는 오탐이라 제거) · 즉시방송 모달 결함 6종(진행 중 닫기 잠금 + 30초 상한·경고 · 닫기(X) 버튼 5모달 · 즉시 송출 토스트 피드백 · 미리듣기 만료 404 게이트 초기화 · 예약 성공 목록 invalidate · 점유 조회 로딩/실패 표시)) · 방송 ack 재생 결과 보고(감사 #11 — BE+FE: ack 선택 본문 {outcome} 3종(PLAYED·FAILED·SKIPPED) · 재시도 정책 BE 소유(FAILED 3회 미만이면 PENDING 유지·재전달, 3회째 MISSED/PLAYBACK_FAILED) · FE 는 자체 재시도 없이 실패마다 그대로 보고하고 실패 dispatchId 영구 블랙리스트를 쿨다운으로 교체(재전달분이 다시 재생됨) · backlog drain=SKIPPED + 긴급은 drain 제외·시작 직후 재생 · 긴급 실패 배너 허위 카피(“본사로 자동 보고됩니다”) 정정 · 본사 송출 이력에 미재생 사유(만료/재생 실패/건너뜀)·“재시도 중 · 실패 N회”·집계 chip 미재생/취소(5종 합==전체) · 본사 오늘 미도달 집계를 PENDING+MISSED · 본사 송출 한정(is_hq_origin)으로 정정(#11-a))) · 큐 안정화(감사 SPEC D — #12·#20: 큐 교체 시 musicId 앵커로 인덱스 재매핑(재생 중이던 곡이 그대로 이어짐 — 고객사 제보 “곡이 끝나면 자꾸 다른 플레이리스트로 넘어가는 느낌”의 정체는 PL 전환이 아니라 재셔플 + 미재매핑) · 큐 소진/[재시작]/backoff 교체는 계약대로 0번부터 · 큐 쿼리 refetchOnReconnect 를 실패한 큐로 한정(재연결마다 재셔플 큐가 도착해 곡이 갈리던 경로 차단) · 큐 30분 주기 refetch(백그라운드 포함 — 본사 PL 편집이 하루 넘게 반영되지 않던 구멍을 닫음. 재매핑이 있어 중간 교체가 안전해진 뒤 도입) · 활성 PL 적용/해제는 새 목록 0번부터(적용해도 아무 일도 안 일어나 보이던 문제) · 일시정지가 크로스페이드도 중단(정지 상태에서 곡이 하나 넘어가던 문제) · 램프·stall 워치독 기준 시계를 performance.now 로(24/7 단말 NTP 보정·시각 변경에 페이드가 끊기거나 정상 방송이 강제 종료되던 경로 차단) · 리셋 의도 만료를 성공/실패 응답 모두에(소진 refetch 실패 시 의도 고착 → 이후 교체 오분류 차단) · 더킹·크로스페이드 램프 경과시간 보간(백그라운드 1초 타이머 클램프에서 400ms 더킹이 14초가 되던 문제 + 진행 정지 시 크로스페이드 미완료 stall 차단)) · 예약·온보딩 정합(감사 SPEC E — FE-only: #9 본사 점유 사전 조회 GET /api/v1/store/slot-occupancy(useGetStoreSlotOccupancy) 연동 — 종전엔 BE·생성 클라이언트에 있는 endpoint 를 FE 만 안 써서 본사 점유 시각이 “예약 가능”으로 보였고, TTS 미리듣기(과금·수 초)까지 마친 뒤 send 409 로만 거절됐다(그 학습마저 모달 닫으면 소실). 이제 슬롯 그리드가 송출 전에 비활성 표시하고 409 는 조회 이후 race 보정으로만 남는다 · #10 온보딩 데드엔드 3종 — 미완료 상태의 [이전] 핑퐁을 [로그아웃]으로 교체 · 비-Chromium 영구 차단에 전용 안내 + 이탈 경로 제공 · writeProgress/mergeProgress 가 저장 성공 여부를 반환하고 실패를 danger 배너로 노출 + StoreEntryGate 세션당 리다이렉트 1회 제한(2회째 fail-open + 경고)으로 무한 리다이렉트 근절) · 점장 플레이어 감사 마무리(SPEC F·G) — BE 무결성: prod·staging Swagger//v3/api-docs 비활성(계약 전량 노출 차단, /sync-api 는 로컬 bootRun 소스) · /actuator/health storage 컴포넌트(OPERATOR 만 상세 · storage.azure.required 기본 false 로 부분 장애를 전면 장애로 확대하지 않음) · 재생 큐 상한을 DB 레벨로 이동 · CM 라운드로빈 커서 원자적 compare-and-set + 재생 불가 CM skip · store.active_playlist_applied_at 전용 컬럼(V52 — 무관한 매장 갱신에 끌려다니지 않음) · 큐 reason=UNPLAYABLE_SOURCES 신설. 문서 정합: Blueprint 죽은 앵커 71건 + 사용설명서(guide) 죽은 앵커 6건 일소 · 폐기된 “곡 끝 삽입 재생” 모델 잔존 정리(docs·guide·소스 주석) · 본사/점장 surface matrix 전면 재정합(사이드바 9/10 enabled·REGION 송출·5분 슬롯 그리드·store endpoint 현황) · 유령 testid/문구 제거 · baseline 5곳 통일 + CI docs-lint 결정적 가드(docs·guide 두 앱 앵커 + baseline 자동 스캔, h1 제외로 nextra 슬러그와 정합) · 점장 플레이어 재감사 후속(2026-07 followup · FE v0.179.0 — apps/space FE-only·BE 무변경: (N1) 빈 큐 + 방송 재생 중 빈 상태 [고객지원 문의] CTA 가 라우트 이동(<Link>)이라 player 를 언마운트해 방송이 끊기고 ack 가 누락되던 유일한 실사용 결함 해소 — 다른 보조 액션과 동일하게 support 모달을 작성 뷰 + PLAYBACK 사전선택(StoreTicketNewClientinitialCategory prop)으로 열어 player 마운트 유지 · (R1) handleRestart·handlePlaylistApplied 진입부에 abortCrossfade() 선행(다른 네 컨트롤과 관례 통일 — 크로스페이드 창에서 [재시작] 시 stale index swap·볼륨 글리치 차단) · (D1) runCrossfade early-return(!incoming)에서 abortCrossfade() 방어적 리셋(현재 도달 불가지만 crossfadingRef 고착 시 handleMusicEnded 영구 무음 불변식 방어). 회귀 테스트 2종(N1 언마운트 방지·R1 abort) 추가. D2(메인 트랙 stall 워치독)는 복구 액션·실패 backoff 통합이 필요해 별도 SPEC 로 백로그 유지). · 시간대별 라이브러리 스케줄링(SPEC #171 · BE v0.126.0/FE v0.181.0 릴리스 — 음악 2계층 위에 “이 시간대엔 이 라이브러리를 튼다”를 하루 30분 단위 48칸 시간표로 얹음: 운영사(OPERATOR) 가 PL 마다 본사 기본 시간표를 편집(apps/admin PL 상세 [시간표] 탭)하고 본사(HQ_MANAGER) 는 읽기 전용으로 열람(apps/space PL 상세 [시간표] 탭·“본사 · 읽기 전용” 배지) · 점장(STORE_MANAGER) 은 매장별로 본사 기본을 복사·수정(override, /store/schedule) · 편집기 본체는 세 페르소나 공유 @linkmusic/ui ScheduleGridEditor(48칸 그리드 페인팅·겹침 구조적 불가·빈칸=전체 셔플) · 시간표 없으면 전 시간대 플레이리스트 전체 셔플로 회귀(리스크 0) · is_default PL 시간표는 산하 매장 기본 스케줄이 되며 점장 커스텀 매장은 override 우선 · V53 playlist_library_schedule·V54 store_library_schedule(30분 배수 CHECK + int4range EXCLUDE 겹침 DB 최종 방어) · PL 기본 @minItems 0(빈 배열=해제 허용)·매장 override @minItems 1) · 매장 음악 재생 가시성(#172 — 점장 player 가 재생 상태·곡 로그를 POST /store/playback/report 로 보고(fire-and-forget·오디오 파이프라인 무간섭, report 실패해도 재생 계속) · 본사 대시보드 “재생 상태” 카드(지금 재생 중/무음/offline·무음·offline 드릴다운, 미도달=방송 옆 음악 축) · V55 play_log(신탁 신고 근거·전량)·store_playback_status(heartbeat·OFFLINE staleness 파생)·playback_daily_rollup) · 본사 하위 매장 CS(#173 — 본사가 산하 매장 점장 CS를 조회·답변·상태 변경(표준 전이 전체), assign·우선순위는 운영사 전담 · 별도 화면 /admin/store-support + 사이드바 ‘매장 문의’ 미해결 수 count 배지·새 활동 dot · 점장 티켓에 소속 본사 hqId 부여(신규 생성 시·기존 V56 백필)로 과노출 차단 정책 변경, 기존 본사 CS엔 storeId IS NULL 가드로 격리 보존 D4 · 임퍼소네이션 세션 네비바 storeCount 실값 주입 D5)) · 본사 CM송 등록·교체 파일 업로드(SPEC #174 — CM송 등록을 음원 URL 직접 입력 → MP3 파일 업로드로 전환(등록 POST /hq/commercials multipart·교체 신규 PUT /hq/commercials/{id}/file multipart) · 파일 선택 시 Web Audio 로 재생 시간 자동 추출(클라)·서버가 MP3 magic byte·20MB·비어있지 않음 검증(as-is 저장·ID3 rewrite 미경유) 후 Azure blob 저장·audioUrl 자동 채움 · 상세 화면에 [오디오 교체] 섹션 추가(같은 blob 덮어쓰기·duration 재추출, 제목·활성은 편집 유지) · audit HQ_COMMERCIAL_CREATED·HQ_COMMERCIAL_FILE_REPLACED(대상유형 COMMERCIAL) 신설 · 파일 문제 400 COMMERCIAL_INVALID_FILE vs title/duration 범위 검증 400 분리 · 새 env 없음)) · 본사 재생 리포트(SPEC #175 · #172 FU-2 — 롤업 playback_daily_rollup 위에 조회 endpoint GET /hq/playback/summary?bucket&from&to(일/주/월·전사 total·전사 버킷별 series·매장별 stores·신탁 분리·폐점 포함·범위 상한 1년) + 본사 전용 페이지 /admin/playback: 기간 컨트롤(bucket 일/주/월 토글 + from·to date, 기본 DAY·최근 30일·366일 클라 사전검증) · 전사 KPI(총 재생시간 h:m·곡수·신탁 비율) · 커스텀 SVG 추이 차트(신탁/일반 스택 막대·운영사 signup-trend 패턴 미러·차트 라이브러리 없음) · 매장별 테이블(재생시간 desc·매장 상세 링크·ErrorState·빈상태) · 대시보드 ‘재생 상태’ 카드 [기간별 보기] 링크 + 사이드바 ‘재생 리포트’ 진입 · 리포트라 refetchIntervalInBackground:false · 준수율(영업시간 대비)·CSV 는 후속 · 새 env 없음)) · 본사 대시보드·재생 리포트 폴리시(FE-only·BE 무변경 — 대시보드 정산 준비중 placeholder 정리: “활성 CM송”→useListHqCommercials(isActive) total 실데이터 카드(→/admin/commercials)·“대기 문의”→산하 매장 CS 미해결 수(사이드바 배지와 같은 unread signal openOrInProgressCount) 실데이터 카드(→/admin/store-support)·“구독·결제”(billing) 카드 제거(스코프 제외) · 재생 리포트를 일/주/월/커스텀 4탭으로 재구성(일=날짜 피커 하루·추이 차트 생략·주=월일 드롭다운·월=1일말일 드롭다운·커스텀=기존 bucket 토글+from/to, 전 탭 같은 summary endpoint·주/월 경계 KST 헬퍼) · 재생 상태 카드 드릴다운을 전체 매장 상태별 그룹(무음·offline 먼저·재생중 포함)으로 확장 + total>0 이면 전부 재생중이어도 열람 가능)) · 신탁 재생 로그 뷰어(SPEC #176 · #172 FU-3 — play_log(#172 전량 적재·곡당 1행) 위에 조회·CSV endpoint 4종: 운영사 GET /admin/playback-logs(+/export, 플랫폼 전체·격리 없음·hqId/storeId 좁힘) · 본사 GET /hq/playback-logs(+/export, hqId 격리·자기 산하만) · 필터 기간(필수·366일 상한)·신탁 토글(기본 신탁만)·본사/매장 · 정렬 started_at desc → id desc · 서버 CSV(상한 5만·X-Export-Truncated·BOM·injection guard·trust-playback-logs-{ts}.csv KST) · 운영사 placeholder 실기능화(/settings/trust-playback-logs 본사→매장 cascading select) + 본사 신규 화면(/admin/playback/logs·사이드바 ‘신탁 재생 로그’) · CSV 는 BFF catch-all blob 스트림(downloadCsvFromBackend) · 신탁 음원 KOMCA 신고 근거(공식 신고 포맷은 외부 규격 게이트·후속) · 마이그레이션·새 env 없음)) · 매장 멀티 기기(SPEC #178 · develop — 릴리스 전: 한 매장 PC 여러 대가 같은 계정을 공유하는 현실을 데이터 모델에 반영 — 브라우저별 localStorage UUID 를 서버에 등록(멱등·매장당 4대·회수는 점장 수동)하고 deviceId 를 큐·재생 보고·방송 ack 에 실어 기기별 활성 PL(선택이 이 PC 에만 적용)·기기별 재생 상태/play_log(마지막 보고 기기가 앞 기기를 덮어쓰던 것·신탁 근거 왜곡 해소)·기기별 방송 ack(첫 기기만 204·나머지 404 였던 것 → 한 대라도 PLAYED 면 종착, 전 기기 실패일 때만 실패 경로)로 분리 · pending 조회 기기 인지로 전 기기 재생 보장(먼저 폴링한 PC 가 재생을 마치면 늦은 PC 가 방송을 놓치던 문제) · 신규 화면 /store/devices + player 상한 배너 · 저장소 차단 기기는 등록 생략 후 매장 단위 폴백(음악 정상) · 전 계약 deviceId 옵션이라 구버전 클라이언트 무영향 · 잔여 FE-4 = playAt 동기 재생·SSE 구독)) · 음원 전송 캐시(SPEC #179 · develop — 릴리스 전: 음원 대역폭(12h 매장 월 ~21GB·멀티 4대 3배 적자)을 클라이언트 캐시 적중으로 자릿수 절감 — BE-1 = 교체 새 UUID key(D1·URL=내용 주소)·업로드 Cache-Control immutable/TTS no-store(D2·D3)·report cacheHit(D10·V67) + FE-1 = 서비스워커 없는 앱 레벨 캐시 계층(fetch(cors)→Cache Storage→objectURL·렌더 시점 동기 src 확정으로 재생 타이밍 불변·트랙 슬롯 소유 revoke·다음 곡 1곡 프리페치·3GB LRU·fail-open(CORS 미설정/API 부재/quota 전부 원격 폴백 — 재생 인질 금지)·손상 캐시 자가 복구·cacheHit 실측 보고) · 잔여 = Azure CORS 룰 + 기존 blob Cache-Control 백필(I1·I2 운영 액션)·1주 실측(M1 egress·M2 적중률 80%+ 목표) · 새 env 없음).

SPEC 별 상세 이력은 Changelog · 화면별 구현 현황은 HQ·Store Surface Matrix · 잔여 도메인은 Pending Features.

페르소나별 구현도

세 페르소나(운영사 · 본사 · 점장)의 surface 구현 진행도. 각 막대는 추정치이며 페르소나별로 독립적인 별개 surface 라 합계·평균은 의미 없다(서로 다른 앱·범위). 근거는 현재 코드 현황 기준.

운영사 백오피스 (OPERATOR) · Surface 1099%
현재: 인증·온보딩(SPEC #147 — 발급 마법사에서 약관 동의 step 제거, 본사 4-step·매장 2-step. 동의는 계정 주인 본인의 setup 단계)·로그인 rate limit 안내(429 RATE_LIMITED·#149)·운영사 앱 OPERATOR 전용화(본사 placeholder 셸 제거·HQ 로그인 시 space 안내, #150)·본사/매장/운영자/점장 계정 관리(발급 성공 시 임시 비번 복사 + 설정 메일 발송 안내 — 본사 온보딩 포함, SPEC #155) · 본사 관리자 계정 라이프사이클 관리(/hq/:id 계정 탭 [관리] — 비밀번호 재설정·정지·복구·회수·설정메일 재발송, 임퍼소네이션 403 차단, SPEC #158) · CS 티켓(점장 store ticket 식별·트리아지 포함, 점장 CS end-to-end · **첨부파일** 업로드/다운로드 — BE PR #324·#325: INTERNAL/공유 가시성 배지 + 「첨부 대상」 선택(내부 메모에 붙이면 운영사만 열람) + 10MB 전 구간 업로드) · 감사 로그(임퍼소네이션·운영자·본사·매장 audit 통합 view + CSV 내보내기 5종) · 약관/개인정보 · 통계 대시보드·전용 통계 페이지(/stats) · 음악 2계층 편집(음원 카탈로그·라이브러리·플레이리스트·매장 적용·본사 기본 PL·음원 타입 enforcement·상태 파생 배지) · PL 시간표 편집(SPEC #171 develop — PL 상세 [시간표] 탭·48칸 그리드로 본사 기본 시간표 편집·전체 교체 저장·빈 시간표=전체 셔플 허용·is_default PL 산하 매장 파급 경고·stale 라이브러리 방어) · 사이드바 미해결 CS 카운트 배지 · 협약 동의 이력·상태 조회(/contracts) · 운영자 설정 8탭 스캐폴딩 + 내 프로필 내실화 + 장르/무드 옵션 CRUD(/settings/music-options) · 운영 통계 CSV 내보내기(/stats — 지표 snapshot·30일 추이, 클라 사이드) · TTS 연결 확인 smoke-test(/settings/notifications — Typecast 실토큰 검증·503/502 구분) · 이메일 템플릿 미리보기·테스트 발송(/settings/email-templates — 계정·인증 이메일 4종 iframe srcDoc 미리보기·라이트/다크 토글·테스트 발송, read-only) · **매장 등록 마법사 = 점장 계정 발급(SPEC #184 — 담당자 3종이 「점장 계정」 섹션으로 승격되고 이메일이 곧 로그인 ID. 등록과 계정이 한 트랜잭션이라 두 이메일이 갈릴 여지가 구조적으로 사라졌다. [점장 미정으로 등록] 건너뛰기 · 기본 소속 프리셋 제거(미선택 강제 — 소속 미지정 매장이 가상 본사로 영구 고정되던 경로 차단) · 등록 완료 화면에서 응답 storeId 로 매장 상세 연결 + 설정 메일 발송 실패 시 재발송 · 매장 목록 「점장 계정: 미발급」 필터(회수 후 재발급 진입점 복원) · 잔존 발급 다이얼로그에 매장 연락 이메일/이름 프리필)**. 잔여: 정산(스코프 제외)·LLM 자동멘트(`/settings/llm-ments` placeholder)·시스템 정책(`/settings/system-policies` placeholder)·**장애 알림·incident 도메인**·**운영사 매장 재생 상태 뷰(본사엔 있고 운영사엔 없음)**·CM송 운영사 view·PDF 내보내기·음원 메타 enrich·통계 기간 선택(핸드오프 대기)·status MISMATCH 배지(큐 필터 자체는 #122·#169 완료). **신탁 재생로그는 SPEC #176 으로 도착**(2026-08-05 note 정정 — 종전 잔여 표기는 stale). 화면별 상세: Surface Matrix(features/hq·store/surface-matrix), 잔여 상세: Pending Features.
본사 모드 (HQ_MANAGER) · Surface 1184%
현재: apps/space 부팅·로그인 라우팅·HQShell·임퍼소네이션 배너·본인 프로필·비밀번호 강제변경 관문 · 대시보드 실데이터 · 산하 매장(목록·상세 조회 + 신규 등록·인라인 편집·CSV 일괄 등록) · 플레이리스트 조회·라이브러리 조회(목록·상세, read-only) · PL 시간표 편집(SPEC #171 — PL 상세 [시간표] 탭에서 본사 기본 시간표를 직접 편집·저장. 2026-08-06 note 정정 — 종전 「읽기 전용」 기재는 stale, 코드는 편집 그리드+저장 바를 렌더한다) · TTS 안내방송(생성·수정·톤 프리셋·삭제) · 본사→매장 송출(ALL/STORES 개별선택·미리듣기 게이트·예약 송출) + 송출 이력(actor·재송출·취소·그룹핑·일괄 액션·매장명 검색·송출 요약) · 본사 audit(생성/수정/삭제·CS 티켓·매장 등록·편집 + CSV·행위자 select) · CM송 관리(목록·등록·상세 + 빈도 설정·매장 override·라운드로빈) · 계정 설정(프로필·매니저 이름 편집·비밀번호 변경) · CS 티켓(목록·작성·상세·댓글 + 상태/우선순위 변경 + 새 답변 dot + **첨부파일**(본인 발신·하위 매장 양쪽, 10MB 전 구간 업로드) — BE PR #324·#325) · 대시보드 bootstrap 체크리스트·오늘 송출 미도달 매장 카드(PENDING+MISSED·본사 송출 한정) · 송출 이력 미재생 사유·재시도 중 표기 · 재생 더킹 설정(본사 default + 매장 per-field override, design_15 시안) · 반복 송출 예약 관리(주기 시각형 HOURLY/EVEN/ODD·타임테이블 전체집계) · 송출/예약 캘린더 view(월 그리드) · 송출 원격 즉시중단(revoke)·rate limit(HQ_CONTROL) · REGION 모드(매장 시/도 region 편집 + 송출 target REGION fan-out) · 임퍼소네이션 만료 종착 통일(만료 후 서브페이지 이동 시 만료 화면 낙착·/login foreign 로그인 dead-end 차단, SPEC #157) · 송출 다이얼로그 미리듣기 audio 로드 실패 시 media 에러 배너 + 재시도(dead-end 해소, 게이트 유지, SPEC #160 H9) · 예약 송출 5분 슬롯(step=300)·과거/임박 차단·대상 매장 100개 초과 잘림 경고+비활성/정지 매장 선택 차단·REGION 0건 문구 정정·반복예약 0건 조합 차단+목록 UX 정합(SPEC #161 D1·D2·D5·D6·D8) · 오늘 송출 미도달 매장 카드 드릴다운(카드→미도달 매장 목록 다이얼로그·매장 상세 링크, count0 비링크, SPEC #162 H11) · **멀티 기기 매장의 방송 정확도(SPEC #178 — BE only, 본사 화면 변경 0): 매장에 PC 가 여러 대면 pending 조회가 기기를 인지해 **전 기기 재생을 보장**하고(먼저 폴링한 PC 가 재생을 마치면 늦은 PC 가 방송을 놓치던 문제), ack 이 `dispatch_device_ack` 에 기기별로 남아 **한 대라도 재생됐으면 PLAYED 로 종착**한다 — 꺼져 있는 PC 한 대 때문에 「오늘 미도달」 로 잘못 잡히지 않는다. 재생 상태·play_log 도 기기별로 갈려 대시보드·신탁 근거 왜곡이 사라진다)**. **즉시방송 실시간 도달(SPEC #180 — BE 통지 지점 보강, 본사 화면 변경 0): 점장 player 가 SSE 로 구독해 [송출] 즉시(리드타임 2초 + α) 매장에서 소리가 난다(종전 최대 20초). 예약 도래·원격 즉시중단(revoke)도 같은 채널로 통지돼 반응이 즉각적이다. `signal_latency_ms` 실측이 쌓여 리드타임 튜닝의 근거가 된다.** **점장 계정 발급 권한(SPEC #184 D4 — 본사에 처음 열린 계정 발급 권한): 매장 등록 폼의 「점장 이메일(로그인 ID)」로 등록과 동시에 계정이 발급되고(비우면 「점장 미정」), 놓친 매장은 매장 상세 [점장 발급]으로 사후 발급한다(`POST /api/v1/hq/stores/{storeId}/managers`). 임시 비밀번호는 서버 생성·미노출이라 계정 설정 메일이 유일한 진입 경로이고, 발송 실패는 화면이 danger 로 드러낸다(운영사 재발송 요청). 매장 목록에 「점장 계정: 미발급」 필터가 생겨 「점장 미정」·회수(WITHDRAWN) 매장을 색출한다. CSV 일괄도 「담당자이메일」 행이 계정까지 발급하며 결과 리포트가 「점장 미정 N건」·「설정 메일 발송 실패 N건」을 분리 안내한다. 발급마다 audit `HQ_STORE_MANAGER_ISSUED`.** design_8·design_9·design_15 시안 정합 완료. 잔여: 매장 상태 전이 본사 위임·점장 계정 **관리**(정지/복구/회수/비번 재설정) 본사 위임(발급은 #184 로 마감)·본사명 변경(운영자 소관으로 재분류)·ticket SLA(**첨부파일은 완료** — BE PR #324·#325, 이미지 4종·PDF·10MB 전 구간·티켓당 5개)·결제(스코프 제외)·LLM 자동멘트·날씨/AI 자동생성·임퍼소네이션 편집·**재생 준수율 리포트(#172 FU-1 — 영업시간 도메인 선행)**·송출 이력 BE bulk endpoint/매장 단위 페이지네이션(#089 F2·F3). 화면별 대응: HQ Surface Matrix(features/hq/surface-matrix), 잔여 상세: Pending Features.
점장 모드 (STORE_MANAGER) · Surface 1282%
현재: 인가 매처·본인 프로필·로그인 라우팅·레이아웃 가드·비밀번호 강제변경 관문·첫 진입 약관 동의 게이트(pmc 통과 후 `/onboarding/reagree`, SPEC #156)·온보딩 완료 게이트(미완료 시 `/store/onboarding` 유도) · 재생 큐 BE(활성 PL→본사 기본 PL fallback) · player 홈(Classic — `<audio>` 재생/seek/볼륨·곡 끝 자동 다음·큐 소진 refetch. **하단 큐 목록·곡 수·[이전]/[다음]은 `SHOW_HIDDEN_PLAYER_SECTIONS=false` 로 현재 숨김**, 코드는 보존) · 본사 안내방송 수신·더킹 + 오버레이 동시재생(SPEC #141)·ack 멱등(`outcome` 보고) + 긴급 인터럽트(preempt·최상위 우선순위) + CM송 사이클 동시재생(본사 설정 빈도·매장 override·라운드로빈) · 즉시방송 작성 3탭(TTS·녹음 MediaRecorder·자주 쓰는 방송 CRUD) + 긴급방송·예약방송 옵션 · 온보딩 풀 플로우(비번변경→환경점검→간단소개→음악시작) · 공개 랜딩(`/landing`) · 프로필(읽기 + 매니저 이름 편집(초기값 = 현재 이름·저장 시 계정 메뉴 즉시 반영, SPEC #155)·비밀번호 변경) · 예약 목록(read-only·페이지네이션) + 예약 취소 + 본사 점유 시각 충돌 차단 + 예약 송출 미리듣기(`<audio>`) · 활성 PL 직접 선택(본사 기본 PL fallback) · 시간대별 시간표 override(SPEC #171 develop — `/store/schedule`·48칸 그리드·본사 기본 따르기 ↔ [눌러서 커스텀 시작하기]으로 매장 override 편집·전체 교체 저장(다음 슬롯 경계부터 반영)·[본사 기본으로 되돌리기]·시간표 없으면 전체 셔플, player 홈 [시간표] 오디오 유지 모달) · 고객지원(목록·작성·상세·댓글, 운영자 식별·트리아지로 end-to-end · player 헤더 새 답변 dot · **첨부파일**(화면 캡처·PDF 업로드/다운로드 — 모달에서 재생 무중단, 업로드 중 닫기 잠금, 10MB 전 구간 업로드) — BE PR #324·#325) · 점장 액션 audit 누적(`store_audit_log`) · player 빈/에러 dead-end CTA([새로고침]·[고객지원 문의] — 문의는 라우트 이동이 아니라 support 모달을 작성 뷰 + PLAYBACK 사전선택으로 열어 player 유지, 재감사 N1) · 재생 더킹(player 2트랙 음악 감쇠/복원, 긴급 제외)·멘트 즉시중단([중단] — 안내방송 ack·CM 폐기)·5분 슬롯 그리드 예약 등록(본인/본사 점유 비활성, design_15 시안) · 예약 등록 성공 배너 + [확인] 명시 닫기(모달 '조용한 성공'·중복 등록 방지, 즉시/예약 분기)·점유 사전조회 size 50(BE clamp 일치)·409 후 선택 슬롯 리셋 + 그리드 점유 우선 표시(재-409 차단, SPEC #160 H13·D3·D4)·레거시 broadcast 라우트({schedule,templates})를 /store 딥링크 모달로 통일(빈 홈 dead-end 제거, SPEC #161 D9) · **무음 근절(감사 SPEC A — 1곡 큐 영구 무음 `queueEpoch` 수정 · 메인 audio `onError` 실패 트랙 스킵 + 전곡 실패 배너·backoff(무한 루프 차단) · `play()` reject 원인 분기(NotAllowedError=시작 오버레이 재노출 · AbortError 등 pause/새 load 로 대체된 경우=무시 · MediaError 있을 때만 스킵, 공용 판정기 + stale run 토큰 + 이중 발화 dedupe) · 고객지원·프로필 모달화(라우트 이동 → player 유지) · [다음] 버튼 상시 노출(stall 탈출구))**. **재감사 후속(2026-07 followup — N1·R1·D1): 빈 상태 [고객지원 문의] CTA 도 모달화(마지막 라우트 잔존분 — 빈 큐 + 방송 중 언마운트로 방송 끊김·ack 누락 차단) · `handleRestart`·`handlePlaylistApplied` 에 `abortCrossfade()` 선행(관례 통일) · `runCrossfade` early-return 방어적 리셋(무음 불변식). D2(메인 트랙 stall 워치독)는 별도 SPEC 백로그**. 점장 API 호출의 세션 스코프 고정(`api/v1/store/**` 는 임퍼소네이션 세션 비채택 — 공용 단말 403 잠김 차단)·refresh 일시 장애(5xx·네트워크) 시 강제 로그아웃 차단(502 surface)·`/api/backend/*` proactive refresh(무인 기기 401 왕복 축소, 감사 SPEC B). **방송 파이프라인 정합(감사 SPEC C — FE분): CM 사이클 카운트+임계치 검사를 곡 종료 공용 콜백 `onSongCompleted` 으로 통합(크로스페이드 swap 경로에서도 발화 — 큐 1랩당 1회 → 곡당 1회) · 방송 폴링 백그라운드 유지(`refetchIntervalInBackground: true`) + 탭 복귀 즉시 invalidate(탭 hidden 중 BE MISSED 영구 소실 차단) · 방송 오버레이 워치독(진행 정지 25초 = stall 만 강제 종료 → 더킹 복원·이후 방송 수신 재개. 길이 상한 없음) · 즉시방송 모달(진행 중 닫기 잠금 + 30초 상한·중복 송출 경고·닫기(X) 버튼·즉시 송출 토스트·미리듣기 만료 404 게이트 초기화·예약 성공 목록 invalidate·점유 조회 로딩/실패 표시)**. **방송 ack 재생 결과 보고(감사 #11): onEnded=PLAYED · onError/워치독 강제 종료=FAILED · backlog drain=SKIPPED. 재시도 정책은 BE 소유라 FE 자체 재시도 카운터 없음 — 실패 dispatchId 를 영구 차단하던 블랙리스트를 25초 쿨다운으로 교체해 **BE 재전달분이 다시 재생**된다. 긴급은 drain 대상에서 제외하고 시작 직후 재생. 긴급 실패 배너 카피를 실제 동작(본사 이력에 실패 기록·자동 재시도)에 맞게 정정**. **큐 안정화(감사 SPEC D — #12·#20): 큐 교체 시 재생 중이던 곡을 `musicId` 로 다시 찾아 인덱스를 재매핑(렌더 중 조정 — 새 배열에서 옛 인덱스는 다른 곡이라 곡이 잘리고 튀던 「다른 플레이리스트로 넘어가는 느낌」의 정체) · 큐 소진/[재시작]/backoff 로 인한 교체는 계약대로 0번부터(재매핑 제외) · 큐 쿼리 refetch 트리거 명시(`refetchOnWindowFocus: false` · `refetchOnReconnect` 는 실패한 큐만 — Wi-Fi 재연결마다 재셔플 큐가 도착하던 경로 차단, 무인 단말 회선 복구는 유지) · 큐 30분 주기 refetch(refetchIntervalInBackground 포함 — 본사 PL 편집 반영 지연 상한) · 활성 PL 적용/해제 시 새 큐 0번부터(리셋 의도 · 재매핑 제외) · 리셋 의도 만료를 성공/실패 응답 둘 다에 · [일시정지]가 진행 중 크로스페이드도 abort · 더킹/크로스페이드 램프와 stall 워치독 기준 시계를 performance.now(단조)로 전환(백그라운드 탭 1초 타이머 클램프에서 400ms 더킹이 14초가 되던 문제 · 크로스페이드는 미디어 시간 + 경과시간 상한으로 진행 정지 stall 도 차단 · 24/7 단말 시각 점프 오작동 차단)예약·온보딩 정합(감사 SPEC E): #9 — 예약 슬롯 그리드가 본사 점유를 `useGetStoreSlotOccupancy`(`GET /api/v1/store/slot-occupancy`)로 **사전 조회**해 송출 전에 비활성 표시(선택 날짜 기준·날짜 변경 시 자동 refetch), 409 `DISPATCH_SLOT_OCCUPIED` 는 조회 이후 race 보정으로만 남고 발생 시 해당 날짜 점유를 즉시 invalidate. 두 점유 조회(내 예약·본사)의 로딩/실패를 그리드가 함께 반영. #10 — 온보딩 데드엔드 3종 해소: 미완료 상태의 [이전] 핑퐁 → [로그아웃](`onboarding-logout`) · 비-Chromium 영구 차단에 전용 안내 배너(원인·현재 주소·해결)와 이탈 경로('그래도 계속' 우회는 의도적으로 두지 않음) · `writeProgress`/`mergeProgress` boolean 반환 + 저장 실패 danger 배너(환경 점검·start) + `StoreEntryGate` 세션당 리다이렉트 1회 제한(2회째 fail-open + warn 배너, sessionStorage + 모듈 변수 이중화)으로 `/store` ↔ `/store/onboarding` 무한 루프 근절**. design_8·design_9·design_10·design_15 시안 정합 완료. 본사 원격 즉시중단(revoke) 수신(폴링 revokedDispatchIds → 재생 중 즉시 정지)·송출/예약 캘린더 view(월 그리드)·daiso식 방송 동시재생(더킹 오버레이)·자동재생 시작 게이트·즉시 송출(예약→지금). **빈 큐 사유 3분기(`NO_ACTIVE_PLAYLIST`·`EMPTY_PLAYLIST`·`UNPLAYABLE_SOURCES` — 음원 URL 이 전부 재생 불가면 플레이리스트명과 함께 문의 유도, BE #284 + 감사 SPEC F·G).** **매장 멀티 기기(SPEC #178): 재생 화면을 열면 이 브라우저가 매장 기기로 자동 등록되고(멱등 — 새로고침·재부팅으로 늘지 않음), `/store/playlist` 선택이 **이 PC 에만** 적용된다(같은 매장 다른 PC 무영향 · 큐 해석 DEVICE→ACTIVE→DEFAULT→NONE). 재생 보고·방송 ack 도 기기별. **신규 화면 `/store/devices`**(목록·마지막 신호·이름 인라인 편집·2단계 회수·「이 기기」 배지) + player 상한 배너 [기기 정리하기] + 프로필 [재생 기기 관리] 진입. 상한(4대) 초과·시크릿 모드 등 저장소 차단 기기는 **매장 단위로 폴백하고 음악은 정상 재생**(기기별 재생목록만 못 씀). 방송은 **전 기기에 전달**되고(pending 조회가 기기 인지 — 먼저 튼 PC 가 ack 해도 나머지 PC 에 남는다) **`playAt` 절대 시각 게이트**로 전 기기가 같은 순간에 시작한다(도래 시각 타이머 · 서버 시각 기준). **점장 즉시방송은 `target_device_id`(V63)로 누른 PC 1대에 DB 차원 한정**된다(예약 앞당김 [즉시 방송]은 전 기기 유지). **시간표도 기기 스코프** — `GET/PUT/DELETE /store/me/schedule` · `POST …/customize` 4종이 optional `deviceId` 를 받아 **큐와 같은 3단 해석**(공용 `StorePlaylistResolver`)으로 편집 대상 PL 을 고르고, FE 가 조회·저장·커스텀·되돌리기 전부에 싣는다(종전엔 기기 지정 PL 로 재생 중인 PC 에서 저장이 200 인데 재생에 반영되지 않는 조용한 no-op). ack 정족수는 **온라인 기기 기준**이고 미도달 사유는 **실패 우선**(FAILED 하나라도 → `PLAYBACK_FAILED`), 기기 미식별 PC 의 레거시 ack 도 정족수를 존중한다. 기기 등록은 네이티브 upsert(동시 요청 500 없음)·PL 삭제 시 기기 지정 자동 해제·기기 관리 audit 3종(`STORE_DEVICE_RENAMED`·`REVOKED`·`PLAYLIST_CHANGED`, 등록은 제외). 잔여 = 기기 목록의 기기별 재생 상태 노출**. **즉시방송 실시간 수신(SPEC #180): 본사가 [송출]하면 폴링 tick 을 기다리지 않고 **즉시** 도착한다 — 단기 티켓으로 백엔드에 직접 붙는 SSE 구독(`use-announcement-stream.ts`)이 신호를 받아 pending 을 곧바로 재조회한다. 연결이 확정된 동안만 폴링을 60초로 완화하고 끊기면 즉시 20초로 복귀하므로 **기능 손실 0**(티켓 실패·CORS·미지원 브라우저·회선 단절은 전부 조용한 폴링 폴백). ack 에 신호 채널·수신/재생 시작 시각을 실어 리드타임 튜닝의 실측 근거를 남긴다.** 잔여: 긴급/CS 후속(audit 표면화·category 자동선택·상태변경 — **첨부파일은 완료**, BE PR #324·#325)·매장 정보 편집(**정책상 read-only 유지** — 변경은 본사 문의)·**장애복구 전용 화면**·커버 이미지·환경점검 서버 device-check·**온보딩 완료 플래그 BE 백드**(현재 기기 localStorage)·**큐 목록/[이전] 버튼 복원**(#168 임시 숨김 — 토글 1개)·메인 트랙 stall 워치독(재감사 D2 백로그)·**오프라인 재생(서비스워커 미구현)**. 화면별 대응: Store Surface Matrix(features/store/surface-matrix), 잔여 상세: Pending Features.