최종 테스트 한번 더 부탁드립니다. (종료) 정보
최종 테스트 한번 더 부탁드립니다. (종료)- ii0000039210 자기소개 아이디로 검색 회원게시물 (115.♡.♡.140)
- 조회 1,057
- 댓글 15
본문
이번에 진행한 개발 내용은 실시간 현재 접속자 확인 기능입니다.
테스트를 위해 커뮤니티 방문을 한 번씩 해주시면 큰 도움이 됩니다.
테스트가 모두 완료되면, 보다 편리하게 사용하실 수 있도록 플러그인 형태로 제작할 예정입니다.
바쁘신 와중에도 시간을 내어 참여해 주신 모든 분들께 진심으로 감사드립니다.
https://currentusers.designonex.com/
테스트 아이디
test
1111
* 참고로 새로고침하실 필요없습니다. 실시간 프로그램이닌까요 ^^
댓글에 대한 예의로 원문은 그대로 남깁니다.
1
댓글 15개

'실시간 현재 접속자 확인 기능'이라면,
기존의 단순 ‘최근 몇 분 내 방문자 수’ 집계(DB 기록) 방식과는 달리
인프라 웹소켓(트리거) 기반으로 실제 접속 상태를 반영하는 구조인지 궁금합니다.
= 인프라 내 웹소켓(트리거) 서버가 상시 구동 중인지도 함께 궁금합니다.
가능하다면 설계 개요나 처리 흐름, 즉
- 연결 유지
- 끊김 감지
- 세션 정리
등의 방식을 간단히 공유해주시면,
피드백이나 테스트 측면에서 더 정확히 검증해볼 수 있을 것 같습니다.
좋은 시도, 응원합니다.
안녕하세요,
메시지 브릿지는 WebSocket 기반으로 동작하며, 별도의 저장 방식을 사용하지 않습니다.
즉, 텔레그램처럼 모든 정보를 서버에 저장하지 않으며, 실시간 전송에 집중합니다.
세션 처리는 별도로 진행하지 않으며, 연결 유지와 끊김 감지는 Ping-Pong 핸들러를 통해 안정적으로 관리됩니다.
자세한 기능과 동작 방식은 메시지 브릿지 설명 페이지에서 확인하실 수 있습니다.
앞으로도 이용자 여러분께 보다 안정적이고 질 높은 실시간 서비스를 제공할 수 있도록 최선을 다하겠습니다.
응원과 관심에 깊이 감사드립니다.
메시지 브릿지 설명
https://designonex.com/bbs/board.php?bo_table=real_time_bridge&wr_id=4

페이지를 새로고침해도,
끊긴 IP와 변경된 IP가 동일 접속자 목록에 계속 남아 있는 것으로 보입니다.
실시간 소켓 세션이라면
새로고침 시 기존 연결은 즉시 끊기고, 새 세션으로 재등록되어야 합니다.
그런데 이전 항목이 유지된다면
이는 DB나 메모리 테이블에 캐시된 접속 정보를 다시 로드하는 것으로 보입니다.
즉, 소켓 기반이 아니라
기존의 'DB 기반 접속자 기록'을 불러오는 형태와 유사합니다.
결론적으로,
실시간(소켓, 트리거 등등) 브릿지 서버와의 연동이 UI에 아직 반영되지 않은 상태로 보입니다.
= 접속_세션_트래킹이 UI에 적용되지 않은 상태.
. . .
@glitter0gim 감사합니다. 진통주사를 맞고 잠이 들었네요.
어렵게 생각하실 필요없습니다.
제가 소스를 드릴테니 님 사이트에서 적용하시면 됩니다.
head.sub.php 파일에 <head></head> 사이에 넣어주세요
<script>
const domain = window.location.hostname;
const MB_ID = "<?php echo $member['mb_id']; ?>";
const USER_IP = "<?php echo $_SERVER['REMOTE_ADDR']; ?>";
const myKey = MB_ID || USER_IP;
let ws;
let activeUsers = new Map(); // {key: data}
let userListString = "";
// ---------------------------
// 웹소켓 연결
// ---------------------------
function connectWebSocket() {
ws = new WebSocket(`wss://designonex.com:14147/?group=${encodeURIComponent(domain)}`);
ws.onopen = () => {
sendEvent({type:"join", mb_id: MB_ID, ip: USER_IP});
};
ws.onmessage = (event) => {
let data;
try { data = JSON.parse(event.data); } catch { return; }
// 초기 접속자 목록
if (data.type === "init") {
activeUsers.clear();
for (const u of data.users) {
const key = u.mb_id || u.ip || Math.random();
activeUsers.set(key, u);
}
}
// 접속/업데이트
else if (data.type === "join" || data.type === "update") {
const key = data.mb_id || data.ip || Math.random();
activeUsers.set(key, data);
}
// 접속 종료
else if (data.type === "leave") {
const key = data.mb_id || data.ip;
activeUsers.delete(key);
}
rebuildUserString();
};
ws.onclose = () => setTimeout(connectWebSocket, 2000);
ws.onerror = () => ws.close();
}
// ---------------------------
// 메시지 전송
// ---------------------------
function sendEvent(jsonData) {
if (ws && ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify(jsonData));
}
}
// ---------------------------
// 종료 시 leave
// ---------------------------
window.addEventListener("beforeunload", () => {
sendEvent({type:"leave", mb_id: MB_ID, ip: USER_IP});
if (ws) ws.close();
});
// ---------------------------
// 탭 재활성 시 재연결
// ---------------------------
document.addEventListener("visibilitychange", () => {
if (document.visibilityState === "visible" && (!ws || ws.readyState === WebSocket.CLOSED)) {
connectWebSocket();
}
});
// ---------------------------
// 문자열 갱신
// ---------------------------
function rebuildUserString() {
userListString = Array.from(activeUsers.values()).map(u => u.mb_id || u.ip).join("|");
requestAnimationFrame(updateDisplay);
}
// ---------------------------
// 화면 갱신
// ---------------------------
function updateDisplay() {
const countEl = document.getElementById("onlineCount");
const listEl = document.getElementById("userList");
if (!countEl || !listEl) return;
countEl.innerText = activeUsers.size;
listEl.innerText = userListString || "-";
}
// ---------------------------
// 최초 실행
// ---------------------------
connectWebSocket();
</script>
그리고. head.php 파일에 넣어주세요
<div style="margin-top:20px;">
<b>현재 접속자 수:</b> <span id="onlineCount">0</span><br><br>
<b>접속 로그 문자열:</b><br>
<div id="userList" style="padding:10px; border:1px solid #ccc; min-height:60px;"></div>
</div>
이게 끝입니다. 여러모로 진심으로 감사드립니다.
@glitter0gim 디비를 사용한것 같다는 말씀에 대해서는 메시지 브릿지는 DB 기반이 아닌 실시간 WebSocket 기반 구조입니다.
새로고침 시 같은 IP가 일시적으로 남아 보이는 것은 UI 단의 표시 타이밍 문제이지,
실제로 서버 메모리에 남아 있는 것은 아닙니다.

@ii0000039210
제시하신 이 프로젝트 논거가
"""실시간 현재 접속자 확인 기능"""입니다.
이는,
연결이 유지되는 동안 세션이 살아 있어야 하고
새로고침(끊김)시 이전 세션이 즉시 해제되어야 하며
접속/이탈이 모든 클라이언트에 브로드캐스트된다는 것입니다.
따라서 **“UI 표시 타이밍 문제”**라고 돌리는 답변은
님과 저의 핵심 논건, "실시간 세션 추적과 반영"을 회피하시는 겁니다.
기존의 단순 ‘최근 몇 분 내 방문자 수’ 집계(DB 기록) 방식과 다르지 않습니다.
@glitter0gim 아 이해했습니다. 맞습니다. 해결책을 찾고 있습니다. 감사합니다. 하지만, 못찾을수도 있습니다 ㅠㅠ
--------------------------------------------추가
음. 정확히 답변을 드리게 맞을 것 같아. 다시 글을 이어갑니다.
솔직히 더이상은 한계에 있습니다.
이 서버에 채팅과 실시간 쪽지, 알림, 웹뷰에서 알림 등을 플러그인으로 만들 예정입니다. 어차피, 회원 접속 부분은 채팅이나 쪽지에서 필요함으로 실시간 현재접속자를 만든것이였는데, 생각해보니 비회원 IP를 생각을 못했던 것이였습니다. 거기에 이건 웹상이라 봇도 있다는 것을 깨달고, 그냥 접어야 하나 생각이였습니다. 그래도 하는데까지 해보자 한게 지금이 최상입니다. 즉, 더는 안되는 내 머리와 환경이라...
하지만 디비에 저장하는것과 비교하시는 것은 좀 자존심이 상하네요 ㅠㅠ.
만약 해결한다면 플러그인에 올려놓겠습니다.
진정한 테스트에 진심으로 고개숙여 감사합니다. (__)
@glitter0gim 추가 설명을 드리면, WebSocket 기반 동작 구조를 확인하실 수 있도록 올린 예시입니다.
클라이언트(JS) 단에서 주기적으로 Ping을 보내는 것만으로도 간단히 연결 상태를 유지할 수 있습니다.
현재 서버는 카페24 윈도우 가상서버(메모리 8MB) 환경에서 운영 중입니다.
이전까지는 동시 접속자 100명 기준으로 생각했지만,
실제 “현재 접속자” 기능을 구현해보니 IP 기준으로 동작하다 보니
“봇 접속도 포함될 수 있겠구나” 하는 생각이 들었습니다.
따라서 동접 1,000명 이상도 안정적으로 처리할 수 있도록 패치를 진행하였습니다. 물론, 추가 패치가 있겠지만, 이 서버로 채팅, 실시간 쪽지, 현재 접속자, 알림 등 수많은 서비스가 진행될 예정입니다.
클라이언트가 수천개가 묶이면, 엄청난 부하가 생기겠지만, 그걸 한번 받아쳐 볼려고 하고 있습니다. 나만 쓴다면 쉽게 5초 핑 날리면 끝이지만, 수십개에서 더 나아가 수천개의 사이트와 앱이 물려있는 프로젝트라 GC에 중점을 두고 개발하고 있는 상황입니다. 말을 길게 하다보니 정신이 없네요.
또 생각나면 댓글 달겠습니다. 다시 한번 감사합니다.

@ii0000039210
“최종 테스트 한 번 더 부탁드립니다.” 하셔서
기술적인 피드백을 드린 것입니다.
그런데 이제 와서 “예시일 뿐”이라 하시는 건,
논리적 일관성이 전혀 없습니다.
테스트 요청은 실제 동작 검증을 전제로 하는 것이지,
임의의 예시 코드를 대상으로 한 형식적 확인이 아닙니다.
아무리 ‘자유게시판’이라지만,
검증되지 않은 일련의 글들을 나열하여
커뮤니티에 테스트 요청을 하는 등 혼란을 초래함은 좀~ 그렀습니다.
@glitter0gim ^^ 맞습니다. 지금 플러그인은 이전에 먼저 테스트 요청을 했던게 잘못이였습니다. 실제 서비스가 들어갔을때를 생각못했던 것이죠.
이 점 저도 깊이 반성하고 있습니다.
너그러이 생각해주시면 감사하겠습니다.
이 플러그인은 종료하겠습니다.
다시 한번 감사드립니다. (__)
@glitter0gim 접속_세션_트래킹이 UI에 적용되지 않은 상태
말씀하신 “접속 세션 트래킹이 UI에 적용되지 않은 상태” 부분은 실제로 서버 쪽에서는 정상 동작 중입니다.
메시지 브릿지는 DB 기반이 아니라 WebSocket 실시간 세션 구조로 되어 있어서, 각 접속은 서버 메모리상에서 실시간으로 관리되고 있습니다.
다만 새로고침 시 브라우저가 leave 이벤트를 즉시 전달하지 못하는 경우가 있어서, UI 갱신이 잠시 늦게 반영되는 현상이 있을 수 있습니다.
이건 세션이 남아 있는 게 아니라 UI 단에서 아직 리스트 정리가 안 된 상태예요.
일정 시간(약 10초) 내에 서버에서 자동으로 정리되며, 실시간 세션 트래킹 자체는 이미 적용되어 있습니다.
즉, 서버 기준으로는 끊긴 세션이 즉시 제거되고 있으니 UI 갱신 타이밍만 조금 보완하면 완전 실시간처럼 동작합니다.
위의 세션으로 말한 글은 님의 이해를 돕기위한 글이고, 다음은 메시지 브릿지의 기능입니다.
현재 메시지 브릿지 구조는 “로그인 세션” 기반이 아니라,
WebSocket 연결 단위로 실시간 접속 상태를 관리하는 구조입니다.
즉, 사용자가 새로고침을 하면 기존 소켓 연결이 끊기고 새 연결이 즉시 만들어집니다.
서버에서는 이 변화를 감지해 기존 연결 정보를 제거하고 새로운 연결로 갱신합니다.
단, 클라이언트(UI) 쪽에서 leave → join 이벤트가 처리되는 타이밍이 짧은 구간 동안 겹칠 수 있어서
접속자 목록이 잠깐 중복되어 보이는 현상이 있을 수 있습니다.
세션이 유지되는 게 아니라,
소켓 단위의 실시간 트래킹이 UI에 반영되는 타이밍 문제라고 보시면 됩니다.

@ii0000039210
브라우저가 새로고침을 했을 때,
이전 세션의 정보에 변경사항이 추가되어 다시 로드되고 있습니다.
이는 기존 캐시(또는 DB와 유사한 저장 데이터)를 불러오는 동작으로,
"실시간 현재 접속자 확인 기능"이라 보기 어렵습니다.
ii0000039210의 설명을 유추해 보건데
“최근 방문자 캐시 표시”를 약 10초 후에 갱신하는 추가 로직이 있을 뿐입니다.
깔끔히 정리해주셨습니다.
이게 제 한계입니다. 수많은 클라이언트에 묶인 것을 어떻게 처리할 지 솔직히 지금은 불가능으로 생각하고 있습니다. 훗날 제가 이 부분을 처리하게 된다면 그땐 팁게시판에 올려놓겠습니다.
감사합니다.

'채널톡'이라고 웹소켓으로 고객CS 응대 프로그램 만들어서 잘 되고 있는 회사가 있습니다.
이렇듯 소켓 잘 만드시면 잠재력은 무궁무진 할꺼에요.
그리고 백엔드에 db는 사용하는게 좋습니다. connection string 관리를 해줘야하고, 확장도 해야하니까요.
파이썬 두개 띄워보시고 중앙db를 통해 커넥션 제어하는것 추천드려요.
차츰차츰 정리는 해야겠죠.
댓글에 힘이 많이 나네요. 감사합니다.
* 댓글에 위의 문제점에 대해 추가하겠습니다.
브라우저 인식 부분을 개선했습니다.
sendEvent()의 문제점(접속/이탈)을 보완했습니다. realtime의 역할을 제가 테스트했을때 100%에 가깝습니다. 감사합니다.
하지만, 완벽하다고 하지는 않다고 생각합니다.
일단 플러그인 배포를 해보고, 사용하는 유저들의 핑 모니터링으로 보강할 수 있으면 보강하도록 하겠습니다.