d2c63e7c19
연구 방향·정책과 구현 세부사항을 두 문서로 명확히 분리한다. CLAUDE.md (연구 방향·정책 전용): - 연구 배경 요약 (BACKGROUND.md 참조) - 제안 기법 ① gRPC over QUIC 통신 모듈 (AIoT 도메인 실증) - 제안 기법 ② AI Agent + IoT 통합 엣지 게이트웨이 - 우선순위별 목표(P0/P1/P2)와 의존성·리스크 - 위험 관리(Risk Register) — quic-go 호환성, tc 작동, 어댑터 범위 등 - 평가 시나리오·KPI·네트워크 조건·통계적 유의성 확보 계획 - 코드 작성 정책(계층 분리, 컨텍스트 전파, 에러 처리, 측정 가능성) IMPLEMENTATION.md (구현 세부사항 전용): - 프로젝트 디렉터리 구조 (proto/, gen/, internal/, cmd/, benchmarks/) - 네이밍 규칙 (Proto·Go) - 개발 워크플로우 (proto 컴파일, 서버 실행, tc 시뮬레이션, 테스트) - 코드 패턴 (Transport 인터페이스, 컨텍스트, gRPC 에러, 인터셉터) - Makefile 타겟 목록 - 멀티 에이전트 작업 분담 가이드 - 자주 수행하는 작업 시나리오 원칙: 디렉터리 경로·명령어·코드 샘플 등 구현 세부사항은 모두 IMPLEMENTATION.md에 두고, CLAUDE.md는 연구 방향과 정책에만 집중한다. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
12 KiB
12 KiB
IMPLEMENTATION.md — 구현 가이드
본 문서는
CLAUDE.md(연구 방향·정책)에서 분리된 구현 세부 사항을 다룬다. 디렉터리 구조, 네이밍 규칙, 개발 워크플로우, 코드 패턴, 멀티 에이전트 작업 분담 등이 포함된다.연구 동기·목표·평가 지표는
CLAUDE.md를 참조한다.
1. 프로젝트 구조
.
├── proto/ # IDL 정의
│ └── aiot/
│ ├── inference/inference.proto # AI 추론 요청/응답
│ ├── device/device.proto # 디바이스 등록·상태 보고
│ └── gateway/gateway.proto # 게이트웨이 데이터 전달 서비스
│
├── gen/ # protoc 자동 생성 파일 (수동 수정 금지)
│ └── aiot/{inference,device,gateway}/
│
├── internal/ # 프로젝트 내부 패키지 (외부 import 불가)
│ ├── transport/
│ │ ├── transport.go # Listener/Dialer 인터페이스 정의
│ │ ├── quic_listener.go # QUIC 리스너 (quic-go 래핑)
│ │ ├── quic_dialer.go # QUIC 다이얼러 (클라이언트 연결)
│ │ └── h2_listener.go # HTTP/2 리스너 (비교군)
│ ├── gateway/
│ │ ├── gateway.go # 게이트웨이 코어 (라우팅·변환 엔진)
│ │ ├── protocol_adapter.go # IoT 프로토콜 어댑터 인터페이스
│ │ ├── mqtt_adapter.go # MQTT → Protobuf
│ │ ├── coap_adapter.go # CoAP → Protobuf
│ │ └── route_table.go # 서비스 디스커버리·라우팅 테이블
│ ├── server/
│ │ ├── inference_server.go # InferenceService 구현
│ │ ├── device_server.go # DeviceRegistry 구현
│ │ └── gateway_server.go # GatewayService 구현
│ ├── client/
│ │ ├── inference_client.go # gRPC 클라이언트 (재시도·타임아웃 포함)
│ │ └── rest_client.go # REST 비교군 클라이언트
│ ├── middleware/
│ │ ├── logging.go # 요청/응답 로깅 인터셉터
│ │ └── metrics.go # latency 측정 인터셉터
│ └── router/
│ └── task_router.go # 엣지 라우팅 로직 (ROI 결과 기반)
│
├── cmd/
│ ├── server/main.go # gRPC 서버 진입점 (--transport=quic|h2 플래그)
│ ├── gateway/main.go # gRPC 게이트웨이 진입점
│ ├── rest-server/main.go # REST 비교군 서버 진입점
│ └── benchmark-runner/main.go # 벤치마크 실행 CLI
│
├── benchmarks/
│ ├── scenarios/
│ │ ├── unary_test.go # Unary RPC 벤치마크
│ │ ├── streaming_test.go # Streaming RPC 벤치마크
│ │ └── rest_compare_test.go # REST vs gRPC 비교 벤치마크
│ └── results/ # 날짜별 결과 (YYYY-MM-DD/)
│ └── .gitkeep
│
├── docs/
│ ├── decisions/ # ADR (Architecture Decision Records)
│ │ └── 001-go-grpc-baseline.md
│ └── open-questions.md # 미해결 질문 및 탐색 주제
│
├── tests/
│ ├── unit/ # 단위 테스트
│ └── integration/ # 통합 테스트 (실제 서버 기동)
│
├── scripts/
│ ├── proto-gen.sh # protoc 컴파일 스크립트
│ ├── tc-setup.sh # tc 네트워크 지연 설정
│ └── tc-reset.sh # tc 설정 초기화
│
├── docker/
│ ├── Dockerfile.server
│ ├── Dockerfile.rest-server
│ └── docker-compose.yml # 전체 실험 환경 구성
│
├── CLAUDE.md # 연구 방향·정책
├── IMPLEMENTATION.md # (본 파일) 구현 세부 사항
├── BACKGROUND.md # 연구 수행 배경
├── go.mod
├── go.sum
└── Makefile
2. 네이밍 규칙
2.1. Proto 정의
| 대상 | 규칙 | 예시 |
|---|---|---|
| 패키지 | aiot.{module} |
aiot.inference |
| 서비스 | {명사}{역할}Service |
InferenceService, DeviceRegistry |
| RPC 메서드 | {동사}{명사} |
PredictFrame, RegisterDevice, StreamSensorData |
| 메시지 | {명사}{동사}Request/Response |
FramePredictRequest, DeviceRegisterResponse |
| 열거형 | UPPER_SNAKE_CASE |
PROCESSING_STATUS_PENDING |
2.2. Go 코드
| 대상 | 규칙 |
|---|---|
| 파일 | snake_case.go |
| 패키지 | 단수 소문자 (server, client, router) |
| 인터페이스 | {명사} 또는 {명사}er (InferenceService, Router) |
| 구조체 | PascalCase |
| 함수/메서드 | camelCase (내부), PascalCase (공개) |
| 에러 변수 | Err{명사} (ErrConnectionTimeout) |
| 컨텍스트 | 첫 번째 인수, 변수명 ctx |
3. 개발 워크플로우
3.1. 초기 셋업
# Go 버전 확인 (1.22 이상 권장)
go version
# protoc 및 플러그인 설치 (최초 1회)
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
# 의존성 설치
go mod tidy
3.2. Proto 컴파일
make proto
# 또는 직접 실행:
# scripts/proto-gen.sh
gen/ 디렉터리 파일은 절대 직접 수정하지 않는다. .proto 파일을 수정 후 재생성한다.
3.3. 서버/클라이언트 실행
# gRPC 서버 (전송 계층 선택)
make run-server # 기본
go run ./cmd/server --transport=quic --port=50051
go run ./cmd/server --transport=h2 --port=50052
# REST 비교군 서버
make run-rest-server
# 벤치마크
make benchmark
go run ./cmd/benchmark-runner --scenario unary --requests 10000 --concurrency 10
3.4. 네트워크 조건 시뮬레이션
# 지연 50ms
sudo ./scripts/tc-setup.sh --delay 50ms --interface eth0
# 지연 200ms + 패킷 손실 1%
sudo ./scripts/tc-setup.sh --delay 200ms --loss 1% --interface eth0
# 초기화
sudo ./scripts/tc-reset.sh --interface eth0
3.5. 테스트
# 단위 테스트
go test ./tests/unit/... -v
# 통합 테스트 (서버 자동 기동)
go test ./tests/integration/... -v -timeout 60s
# 벤치마크 (Go 표준)
go test ./benchmarks/... -bench=. -benchmem -count=5
4. 코드 패턴
4.1. Transport 인터페이스 설계
QUIC↔TCP를 동일한 코드 경로에서 교체할 수 있도록, 전송 계층은 인터페이스로 추상화한다.
// internal/transport/transport.go
type Listener interface {
Accept(ctx context.Context) (net.Conn, error)
Addr() net.Addr
Close() error
}
type Dialer interface {
Dial(ctx context.Context, addr string) (net.Conn, error)
Close() error
}
- gRPC 서버/클라이언트 생성 시
Listener/Dialer를 주입받음 - 벤치마크는 동일 코드 경로로 전송 계층만 변경하여 공정 비교
4.2. 컨텍스트와 타임아웃
타임아웃은 클라이언트에서 설정한다. 서버는 컨텍스트 취소를 존중한다.
func (c *InferenceClient) PredictFrame(ctx context.Context, req *pb.FramePredictRequest) (*pb.FramePredictResponse, error) {
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
return c.stub.PredictFrame(ctx, req)
}
4.3. gRPC 에러 처리
import (
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// 서버 측
return nil, status.Errorf(codes.InvalidArgument, "frame data is empty")
// 클라이언트 측
if st, ok := status.FromError(err); ok {
switch st.Code() {
case codes.DeadlineExceeded:
// 타임아웃 처리
case codes.Unavailable:
// 재연결 처리
}
}
4.4. Latency 측정 인터셉터
internal/middleware/metrics.go의 인터셉터를 RPC 등록 시 함께 등록한다. 직접 측정이 필요한 경우 구간을 명시적으로 기록한다.
// 등록 예시
grpc.NewServer(
grpc.UnaryInterceptor(middleware.MetricsUnaryInterceptor),
grpc.StreamInterceptor(middleware.MetricsStreamInterceptor),
)
5. Makefile 타겟
make proto # .proto → gen/ Go 코드 생성
make build # 모든 cmd/ 바이너리 빌드
make run-server # gRPC 서버 실행 (기본 포트 :50051)
make run-rest # REST 서버 실행 (기본 포트 :8080)
make test # 단위 + 통합 테스트
make benchmark # 전체 벤치마크 시나리오 실행
make lint # golangci-lint 실행
make docker-up # docker-compose로 전체 환경 기동
make docker-down # 환경 종료
make clean # 빌드 아티팩트 및 gen/ 삭제
6. 멀티 에이전트 작업 분담 가이드
여러 에이전트가 동시에 작업할 경우 아래 경계를 지킨다.
6.1. 독립 작업 도메인
| 도메인 | 담당 파일/디렉터리 | 주의사항 |
|---|---|---|
| Proto 설계 | proto/ |
필드 번호 변경은 전체 팀에 공지 후 진행 |
| QUIC 전송 계층 | internal/transport/ |
인터페이스(Listener/Dialer) 확정 후 구현, quic-go 의존 |
| gRPC 게이트웨이 | internal/gateway/, cmd/gateway/ |
transport 인터페이스 의존, 프로토콜 어댑터는 독립 개발 가능 |
| gRPC 서버 구현 | internal/server/, cmd/server/ |
gen/ + transport 인터페이스 의존 |
| REST 비교군 | internal/client/rest_client.go, cmd/rest-server/ |
gRPC 서버와 동일한 시나리오 구현 필수 |
| 벤치마크 스크립트 | benchmarks/, cmd/benchmark-runner/ |
서버/클라이언트 인터페이스 확정 후 구현 |
| 라우터 로직 | internal/router/ |
인터페이스만 맞추면 독립 개발 가능 |
| 문서/분석 | docs/, benchmarks/results/ |
코드 변경 없이 병행 가능 |
6.2. 의존 순서
Proto 정의 완료
└─→ gen/ 코드 생성
│
Transport 인터페이스 확정 (internal/transport/transport.go)
│
├─→ QUIC Listener/Dialer 구현 (internal/transport/quic_*.go)
├─→ H2 Listener/Dialer 구현 (internal/transport/h2_*.go)
│
├─→ gRPC 서버 구현 (transport 주입)
├─→ gRPC 게이트웨이 구현
│ ├─→ 프로토콜 어댑터 (MQTT, CoAP — 독립 병행 가능)
│ └─→ 라우팅 테이블
├─→ gRPC 클라이언트 구현
├─→ REST 비교군 구현
│
└─→ 벤치마크 구현
└─→ 실험 실행 → 결과 기록
6.3. 충돌 방지
go.mod/go.sum변경 시 다른 에이전트에 즉시 알린다proto/파일에 새 필드 추가 시 필드 번호를 기존과 겹치지 않게 유보 번호를 확인한다- 실험 결과 파일은 날짜/시나리오명으로 고유하게 명명하여 덮어쓰기를 방지한다
7. 자주 수행하는 작업 시나리오
7.1. 새 RPC 추가
proto/aiot/{module}/{service}.proto에 메시지·서비스 정의make proto—gen/코드 생성internal/server/에 핸들러 구현internal/middleware/metrics.go인터셉터로 측정 자동화tests/unit/에 단위 테스트 추가benchmarks/scenarios/에 벤치마크 시나리오 추가
7.2. 새 전송 계층 추가
internal/transport/transport.go의Listener/Dialer인터페이스 충족 확인internal/transport/{name}_listener.go,{name}_dialer.go구현cmd/server/main.go의--transport플래그 분기에 추가- 벤치마크 실행 시
--transport={name}옵션으로 비교군 등록
7.3. 새 IoT 프로토콜 어댑터 추가 (게이트웨이)
internal/gateway/protocol_adapter.go의 어댑터 인터페이스 확인internal/gateway/{protocol}_adapter.go구현- 게이트웨이 부트스트랩(
cmd/gateway/main.go)에서 어댑터 등록 tests/integration/에 end-to-end 테스트 추가