# 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 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](PROTOCOL.md)의 `Receive Ordering and Backpressure` 섹션에 정의하고, 새 언어 구현 기준은 [PORTING_GUIDE.md](PORTING_GUIDE.md)의 `반드시 유지해야 하는 구조` 표를 따른다. --- ## 프로토콜 전체 wire format 명세는 [PROTOCOL.md](PROTOCOL.md)를 본다. ```text [4-byte big-endian length] [PacketBase protobuf bytes] ``` WebSocket/WSS transport에서는 각 binary frame이 TCP length header 없이 `PacketBase` protobuf payload 하나를 담는다. 프로토콜 호환성은 언어별 package version과 분리해서 추적한다. 자세한 정책은 [VERSIONING.md](VERSIONING.md)를 본다. 패키지를 아직 registry에 배포하지 않더라도, 저장소에 포함된 구현은 문서화된 프로토콜 계약을 지켜야 한다. --- ## 구현체 | 언어 | 상태 | 경로 | 사용처 | |------|------|------|--------| | Dart | 사용 가능 | [dart/](dart/) | Flutter, Dart server | | Kotlin | 사용 가능 | [kotlin/](kotlin/) | Android, JVM | | Go | 사용 가능 | [go/](go/) | Server, tooling, scripting | | TypeScript | 사용 가능 | [typescript/](typescript/) | Browser, Node.js | | Python | 사용 가능 | [python/](python/) | Server, tooling, scripting | | C# | 계획 / 보류 | `csharp/` | Unity, .NET | | Swift | 계획 / 보류 | `swift/` | iOS, macOS | 새 언어 구현은 [PORTING_GUIDE.md](PORTING_GUIDE.md)와 [agent-ops/skills/project/add-proto-socket-crosstest-language/templates/](agent-ops/skills/project/add-proto-socket-crosstest-language/templates/)의 템플릿에서 시작한다. 동일 언어 테스트와 크로스 언어 테스트가 통과한 뒤에만 구현 상태를 사용 가능으로 표시한다. 현재 구현 상태는 이 README를 공개 snapshot으로 본다. 현재 작업 맥락은 [agent-roadmap/current.md](agent-roadmap/current.md)에서 선택하고, 큰 로드맵 방향은 [agent-roadmap/ROADMAP.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/](proto/) | canonical protobuf schema 원본 | | [dart/](dart/) | Dart / Flutter 구현체와 테스트 | | [go/](go/) | Go 구현체, 테스트, Go 주도 crosstest | | [kotlin/](kotlin/) | Kotlin / JVM 구현체와 crosstest | | [python/](python/) | Python 구현체와 crosstest | | [typescript/](typescript/) | TypeScript 구현체와 crosstest | | [tools/](tools/) | proto 생성과 sync helper | | [examples/](examples/) | 소비자 지향 예제 | | [agent-ops/](agent-ops/) | 프로젝트 규칙, 로드맵, 반복 agent workflow | --- ## 빠른 시작: Dart ```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((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을 재생성한다. ```bash 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 ```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/](examples/go-module-consumer/)를 본다. --- ## 테스트 실행 개발과 문제 재현을 위한 로컬 명령은 이 문서에 정리한다. 반복 가능한 저장소 로컬 검증 진입점은 다음 명령이다. ```bash 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: ```bash tools/check_proto_sync.sh ``` 동일 언어 검증: ```bash 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 ``` ```bash cd go go test ./... ``` ```bash cd kotlin ./gradlew test ``` ```bash cd python python3 -m pytest -q ``` ```bash cd typescript npm run check npm test ``` 크로스 언어 검증: ```bash 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 ``` ```bash 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 ``` ```bash 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 ``` ```bash cd python python3 crosstest/python_dart.py python3 crosstest/python_go.py python3 crosstest/python_kotlin.py python3 crosstest/python_typescript.py ``` ```bash 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개 시나리오를 검증한다. ```bash 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](agent-ops/rules/project/rules.md)의 프로젝트 규칙과 해당 경로의 domain rule을 확인한다. 활성 로드맵 창은 [agent-roadmap/current.md](agent-roadmap/current.md)에 있다. 안정화 경계는 다음과 같다. - 명시적인 호환성 작업 요청이 없으면 프로토콜 `0.1` 동작을 안정적으로 유지한다. - 유지보수 모드 작업에는 bug fix, 문서 정정, 테스트 강화, 호환성을 유지하는 구현 수정이 포함될 수 있다. - C#/Swift, package registry release, protocol/API 변경 작업은 구체적인 소비자 수요, 호환성 계획, 전체 검증 매트릭스 통과 조건이 있을 때 별도 작업으로 재개한다. 현재 릴리즈 표준은 Git ref/tag 기준이다. - 상위 `../oto` workflow가 결정되기 전까지 이 저장소에서 외부 CI/CD runner를 직접 연결하지 않는다.