/** * 핸들 표시/URL canonical form 정규화 — 앞에 붙은 '@' 모두 제거. * YouTube API 의 customUrl 이 '@foo' 형태로 내려오기 때문에 legacy 데이터가 '@foo' 로 저장돼 있을 수 있어 * 프론트 표시/URL 조합 시점에 한 번 더 방어적 정규화를 수행한다. */ function stripAtPrefix(value: string): string { let i = 0; while (i < value.length && value[i] === '@') { i++; } return value.slice(i); } /** * 채널 URL 생성 헬퍼. * - handle 이 있으면 `/channel/@handle` 형식 (짧고 친숙한 URL) * - 없으면 기존 `/channel/{channelSID}` 형식 (YouTube Channel ID 폴백) * * handle 은 의도적으로 URL 인코딩하지 않는다. * Watch 페이지의 canonical 리다이렉트 로직이 decoded URL 기준이라 * 여기서 인코딩하면 `current !== canonical` 무한 리다이렉트 루프가 발생. * 브라우저가 navigation 시 필요한 문자만 자동 인코딩하므로 안전하다. */ export function buildChannelUrl(channel: { handle?: string|null; channelSID: string }): string { if (channel.handle) { return `/channel/@${stripAtPrefix(channel.handle)}`; } return `/channel/${channel.channelSID}`; } /** * 라이브 시청 URL 생성 헬퍼. (/watch 프리픽스) */ export function buildWatchUrl(channel: { handle?: string|null; channelSID: string }): string { if (channel.handle) { return `/watch/@${stripAtPrefix(channel.handle)}`; } return `/watch/${channel.channelSID}`; } /** * 핸들 표시 문자열 생성 — `@foo` 형식으로 반환. 이중 `@` 방지. */ export function formatHandle(handle: string): string { return `@${stripAtPrefix(handle)}`; } /** * URL 파라미터(identifier)가 @handle 인지 SID 인지 판별. */ export function isHandleIdentifier(identifier: string): boolean { return identifier.length > 0 && identifier[0] === '@'; } /** * URL 파라미터에서 실제 핸들 값만 추출 (`@` 제거). */ export function normalizeHandle(identifier: string): string { return stripAtPrefix(identifier); } /** * 큰 수를 축약 표기 (10000 이상 → 'N.N만', 1000 이상 → 'N.NK'). */ export function formatCount(n: number): string { if (n >= 10000) { return `${(n / 10000).toFixed(1)}만`; } if (n >= 1000) { return `${(n / 1000).toFixed(1)}K`; } return n.toString(); }