v1.3.4: 통계 확장(체류·스크롤·실시간 접속자)
- 031 마이그레이션: 체류·스크롤 집계, analytics_active_sessions - heartbeat API, 관리자 realtime API, 클라이언트 heartbeat - 대시보드: 현재 접속자 목록(로그인 닉네임·아바타), 참여 지표
This commit is contained in:
@@ -63,3 +63,80 @@ export const createDailyVisitorHash = ({ day, ip, userAgent, secret }) => {
|
||||
* @returns {string} 정규화된 slug
|
||||
*/
|
||||
export const normalizePostSlugForAnalytics = (slug) => (slug || '').trim()
|
||||
|
||||
/** @type {number} heartbeat 체류시간 상한(초) */
|
||||
export const ANALYTICS_MAX_DURATION_SECONDS = 1800
|
||||
|
||||
/** @type {number} engaged_views 집계 최소 체류(초) */
|
||||
export const ANALYTICS_ENGAGED_MIN_SECONDS = 10
|
||||
|
||||
/** @type {number} 현재 접속자 판정 TTL(초) */
|
||||
export const ANALYTICS_ACTIVE_SESSION_TTL_SECONDS = 90
|
||||
|
||||
/** @type {number[]} 스크롤 구간 임계값 */
|
||||
export const ANALYTICS_SCROLL_THRESHOLDS = [0.25, 0.5, 0.75, 1]
|
||||
|
||||
/**
|
||||
* 체류시간(초)을 상한 내로 보정한다.
|
||||
* @param {number} seconds - 체류시간
|
||||
* @returns {number} 보정된 초
|
||||
*/
|
||||
export const clampAnalyticsDurationSeconds = (seconds) => {
|
||||
const value = Number(seconds)
|
||||
if (!Number.isFinite(value) || value < 0) {
|
||||
return 0
|
||||
}
|
||||
|
||||
return Math.min(Math.floor(value), ANALYTICS_MAX_DURATION_SECONDS)
|
||||
}
|
||||
|
||||
/**
|
||||
* 스크롤 비율을 0~1로 보정한다.
|
||||
* @param {number} ratio - 스크롤 비율
|
||||
* @returns {number} 보정된 비율
|
||||
*/
|
||||
export const clampAnalyticsScrollRatio = (ratio) => {
|
||||
const value = Number(ratio)
|
||||
if (!Number.isFinite(value) || value < 0) {
|
||||
return 0
|
||||
}
|
||||
|
||||
return Math.min(value, 1)
|
||||
}
|
||||
|
||||
/**
|
||||
* 새로 통과한 스크롤 구간 컬럼명 목록을 반환한다.
|
||||
* @param {number} previousRatio - 이전 최대 스크롤
|
||||
* @param {number} nextRatio - 갱신된 최대 스크롤
|
||||
* @returns {Array<'scroll_25' | 'scroll_50' | 'scroll_75' | 'scroll_100'>} 신규 구간
|
||||
*/
|
||||
export const getNewScrollBucketColumns = (previousRatio, nextRatio) => {
|
||||
const previous = clampAnalyticsScrollRatio(previousRatio)
|
||||
const next = clampAnalyticsScrollRatio(nextRatio)
|
||||
const columns = []
|
||||
|
||||
if (previous < 0.25 && next >= 0.25) {
|
||||
columns.push('scroll_25')
|
||||
}
|
||||
if (previous < 0.5 && next >= 0.5) {
|
||||
columns.push('scroll_50')
|
||||
}
|
||||
if (previous < 0.75 && next >= 0.75) {
|
||||
columns.push('scroll_75')
|
||||
}
|
||||
if (previous < 1 && next >= 1) {
|
||||
columns.push('scroll_100')
|
||||
}
|
||||
|
||||
return columns
|
||||
}
|
||||
|
||||
/**
|
||||
* 실시간 세션 해시를 생성한다.
|
||||
* @param {{ clientSessionId: string, visitorHash: string, secret: string }} input - 해시 입력
|
||||
* @returns {string} session hash
|
||||
*/
|
||||
export const createRealtimeSessionHash = ({ clientSessionId, visitorHash, secret }) => {
|
||||
const payload = `${clientSessionId}|${visitorHash}|${secret}`
|
||||
return createHash('sha256').update(payload).digest('hex')
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user