sgcWebSockets 2026.7은 프로덕션 운영에 관한 릴리스입니다. 이제 WebSocket 애플리케이션을 여러 서버에 분산하면서도 채널, pub/sub, 프레즌스가 모든 서버에서 함께 동작하게 할 수 있습니다. 채널은 최근 메시지를 기억할 수 있으므로, 끊겼다가 돌아온 클라이언트는 놓친 메시지를 받아 갑니다. 클라이언트는 서버를 일제히 두들기는 대신 지수 백오프와 지터를 적용해 재접속하고, 전송 큐에 상한을 두어 느린 소비자 하나가 서버 메모리를 고갈시키지 못하게 할 수 있으며, HTTP 클라이언트에는 크리덴셜을 유출하는 리다이렉트, 다운그레이드된 TLS, 크기 제한이 없는 응답에 대한 보호 장치가 추가되었습니다.
여기에 더해 Delphi용 async / await 계층, HTTP 서버의 gzip과 deflate, 새로운 HTTP QUERY 메서드, 종단 간 암호화를 위한 아이덴티티 검증, MCP 클라이언트의 OAuth2, 그리고 긴 수정 목록이 있습니다. 이 글은 그 안내 투어입니다. 각 절은 해당 기능을 깊이 다루는 글로 연결됩니다.
여러 서버에 걸친 클러스터링
새로운 TsgcWSCluster 컴포넌트는 여러 대의 sgcWebSockets 서버를 하나의 논리적 서버로 묶습니다. 노드 A에 게시된 메시지가 노드 B에 접속한 구독자에게 도달하고, 프레즌스는 로컬 노드만이 아니라 클러스터 전체의 명단을 반환합니다. 두 가지 백플레인을 사용할 수 있습니다. 노드들이 서로 직접 통신하며 추가 설치가 필요 없는 내장 메시, 그리고 더 큰 규모의 배포를 위한 Redis Pub/Sub(Enterprise)입니다.
uses
sgcWebSocket, sgcWebSocket_Protocols, sgcWebSocket_Cluster;
oCluster := TsgcWSCluster.Create(nil);
oCluster.EngineType := clusterMesh; // no extra infrastructure
oCluster.ClusterPort := 5410; // this node's mesh listener
oCluster.Peers.Add('192.168.1.101:5410'); // the other nodes
oCluster.Attach(oProtocol); // cluster this protocol's pub/sub
oCluster.Start;
자세히 보기: 여러 노드로 WebSocket 서버 확장하기.
메시지 히스토리와 재접속 복구
이제 sgc 프로토콜 서버는 각 채널의 최근 N개 메시지를 보관할 수 있으며, 선택적으로 만료 시간을 지정할 수 있습니다. 클라이언트가 재접속해서 다시 구독할 때 실제로 마지막으로 확인한 메시지의 오프셋을 보내면, 서버는 그 클라이언트가 자리를 비운 동안 게시된 모든 메시지를 재전송합니다. 이미 하고 있는 재구독 외에는 클라이언트 코드에서 바뀌는 것이 없으며, 활성화하지 않는 한 이 기능 전체가 꺼져 있습니다.
oProtocol.History.Enabled := True;
oProtocol.History.Size := 1000; // last 1000 messages per channel
oProtocol.History.TTLSeconds := 3600; // optional: also expire after 1 hour
자세히 보기: 메시지 히스토리와 재접속 복구.
async / await, 그리고 진짜로 기다리는 Connect
새로운 sgcBase_AsyncAwait 유닛이 라이브러리에 태스크와 퓨처를 도입합니다. 이벤트 핸들러를 하나도 연결하지 않고도 HTTP 요청, WebSocket 연결, AI 채팅 완성을 await 할 수 있고, 결과를 ThenProc과 OnError로 이어 붙일 수 있으며, 실제로 하부 요청을 중단시키는 방식으로 태스크를 취소할 수 있습니다.
같은 맥락에서, 이제 Connect는 연결이 단지 시작된 것이 아니라 완전히 수립될 때까지 블로킹하고, Disconnect는 소켓이 실제로 닫힐 때까지 기다립니다. 빠른 재접속 중에 물리곤 하던 경합 상태가 이로써 닫힙니다. 접속이나 전송이 실패하면 그 이유를 새로운 LastError 프로퍼티에서 확인할 수 있고, 오류 문자열을 곧바로 돌려주는 Connect 오버로드도 있으므로 OnError를 구독하지 않고도 실패를 진단할 수 있습니다.
var
vError: string;
begin
if sgcWebSocketClient1.Connect(10000) then
sgcWebSocketClient1.WriteData('hello')
else
ShowMessage('Connect failed: ' + sgcWebSocketClient1.LastError);
// or get the reason back directly from the call
if not sgcWebSocketClient1.Connect(vError, 10000) then
ShowMessage('Connect failed: ' + vError);
end;
자세히 보기: Delphi의 async / await.
HTTP 클라이언트 강화
토큰을 잃는 가장 쉬운 길은 리다이렉트입니다. 인증된 요청에 서버가 다른 호스트로 향하는 302로 응답하면, 클라이언트는 아무렇지 않게 Authorization과 Cookie 헤더를 그쪽으로 다시 보냅니다. 2026.7은 교차 사이트 리다이렉트에서 그 헤더를 제거하는 StripAuthOnCrossHostRedirect와, HTTPS를 평문 HTTP로 낮추는 리다이렉트를 거부하는 NoInsecureRedirect를 추가합니다.
이제 응답에도 상한을 둘 수 있습니다. 청크 응답의 한 조각에 대한 MaxChunkSize와 전체에 대한 MaxResponseSize가 그것입니다. RejectPublicSuffixCookies는 "com"이나 "co.uk"처럼 지나치게 넓은 도메인을 주장하는 쿠키를 버립니다. 업로드를 위해서는 큰 본문을 보내기 전에 서버의 허락을 구하는 Expect: 100-continue와, 크기를 아직 알 수 없는 업로드를 스트리밍하기 위한 청크 요청 본문이 있습니다.
oHTTP.StripAuthOnCrossHostRedirect := True;
oHTTP.NoInsecureRedirect := True;
oHTTP.MaxResponseSize := 10 * 1024 * 1024; // 10 MB, 0 = unlimited
oHTTP.MaxChunkSize := 1024 * 1024; // 1 MB per chunk
oHTTP.RejectPublicSuffixCookies := True;
이 스위치들은 모두 기본적으로 꺼져 있으며, 어느 것도 저수준 HTTP 객체를 직접 다룰 필요가 없습니다. 기성 API 클라이언트(AI/LLM, 암호화폐 거래소, OAuth2, Google Cloud, WhatsApp, AWS SQS, WebPush)에도 함께 공개되어 있습니다. 리다이렉트와 크기 보호 장치는 HTTP/2 클라이언트에도 있고, HTTP/3에는 응답 크기 제한이 추가되었습니다. 자세히 보기: HTTP 클라이언트 강화하기.
재시도, 백오프, 지터
예전의 WatchDog는 고정 간격으로 재접속했는데, 이는 서버를 잃은 천 개의 클라이언트가 모두 같은 순간에 돌아온다는 뜻입니다. 이제 배수, 상한, 무작위 지터를 갖춘 지수 백오프를 지원하므로 클라이언트 무리가 스스로 시간을 벌려 흩어집니다. 고정 간격이 여전히 기본값이므로, 요청하기 전까지는 아무것도 달라지지 않습니다.
HTTP 클라이언트는 자체 재시도 정책을 갖게 되었습니다. 설정 가능한 상태 코드 목록에 대해 재시도하고, 매번 더 오래 기다리며, 서버가 Retry-After 힌트를 보내면 그것을 존중합니다. OpenAI, Anthropic, Gemini 클라이언트도 이를 사용합니다.
sgcWebSocketClient1.WatchDog.Backoff := wdbExponential;
sgcWebSocketClient1.WatchDog.BackoffMultiplier := 2.0; // double every attempt
sgcWebSocketClient1.WatchDog.MaxInterval := 60; // seconds, the ceiling
sgcWebSocketClient1.WatchDog.Jitter := 0.2; // up to 20% random spread
자세히 보기: 지수 백오프, 지터, 그리고 Retry-After.
전송 큐의 백프레셔
클라이언트가 여러분이 쓰는 속도보다 느리게 읽으면, 큐에 쌓인 메시지가 서버 메모리에 쌓입니다. 이제 QueueOptions.MaxQueueSize로 연결당 대기 메시지 수에 상한을 두고, 상한에 도달했을 때의 동작을 선택할 수 있습니다. 가장 오래된 메시지를 버리거나, 가장 새로운 메시지를 버리거나, 연결을 끊는 것입니다. 새로운 OnSendBufferFull 이벤트는 무언가가 버려지기 전에 발생하므로, 이를 로깅하거나 집계하거나, 잘라 내면 안 되는 연결에 대해서는 폐기를 거부할 수 있습니다. 큐는 기본적으로 무제한입니다.
oServer.QueueOptions.MaxQueueSize := 1000;
oServer.QueueOptions.OverflowPolicy := qopDropOldest; // or qopDropNewest, qopDisconnect
자세히 보기: 느린 WebSocket 클라이언트를 위한 전송 큐 제한.
HTTP 서버의 gzip과 deflate
이제 HTTP 서버는 클라이언트가 Accept-Encoding을 알릴 때마다 응답을 압축할 수 있습니다. DocumentRoot에서 제공되는 파일과 OnCommandGet에서 직접 만든 응답 모두에 적용되고, 최소 크기 미만의 본문과 압축해도 작아지지 않을 콘텐츠 타입은 건너뛰며, HTTP.sys 서버에서도 사용할 수 있습니다. 압축은 기본적으로 꺼져 있으며, 현재로서는 HTTP/1.1 응답만 압축됩니다.
sgcWebSocketHTTPServer1.HTTPCompression.Enabled := True;
sgcWebSocketHTTPServer1.HTTPCompression.Level := 6; // 1..9
sgcWebSocketHTTPServer1.HTTPCompression.MinSize := 1024; // bytes
자세히 보기: HTTP 서버의 gzip 및 deflate 압축.
HTTP QUERY 메서드
QUERY는 검색 조건을 POST처럼 요청 본문에 담아 보내면서도 GET처럼 안전하고 캐시 가능한 새 IETF HTTP 메서드입니다. 덕분에 질의를 길이 제한이 있는 URL에 억지로 밀어 넣지 않아도 됩니다. 2026.7은 HTTP/1.x, HTTP/2, HTTP/3, REST 클라이언트에서 이를 구현했고, 서버와 프록시도 이를 처리합니다.
vResult := oClient.Query('https://api.example.org/contacts', oQuery);
자세히 보기: Delphi의 새로운 HTTP QUERY 메서드.
E2EE 아이덴티티 검증
종단 간 암호화는 페이로드를 비공개로 유지하지만, 그 자체만으로는 상대편이 누구인지 알려 주지 않습니다. 중간의 서버나 릴레이가 양쪽에 각각 자기 키를 건네주고 모든 것을 읽을 수 있습니다. 2026.7에서는 각 측이 장기 아이덴티티 키로 자신의 암호화 키에 서명할 수 있고, 상대편이 그 서명을 검증하므로, 조용한 키 교체가 받아들여지는 대신 탐지됩니다.
일대일 대화와 그룹 채팅 모두에서 동작하고, 상대의 아이덴티티를 승인하거나 고정하는 이벤트와 상대의 아이덴티티 키가 바뀌었을 때 경고하는 이벤트가 추가되며, 기본적으로 꺼져 있고 2026.6 피어와 완전히 하위 호환됩니다.
E2EE.E2EE_Options.Identity.Enabled := True;
E2EE.E2EE_Options.Identity.PrivateKey := LoadIdentityPrivateKey; // PEM
E2EE.E2EE_Options.Identity.PublicKey := LoadIdentityPublicKey; // PEM
E2EE.OnE2EEVerifyPeerIdentity := OnE2EEVerifyPeerIdentityEvent;
E2EE.OnE2EEKeyChange := OnE2EEKeyChangeEvent;
자세히 보기: E2EE 아이덴티티 검증.
MCP 클라이언트를 위한 OAuth2, 그리고 Identity Assertion 그랜트
이제 MCP 클라이언트는 정적 API 키 대신 OAuth2로 인증할 수 있습니다. 토큰을 직접 가져와 만료될 때까지 캐시하고 갱신하며, OAuth2가 활성화되어 있으면 API 키보다 우선합니다. 이와 함께 OAuth2 클라이언트에는 새 그랜트 타입인 Identity Assertion Authorization Grant가 추가되었습니다. 이 그랜트는 한 도메인의 아이덴티티를 다른 도메인으로 연결하며, 클라이언트가 여러 단계로 이루어진 교환 전체를 대신 수행하고 각 단계를 따라갈 수 있도록 이벤트를 발생시킵니다.
MCPClient.MCPOptions.AuthenticationOptions.OAuth2.Enabled := True;
MCPClient.MCPOptions.AuthenticationOptions.OAuth2.GrantType := auth2ClientCredentials;
MCPClient.MCPOptions.AuthenticationOptions.OAuth2.TokenURL := 'https://auth.example.com/oauth2/token';
MCPClient.MCPOptions.AuthenticationOptions.OAuth2.ClientId := 'YOUR_CLIENT_ID';
MCPClient.MCPOptions.AuthenticationOptions.OAuth2.ClientSecret := 'YOUR_CLIENT_SECRET';
자세히 보기: MCP 클라이언트를 위한 OAuth2.
암호화폐 피드: 재구독과 레이트 제한
지금까지는 시세 데이터 소켓이 끊겼다가 WatchDog가 되살리면, 소켓은 살아 있어도 모든 구독을 직접 다시 보내기 전까지는 아무것도 구독하지 않은 상태였습니다. 이제 거래소 클라이언트가 그 일을 대신해 줍니다. Resubscribe := True로 설정하면 재접속 후 스트림이 복원됩니다. Binance, Kraken, Coinbase를 비롯한 열네 곳 남짓의 거래소와 XTB의 세션 기반 피드를 지원합니다. 기본값은 False입니다.
REST API 클라이언트에는 선택적인 클라이언트 측 레이트 리미터가 추가되어, 요청 버스트 때문에 거래소로부터 일시 차단당하는 일이 없도록 합니다. 일정 시간 창 안에서 보낼 요청 수를 제한하고, 초과 요청이 대기할지 거부될지 선택하세요. 기본적으로 꺼져 있습니다.
oBinance.RateLimit.Enabled := True;
oBinance.RateLimit.IntervalMs := 60000; // one minute window
oBinance.RateLimit.MaxRequests := 1000;
oBinance.RateLimit.Behavior := rlbWait;
자세히 보기: 재구독과 클라이언트 측 레이트 리미터.
그 밖의 추가 사항
- TLS 프리셋. 이제 모든 TLS 옵션 객체가
Preset프로퍼티를 공개합니다.TLSOptions.Preset := tlspSecureDefaults로 설정하면 인증서 검증, TLS 1.2 이상, 호스트명 확인이 한 줄로 켜집니다. 기본값인tlspCustom은 현재 설정을 그대로 둡니다. - 커스텀 HTTP 세션 클래스. HTTP 서버를 시작하기 전에
TIdHTTPSession의 자체 파생 클래스를 서버의SessionClass에 지정하면 세션 안에 원하는 데이터를 저장할 수 있으며, 세션 ID, 쿠키, 타임아웃, 정리 동작은 이전과 똑같이 유지됩니다. 세션이 어디에 보관될지까지 완전히 제어하고 싶다면 자체SessionList를 지정할 수도 있습니다. - RFC 2253 인증서 헬퍼. 인증서의 주체와 발급자를 표준 RFC 2253 식별 이름 문자열로 읽어 오는 새 함수가 추가되었습니다.
- WinHTTP WebSocket 클라이언트. 이제 표준 클라이언트처럼 토큰/베어러 및 URL 로그인을 지원하고, 연결 요청에 자체 헤더를 추가할 수 있도록
OnHandshake이벤트를 공개하며, 접속과 종료 그리고 주고받은 모든 텍스트 및 바이너리 메시지를 기록하는 로그 파일을 쓸 수 있습니다. OAuth2, JWT, 세션 로그인은 여전히 사용할 수 없는데, Windows 내장 네트워킹 스택을 사용하며 Indy를 포함하지 않기 때문입니다.
수정과 강화
2026.7에는 긴 수정 목록이 담겨 있습니다. 전부 나열하는 대신, 그것들이 무엇을 뜻하는지 정리했습니다.
메모리 안전성과 파서 강화. 버퍼 끝을 넘어서는 읽기가 MQTT 5 클라이언트(보낸 것보다 많은 데이터를 주장하는 브로커), AMQP 1.0 클라이언트(UUID 디코딩), STUN/TURN 파서(무한 루프와 IPv6 주소 포함), WebRTC SCTP 데이터 채널, 원시 TCP 프레임 스캐너에서 수정되었습니다. UDP 클라이언트와 서버는 DTLS를 포함해 소켓을 두 번 해제하거나 연결 종료 시 소켓을 매달린 상태로 남기지 않습니다. 잘못 동작하는 피어가 메모리를 무한정 키울 수 있었던 지점에는 내부 상한이 추가되었습니다. MQTT 대기 메시지 큐, sgc 프로토콜의 연결당 구독 수, WAMP 서버의 대기 중인 호출, 청크 응답의 트레일러 줄, AMQP 필드 테이블의 중첩 깊이가 그것입니다.
HTTP/2. 압축 해제 크기 상한(기본 64 MB)이 "zip bomb" 응답으로 인한 메모리 고갈 구멍을 막고, 응답 크기 제한을 응답의 마지막 부분으로 우회할 수 없게 되었으며, 실패한 요청은 이제 타임아웃을 끝까지 기다리지 않고 즉시 실패합니다.
암호화와 난수. WebAuthn 서버, OAuth2 인가 서버, 내장 HTTP 세션 ID가 이제 암호학적으로 안전한 난수 생성기를 사용하고, 챌린지는 일회용이며, 비밀 값은 상수 시간으로 비교됩니다(클라이언트 시크릿, JWT 서명, HTTP API Basic 비밀번호). 서명은 유효하지만 클레임이 잘못된 JWT는 이제 받아들여지는 대신 거부됩니다. OpenSSL의 StrictVerify를 켜면 인증서가 호스트명과도 대조되므로, 다른 호스트에 발급된 유효한 인증서는 거부됩니다. IP 주소로의 연결과 기존 설정은 영향을 받지 않습니다.
Server-Sent Events. 한 번의 읽기로 여러 이벤트를 전달하거나 이벤트가 두 번의 읽기에 걸쳐 나뉜 스트리밍 응답에서 첫 번째를 제외한 나머지가 유실되었습니다. 이 문제가 수정되어 AI/LLM 클라이언트의 토큰 단위 스트리밍이 복원되었습니다. OpenAI 스트리밍 채팅은 스트림 플래그를 설정하지 못하는 문제도 있었는데, 이제 다른 공급자들과 마찬가지로 스트리밍합니다.
거래소 클라이언트. Kraken 요청 ID는 이제 엄격히 증가하고, BitMEX는 만료 시각을 밀리초가 아니라 초 단위로 보내며, MEXC는 실제로 전송하는 인코딩된 파라미터에 서명하고, Cryptorobotics는 해시 기반 호출에서 두 값을 뒤바꾸지 않으며, Deribit은 로그인 토큰이 만료되기 전에 갱신하고, Forex 클라이언트는 계정 ID를 읽는 동안 삼켜 버리던 오류를 이제 보고합니다.
Lazarus와 Free Pascal. 세 가지 컴파일 수정입니다. JSON 유닛의 인터페이스가 이제 플랫폼별로 올바른 호출 규약을 사용하고(Linux에서 컴파일에 실패했습니다), Forex 클라이언트가 숫자 형식을 읽기 위해 Delphi 전용 함수를 호출하지 않으며, 포스트 양자 키 교환 유닛이 더 이상 size_t에 의존하지 않습니다.
history.txt에는 더 많은 내용이 있습니다. gRPC 클라이언트(동시 호출이 서로의 응답을 받을 수 있었고, 전송 실패가 성공으로 보고되었습니다), OpenAPI 서버(스펙 로딩이 이제 스레드 안전하며 내부 잠금이 걸린 동안 핸들러가 실행되지 않습니다), 로그인 실패 때마다 발생하던 서버 연결 및 소켓 누수, Ollama와 LM Studio처럼 "message" 필드가 없는 OpenAI 응답에서 발생하던 크래시, WinHTTP 클라이언트의 WriteAndWaitData 수정, 그리고 HTTP 클라이언트 로그의 비밀 값 마스킹(이제 로깅을 켜도 토큰이 평문으로 디스크에 기록되지 않습니다)이 포함됩니다.
.NET 에디션
sgcWebSockets .NET 2026.7은 HTTP 클라이언트에 집중합니다. Delphi 에디션과 동일한 보호 장치 세트를 제공합니다. 교차 사이트 리다이렉트에서의 크리덴셜 제거, HTTPS에서 HTTP로의 다운그레이드 차단, 청크별 및 전체 응답 크기 상한, 퍼블릭 서픽스 쿠키 거부, Expect: 100-continue, 청크 요청 본문입니다. 이들은 HTTP 클라이언트 컴포넌트와 기성 API 클라이언트(AI/LLM 클라이언트, OAuth2, Google Cloud, WhatsApp, AWS SQS, WebPush)에서 사용할 수 있고, 동일한 리다이렉트 및 크기 보호 장치가 HTTP/2 클라이언트에도 있습니다. 리다이렉트 처리와 쿠키 만료 수정도 포함되어 있습니다. 모든 것이 기본적으로 꺼져 있습니다.
업그레이드
2026.7은 기존 2026.x 프로젝트에 그대로 끼워 넣을 수 있는 업그레이드입니다. 이번 릴리스의 모든 새 기능은 기본적으로 꺼져 있습니다. 클러스터링, 메시지 히스토리, 백오프, 전송 큐 상한, HTTP 압축, 클라이언트 보호 장치, E2EE 아이덴티티 검증, 거래소 재구독은 모두 명시적으로 켜야 하므로, 새 빌드를 설치해도 여러분이 선택하기 전까지는 동작이 달라지지 않습니다.
활성 구독 중인 고객은 고객 영역에서 또는 esegece.com/products/websockets/download에서 새 빌드를 내려받을 수 있습니다.
질문이나 피드백이 있거나 마이그레이션에 도움이 필요하신가요? 문의해 주세요. 코드를 직접 작성한 사람들이 답변해 드립니다.
