alt/agent-task/m-contract-codegen-baseline/03+02_api_parser_map/PLAN-cloud-G06.md
toki 88c673d973 feat: contract codegen baseline - add proto code generation for client and server
- Add proto files for backtest and market domains
- Generate Go code in packages/contracts/gen/go
- Generate Dart code in apps/client/lib/src/generated
- Add contracts-gen and contracts-check binaries
- Update pubspec.yaml with protoc dependencies
- Update go.work with new module
2026-05-28 05:29:03 +09:00

7.3 KiB

Plan - API Parser Map

이 파일을 읽는 구현 에이전트에게

CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 반드시 채운다. 검증 명령을 실행하고 실제 stdout/stderr를 기록한 뒤 active 파일을 그대로 두고 리뷰 준비를 보고한다. 최종화는 code-review 스킬 전용이다.

배경

Go API는 proto-socket session boundary를 맡지만 현재 ALT application payload parser map을 등록하지 않는다. 02+01_codegen_check가 generated Go contracts를 만든 뒤, API 내부에 표준 parser map 위치를 두면 다음 Socket Session Loop Milestone에서 handshake/request-response를 바로 얹을 수 있다.

분석 결과

읽은 파일

  • agent-roadmap/current.md
  • agent-roadmap/phase/foundation-alignment/PHASE.md
  • agent-roadmap/phase/foundation-alignment/milestones/contract-codegen-baseline.md
  • agent-ops/rules/project/domain/api/rules.md
  • agent-ops/rules/project/domain/contracts/rules.md
  • services/api/go.mod
  • services/api/internal/socket/server.go
  • services/api/internal/config/config.go
  • services/api/cmd/alt-api/main.go
  • packages/contracts/proto/alt/v1/common.proto
  • packages/contracts/proto/alt/v1/market.proto
  • packages/contracts/proto/alt/v1/backtest.proto
  • ../proto-socket/go/communicator.go
  • bin/test
  • bin/lint

테스트 커버리지 공백

  • API parser map helper: 기존 테스트 없음. 새 unit test가 모든 ALT request/response/result message key와 parse success를 검증해야 한다.
  • socket.NewServer parser map 주입: 기존 테스트 없음. 최소한 helper가 non-empty이고 server construction이 컴파일되는 것을 go test ./...로 검증한다.

심볼 참조

  • renamed/removed symbols: none.

분할 판단

분할 정책을 먼저 평가했다. 이 plan은 03+02_api_parser_map이며 02+01_codegen_checkcomplete.log를 만든 뒤 시작한다. Client Dart parser map은 별도 04+02_client_parser_map에서 병렬 처리한다.

범위 결정 근거

이 subtask는 services/api/**와 API module dependency만 다룬다. Flutter client parser map, UI, actual request handlers, worker execution은 범위 밖이다.

빌드 등급

build=cloud-G06, review=cloud-G06. Protocol parser registration이 API runtime boundary에 들어가고 generated module dependency를 추가하므로 cloud review가 필요하다.

의존 관계 및 구현 순서

03+02_api_parser_map은 같은 task group의 02+01_codegen_checkcomplete.log를 만든 뒤 시작한다. 이 의존성은 directory name의 +02가 source of truth다.

구현 체크리스트

  • [API-1] API 내부 contract parser map helper와 unit test를 추가한다.
  • [API-2] socket server construction이 API parser map을 사용하도록 연결하고 module dependency를 정리한다.
  • CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

[API-1] API contract parser map helper

문제

../proto-socket/go/communicator.go:20ParserMapmap[string]func([]byte) (proto.Message, error)이고, parser가 없으면 ../proto-socket/go/communicator.go:304에서 error를 반환한다. ALT API에는 generated alt.v1 messages를 parser map으로 묶는 표준 위치가 없다.

Before:

// ../proto-socket/go/communicator.go:304
func (c *Communicator) parse(typeName string, data []byte) (proto.Message, error) {
	c.mu.RLock()
	parser := c.parserMap[typeName]
	c.mu.RUnlock()
	if parser == nil {
		return nil, fmt.Errorf("protobuf parser is not registered for type %s", typeName)
	}
	return parser(data)
}

해결 후 형태:

package contracts

func ParserMap() protoSocket.ParserMap {
	return protoSocket.ParserMap{
		protoSocket.TypeNameOf(&altv1.HelloRequest{}): parserFor(func() proto.Message { return &altv1.HelloRequest{} }),
	}
}

해결 방법

services/api/internal/contracts/parser_map.go를 만들고 generated altv1 message 전체를 등록한다. request/response/result payload를 모두 포함하고 helper는 map을 새로 반환해 호출자가 mutate해도 package 전역 상태가 생기지 않게 한다.

수정 파일 및 체크리스트

  • services/api/internal/contracts/parser_map.go: ParserMap()과 shared parserFor helper 추가.
  • services/api/internal/contracts/parser_map_test.go: map keys와 parse success 검증.
  • services/api/go.mod: generated Go module require 추가.

테스트 작성

작성한다. TestParserMapIncludesAltMessagesHelloRequest, HelloResponse, ListInstrumentsRequest, ListInstrumentsResponse, ListBarsRequest, ListBarsResponse, StartBacktestRequest, StartBacktestResponse, GetBacktestRunRequest, GetBacktestRunResponse, GetBacktestResultRequest, GetBacktestResultResponse, BacktestResult를 등록/parse한다.

중간 검증

cd services/api && go test ./internal/contracts

기대 결과: exit 0.

[API-2] Wire parser map into socket server

문제

services/api/internal/socket/server.go:12protoSocket.ParserMap{}를 전달해 ALT payload가 들어와도 parse할 수 없다.

Before:

// services/api/internal/socket/server.go:10
func NewServer(cfg config.Config) *protoSocket.WsServer {
	return protoSocket.NewWsServer(cfg.Host, cfg.Port, cfg.SocketPath, func(conn *websocket.Conn) *protoSocket.WsClient {
		return protoSocket.NewWsClient(conn, cfg.HeartbeatIntervalSec, cfg.HeartbeatWaitSec, protoSocket.ParserMap{})
	})
}

해결 후 형태:

import (
	apiContracts "git.toki-labs.com/toki/alt/services/api/internal/contracts"
)

return protoSocket.NewWsClient(conn, cfg.HeartbeatIntervalSec, cfg.HeartbeatWaitSec, apiContracts.ParserMap())

해결 방법

server.go에서 API contracts helper를 import하고 NewWsClientapiContracts.ParserMap()을 전달한다. go mod tidy로 generated contract module dependency를 정리한다.

수정 파일 및 체크리스트

  • services/api/internal/socket/server.go: empty parser map을 API parser map으로 교체.
  • services/api/go.mod: generated contract module dependency 유지.
  • services/api/go.sum: 필요한 checksum 변경 반영.

테스트 작성

별도 socket server unit test는 작성하지 않는다. 이 subtask는 parser map helper unit test와 go test ./... 컴파일 검증으로 충분하다. 실제 handshake/request-response behavior는 후속 Socket Session Loop Milestone 범위다.

중간 검증

cd services/api && go test ./...

기대 결과: exit 0.

수정 파일 요약

파일 항목
services/api/internal/contracts/parser_map.go API-1
services/api/internal/contracts/parser_map_test.go API-1
services/api/internal/socket/server.go API-2
services/api/go.mod API-1, API-2
services/api/go.sum API-2

최종 검증

cd services/api && go test ./...
bin/test
bin/lint

기대 결과: 모든 명령 exit 0. Go test cache output은 허용한다.

모든 코드 변경 완료 후 반드시 CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.