Files
grpccanary/project_analysis_report.md

15 KiB
Raw Permalink Blame History

프로젝트 분석 보고서: grpccanary

1. 개요

grpccanaryGo 언어로 JSON 처리 → HTTP 서버 → gRPC 서버/클라이언트로 이어지는 학습 여정을 다루는 한국어 튜토리얼 저장소입니다. 저장소 이름(grpccanary)과 달리 실제 내용은 gRPC 단일 주제가 아니라, "데이터 직렬화(JSON) → 웹 서버(HTTP/gin) → 원격 프로시저 호출(gRPC)"로 난이도를 높여가는 3단계 실습 커리큘럼이며, 현재는 gRPC 파트가 가장 완성도 높게 구현되어 있습니다.

저장소 루트에는 이 학습 프로젝트와는 성격이 다른 multi-agent-mux라는 다중 에이전트(Claude Code) 오케스트레이션 인프라가 함께 자리 잡고 있습니다(.agents/, .mam/ 디렉토리). 이 인프라는 tmux 세션 + MQTT 메시지 브로커를 이용해 여러 AI 에이전트(팀장/리뷰어 역할)가 하나의 작업 저장소를 공유하며 협업하도록 설계된 별도의 도구 체계이며, 실제로 이 보고서를 작성하는 작업(job 702ea1d8) 자체도 이 인프라를 통해 위임되었습니다.

즉 이 저장소는 **"gRPC를 배우기 위한 Go 예제 코드"**와 **"그 예제 코드를 여러 AI 에이전트가 함께 작업하도록 돕는 협업 프레임워크"**가 한 저장소 안에 공존하는 구조입니다.


2. 저장소 구조

grpccanary/
├── README.md                    # 메인 튜토리얼 (JSON/HTTP/gRPC 개념 설명 + 실행 가이드, 한국어)
├── AGENTS.md                    # LLM 코딩 행동 지침 (일반 원칙)
├── go.mod / go.sum               # Go 모듈 정의 (module grpccanary, go 1.25.4)
├── protoapi.proto                # gRPC 서비스 IDL(Protocol Buffers) 정의 원본
├── protoapi/                     # protoc로 생성된 Go stub 코드
│   ├── protoapi.pb.go             # 메시지 타입 (protoc-gen-go)
│   └── protoapi_grpc.pb.go        # 서비스/클라이언트 stub (protoc-gen-go-grpc)
├── obj.json                      # JSON 예제용 샘플 데이터 파일
├── lib/
│   ├── main.go                    # 실행 진입점 (현재는 JSON 예제만 호출하도록 설정됨)
│   ├── jsonexample/
│   │   └── json_parser.go         # encoding/json 마샬링·언마샬링 예제
│   ├── httpentity/
│   │   ├── server.go               # gin 기반 HTTP 서버 (전체가 주석 처리된 미완성 스텁)
│   │   └── client.go               # 패키지 선언만 있는 빈 파일
│   └── grpcentity/
│       ├── server.go               # gRPC 서버 구현 (Random 서비스)
│       ├── client.go               # gRPC 클라이언트 구현
│       └── README.md               # gRPC 서버/클라이언트 구현 상세 해설 (한국어)
├── docs/
│   └── Working with JSON/
│       ├── README.md               # JSON 관련 학습 자료 (영문)
│       └── README-kr.md            # JSON 관련 학습 자료 (한글)
├── scripts/
│   └── generate-env.sh             # .env.example → .env 복사 스크립트 (멀티에이전트 인프라용)
├── .env.example                  # 멀티에이전트 인프라(MQTT 브로커 등) 설정 템플릿
├── .agents/                      # 멀티에이전트 오케스트레이션 규칙·스킬 (아래 4절 참고)
└── .mam/                         # 멀티에이전트 잡(Job) 레지스트리 및 세션 상태 (런타임 산출물)

빌드 확인 결과 go build ./...go vet ./... 모두 오류 없이 통과했으며, 테스트 파일(*_test.go)은 저장소에 존재하지 않습니다.


3. 핵심 구성 요소 (학습용 Go 코드)

3.1 진입점 — lib/main.go

  • 프로젝트의 유일한 main() 함수. var port = ":8080"을 정의하고 있으며, 기본 상태에서는 jsonexample.JsonParsingExample()만 호출합니다.
  • gRPC 예제를 실행하려면 README.md 안내에 따라 main() 내부를 수동으로 편집해 grpcSample()을 호출하도록 바꿔야 합니다(미사용 import 오류 방지를 위해 jsonexample import를 주석 처리해야 함). 즉 하나의 코드베이스 안에서 학습 단계별로 진입점을 수동 전환하는 방식으로 설계되어 있습니다.
  • grpcSample() 함수는 entity.ServerRun(port)를 고루틴으로 백그라운드 실행한 뒤 1초 대기 후 entity.ClientRun(...)을 호출해, 같은 프로세스 안에서 서버·클라이언트가 통신하는 데모를 구성합니다.

3.2 JSON 파싱 예제 — lib/jsonexample/json_parser.go

  • encoding/json 표준 라이브러리를 이용해 (1) map[string]interface{} ↔ JSON 문자열 변환, (2) 구조체(Person) ↔ JSON 변환의 마샬링/언마샬링을 시연합니다.
  • 외부 의존성 없이 표준 라이브러리만 사용하는 가장 단순한 예제로, 커리큘럼의 1단계 역할을 합니다.

3.3 HTTP 서버 예제 — lib/httpentity/

  • server.gogin-gonic/gin을 이용한 REST API 서버(정적 파일 서빙 + /api/randomNumber, /api/randomPassword, /api/randomDate 라우트) 초안이 전체 주석 처리된 상태로만 존재합니다. 즉 코드는 작성되어 있으나 활성화되지 않은 미완성/보류 상태입니다.
  • client.gopackage httpentity 선언 한 줄만 있는 빈 파일입니다.
  • go.mod에는 gin-gonic/gin 의존성이 여전히 선언되어 있어, 이 파트가 완전히 폐기된 것이 아니라 추후 재개를 염두에 둔 진행 중(work-in-progress) 상태로 보입니다.

3.4 gRPC 서비스 정의 — protoapi.proto

  • proto3 문법으로 Random이라는 gRPC 서비스를 정의하며 3개의 RPC 메서드를 제공합니다.
    • GetDate(RequestDateTime) returns (DateTime) — 서버의 현재 날짜/시간 반환
    • GetRandom(RandomParams) returns (RandomInt) — 시드(Seed)와 위치(Place)를 기반으로 한 의사난수 생성
    • GetRandomPass(RequestPass) returns (RandomPass) — 시드와 길이(Length)를 받아 무작위 ASCII 비밀번호 생성
  • option go_package = "./protoapi/;protoapi"로 지정되어 있어, protoc 컴파일 시 protoapi/ 디렉토리에 Go 패키지 protoapi가 생성됩니다.
  • 이미 컴파일된 stub(protoapi/protoapi.pb.go, protoapi/protoapi_grpc.pb.go)이 저장소에 커밋되어 있어 protoc 재설치 없이 바로 빌드/실행이 가능합니다.

3.5 gRPC 서버 구현 — lib/grpcentity/server.go

  • RandomServer 구조체가 protoapi.UnimplementedRandomServer를 임베딩하여 3개 RPC 메서드(GetDate, GetRandom, GetRandomPass)를 구현합니다.
  • getString(len int64)은 ASCII 코드 !(33)부터 94개 범위 내에서 문자를 뽑아 임의 문자열(비밀번호)을 생성하는 헬퍼입니다.
  • ServerRun(addr string)grpc.NewServer()로 서버를 만들고 reflection.Register(server)로 gRPC reflection(예: grpcurl 같은 외부 CLI 디버깅 도구 지원)을 활성화한 뒤 TCP 포트를 리슨합니다.
  • 주의(코드상 특이점): ServerRun은 인자로 받은 addr을 실제로 사용하지 않고 패키지 전역 변수 port = ":8080"으로 리슨합니다(net.Listen("tcp", port)). 따라서 현재 코드는 항상 :8080에서만 동작하며, 호출부에서 다른 포트를 넘겨도 무시됩니다.
  • rand.Seed(r.GetSeed())(패키지 전역 시드 설정, Go 1.20+에서는 deprecated)와 rand.NewSource를 혼용하고 있어 스레드 안전성이나 API 일관성 측면에서 다소 오래된 패턴을 보입니다(학습용 예제이므로 의도된 단순화로 판단됨).

3.6 gRPC 클라이언트 구현 — lib/grpcentity/client.go

  • grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials()))로 평문(TLS 미적용) 채널을 생성합니다(로컬 테스트 목적).
  • AskingDateTime, AskPass, AskRandom 세 개의 래퍼 함수가 각각 대응하는 RPC를 호출합니다.
  • ClientRun(addr)이 실행 엔트리포인트로, 날짜/시간 조회 1회, 비밀번호 생성 1회, 서로 다른 파라미터로 난수 생성 2회를 순차 호출하며 결과를 표준 출력으로 출력합니다.

3.7 문서 자료

  • README.md(루트): 이야기체(스타트업 개발자 '민우'의 사례)로 gRPC 도입 배경(REST/JSON의 한계, Protobuf 계약, HTTP/2)을 설명한 뒤, Go 버전 요구사항(1.25.4+), 의존성 설치, main.go 수동 편집 방법, 실행 명령(go run ./examples), 기대 출력 예시, 트러블슈팅(포트 충돌, 모듈 로드 오류)까지 안내하는 실행 가이드로 구성되어 있습니다.
  • lib/grpcentity/README.md: .proto 파일 문법 요소별 상세 해설, protoc/protoc-gen-go/protoc-gen-go-grpc 설치 및 컴파일 절차, 서버·클라이언트 코드의 구현 단계별 설명을 담은 심화 가이드입니다.
  • docs/Working with JSON/: JSON 관련 별도 학습 자료(영/한 병기)가 준비되어 있으나, 루트 README와 직접 링크되어 있지는 않습니다.

4. 두 번째 레이어 — 멀티에이전트 오케스트레이션 인프라

저장소에는 학습 콘텐츠와 무관한 AI 에이전트 협업 인프라가 함께 포함되어 있습니다.

  • AGENTS.md: 일반적인 LLM 코딩 행동 지침(가정하지 말 것, 최소 변경, 외과적 수정 등)을 규정합니다.
  • .agents/MULTI_AGENT_RULES.md(+ 한국어판): MQTT 메시징 백플레인과 tmux 기반 다중 에이전트 협업 프로토콜을 정의합니다. 총괄 매니저(Orchestrator) → 팀장(개발/리뷰) → 작업 위임 및 리뷰 루프 → 완료 보고로 이어지는 워크플로우, Job 레지스트리(.mam/jobs/<id>.json, fcntl 파일 락), 세션 레지스트리(.mam/agent-sessions.db, SQLite WAL), HMAC-SHA256 기반 메시지 인증(PoC 모드에서는 비활성) 등을 상세히 규정합니다.
  • .agents/skills/: multi-agent-mux-{create,delegate-job,loop,monitor,resume,status,stop} 등 실제 세션 생성·작업 위임·모니터링·중지를 수행하는 스킬(스크립트) 모음입니다.
  • .mam/: 위 인프라의 런타임 상태 저장소로, jobs/(작업 레지스트리 및 브리프 파일), agent-sessions.yaml/.db(세션 상태), agent_homes/(에이전트별 메모리), delegate_job_logs/(위임 감사 로그)를 포함합니다. 이번 작업 브리프(.mam/jobs/702ea1d8/brief.md) 역시 이 구조를 통해 전달되었습니다.
  • scripts/generate-env.sh, .env.example: 이 인프라가 사용하는 MQTT 브로커 접속 정보, 경로 설정 등을 .env로 초기화하는 헬퍼입니다. 실제 비밀 값은 .env(git-ignored)에만 두고 .env.example에는 플레이스홀더만 커밋하도록 설계되어 있습니다.

참고: 새로 추가된 .gitignore.agents/, .mam/, .env, AGENTS.md 등을 앞으로 git 추적 대상에서 제외하도록 지정하고 있습니다. 다만 git log를 보면 AGENTS.md, lib/grpcentity/README.md 등은 과거 커밋(init 251117, update 260609, 이후 docs 커밋들)에서 이미 저장소에 커밋되어 있으므로, 이번 .gitignore 추가는 향후 신규/변경 파일이 실수로 다시 커밋되는 것을 막기 위한 조치로 보입니다(기존에 추적 중인 파일을 소급 제외하지는 않음).


5. 실행 방법

5.1 사전 준비

go version        # Go 1.25.4 이상 필요
go mod download    # 의존성 다운로드

5.2 JSON 예제 실행 (기본값, 별도 수정 불필요)

go run ./examples

lib/main.go가 기본적으로 jsonexample.JsonParsingExample()만 호출하므로, 별도 수정 없이 바로 JSON 마샬링/언마샬링 결과가 콘솔에 출력됩니다.

5.3 gRPC 예제 실행 (수동 편집 필요)

  1. lib/main.go를 열어 jsonexample import를 주석 처리하고 main() 안에서 grpcSample()을 호출하도록 변경.
  2. 저장 후 실행:
    go run ./examples
    
  3. 내부적으로 :8080 포트에서 gRPC 서버(고루틴)가 기동되고, 1초 후 클라이언트가 GetDate, GetRandomPass, GetRandom(2회) 순으로 RPC를 호출하며 결과를 출력합니다.
  4. 포트 충돌 시(bind: address already in use) lib/grpcentity/server.goport 전역 변수를 변경해야 합니다(3.5절에서 언급했듯 ServerRunaddr 인자는 무시되므로, 포트 변경은 이 전역 변수 수정으로만 가능).

5.4 HTTP 예제

httpentity 패키지는 서버 로직 전체가 주석 처리되어 있고 클라이언트 파일은 비어 있어, 현재 상태로는 실행 가능한 산출물이 없습니다. 향후 주석을 해제하고 main.go에 호출부를 추가해야 실습이 가능합니다.

5.5 .proto 재컴파일 (선택)

.proto 정의를 수정하고 stub을 재생성하려면:

protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. \
  --go-grpc_opt=paths=source_relative protoapi.proto

사전에 protoc, protoc-gen-go, protoc-gen-go-grpc가 설치되어 있어야 합니다(설치 방법은 lib/grpcentity/README.md 참고).


6. 주요 관찰 사항 및 특이점

  1. 단일 진입점, 수동 전환 방식: lib/main.go가 유일한 실행 지점이며, 학습 단계(JSON/HTTP/gRPC)를 전환하려면 코드를 직접 편집해야 합니다. 각 예제를 독립적으로 실행할 수 있는 별도 커맨드나 플래그는 없습니다.
  2. ServerRun(addr)addr 인자 미사용: gRPC 서버는 전달받은 인자 대신 패키지 전역 port 변수로 리슨하므로, 함수 시그니처와 실제 동작이 일치하지 않는 잠재적 혼동 요소입니다.
  3. HTTP 파트 미완성: gin 의존성은 go.mod에 존재하지만 실제 코드는 비활성 상태로, 커리큘럼상 "다음 실습 예정" 단계로 보입니다.
  4. 테스트 부재: 저장소 전체에 자동화된 테스트(*_test.go)가 없어, 코드 정상 동작 여부는 수동 실행(go run)과 콘솔 출력 확인에 의존합니다. go build ./..., go vet ./...는 통과합니다.
  5. 레거시 API 사용: rand.Seed(...)(전역 시드 설정) 등 Go 최신 버전에서 권장되지 않는(deprecated) 패턴이 사용되고 있으나, 학습용 예제의 단순성을 위한 의도적 선택으로 보입니다.
  6. 이중 성격의 저장소: 순수 학습 콘텐츠(gRPC/JSON/HTTP 튜토리얼)와 AI 에이전트 협업 인프라(multi-agent-mux)가 한 저장소에 공존하며, 두 영역은 서로 기능적으로 독립적입니다. 협업 인프라(.agents/, .mam/)는 이 학습 프로젝트를 대상으로 여러 AI 에이전트가 작업을 위임받고 결과를 보고하는 용도로 사용되고 있습니다(본 보고서 작성 작업 자체가 그 예시).