저희 회사는 셀러가 자사몰을 쉽게 만들고 상품을 판매할 수 있도록 있도록 홈페이지 빌더 서비를 제공하고 있습니다.
때때로 셀러들이 현재 자사몰에 접속 중인 유저가 몇 명인지 궁금해하는 경우가 있어 쇼핑몰별 실시간 접속자 수를 가져오는 코드를 테스트해봤습니다.
- 실시간 접속자는 최근 30분 이내 접속한 유저로 정의했습니다.
(테스트 결과 샘플)
Redis의 Sorted Set (ZSET)을 활용하여 실시간 접속자 수를 관리
✔ 유저가 자사몰에 접속하면 session_id를 발급
✔ Redis ZSET(active_users:sellerId)에 session_id와 timestamp를 저장
(POST /track-active-users)
✔ 30분이 지난 유저 세션은 삭제 (접속자 목록을 조회하는 시점에 삭제)
✔ API를 호출하여 현재 접속자 목록을 확인 가능
(GET /get-active-users/:sellerId)
✅ 주요 기능
import express, { Request, Response, Application } from "express";
import cors from "cors";
import { createClient } from "redis";
import dotenv from "dotenv";
dotenv.config(); // .env 환경 변수 로드
const app: Application = express(); // ✅ Application 타입 지정
app.use(express.json()); // JSON 요청을 처리하기 위한 미들웨어 추가
app.use(cors());
// 🔹 Redis 클라이언트 설정 (비밀번호 포함)
const redisClient = createClient({
url: `redis://:${process.env.REDIS_PASSWORD}@${process.env.REDIS_HOST}:${process.env.REDIS_PORT}`, // Redis 인증 추가
});
redisClient.on("error", (err) => console.error("Redis error:", err));
(async () => {
try {
await redisClient.connect();
console.log("✅ Connected to Redis");
} catch (err) {
console.error("❌ Redis connection failed:", err);
}
})();
// 🔹 요청 데이터 타입 정의
interface TrackActiveUsersRequest {
sellerId: string;
sessionId: string;
}
// 🔹 방문자 추적 API (타입 오류 해결)
app.post(
"/track-active-users",
async (req: Request<{}, {}, TrackActiveUsersRequest>, res: Response): Promise<void> => {
try {
const { sellerId, sessionId } = req.body;
// 🔹 요청 데이터 유효성 검사
if (typeof sellerId !== "string" || typeof sessionId !== "string") {
res.status(400).json({ error: "Invalid sellerId or sessionId" });
return;
}
const now = Math.floor(Date.now() / 1000); // 현재 타임스탬프 (초 단위)
// 🔹 Redis ZSET에 추가 (배열 형식 사용)
await redisClient.zAdd(`active_users:${sellerId}`, [
{ score: now, value: sessionId },
]);
res.json({ message: "User tracked successfully" });
} catch (error) {
console.error("❌ Error tracking active users:", error);
res.status(500).json({ error: "Internal server error" });
}
}
);
// 🔹 요청 데이터 타입 정의
interface SellerRequest {
sellerId: string;
}
// 🔹 최근 30분 내 접속 유저 가져오는 API
app.get("/active-users/:sellerId", async (req: Request<SellerRequest>, res: Response): Promise<void> => {
try {
const { sellerId } = req.params;
if (!sellerId) {
res.status(400).json({ error: "Missing sellerId" });
return;
}
const now = Math.floor(Date.now() / 1000); // 현재 타임스탬프 (초 단위)
const cutoffTime = now - 1800; // 30분 전 시간 계산
// 🔹 1️⃣ 30분이 지난 유저 삭제
await redisClient.zRemRangeByScore(`active_users:${sellerId}`, 0, cutoffTime);
// 🔹 2️⃣ 최근 30분 내 접속한 유저 목록 가져오기
const activeUsers = await redisClient.zRangeByScore(`active_users:${sellerId}`, cutoffTime, now);
res.json({ sellerId, activeUsers, count: activeUsers.length });
} catch (error) {
console.error("❌ Error fetching active users:", error);
res.status(500).json({ error: "Internal server error" });
}
});
// 🔹 서버 실행
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`🚀 Server running on http://localhost:${PORT}`);
});
✅ 주요 기능
<script setup>
import { onMounted } from "vue";
import { useRoute } from "vue-router";
import { v4 as uuidv4 } from "uuid";
const route = useRoute();
const sellerId = route.params.sellerId;
onMounted(() => {
let sessionId = localStorage.getItem("session_id");
if (!sessionId) {
sessionId = uuidv4();
localStorage.setItem("session_id", sessionId);
}
fetch("http://localhost:3000/track-active-users", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ sellerId, sessionId }),
});
});
</script>
<template>
<div>
<h2>Welcome to {{ sellerId }}'s Shop</h2>
<p>Tracking your visit...</p>
</div>
</template>
Redis ZSET을 이용하니 생각보다 간단하게 실시간 접속자를 추적할 수 있었고 아래에 사용한 Redis ZSET 명령어를 정리해 봤습니다.
- ZSET 특징: 집합의 멤버들이 Score순으로 자동 정렬되고 O(N)이 아닌 O(log N + M) → 탐색(log N) + 삭제 개수(M)의 효율적인 시간복잡도로 멤버를 삭제할 수 있습니다.
기능 | Redis 명령어 | 명령어 샘플 | 설명 |
유저 접속 기록 저장 | ZADD | ZADD active_users:ABCShop 1717215600 "session_123" | 유저 세션을 ZSET에 저장 |
30분이 지난 유저 삭제 | ZREMRANGEBYSCORE | ZREMRANGEBYSCORE active_users:ABCShop 0 {current_timestamp - 1800} | 30분 이전 데이터 삭제 |
최근 30분 접속자 조회 | ZRANGEBYSCORE | ZRANGEBYSCORE active_users:ABCShop {current_timestamp - 1800} {current_timestamp} | 특정 시간 범위의 유저 조회 |
상세 코드 링크도 아래에 남깁니다.
혹시 테스트하고 싶은 분들은 편하게 사용하세요.
GitHub: https://github.com/menthamin/live-user-monitor
GitHub - menthamin/live-user-monitor
Contribute to menthamin/live-user-monitor development by creating an account on GitHub.
github.com
데이터 분석, 추출, 대시보드 제작 시 고려하면 좋은 것들 (0) | 2025.03.02 |
---|