// 프로필 아바타 유틸 — 표시 우선순위(커스텀 > 구글 > 이니셜)와 업로드용 다운스케일. /** * 표시할 아바타 이미지 소스를 고른다. * 우선순위: 사용자 지정(profileImage) > 구글 아바타(googlePicture) > null(이니셜 폴백) */ export function avatarSrc(user) { if (!user) return null return user.profileImage || user.googlePicture || null } /** 이름/아이디에서 이니셜(첫 글자) 한 글자 — 이미지 없을 때 폴백 */ export function avatarInitial(user) { const s = (user?.name || user?.loginId || '').trim() return s ? s[0].toUpperCase() : '?' } /** * 업로드용으로 이미지 파일을 정사각형으로 크롭·축소해 JPEG data URL 로 변환한다. * (서버/로컬 저장 용량을 작게 — 기본 192px) */ export function fileToAvatarDataUrl(file, size = 192, quality = 0.85) { return new Promise((resolve, reject) => { if (!file || !file.type?.startsWith('image/')) { reject(new Error('이미지 파일이 아닙니다.')) return } const url = URL.createObjectURL(file) const img = new Image() img.onload = () => { try { const canvas = document.createElement('canvas') canvas.width = size canvas.height = size const ctx = canvas.getContext('2d') // 중앙 정사각형 크롭(cover) const min = Math.min(img.width, img.height) const sx = (img.width - min) / 2 const sy = (img.height - min) / 2 ctx.drawImage(img, sx, sy, min, min, 0, 0, size, size) const dataUrl = canvas.toDataURL('image/jpeg', quality) URL.revokeObjectURL(url) resolve(dataUrl) } catch (e) { URL.revokeObjectURL(url) reject(e) } } img.onerror = () => { URL.revokeObjectURL(url) reject(new Error('이미지를 읽지 못했습니다.')) } img.src = url }) }