// #304 어드민 페이지 공통 유틸. // 결함: localStorage 직접 접근 10+곳 / SSE 파싱 코드 6곳 중복. const TOKEN_KEY = "tasteby_token"; export function getAdminToken(): string | null { if (typeof window === "undefined") return null; return localStorage.getItem(TOKEN_KEY); } export function authHeaders(): Record { const token = getAdminToken(); return token ? { Authorization: `Bearer ${token}` } : {}; } /** * SSE(Server-Sent Events) 스트림을 라인 단위로 파싱하여 onEvent 콜백을 호출. * 호환 패턴: `data: { ...json... }` 한 줄 = 한 이벤트. * 비어있는 줄은 무시. JSON 파싱 실패 시 콜백 skip. */ export async function consumeSseStream( response: Response, onEvent: (event: unknown) => void, onError?: (err: unknown) => void, ): Promise { const reader = response.body?.getReader(); if (!reader) return; const decoder = new TextDecoder(); let buffer = ""; try { while (true) { const { done, value } = await reader.read(); if (done) break; buffer += decoder.decode(value, { stream: true }); const lines = buffer.split("\n"); buffer = lines.pop() ?? ""; for (const line of lines) { const trimmed = line.trim(); if (!trimmed.startsWith("data:")) continue; const payload = trimmed.slice(5).trim(); if (!payload) continue; try { onEvent(JSON.parse(payload)); } catch { // 무시: 일부 SSE 줄이 JSON이 아닐 수도 있음 } } } } catch (err) { onError?.(err); } }