// lib/middleware/page-tracking.ts - 페이지 방문 추적 미들웨어 import { NextRequest, NextResponse } from 'next/server' import { getToken } from 'next-auth/jwt' import { UAParser } from 'ua-parser-js'; import { SessionRepository } from '../session/repository' export async function trackPageVisit(request: NextRequest) { try { const token = await getToken({ req: request }) const url = new URL(request.url) // API 경로나 정적 파일은 추적하지 않음 if (url.pathname.startsWith('/api') || url.pathname.startsWith('/_next') || url.pathname.includes('.')) { return } const userAgent = request.headers.get('user-agent') || '' const parser = new UAParser(userAgent) const result = parser.getResult() // 활성 세션 조회 let sessionId = null if (token?.id) { const activeSession = await SessionRepository.getActiveSessionByUserId(token.id) if (activeSession) { sessionId = activeSession.id // 세션 활동 시간 업데이트 await SessionRepository.updateLoginSession(activeSession.id, { lastActivityAt: new Date() }) } } // 페이지 방문 기록 await SessionRepository.recordPageVisit({ userId: token?.id || undefined, sessionId, route: url.pathname, pageTitle: extractPageTitle(url.pathname), // 구현 필요 referrer: request.headers.get('referer') || undefined, ipAddress: getClientIP(request), userAgent, queryParams: url.search ? url.search.substring(1) : undefined, deviceType: getDeviceType(result.device.type), browserName: result.browser.name, osName: result.os.name, }) } catch (error) { console.error('Failed to track page visit:', error) } } function getClientIP(request: NextRequest): string { const forwarded = request.headers.get('x-forwarded-for'); const realIP = request.headers.get('x-real-ip'); const cfConnectingIP = request.headers.get('cf-connecting-ip'); // Cloudflare if (cfConnectingIP) { return cfConnectingIP; } if (forwarded) { return forwarded.split(',')[0].trim(); } if (realIP) { return realIP; } // NextRequest에는 ip 프로퍼티가 없으므로 기본값 반환 return '127.0.0.1'; } function getDeviceType(deviceType?: string): string { if (!deviceType) return 'desktop' if (deviceType === 'mobile') return 'mobile' if (deviceType === 'tablet') return 'tablet' return 'desktop' } function extractPageTitle(pathname: string): string { // 라우트 기반 페이지 제목 매핑 const titleMap: Record = { '/': 'Home', '/dashboard': 'Dashboard', '/profile': 'Profile', '/settings': 'Settings', // 추가 필요 } return titleMap[pathname] || pathname }