-
지금 누가 내 게이트웨이에 붙어 있나 — OpenClaw가 비콘 한 줄로 연결 현황을 집계하는 방법IT 2026. 7. 10. 22:00
개인 AI 어시스턴트를 하나의 게이트웨이(gateway, 여러 클라이언트의 요청을 한 곳에서 받아 처리하는 중계 서버)로 돌리기 시작하면, 어느 순간 거기에 붙는 기기가 한 대가 아니게 된다. 데스크톱 브라우저로 한 번, 폰에 깐 PWA로 또 한 번, 거실 노트북에서 원격으로 한 번 — 같은 게이트웨이에 동시에 연결된다. 그러면 자연스럽게 떠오르는 질문이 있다. "지금 이 게이트웨이에 누가/무엇이 붙어 있지?" OpenClaw(오픈소스 개인 AI 어시스턴트)의 Control UI에는 이 질문에 답하는 Instances(인스턴스) 메뉴가 있다. 이 글은 그 화면이 보여주는 "연결 현황(presence)"이 어떻게 모이고, 왜 하필 최근 5분이라는 창(window)으로 잘리는지를 코드 근거와 함께 풀어 본다.
배경 — 게이트웨이 하나에 기기 여러 대가 붙는 환경
OpenClaw의 Control UI는 게이트웨이가 직접 서빙하는 작은 단일 페이지 앱이고, 같은 포트(기본
18789)의 WebSocket(브라우저와 서버가 연결을 끊지 않고 양방향으로 메시지를 주고받는 프로토콜)으로 게이트웨이와 대화한다. 핵심은 이 게이트웨이가 한 사람의 여러 기기를 동시에 받아들이도록 설계됐다는 점이다 — 로컬 루프백 브라우저, Tailnet(테일스케일이 만드는 사설 VPN 네트워크)을 통한 원격 접속, LAN의 다른 노트북, 폰의 설치형 PWA까지.여기서 두 종류의 연결을 구분해야 한다. 하나는 사람이 조작하는 클라이언트(operator) — 브라우저로 채팅을 보내는 운영자다. 다른 하나는 노드(node) — 게이트웨이에 붙어 작업을 분담하는 다른 머신이다. 이 둘이 뒤섞여 한 게이트웨이에 붙으면, 운영자 입장에서는 "내가 모르는 무언가가 연결돼 있는 건 아닌지", "아까 폰으로 연 세션이 아직 살아 있는지"가 보안과 운영 양면에서 궁금해진다.
다이어그램 설명. 이 그림은 Instances 메뉴가 답하려는 상황을 한눈에 보여준다. 하나의 게이트웨이에 데스크톱 브라우저·폰·원격 노드가 동시에 붙고, 게이트웨이는 그 모두의 연결 현황을 모아 Control UI의 목록으로 내보낸다. 점선으로 표시한 "게이트웨이 자기 자신"이 중요한데, 외부에서 아무것도 안 붙어 있어도 게이트웨이 자신은 항상 목록에 한 줄로 등장한다(뒤에서 다룬다). 이 패턴이 필요한 이유는 단순하다 — 연결이 여러 출처에서 들어오니, 그 출처들을 한 군데로 모아 "지금 살아 있는 것"만 추려 보여줄 단일 화면이 있어야 운영자가 상황을 파악할 수 있기 때문이다. 함정은 이 목록이 "과거에 한 번이라도 붙었던 모든 기기"가 아니라 "지금 살아 있는 것"만 담는다는 점 — 그 경계가 바로 다음 절의 주제다.
그래서 무슨 가치인가: 여러 기기가 한 게이트웨이를 공유하는 환경에서, 연결 현황을 한 화면에 모아 보는 것만으로 "내가 모르는 접속이 있나"라는 보안 질문과 "그 세션 아직 살아 있나"라는 운영 질문을 동시에 해소할 토대가 생긴다.
핵심 문제 — 연결은 끊겨도 흔적이 남는다
연결 현황을 보여주겠다는 발상은 쉽지만, 막상 만들려고 하면 까다로운 지점이 하나 있다. "연결이 끊겼다"는 사실을 어떻게 아는가? 브라우저는 탭을 닫거나, 와이파이가 끊기거나, 노트북이 잠자기로 들어가면 깔끔한 "나 나간다"는 통지 없이 사라진다. 그 결과 게이트웨이가 단순히 "한 번 붙었던 기기"를 다 모아 보여주면, 화면은 금세 유령(ghost) 항목으로 가득 찬다 — 어제 폰으로 잠깐 열었던 세션, 30분 전 잠자기로 들어간 노트북이 여전히 "연결됨"처럼 보이는 것이다.
반대 극단도 문제다. 끊김을 너무 엄격하게 판정하려고 매 연결마다 끊임없이 "살아 있나?" 신호(keepalive)를 주고받으며 즉시 제거하면, 잠깐 네트워크가 출렁이는 것만으로 멀쩡한 기기가 목록에서 사라졌다 나타났다 깜빡인다. 모바일에서는 화면을 잠그기만 해도 연결이 잠시 멎으므로, 이 깜빡임은 흔하게 일어난다.
다이어그램 설명. 위 그림은 연결 현황 화면을 만들 때 마주치는 트레이드오프를 세 갈래로 펼쳐 보여준다. 기기가 붙어 목록에 뜬 뒤, 통지 없이 사라지는 상황에서 "언제 지울 것인가"가 분기의 핵심이다. 위쪽 두 경로는 양 극단의 실패다 — 영원히 안 지우면 유령 항목이 쌓이고, 즉시 지우면 짧은 끊김에도 화면이 깜빡인다. 가운데 경로, 즉 "일정 시간 무소식이면 지운다"가 OpenClaw가 택한 절충이다. 이 패턴이 좋은 이유는 끊김을 직접 감지하려 애쓰지 않고 "최근에 소식이 있었는가"라는 더 쉬운 질문으로 바꿔치기하기 때문이다. 놓치기 쉬운 함정은 가운데 경로의 "일정 시간"을 얼마로 잡느냐에 따라 유령과 깜빡임 사이 어디로 치우칠지가 결정된다는 점 — 그 숫자가 바로 5분이다.
그래서 무슨 가치인가: "끊김을 감지한다"는 어려운 문제를 "최근 소식이 있었나"라는 쉬운 문제로 바꾸면, 통지 없이 사라지는 기기까지 자연스럽게 처리되어 화면이 늘 현실과 비슷하게 유지된다.
해결 방법 — 비콘 한 줄과 5분 만료 창
OpenClaw의 답은 비콘(beacon) 기반 presence 집계다. 비콘이란 각 클라이언트·노드가 게이트웨이에 주기적으로 보내는 "나 여기 살아 있어"라는 짧은 신호다(등대 불빛처럼 일정 간격으로 깜빡인다는 비유에서 온 이름). 게이트웨이는 비콘을 받을 때마다 그 기기의 마지막 소식 시각(timestamp)을 갱신해 두고, 목록을 보여 줄 때 "최근 5분 안에 소식이 있었던 것"만 추려서 내보낸다.
비콘은 거창한 별도 프로토콜이 아니라
system-event라는 한 줄짜리 텍스트 이벤트다. 노드가 보내는 비콘은 다음과 같은 정해진 형식의 문자열이고, 게이트웨이는 정규식(regex, 문자열에서 정해진 패턴을 뽑아내는 규칙)으로 이 한 줄을 분해해 호스트·IP·버전·마지막 입력 경과·모드·사유로 쪼갠다.Node: 거실노트북 (192.168.0.21) · app 2026.6.5 · last input 12s ago · mode node · reason heartbeat코드 설명. 이 한 줄이 비콘의 실체다.
Node:로 시작하는 텍스트 안에 호스트명, 괄호 속 IP, 앱 버전, "마지막 입력이 몇 초 전이었는지", 연결 모드, 그리고 이 신호를 보낸 사유가 가운뎃점(·)으로 구분돼 담겨 있다. 게이트웨이는 이 줄을 받으면 정규식으로 각 필드를 떼어내 구조화된 항목으로 저장한다. 이 패턴을 쓴 이유는 비콘을 사람이 그대로 읽을 수 있는 한 줄로 만들면, 별도의 바이너리 포맷이나 새 RPC 메서드 없이도 시스템 이벤트 스트림에 자연스럽게 흘려보낼 수 있기 때문이다. 함정은 형식이 조금만 어긋나도(가운뎃점 누락 등) 정규식이 매칭에 실패한다는 점인데, 이때는 통째로 버리지 않고 원문 텍스트와 수신 시각만 보존해 둔다.핵심 장치는 게이트웨이 메모리 안의 작은 표다. 각 기기를 키(key)로 삼아 마지막 비콘 정보를 담아 두고, 목록을 요청받을 때마다 마지막 소식이 5분(
TTL_MS = 5 * 60 * 1000)을 넘긴 항목을 그 자리에서 솎아낸다. TTL은 Time-To-Live, 즉 "이 정보가 유효하다고 인정할 시간"이라는 뜻으로, 캐시·DNS 등에서 항목의 수명을 정할 때 널리 쓰는 개념이다.다이어그램 설명. 이 그림은 비콘 한 줄이 화면 위 목록 한 줄이 되기까지의 전체 흐름을 보여준다. 오른쪽 위에서 기기가 비콘을 보내면 게이트웨이가 정규식으로 분해해 메모리 표에 기기별로 저장하고 수신 시각을 새로 찍는다(이 갱신이 "살아 있음"의 증거다). 왼쪽에서 Control UI가 목록을 요청하면 — 새로고침 버튼을 누르거나 화면을 열 때 — 그 순간에 5분 넘은 항목을 솎고, 200개를 넘으면 가장 오래된 것부터 잘라내고, 최신순으로 정렬해 돌려준다. 이 구조의 핵심 설계 의도는 만료 판정을 "쓰는 시점"이 아니라 "읽는 시점"에 한다는 점이다. 끊김을 감시하는 별도 타이머를 돌리지 않고, 목록을 보여달라는 요청이 올 때 그제서야 청소하므로 백그라운드 부담이 없다. 놓치기 쉬운 함정은 200개라는 상한과 5분이라는 만료가 각각 다른 보호를 한다는 것 — 5분은 유령을 막고, 200개 상한은 짧은 시간에 수많은 기기가 몰려도 메모리가 무한히 부풀지 않도록 막는다(오래된 것부터 버리는 LRU 방식이다. LRU는 Least Recently Used, 가장 오래 안 쓰인 것을 먼저 버리는 교체 전략).
그렇다면 왜 하필 5분인가? 이건 앞 절에서 본 트레이드오프를 숫자로 푼 결과다. 비콘은 보통 수십 초 간격으로 들어오므로(예시 비콘의 "12초 전 입력"처럼 잦다), 잠깐의 네트워크 출렁임이나 폰 화면 잠금으로 비콘 한두 번을 놓쳐도 5분 창 안에서는 항목이 사라지지 않는다 — 깜빡임 방지. 동시에, 기기가 진짜로 떠나 비콘이 완전히 멎으면 5분 뒤에는 확실히 목록에서 빠진다 — 유령 방지. 1분처럼 너무 짧으면 멀쩡한 모바일이 자주 사라지고, 30분처럼 너무 길면 떠난 기기가 한참 남는다. 5분은 그 사이에서 "사람이 '방금까지 쓰던 것'이라고 느끼는 체감 시간"에 맞춘 절충값이라고 보는 것이 합리적이다(이 5분이 그런 의도로 골라졌으리라는 건 설계 추론이며, 코드에 박힌 사실은 값이 5분이라는 것뿐이다).
한 가지 더. 외부에서 아무 비콘이 안 와도 목록이 텅 비지 않는다. 게이트웨이는 시작할 때 자기 자신을 presence 항목으로 한 줄 심어 두고(
mode: gateway,reason: self), 목록을 돌려줄 때마다 그 자기 항목의 시각을 새로 찍어 절대 만료되지 않게 한다. 그래서 Instances 화면은 최소한 "이 게이트웨이 자신"은 늘 보여 준다 — 화면이 비어 운영자가 "연결이 끊긴 건가?"라고 오해하는 일을 막는 장치다.그래서 무슨 가치인가: 만료를 읽는 시점에 처리하고 비콘을 한 줄 텍스트로 흘려보내는 이 단순한 설계 덕분에, 끊김 감지·타이머·복잡한 상태 동기화 없이도 "지금 살아 있는 것"만 담긴 목록이 거의 공짜로 유지된다.
결과 — 한 화면에서 읽는 '지금 누가 붙어 있나'
이 메커니즘이 최종적으로 운영자에게 보여 주는 것이 Instances 메뉴다. 화면은 카드 하나에 연결 현황 목록을 쌓아 보여 주고, 각 줄은 한 기기의 호스트·IP를 제목으로, 그 아래에 칩(chip, 작고 둥근 라벨 태그) 형태로 부가 정보를 단다 — 연결 모드(운영자인지 노드인지 게이트웨이인지), 역할(roles), 권한 범위(scopes), 플랫폼(macOS·Linux·Windows), 기기 종류, 모델 식별자, 버전. 오른쪽에는 "마지막 소식이 언제였는지"를 사람이 읽기 쉬운 상대 시간으로(예: "2분 전") 붙이고, 마지막 입력 경과와 비콘 사유도 함께 보여 준다.
다이어그램 설명. 이 그림은 Instances 화면의 구성 요소를 분해한 것이다. 카드 하나 안에 연결 항목들이 줄로 쌓이고, 각 줄은 위쪽의 호스트·IP 제목, 가운데의 부가 정보 칩 묶음, 오른쪽의 시간 정보로 나뉜다. 카드 머리에는 두 개의 조작 버튼이 있는데, 눈 모양 토글과 새로고침이다. 이 배치가 의미 있는 이유는 운영자가 한 줄만 봐도 "이게 누구의 어떤 기기이고, 어떤 권한으로, 언제까지 살아 있었나"를 즉시 읽을 수 있도록 정보를 계층화했기 때문이다. 놓치기 쉬운 함정은 새로고침 버튼의 역할이다 — 이 목록은 자동으로 실시간 갱신되기도 하지만(게이트웨이가 presence 스냅샷을 연결된 클라이언트에 방송한다), 새로고침 버튼은 지금 이 순간의 만료 처리를 강제로 한 번 더 돌려 가장 최신 상태를 당겨오는 수단이다.
눈 모양 토글은 작지만 의미 있는 보안 배려다. 기본값은 호스트명과 IP를 가린(redacted) 상태로 두고, 토글을 눌러야 실제 값이 드러난다. 게이트웨이를 화면 공유하거나 누가 옆에서 볼 수 있는 상황에서, 내부 호스트명·사설 IP 같은 네트워크 지형 정보가 무심코 노출되는 걸 한 단계 막아 주는 것이다. 운영자가 의도적으로 "보기"를 눌렀을 때만 노출되므로, 기본은 안전(safe by default) 쪽으로 기울어 있다.
이 메뉴를 호출하는 RPC(원격 프로시저 호출, 다른 컴퓨터의 함수를 마치 내 함수처럼 부르는 방식) 이름은
system-presence이고, 권한 범위는operator.read로 묶여 있다 — 즉 읽기 권한만 있으면 호출할 수 있고, 이 호출 자체로는 어떤 기기를 강제로 끊거나 바꾸지 못한다. 연결 현황은 어디까지나 관찰용 화면이라는 설계 의도가 권한 이름에 드러나 있다.그래서 무슨 가치인가: Instances 메뉴는 결국 "여러 기기가 한 게이트웨이를 공유할 때 운영자가 상황을 한눈에 읽을 단일 관찰 지점"이다. 비콘과 5분 만료라는 단순한 토대 위에서, 운영자는 추가 도구 없이 "지금 누가/무엇이 붙어 있고, 얼마나 최근에 활동했나"를 한 화면에서 확인하고, 기본 가림과 읽기 전용 권한으로 그 정보가 안전하게 다뤄지도록 보장받는다.
이 글은 생성형 AI의 도움을 받아 작성되었습니다. 원본 자료를 기반으로 AI가 초안을 생성하고, 작성자가 검토·편집하였습니다.
'IT' 카테고리의 다른 글
능력을 다 켜 두면 에이전트가 위험해진다 — OpenClaw가 Skills 메뉴로 능력을 골라 켜는 방법 (1) 2026.07.12 에이전트 다섯개가 한 서버에 산다 — OpenClaw가 에이전트마다 격리된 워크스페이스를 한 화면에서 점검하는 방법 (0) 2026.07.11 에이전트가 알아서 일하게 만들고도 통제권은 쥐고 싶다 — OpenClaw Control UI의 예약 작업 화면 (0) 2026.07.11 토큰을 어디에 얼마나 썼는지 모르면 통제가 안 된다 — OpenClaw가 멀티 프로바이더 사용량을 집계한 방법 (0) 2026.07.11 한 봇이 채널마다 다른 두뇌를 쓰게 하는 법 — OpenClaw의 Sessions 메뉴 (0) 2026.07.10 에이전트가 세 가지 일을 동시에 돌릴 때 — OpenClaw Workboard가 '지금 뭐가 막혔는지'를 보여주는 방법 (0) 2026.07.10 에이전트가 방금 내 시스템에서 뭘 했지? — OpenClaw가 Activity 탭으로 답한 방법 (0) 2026.07.09 에이전트가 지금 무슨 도구를 쓰는지 안 보인다 — OpenClaw 웹 채팅이 WebSocket 스트리밍으로 푼 방법 (0) 2026.07.09 에이전트가 지금 살아 있나 — OpenClaw가 흩어진 상태를 한 화면(Overview)에 모은 방법 (0) 2026.07.09 근거가 이 문장을 정말 뒷받침하나 — 로컬 NLI로 매일 $0 사실 검증 (0) 2026.07.08