메시지 브릿지

매뉴얼 [웹] 기본 엔진 소스 설명 (v1.4.1 보안 패치 적용)

페이지 정보

본문

서버 보안 패치 v1.4.1이 적용됨에 따라, 기본 엔진 소스 역시 서버 업데이트 정책과 동일한 기준으로 수정되었습니다. 당초 엔진 소스의 변경 폭이 크지 않을 것으로 예상했으나, 보안 정책 강화와 시스템 안정성 향상을 위해 서버와 동일한 변경 사항이 반영되었습니다.

향후에도 보안 패치 적용 시 변경되는 엔진 소스 내용을 지속적으로 공유드리겠습니다.


document.addEventListener("DOMContentLoaded", function() {


페이지(웹사이트)가 모두 준비되면 안쪽 함수(코드)를 실행해 주세요.

웹사이트는 그림/글/버튼을 전부 불러오는 시간이 있는데, 다 불러온 후 실행해야 오류가 안 나기 때문에 이걸 사용합니다.


const domain = window.location.hostname;


현재 웹사이트 주소의 도메인(예: example.com)을 domain이라는 상자에 저장해요.


const MB_ID = "회원아이디";


서버가 알려준 사용자 ID를 MB_ID에 넣어요.


const USER_IP = "아이피";


사용자의 IP 주소를 USER_IP에 넣어요.

서버스크립트 언어는 해당아이피를 가져올 수 있어요. 하지만, 정적 사이트인 경우에는 스크립트로 아이피를 얻어야 합니다. 


const myKey = MB_ID || USER_IP;


MB_ID가 있으면 그걸 쓰고, 없으면 USER_IP를 쓴다는 뜻이에요.


let ws;


웹소켓 연결을 담을 상자를 만들어요.


let activeUsers = new Map();


접속 중인 사람들 정보를 저장할 빈 맵을 만들어요. 


let reconnectTimer = null;


재접속 예약을 관리하기 위한 타이머 상자예요. 처음에는 아무 것도 없음


let retryDelay = 1000;


처음 재접속을 시도할 때 기다리는 시간(밀리초)이에요. 1000ms = 1초.


const maxDelay = 10000;


재접속 딜레이의 최대값이에요. 10000ms = 10초.


function gracefulDisconnect() {


'gracefulDisconnect'라는 이름의 함수(작은 일꾼)를 만들어요. 안전하게 나갈 때 이걸 호출해요.


const leaveData = {type: "leave", mb_id: MB_ID, ip: USER_IP};


서버에 보낼 '나간다' 메시지 내용을 만듭니다. type은 "leave"예요.


if (ws && ws.readyState === WebSocket.OPEN) {
    try { ws.send(JSON.stringify(leaveData)); ws.close(); } catch(e) {}
}


만약 웹소켓이 열려 있으면(연결되어 있으면) leaveData를 서버에 보내고 소켓을 닫아요. (오류가 나도 멈추지 않게 try/catch로 감쌉니다.)


try {
    navigator.sendBeacon("/ws-leave", new Blob([JSON.stringify(leaveData)], {type:"application/json"}));
} catch(e) {}


페이지를 닫거나 나갈 때도 서버에 '내가 떠난다'를 확실히 알리기 위해 sendBeacon을 시도해요. 이건 페이지가 닫혀도 요청이 보낼 수 있게 도와줘요.


window.addEventListener("beforeunload", gracefulDisconnect);


브라우저가 "이 페이지를 떠나겠어요" 할 때(새로고침, 탭 닫기 등) gracefulDisconnect를 불러요.


window.addEventListener("pagehide", gracefulDisconnect);


모바일 브라우저 등에서 페이지가 숨겨질 때도 안전하게 알리기 위해 호출해요.


document.addEventListener("visibilitychange", () => {
    if (document.visibilityState === "hidden") gracefulDisconnect();
});


탭이 숨겨졌을 때(다른 탭으로 갔을 때)도 떠나는 것으로 처리하려면 gracefulDisconnect를 호출해요.


function rebuildUserString(lastMessage) {


rebuildUserString이라는 함수: 접속자 정보가 바뀌면 이걸 불러 UI를 갱신하게 해요.


if (typeof window.onWSMessage === "function") {
    window.onWSMessage(activeUsers, lastMessage);
}


만약 페이지에 onWSMessage라는 "내가 만든 함수"가 있으면(=UI가 준비되어 있으면) 현재 접속자 목록과 마지막 받은 메시지를 넘겨서 화면을 갱신하라고 말해요.


function sendEvent(jsonData) {
    if (ws && ws.readyState === WebSocket.OPEN) {
        ws.send(JSON.stringify(jsonData));
    }
}


sendEvent는 주어진 데이터를 JSON(문자열)로 바꿔서 서버에 보내요. 단, 소켓이 열려 있을 때만 보냅니다.


function scheduleReconnect() {
    if (reconnectTimer) return;
        reconnectTimer = setTimeout(() => {
        reconnectTimer = null;
        retryDelay = Math.min(maxDelay, retryDelay + 1000);
        connectWebSocket();
    }, retryDelay);
}


scheduleReconnect는 재접속을 예약해요. 이미 예약되어 있으면 아무 것도 하지 않아요.

예약 시간이 지나면 retryDelay를 조금 키우고(최대 maxDelay까지), 다시 connectWebSocket()를 호출해서 연결을 시도합니다.


window.addEventListener("online", () => {
    if (!ws || ws.readyState !== WebSocket.OPEN) {
         retryDelay = 1000;
         connectWebSocket();
    }
});


브라우저가 오프라인→온라인으로 바뀌면(인터넷 돌아오면) 재접속을 시도해요. 재접속 딜레이를 초기화합니다.


document.addEventListener("visibilitychange", () => {
    if (document.visibilityState === "visible" && (!ws || ws.readyState !== WebSocket.OPEN)) {
        connectWebSocket();
    }
});


탭으로 돌아왔을 때(보이게 됐을 때) 연결이 없으면 바로 재접속 시도합니다.


function connectWebSocket() {
        const WS_URL = 'wss://designonex.com:14147/?group=' + encodeURIComponent(domain);
        console.log("%c[WS] Connecting...", "color: cyan");
        ws = new WebSocket(WS_URL);


connectWebSocket()를 호출하면 WS_URL(웹소켓 주소)을 만들고, 그 주소로 새로운 WebSocket을 엽니다. group= 파라미터에 도메인을 넣어 서버가 그룹을 알 수 있게 합니다.


ws.onopen = () => {
    console.log("%c[WS] Connected", "color: lime");
    retryDelay = 1000;
    sendEvent({type:"join", mb_id:MB_ID, ip:USER_IP});
};


소켓이 열렸을 때(연결 성공), 콘솔에 찍고 재접속 딜레이 초기화, 그리고 서버에 join 메시지를 보냅니다(내가 들어왔어요).


ws.onmessage = (event) => {
   let data;
   try { data = JSON.parse(event.data); } catch { return; }


서버에서 메시지가 오면 먼저 JSON으로 바꿔요. 실패하면 그냥 무시합니다.


if (data.type === "group_warn") {
    alert(data.msg);   // ← 단순 ALERT 출력
    return;
}


만약 서버가 type: "group_warn"를 보냈다면 alert로 메시지를 보여주고 return으로 이 메시지에 대한 추가 처리를 건너뜁니다.


let changed = false;


접속자 목록이 바뀌었는지 표시하는 changed라는 깃발을 만든다(초기값 false).


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);
        changed = true;
    }
} else if (data.type === "join" || data.type === "update") {
    const key = data.mb_id || data.ip || Math.random();
    activeUsers.set(key, data);
    changed = true;
} else if (data.type === "leave") {
    const key = data.mb_id || data.ip;
    if (activeUsers.has(key)) {
        activeUsers.delete(key);
        changed = true;
    }
}


서버에서 온 메시지 타입에 따라:


  • init: 처음 접속할 때 전체 사용자 목록을 받아와서 activeUsers를 새로 채워요.
  • join 또는 update: 한 명이 들어오거나 정보가 바뀌었을 때 그 사람을 기록해요.
  • leave: 누군가 떠났으면 activeUsers에서 지워요.


바뀌는 게 있으면 changed = true로 표시합니다.


if (changed) rebuildUserString(data);


접속자 목록이 바뀌었으면 rebuildUserString()를 불러 UI를 갱신합니다.


ws.onerror = () => console.warn("%c[WS] ERROR", "color: red");


소켓에서 에러가 나면 콘솔에 빨간 경고를 찍어요.


ws.onclose = () => { console.warn("%c[WS] CLOSED", "color: orange"); 
scheduleReconnect(); };


소켓이 닫히면 콘솔에 닫혔다고 찍고 재접속을 예약합니다.


connectWebSocket();


파일 끝에서 페이지 준비되면 connectWebSocket()을 실행해서 연결을 시작합니다.

전체소스

document.addEventListener("DOMContentLoaded", function() {
    const domain = window.location.hostname;
    const MB_ID = "아이디";
    const USER_IP = "아이피";
    const myKey = MB_ID || USER_IP;

    let ws;
    let activeUsers = new Map();
    let reconnectTimer = null;
    let retryDelay = 1000;
    const maxDelay = 10000;

    // -------------------------
    // 안전한 퇴장 처리
    // -------------------------
    function gracefulDisconnect() {
        const leaveData = {type: "leave", mb_id: MB_ID, ip: USER_IP};
        if (ws && ws.readyState === WebSocket.OPEN) {
            try { ws.send(JSON.stringify(leaveData)); ws.close(); } catch(e) {}
        }
        try {
            navigator.sendBeacon("/ws-leave", new Blob([JSON.stringify(leaveData)], {type:"application/json"}));
        } catch(e) {}
    }

    window.addEventListener("beforeunload", gracefulDisconnect);
    window.addEventListener("pagehide", gracefulDisconnect);
    document.addEventListener("visibilitychange", () => {
        if (document.visibilityState === "hidden") gracefulDisconnect();
    });

    // -------------------------
    // 접속자 문자열 갱신 (UI 브릿지 호출)
    // -------------------------
    function rebuildUserString(lastMessage) {
        if (typeof window.onWSMessage === "function") {
            window.onWSMessage(activeUsers, lastMessage);
        }
    }

    // -------------------------
    // 메시지 전송
    // -------------------------
    function sendEvent(jsonData) {
        if (ws && ws.readyState === WebSocket.OPEN) {
            ws.send(JSON.stringify(jsonData));
        }
    }

    // -------------------------
    // 자동 재접속
    // -------------------------
    function scheduleReconnect() {
        if (reconnectTimer) return;
        reconnectTimer = setTimeout(() => {
            reconnectTimer = null;
            retryDelay = Math.min(maxDelay, retryDelay + 1000);
            connectWebSocket();
        }, retryDelay);
    }

    window.addEventListener("online", () => {
        if (!ws || ws.readyState !== WebSocket.OPEN) {
            retryDelay = 1000;
            connectWebSocket();
        }
    });

    document.addEventListener("visibilitychange", () => {
        if (document.visibilityState === "visible" && (!ws || ws.readyState !== WebSocket.OPEN)) {
            connectWebSocket();
        }
    });

    // -------------------------
    // WebSocket 연결
    // -------------------------
    function connectWebSocket() {
        const WS_URL = 'wss://designonex.com:14147/?group=' + encodeURIComponent(domain);
        console.log("%c[WS] Connecting...", "color: cyan");
        ws = new WebSocket(WS_URL);

        ws.onopen = () => {
            console.log("%c[WS] Connected", "color: lime");
            retryDelay = 1000;
            sendEvent({type:"join", mb_id:MB_ID, ip:USER_IP});
        };

        ws.onmessage = (event) => {
            let data;
            try { data = JSON.parse(event.data); } catch { return; }

            // ---------------------------------------------------
            // ★ ALERT 경고 메시지 처리 (추가 부분)
            // ---------------------------------------------------
            if (data.type === "group_warn") {
                alert(data.msg);   // ← 단순 ALERT 출력
                return;
            }

            let changed = false;

            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);
                    changed = true;
                }
            } else if (data.type === "join" || data.type === "update") {
                const key = data.mb_id || data.ip || Math.random();
                activeUsers.set(key, data);
                changed = true;
            } else if (data.type === "leave") {
                const key = data.mb_id || data.ip;
                if (activeUsers.has(key)) {
                    activeUsers.delete(key);
                    changed = true;
                }
            }

            if (changed) rebuildUserString(data);
        };

        ws.onerror = () => console.warn("%c[WS] ERROR", "color: red");
        ws.onclose = () => { console.warn("%c[WS] CLOSED", "color: orange"); scheduleReconnect(); };
    }

    // -------------------------
    // 페이지 로드 후 WebSocket 연결
    // -------------------------
    connectWebSocket();

});

 

댓글목록

등록된 댓글이 없습니다.

메시지 브릿지

Total 22건 1 페이지
  • RSS

메시지 브릿지는 무료 서비스입니다.

안녕하세요. 디자인원엑스(Designonex) 입니다.저희 메시지 브릿지(Message Bridge) 서비스는누구나 자유롭게 사용할 수 있는 무료 실시간 통신 플랫폼입니다.웹사이트나 커뮤니티, 그리고 프로젝트 환경 내에서사용자 간 메시지 전달, 알림, 접속 상태 공유 등다양한 기능을 보다 쉽게 구현할..

작성자: 관리자 댓글 0 조회 1000

메시지 브릿지(Message Bridge) 설명

메시지 브릿지(Message Bridge) 서버와 클라이언트를 가장 빠르고 안정적으로 연결하는 실시간 메시지 중계 서버   메시지 브릿지(Message Bridge)는 초경량·초고속 WebSocket 기반의 실시간 메시지 중계 시스템입니다. 간단히 연결만 하면, 서로 다른 기기·웹사이..

작성자: 관리자 댓글 0 조회 1358

열람중 [웹] 기본 엔진 소스 설명 (v1.4.1 보안 패치 적용) N새글

서버 보안 패치 v1.4.1이 적용됨에 따라, 기본 엔진 소스 역시 서버 업데이트 정책과 동일한 기준으로 수정되었습니다. 당초 엔진 소스의 변경 폭이 크지 않을 것으로 예상했으나, 보안 정책 강화와 시스템 안정성 향상을 위해 서버와 동일한 변경 사항이 반영되었습니다. 향후에도 보안 패치 적용 시 변..

작성자: 관리자 댓글 0 조회 10

메시지 브릿지 1.4.1 보안 패치 N새글

메시지 브릿지 1.4.1 — 서버 보안 패치 설명1. 중복 그룹(duplicate group) 공격 차단그룹 ID 별 중복 등록 방지 장치를 서버 측에서 추가동일 그룹 ID가 이미 활성 상태라면 서버가 별도 처리 없이 즉시 차단 및 경고 메시지 전송악성 혹은 오작동하는 클라이언트가 반복 요청을 보내도..

작성자: 관리자 댓글 0 조회 11

메시지 브릿지 1.4.0 기능 패치

클라이언트 개발자님께 힘이 되고자 다음의 기능을 추가했습니다. 감사합니다.1) 유령 처리 방식 개선 (삼세판 적용)Ping 실패 1번 = 무시3번 연속 실패할 때만 유령(끊긴 사용자)로 판단해 제거→ 정상적인 모바일 백그라운드/일시적인 끊김은 절대 문제 없음.2) 자동 재접속 안정화클라이언트에서 연결..

작성자: 관리자 댓글 0 조회 98

[웹] 서버에서 받은 소스 설명

updateDisplay() 내 postData의 역할 & 활용 설명updateDisplay() 안에서 만들어지는 postData는서버(WebSocket)에서 받은 메시지를 프론트에서 활용하기 좋은 ‘가공용 데이터 꾸러미’로 만들어둔 것입니다.const postData = { type..

작성자: 관리자 댓글 0 조회 210

[웹] WebSocket 서버 전송 소스 설명

아래 코드는 실시간 알림 게시판에 적용되는 예제 소스입니다.WebSocket 연결 상태를 자동으로 확인하여, 알림 데이터를 서버에 안전하게 전송하는 역할을 합니다.이 코드를 정상적으로 동작시키기 위해서는 반드시 페이지 상단에 WebSocket 엔진 소스(WS 초기화, sendEvent, connect..

작성자: 관리자 댓글 0 조회 211

[웹] 기본 엔진 소스 설명

WebSocket 엔진(상단 핵심 로직) 설명서※ 이 영역은 전체 시스템의 핵심 엔진이므로 가능한 수정 없이 그대로 사용하는 것을 권장합니다.※ 필요한 기능 확장은 하단 UI 영역 또는 별도 함수에서 처리하는 것을 권장합니다.1. 기본 정보 설정const domain = window.location...

작성자: 관리자 댓글 0 조회 200

메시지 브릿지 1.3.1 보안,성능 패치

1) 메인 이벤트 루프의 차단 요인 제거비동기 흐름을 방해하던 일부 연산이 별도 경로로 이관되었습니다.그 결과, 메시지 처리 경로가 불필요하게 정지하거나 응답 지연이 발생하는 사례가 감소했습니다.2) 안정성 확보단시간에 대량 로그가 발생할 때 출력 루프가 굳어버리던 문제를 재구성했습니다.출력 배치, ..

작성자: 관리자 댓글 0 조회 240

메시지 브릿지 1.2.1 보안 패치

1. 대용량 메시지 차단 기능 추가2. 서버 안정성 강화대용량 파일과 동영상 업로드는 별도의 기술 구조를 통해 처리할 계획입니다.

작성자: 관리자 댓글 0 조회 373

메시지 브릿지 1.2.0 기능패치

일회용 보안 토큰(One-Time Token) 기능 추가클라이언트가 서버에 연결할 때 요청할 수 있는 일회용 인증 토큰 기능이 새롭게 추가되었습니다.서버에서 직접 안전한 토큰을 생성하며, 일정 시간 후 자동으로 폐기됩니다.특정 프레임워크나 언어에 종속되지 않아 웹, 앱, 서버 등 모든 환경에서 사용 ..

작성자: 관리자 댓글 0 조회 586

검색


© 2025 Designonex. All rights reserved. · 이용약관 · 개인정보 처리방침