개발 가이드¶
이 가이드는 Continuum Router 프로젝트의 개발 설정, 아키텍처, 테스트 및 기여 가이드라인을 다룹니다.
목차¶
사전 요구 사항¶
필수 도구¶
- Rust: 1.75 이상 (Rust 설치)
- Git: 버전 관리용
- C 컴파일러: gcc, clang, 또는 MSVC (네이티브 의존성용)
권장 도구¶
- cargo-watch: 개발 중 자동 재컴파일
- cargo-edit: 의존성 관리
- cargo-audit: 보안 취약점 검사
- cargo-flamegraph: 성능 프로파일링
- rust-analyzer: IDE 지원 (VS Code, IntelliJ, vim/neovim)
개발 도구 설치¶
# 필수 개발 도구 설치
cargo install cargo-watch cargo-edit cargo-audit cargo-flamegraph
# 코드 커버리지 도구 설치
cargo install cargo-tarpaulin
# 벤치마킹 도구 설치
cargo install cargo-criterion
개발 설정¶
복제 및 설정¶
# 저장소 복제
git clone https://github.com/lablup/continuum-router.git
cd continuum-router
# 컴파일 확인
cargo check
# 초기 테스트 스위트 실행
cargo test
# 개발 버전 빌드
cargo build
IDE 설정¶
VS Code¶
rust-analyzer확장 설치- 디버깅을 위해
CodeLLDB확장 설치 - 제공된
.vscode/settings.json사용:
{
"rust-analyzer.cargo.features": "all",
"rust-analyzer.checkOnSave.command": "clippy",
"editor.formatOnSave": true,
"[rust]": {
"editor.defaultFormatter": "rust-lang.rust-analyzer"
}
}
IntelliJ IDEA / CLion¶
- Rust 플러그인 설치
- 프로젝트 루트 열기
- Preferences → Build → Cargo에서 Cargo 설정 구성
Vim/Neovim¶
- Mason 또는 수동으로
rust-analyzer설치 - LSP 클라이언트 구성 (nvim-lspconfig, coc.nvim 등)
- nvim-lspconfig 설정 예제:
require('lspconfig').rust_analyzer.setup({
settings = {
["rust-analyzer"] = {
cargo = { features = "all" },
checkOnSave = { command = "clippy" }
}
}
})
프로젝트 구조¶
계층 아키텍처¶
프로젝트는 깔끔한 4계층 아키텍처를 따릅니다:
┌─────────────────────────────────┐
│ HTTP Layer │ ← Routes, Middleware, DTOs
├─────────────────────────────────┤
│ Services Layer │ ← 비즈니스 로직, 오케스트레이션
├─────────────────────────────────┤
│ Infrastructure Layer │ ← Backends, Cache, HTTP Client
├─────────────────────────────────┤
│ Core Layer │ ← 도메인 모델, Traits, Errors
└─────────────────────────────────┘
디렉토리 구조¶
src/
├── main.rs # 애플리케이션 진입점 및 CLI
├── lib.rs # 라이브러리 익스포트 및 모듈 선언
│
├── core/ # 핵심 도메인 계층 (외부 의존성 없음)
│ ├── mod.rs # Core 모듈 익스포트
│ ├── models/ # 도메인 모델
│ │ ├── backend.rs # 백엔드 설정 및 상태
│ │ ├── model.rs # LLM 모델 표현
│ │ ├── request.rs # 요청/응답 모델
│ │ └── health.rs # 헬스 체크 모델
│ ├── errors.rs # 오류 타입 및 처리
│ ├── traits.rs # 도메인 트레이트 및 인터페이스
│ ├── retry/ # 재시도 로직 및 정책
│ │ ├── policy.rs # 재시도 정책
│ │ └── backoff.rs # 백오프 전략
│ └── config/ # 설정 모델
│ ├── mod.rs # Config 모듈 익스포트
│ ├── models.rs # 설정 구조체
│ └── validation.rs # 설정 유효성 검사 로직
│
├── services/ # 비즈니스 로직 계층
│ ├── mod.rs # 서비스 레지스트리 및 DI 컨테이너
│ ├── backend_service.rs # 백엔드 관리 및 선택
│ ├── model_service.rs # 모델 집계 및 캐싱
│ ├── proxy_service.rs # 요청 라우팅 및 프록시
│ ├── health_service.rs # 헬스 모니터링
│ └── streaming/ # SSE 스트리밍 지원
│ ├── mod.rs # Streaming 모듈 익스포트
│ ├── parser.rs # SSE 이벤트 파싱
│ └── handler.rs # 스트림 처리 로직
│
├── infrastructure/ # 외부 통합 계층
│ ├── mod.rs # Infrastructure 익스포트
│ ├── backends/ # 백엔드 클라이언트 구현
│ │ ├── mod.rs # 백엔드 트레이트 및 팩토리
│ │ ├── openai.rs # OpenAI 호환 백엔드
│ │ ├── ollama.rs # Ollama 백엔드
│ │ └── vllm.rs # vLLM 백엔드
│ ├── cache/ # 캐싱 구현
│ │ ├── mod.rs # 캐시 트레이트
│ │ ├── lru.rs # LRU 캐시
│ │ └── ttl.rs # TTL 기반 캐시
│ ├── config/ # 설정 로딩
│ │ ├── loader.rs # 설정 파일 로딩
│ │ └── watcher.rs # 핫 리로드 지원
│ └── http_client.rs # 풀링이 있는 HTTP 클라이언트
│
├── http/ # HTTP 계층
│ ├── mod.rs # HTTP 모듈 익스포트
│ ├── routes.rs # 라우트 정의
│ ├── handlers/ # 요청 핸들러
│ │ ├── chat.rs # 채팅 완료 엔드포인트
│ │ ├── models.rs # 모델 목록 엔드포인트
│ │ ├── health.rs # 헬스 체크 엔드포인트
│ │ └── admin.rs # 관리자 엔드포인트
│ ├── middleware/ # HTTP 미들웨어
│ │ ├── auth.rs # 인증
│ │ ├── logging.rs # 요청 로깅
│ │ ├── metrics.rs # 메트릭 수집
│ │ └── error.rs # 오류 처리
│ └── dto/ # Data Transfer Objects
│ ├── request.rs # 요청 DTO
│ └── response.rs # 응답 DTO
│
└── metrics/ # 메트릭 및 모니터링
├── mod.rs # Metrics 모듈 익스포트
├── backend.rs # 백엔드별 메트릭
├── routing.rs # 라우팅 메트릭
├── model.rs # 모델 서비스 메트릭
└── streaming.rs # 스트리밍 메트릭
tests/
├── integration/ # 통합 테스트
│ ├── health_test.rs # 헬스 엔드포인트 테스트
│ ├── chat_test.rs # 채팅 완료 테스트
│ ├── models_test.rs # 모델 목록 테스트
│ └── routing_test.rs # 라우팅 로직 테스트
├── unit/ # 단위 테스트
│ ├── core/ # Core 계층 테스트
│ ├── services/ # Service 계층 테스트
│ └── infrastructure/ # Infrastructure 테스트
└── common/ # 테스트 유틸리티
├── fixtures.rs # 테스트 픽스처
├── mocks.rs # 모킹 구현
└── helpers.rs # 테스트 헬퍼 함수
아키텍처 개요¶
의존성 흐름¶
HTTP Layer → Services Layer → Infrastructure Layer → Core Layer
↓ ↓ ↓ ↑
└────────────┴────────────────┴────────────────────┘
Core 타입 사용
주요 디자인 패턴¶
- 의존성 주입: 서비스가 컨테이너를 통해 주입됨
- 리포지토리 패턴: 백엔드 구현이 데이터 접근을 추상화
- 전략 패턴: 로드 밸런싱 전략이 플러그 가능
- 옵저버 패턴: 헬스 모니터링이 이벤트 기반 업데이트 사용
- 서킷 브레이커: 연쇄 장애 방지
- 백오프와 재시도: 일시적 실패 처리
빌드 및 실행¶
개발 빌드¶
# 빠른 개발 빌드
cargo build
# 디버그 출력과 함께 실행
RUST_LOG=debug ./target/debug/continuum-router
# 자동 리로드와 함께 실행
cargo watch -x run
# 특정 설정과 함께 실행
cargo run -- --config dev-config.yaml
릴리스 빌드¶
# 최적화된 릴리스 빌드
cargo build --release
# 릴리스 빌드 실행
./target/release/continuum-router
# 특정 기능과 함께 빌드
cargo build --release --features "metrics,tracing"
크로스 컴파일¶
# cross 설치
cargo install cross
# Linux x86_64용 빌드
cross build --release --target x86_64-unknown-linux-gnu
# macOS ARM64용 빌드
cross build --release --target aarch64-apple-darwin
# Windows용 빌드
cross build --release --target x86_64-pc-windows-gnu
테스트¶
테스트 실행¶
# 모든 테스트 실행
cargo test
# 출력과 함께 실행
cargo test -- --nocapture
# 특정 테스트 실행
cargo test test_health_check
# 단일 스레드로 테스트 실행 (디버깅용)
cargo test -- --test-threads=1
# 통합 테스트만 실행
cargo test --test integration
# 단위 테스트만 실행
cargo test --lib
# 문서 테스트 실행
cargo test --doc
테스트 작성¶
단위 테스트 예제¶
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_backend_selection() {
let backends = vec![
Backend::new("backend1", "http://localhost:8000"),
Backend::new("backend2", "http://localhost:8001"),
];
let selector = RoundRobinSelector::new(backends);
let selected = selector.select().unwrap();
assert_eq!(selected.name(), "backend1");
}
#[tokio::test]
async fn test_async_health_check() {
let backend = Backend::new("test", "http://localhost:8000");
let result = backend.health_check().await;
assert!(result.is_ok());
}
}
통합 테스트 예제¶
// tests/integration/chat_test.rs
use continuum_router::test_utils::TestServer;
#[tokio::test]
async fn test_chat_completion() {
let server = TestServer::new().await;
let response = server
.post("/v1/chat/completions")
.json(&json!({
"model": "gpt-3.5-turbo",
"messages": [{"role": "user", "content": "Hello"}]
}))
.send()
.await;
assert_eq!(response.status(), 200);
}
테스트 커버리지¶
# 커버리지 리포트 생성
cargo tarpaulin --out Html
# 특정 임계값과 함께 커버리지 생성
cargo tarpaulin --fail-under 80
# Codecov 리포트 생성
cargo tarpaulin --out Xml
코드 품질¶
포매팅¶
린팅¶
# 경고를 오류로 처리하여 clippy 실행
cargo clippy -- -D warnings
# pedantic 린트와 함께 clippy 실행
cargo clippy -- -W clippy::pedantic
# 자동 수정과 함께 clippy 실행
cargo clippy --fix
보안 감사¶
# 알려진 취약점 확인
cargo audit
# 취약한 의존성 수정
cargo audit fix
# 보안 리포트 생성
cargo audit --json > audit-report.json
문서화¶
# 문서 빌드
cargo doc
# 문서 빌드 및 열기
cargo doc --open
# 비공개 항목 포함하여 문서 빌드
cargo doc --document-private-items
# 문서 예제 확인
cargo test --doc
디버깅¶
디버그 로깅¶
# 디버그 로깅 활성화
RUST_LOG=debug cargo run
# 특정 모듈에 대해 trace 로깅 활성화
RUST_LOG=continuum_router::services=trace cargo run
# 구조화된 로깅 활성화
RUST_LOG=info RUST_LOG_FORMAT=json cargo run
디버거 사용¶
VS Code¶
.vscode/launch.json생성:
{
"version": "0.2.0",
"configurations": [
{
"type": "lldb",
"request": "launch",
"name": "Debug Router",
"cargo": {
"args": ["build", "--bin=continuum-router"],
"filter": {
"name": "continuum-router",
"kind": "bin"
}
},
"args": ["--config", "dev-config.yaml"],
"cwd": "${workspaceFolder}"
}
]
}
- 브레이크포인트 설정 후 F5 눌러 디버그
명령줄 (lldb)¶
# 디버그 심볼과 함께 빌드
cargo build
# lldb로 실행
lldb target/debug/continuum-router
# 브레이크포인트 설정
(lldb) b src/main.rs:50
# 프로그램 실행
(lldb) r --config config.yaml
메모리 디버깅¶
# valgrind 사용 (Linux)
valgrind --leak-check=full ./target/debug/continuum-router
# Address Sanitizer 사용
RUSTFLAGS="-Z sanitizer=address" cargo build -Z build-std --target x86_64-unknown-linux-gnu
./target/x86_64-unknown-linux-gnu/debug/continuum-router
성능 프로파일링¶
CPU 프로파일링¶
# 플레임 그래프 생성
cargo flamegraph -- --config config.yaml
# 특정 시나리오 프로파일
cargo flamegraph --bin continuum-router -- --benchmark
# perf 사용 (Linux)
perf record -g ./target/release/continuum-router
perf report
벤치마킹¶
# 벤치마크 실행
cargo bench
# 특정 벤치마크 실행
cargo bench routing
# 벤치마크 결과 비교
cargo bench -- --save-baseline main
git checkout feature-branch
cargo bench -- --baseline main
벤치마크 작성¶
// benches/routing_bench.rs
use criterion::{black_box, criterion_group, criterion_main, Criterion};
fn benchmark_routing(c: &mut Criterion) {
c.bench_function("round_robin_selection", |b| {
let selector = RoundRobinSelector::new(backends);
b.iter(|| {
selector.select(black_box(&request))
});
});
}
criterion_group!(benches, benchmark_routing);
criterion_main!(benches);
메모리 프로파일링¶
# heaptrack 사용 (Linux)
heaptrack ./target/release/continuum-router
heaptrack_gui heaptrack.continuum-router.*.gz
# Instruments 사용 (macOS)
cargo instruments -t Allocations
기여하기¶
개발 워크플로우¶
-
Fork 및 Clone
-
Feature 브랜치 생성
-
변경 사항 작성
- Rust 규칙을 따르는 코드 작성
- 새 기능에 대한 테스트 추가
-
문서 업데이트
-
변경 사항 테스트
-
변경 사항 커밋
-
Push 및 PR 생성
코드 표준¶
- Rust 명명 규칙 준수 (함수는 snake_case, 타입은 CamelCase)
- 포괄적인 테스트 작성 (커버리지 >90% 목표)
- 공개 API에 doc 주석으로 문서화
- 일관된 포매팅을 위해
cargo fmt사용 - 모든
cargo clippy경고 해결 - 함수를 작고 집중되게 유지
- 상속보다 합성 선호
- 적절한 오류 처리 사용 (Result/Option)
커밋 메시지 형식¶
Conventional Commits 사양 준수:
타입:
feat: 새 기능fix: 버그 수정docs: 문서만style: 코드 스타일 변경refactor: 코드 리팩토링perf: 성능 개선test: 테스트 추가chore: 유지보수 작업
Pull Request 가이드라인¶
- 제목: 명확하고 설명적
- 설명: 무엇을, 왜, 어떻게 설명
- 테스트: 새 기능에 대한 테스트 포함
- 문서: 관련 문서 업데이트
- Breaking Changes: 명확히 표시
- Issues: 관련 이슈 참조
리뷰 프로세스¶
- 자동화된 검사 통과 필수
- 최소 한 명의 메인테이너 승인
- 해결되지 않은 대화 없음
- main 브랜치와 최신 상태
문제 해결¶
일반적인 문제¶
컴파일 오류¶
테스트 실패¶
# 상세하게 테스트 실행
cargo test -- --nocapture --test-threads=1
# 경쟁 조건 확인
cargo test -- --test-threads=1
# 테스트 환경 확인
cargo test -- --ignored
성능 문제¶
# 릴리스 빌드 프로파일
cargo build --release
perf record ./target/release/continuum-router
perf report
# 블로킹 연산 확인
RUST_LOG=trace cargo run 2>&1 | grep "blocking"