작은 WS frame을 isolate 왕복 없이 처리해 full 성능 검증에서 확인된 Dart WS burst hard-gate 회귀를 닫는다. 성능 검증 루프의 task archive와 로드맵 완료 근거도 함께 보존한다. |
||
|---|---|---|
| .claude | ||
| .tmp | ||
| .vscode | ||
| agent-ops | ||
| agent-roadmap | ||
| agent-task | ||
| dart | ||
| examples/go-module-consumer | ||
| go | ||
| kotlin | ||
| proto | ||
| python | ||
| tools | ||
| typescript | ||
| .aiexclude | ||
| .clineignore | ||
| .clinerules | ||
| .codex | ||
| .cursorignore | ||
| .cursorrules | ||
| .geminiignore | ||
| .gitignore | ||
| AGENTS.md | ||
| CLAUDE.md | ||
| GEMINI.md | ||
| opencode.json | ||
| PORTING_GUIDE.md | ||
| PROTOCOL.md | ||
| README.md | ||
| VERSIONING.md | ||
Proto Socket
Proto Socket은 여러 언어와 플랫폼 사이에서 양방향 통신을 하기 위한 바이너리 소켓 프로토콜 라이브러리다.
Protocol Buffers 직렬화, TCP 4바이트 빅엔디안 길이 프리픽스 프레이밍, WebSocket 바이너리 프레임, 타입 기반 메시지 라우팅, 요청-응답 상관관계, 내장 하트비트를 제공한다.
현재 상태
현재 프로토콜 0.1의 사용 가능 호환 구현은 Dart, Go, Kotlin, Python, TypeScript다.
이 프로젝트는 안정화와 유지보수 단계에 있다. 현재 릴리즈 표준은 이 저장소를 Git ref/tag 기준으로 소비하는 방식이며, package registry 배포는 현재 목표가 아니다. C#과 Swift는 계획된 대상이지만, 실제 수요가 생기기 전까지 구현을 미룬다.
프로토콜이나 구현을 바꿀 때는 아래 내부 로컬 검증 매트릭스를 안정성 gate로 사용한다. 현재 주 사용 방식은 Git 기반 소비이므로, CI/CD runner 연결은 기본 운영 모델이 아니며 이 프로젝트 규모와 배포 방식에서는 설계상 과하다. Coverage는 라인 수치가 아니라 프로토콜 계약의 모든 필수 시나리오가 매트릭스에 포함되어 PASS인지로 판단한다.
bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_matrix.sh --all
프로토콜 또는 공개 API 변경은 호환성 작업으로 다룬다. 관련 문서를 갱신하고, 필요한 경우 protobuf binding을 재생성하며, 전체 검증 매트릭스가 통과해야 한다.
설계 원칙
- core transport layer는 얇고 안정적으로 유지한다.
- 언어 간 통신에 필요한 최소 공통 기반만 제공한다.
- framing, serialization, routing, request-response correlation, heartbeat를 표준화한다.
- Protocol Buffers를 핵심 프로토콜 의존성으로 둔다. 다른 런타임 의존성은 native-first, 좁은 범위, 기능에 비례한 수준으로 유지한다.
- 애플리케이션 의미를 프로토콜 core에 넣지 않는다.
- auth, session, agent workflow, chat, game logic 같은 도메인 관심사는 상위 레이어 구현의 책임이다.
- 수신 처리 계약(inbound queue 순서, receive coordinator, backpressure, close/drain/cancel 정책)은 PROTOCOL.md의
Receive Ordering and Backpressure섹션에 정의하고, 새 언어 구현 기준은 PORTING_GUIDE.md의반드시 유지해야 하는 구조표를 따른다.
프로토콜
전체 wire format 명세는 PROTOCOL.md를 본다.
[4-byte big-endian length] [PacketBase protobuf bytes]
WebSocket/WSS transport에서는 각 binary frame이 TCP length header 없이 PacketBase protobuf payload 하나를 담는다.
프로토콜 호환성은 언어별 package version과 분리해서 추적한다. 자세한 정책은 VERSIONING.md를 본다. 패키지를 아직 registry에 배포하지 않더라도, 저장소에 포함된 구현은 문서화된 프로토콜 계약을 지켜야 한다.
구현체
| 언어 | 상태 | 경로 | 사용처 |
|---|---|---|---|
| Dart | 사용 가능 | dart/ | Flutter, Dart server |
| Kotlin | 사용 가능 | kotlin/ | Android, JVM |
| Go | 사용 가능 | go/ | Server, tooling, scripting |
| TypeScript | 사용 가능 | typescript/ | Browser, Node.js |
| Python | 사용 가능 | python/ | Server, tooling, scripting |
| C# | 계획 / 보류 | csharp/ |
Unity, .NET |
| Swift | 계획 / 보류 | swift/ |
iOS, macOS |
새 언어 구현은 PORTING_GUIDE.md와 agent-ops/skills/project/add-proto-socket-crosstest-language/templates/의 템플릿에서 시작한다. 동일 언어 테스트와 크로스 언어 테스트가 통과한 뒤에만 구현 상태를 사용 가능으로 표시한다.
현재 구현 상태는 이 README를 공개 snapshot으로 본다. 현재 작업 맥락은 agent-roadmap/current.md에서 선택하고, 큰 로드맵 방향은 agent-roadmap/ROADMAP.md에 둔다.
주요 명령
| 목적 | 명령 | 비고 |
|---|---|---|
| 전체 로컬 검증 | bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_matrix.sh --all |
proto sync, 동일 언어 테스트, 사용 가능 구현의 크로스 언어 검증을 실행한다. |
| Proto schema sync 확인 | tools/check_proto_sync.sh |
언어별 proto copy가 허용된 언어 옵션을 제외하고 proto/message_common.proto와 일치하는지 확인한다. |
| Protobuf binding 재생성 | tools/generate_proto.sh |
canonical proto schema를 바꾼 뒤 실행한다. |
| Dart 테스트 | cd dart && dart pub get && dart test && dart compile js test/browser_ws_import_compile.dart -o /tmp/proto_socket_browser_ws_import_compile.js |
Dart VM/IO 테스트와 web import compile 확인 |
| Go 테스트 | cd go && go test ./... |
Go 동일 언어 검증 |
| Kotlin 테스트 | cd kotlin && ./gradlew test |
Kotlin 동일 언어 검증 |
| Python 테스트 | cd python && python3 -m pytest -q |
Python 동일 언어 검증 |
| TypeScript 확인 | cd typescript && npm run check && npm test |
TypeScript type check와 테스트 |
저장소 구조
| 경로 | 역할 |
|---|---|
| proto/ | canonical protobuf schema 원본 |
| dart/ | Dart / Flutter 구현체와 테스트 |
| go/ | Go 구현체, 테스트, Go 주도 crosstest |
| kotlin/ | Kotlin / JVM 구현체와 crosstest |
| python/ | Python 구현체와 crosstest |
| typescript/ | TypeScript 구현체와 crosstest |
| tools/ | proto 생성과 sync helper |
| examples/ | 소비자 지향 예제 |
| agent-ops/ | 프로젝트 규칙, 로드맵, 반복 agent workflow |
빠른 시작: Dart
import 'package:proto_socket/proto_socket.dart';
// 1. message_common.proto에 메시지를 정의하고 protoc로 생성한다.
// 2. client를 구현한다.
class MyClient extends ProtobufClient {
MyClient(Socket socket) : super(socket, 30, 10, {
MyMessage.getDefault().info_.qualifiedMessageName: MyMessage.fromBuffer,
});
}
// 3. server를 구현한다.
class MyServer extends ProtobufServer {
MyServer() : super('0.0.0.0', 9090, (socket) => MyClient(socket));
@override
void onClientConnected(ProtobufClient client) {
client.addListener<MyMessage>((msg) => print('Received: ${msg}'));
}
}
// 4. server를 시작한다.
final server = MyServer();
await server.start();
// 5. 연결하고 메시지를 보낸다.
final socket = await Socket.connect('localhost', 9090);
final client = MyClient(socket);
await client.send(MyMessage()..text = 'hello');
메시지 타입 추가
canonical proto인 proto/message_common.proto를 수정한 뒤, 저장소에 포함된 모든 binding을 재생성한다.
tools/generate_proto.sh
tools/check_proto_sync.sh
Go와 Kotlin proto copy에는 option go_package, Java package/class option 같은 언어별 옵션만 추가로 허용한다. 메시지 schema가 proto/message_common.proto와 달라지면 tools/check_proto_sync.sh가 diff와 함께 실패한다.
빠른 시작: Go
package main
import (
"context"
"net"
"time"
"google.golang.org/protobuf/proto"
protoSocket "git.toki-labs.com/toki/proto-socket/go"
"git.toki-labs.com/toki/proto-socket/go/packets"
)
func parserMap() protoSocket.ParserMap {
return protoSocket.ParserMap{
protoSocket.TypeNameOf(&packets.TestData{}): func(b []byte) (proto.Message, error) {
m := &packets.TestData{}
return m, proto.Unmarshal(b, m)
},
}
}
func main() {
ctx := context.Background()
server := protoSocket.NewTcpServer("127.0.0.1", 9090, func(conn net.Conn) *protoSocket.TcpClient {
return protoSocket.NewTcpClient(conn, 30, 10, parserMap())
})
server.OnClientConnected = func(client *protoSocket.TcpClient) {
protoSocket.AddRequestListenerTyped[*packets.TestData, *packets.TestData](
&client.Communicator,
func(req *packets.TestData) (*packets.TestData, error) {
return &packets.TestData{Index: req.GetIndex(), Message: "echo: " + req.GetMessage()}, nil
},
)
}
if err := server.Start(ctx); err != nil {
panic(err)
}
defer server.Stop()
client, err := protoSocket.DialTcp(ctx, "127.0.0.1", 9090, 30, 10, parserMap())
if err != nil {
panic(err)
}
defer client.Close()
res, err := protoSocket.SendRequestTyped[*packets.TestData, *packets.TestData](
&client.Communicator,
&packets.TestData{Index: 1, Message: "hello"},
2*time.Second,
)
if err != nil {
panic(err)
}
println(res.GetMessage())
}
Go 구현체는 다음 표면도 제공한다.
- TCP:
NewTcpServer,DialTcp,NewTcpServerTLS,DialTcpTLS - WebSocket:
NewWsServer,NewWsServerWithOptions,DialWs,NewWsServerTLS,NewWsServerTLSWithOptions,DialWss - Shared helpers:
Send,SendRequest,AddListenerTyped,AddRequestListenerTyped,Broadcast
다른 저장소에서 이 모듈을 사용하는 복사 가능한 scaffold가 필요하면 examples/go-module-consumer/를 본다.
테스트 실행
개발과 문제 재현을 위한 로컬 명령은 이 문서에 정리한다. 반복 가능한 저장소 로컬 검증 진입점은 다음 명령이다.
bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_matrix.sh --all
이 명령은 proto schema sync, 동일 언어 테스트, 크로스 언어 검증을 실행하고, 실패가 있으면 재현 명령과 함께 PASS/FAIL 표를 출력한다. 더 작은 범위만 확인하려면 --proto, --unit, --cross를 사용한다.
외부 CI runner 연동은 이 저장소의 기본 운영 목표가 아니다. 현재 주 사용 방식은 Git 기반 소비와 내부 로컬 매트릭스 결과 확인이며, 이 범위에서는 별도 CI/CD runner 연결을 요구하지 않는다.
Proto schema sync:
tools/check_proto_sync.sh
동일 언어 검증:
cd dart
dart pub get
dart test
dart compile js test/browser_ws_import_compile.dart -o /tmp/proto_socket_browser_ws_import_compile.js
cd go
go test ./...
cd kotlin
./gradlew test
cd python
python3 -m pytest -q
cd typescript
npm run check
npm test
크로스 언어 검증:
cd go
go run ./crosstest/go_dart.go
go run ./crosstest/go_kotlin.go
go run ./crosstest/go_python.go
go run ./crosstest/go_typescript.go
cd dart
dart run crosstest/dart_go.dart
dart run crosstest/dart_kotlin.dart
dart run crosstest/dart_python.dart
dart run crosstest/dart_typescript.dart
cd kotlin
./gradlew run -PmainClass=com.tokilabs.proto_socket.crosstest.KotlinDartKt
./gradlew run -PmainClass=com.tokilabs.proto_socket.crosstest.KotlinGoKt
./gradlew run -PmainClass=com.tokilabs.proto_socket.crosstest.KotlinPythonKt
./gradlew run -PmainClass=com.tokilabs.proto_socket.crosstest.KotlinTypescriptKt
cd python
python3 crosstest/python_dart.py
python3 crosstest/python_go.py
python3 crosstest/python_kotlin.py
python3 crosstest/python_typescript.py
cd typescript
./node_modules/.bin/tsx crosstest/typescript_dart.ts
./node_modules/.bin/tsx crosstest/typescript_go.ts
./node_modules/.bin/tsx crosstest/typescript_kotlin.ts
./node_modules/.bin/tsx crosstest/typescript_python.ts
Dart.io와 Dart.web E2E 매트릭스
위 표의 Dart 열은 Dart VM/IO client를 뜻한다. Dart.web은 별도 축이다. 브라우저 WS/WSS client만 가능하며, 브라우저에서는 TCP와 server 역할이 불가능하므로 해당 cell은 N/A다. Dart.web WSS는 테스트용 self-signed certificate의 SPKI를 허용하는 Chrome 실행 wrapper로 local/remote 브라우저 검증에 포함한다.
| Server | Dart.io TCP | Dart.io WS | Dart.io TLS TCP | Dart.io WSS | Dart.web WS | Dart.web WSS | Dart.web TCP |
|---|---|---|---|---|---|---|---|
| Dart.io | Covered | Covered | Covered | Covered | Covered | Covered | N/A |
| Go | Covered | Covered | Covered | Covered | Covered | Covered | N/A |
| Kotlin | Covered | Covered | Covered | Covered | Covered | Covered | N/A |
| Python | Covered | Covered | Covered | Covered | Covered | Covered | N/A |
| TypeScript | Covered | Covered | Covered | Covered | Covered | Covered | N/A |
각 Dart.web cell은 server-language runner가 WS와 WSS server를 함께 띄우고, matching browser test file에 대해 dart test -p chrome을 실행하며, WS send-push/request-response와 WSS send-push/request-response 4개 시나리오를 검증한다.
cd dart && dart run crosstest/dart_web.dart
cd go && go run ./crosstest/go_dart_web.go
cd kotlin && ./gradlew run -PmainClass=com.tokilabs.proto_socket.crosstest.KotlinDartWebKt
cd python && python3 crosstest/python_dart_web.py
cd typescript && ./node_modules/.bin/tsx crosstest/typescript_dart_web.ts
작업 맥락
코드나 프로토콜을 변경하기 전에 agent-ops/rules/project/rules.md의 프로젝트 규칙과 해당 경로의 domain rule을 확인한다. 활성 로드맵 창은 agent-roadmap/current.md에 있다.
안정화 경계는 다음과 같다.
- 명시적인 호환성 작업 요청이 없으면 프로토콜
0.1동작을 안정적으로 유지한다. - 유지보수 모드 작업에는 bug fix, 문서 정정, 테스트 강화, 호환성을 유지하는 구현 수정이 포함될 수 있다.
- C#/Swift, package registry release, protocol/API 변경 작업은 구체적인 소비자 수요, 호환성 계획, 전체 검증 매트릭스 통과 조건이 있을 때 별도 작업으로 재개한다. 현재 릴리즈 표준은 Git ref/tag 기준이다.
- 상위
../otoworkflow가 결정되기 전까지 이 저장소에서 외부 CI/CD runner를 직접 연결하지 않는다.