docs: 연구 방향(CLAUDE.md)과 구현 가이드(IMPLEMENTATION.md) 분리
연구 방향·정책과 구현 세부사항을 두 문서로 명확히 분리한다. 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>
This commit is contained in:
@@ -0,0 +1,345 @@
|
||||
# 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. 초기 셋업
|
||||
|
||||
```bash
|
||||
# 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 컴파일
|
||||
|
||||
```bash
|
||||
make proto
|
||||
# 또는 직접 실행:
|
||||
# scripts/proto-gen.sh
|
||||
```
|
||||
|
||||
**`gen/` 디렉터리 파일은 절대 직접 수정하지 않는다.** `.proto` 파일을 수정 후 재생성한다.
|
||||
|
||||
### 3.3. 서버/클라이언트 실행
|
||||
|
||||
```bash
|
||||
# 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. 네트워크 조건 시뮬레이션
|
||||
|
||||
```bash
|
||||
# 지연 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. 테스트
|
||||
|
||||
```bash
|
||||
# 단위 테스트
|
||||
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를 동일한 코드 경로에서 교체할 수 있도록, 전송 계층은 인터페이스로 추상화한다.
|
||||
|
||||
```go
|
||||
// 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. 컨텍스트와 타임아웃
|
||||
|
||||
타임아웃은 **클라이언트에서 설정**한다. 서버는 컨텍스트 취소를 존중한다.
|
||||
|
||||
```go
|
||||
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 에러 처리
|
||||
|
||||
```go
|
||||
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 등록 시 함께 등록한다. 직접 측정이 필요한 경우 구간을 명시적으로 기록한다.
|
||||
|
||||
```go
|
||||
// 등록 예시
|
||||
grpc.NewServer(
|
||||
grpc.UnaryInterceptor(middleware.MetricsUnaryInterceptor),
|
||||
grpc.StreamInterceptor(middleware.MetricsStreamInterceptor),
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Makefile 타겟
|
||||
|
||||
```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 추가
|
||||
|
||||
1. `proto/aiot/{module}/{service}.proto`에 메시지·서비스 정의
|
||||
2. `make proto` — `gen/` 코드 생성
|
||||
3. `internal/server/`에 핸들러 구현
|
||||
4. `internal/middleware/metrics.go` 인터셉터로 측정 자동화
|
||||
5. `tests/unit/`에 단위 테스트 추가
|
||||
6. `benchmarks/scenarios/`에 벤치마크 시나리오 추가
|
||||
|
||||
### 7.2. 새 전송 계층 추가
|
||||
|
||||
1. `internal/transport/transport.go`의 `Listener`/`Dialer` 인터페이스 충족 확인
|
||||
2. `internal/transport/{name}_listener.go`, `{name}_dialer.go` 구현
|
||||
3. `cmd/server/main.go`의 `--transport` 플래그 분기에 추가
|
||||
4. 벤치마크 실행 시 `--transport={name}` 옵션으로 비교군 등록
|
||||
|
||||
### 7.3. 새 IoT 프로토콜 어댑터 추가 (게이트웨이)
|
||||
|
||||
1. `internal/gateway/protocol_adapter.go`의 어댑터 인터페이스 확인
|
||||
2. `internal/gateway/{protocol}_adapter.go` 구현
|
||||
3. 게이트웨이 부트스트랩(`cmd/gateway/main.go`)에서 어댑터 등록
|
||||
4. `tests/integration/`에 end-to-end 테스트 추가
|
||||
Reference in New Issue
Block a user