소개
실제 Rust 프로그램은 모든 기능을 처음부터 구현하는 대신 특정 기능에 집중한 라이브러리를 재사용하는 경우가 많습니다. 게시된 Rust 라이브러리를 crate라고 하며, 패키지에서 사용하는 crate를 종속성이라고 합니다. 종속성을 안전하게 선택하고 추가하는 일은 Rust 개발의 일반적인 과정입니다.
이 실습에서는 준비된 인사말 프로그램에 명령줄 파서인 clap을 추가합니다. 소스 코드를 두 군데 간단히 수정하고, 생성된 도움말과 유효성 검사를 확인하며, Cargo.toml과 Cargo.lock의 서로 다른 역할을 배웁니다. 첫 번째 빌드를 빠르게 진행할 수 있도록 설정 과정에서 종속성을 미리 다운로드해 두었지만, 학습자 프로젝트에 종속성을 추가하는 작업은 직접 수행합니다.
Cargo로 종속성 추가
이 단계에서는 준비된 패키지에 clap을 추가하고, Cargo가 매니페스트를 어떻게 변경하는지 확인합니다.
프로젝트는 /home/labex/project/hello-cli에 있습니다. Cargo 명령을 실행하기 전에 해당 디렉터리로 이동합니다.
cd /home/labex/project/hello-cli
nano로 패키지 매니페스트를 엽니다.
nano Cargo.toml
Cargo.toml은 패키지와 직접 종속성을 설명합니다. 비어 있는 [dependencies] 테이블은 현재 이 프로젝트가 Rust 표준 라이브러리만 사용한다는 뜻입니다. 파일을 변경하지 않고 nano를 종료하려면 Ctrl+X를 누릅니다.
cargo add 명령은 종속성 테이블을 안전하게 업데이트합니다. clap 버전 4.6.7을 추가하고 derive 기능을 활성화합니다.
cargo add clap@4.6.7 --features derive
**기능(feature)**은 crate의 선택적 부분을 활성화합니다. 여기서 derive는 Rust 구조체와 열거형을 명령줄 파서로 변환하는 매크로를 활성화합니다. Cargo는 활성화된 기능을 +, 비활성화된 기능을 -와 함께 출력합니다. 비활성화된 기능은 오류가 아닙니다.
매니페스트를 다시 엽니다.
nano Cargo.toml
이제 종속성 테이블에 다음과 같은 줄이 포함됩니다.
clap = { version = "4.6.7", features = ["derive"] }
버전은 호환성 요구 사항입니다. Cargo는 호환되는 더 최신 4.x 릴리스를 선택할 수 있으며, 실제로 선택된 정확한 버전은 Cargo.lock에 별도로 기록됩니다. nano를 종료하려면 Ctrl+X를 누릅니다.
명령줄 파서 파생
이 단계에서는 derive 매크로와 명령 메타데이터를 사용해 Cli 구조체를 clap에 연결합니다.
준비된 소스 파일을 엽니다.
nano src/main.rs
초급 과정에서 이미 #[derive(Debug)]를 사용했습니다. derive 매크로는 타입의 구조를 바탕으로 crate가 trait 구현을 생성하도록 요청합니다. struct Cli 위에 있는 첫 번째 TODO 주석을 다음 두 줄로 바꿉니다.
#[derive(Parser)]
#[command(version, about = "Create a friendly greeting")]
#[derive(Parser)]는 파싱 동작을 생성합니다. #[command(...)] 속성은 명령 전체에 대한 정보를 제공합니다. version은 Cargo.toml에서 패키지 버전을 읽고, about은 짧은 설명을 제공합니다.
Ctrl+O를 눌러 저장하고 Enter를 누른 다음, Ctrl+X를 눌러 종료합니다. 프로그램을 실행하지 않고 확인합니다.
cargo check
첫 번째 확인에서는 clap과 지원 crate를 컴파일하므로 Compiling과 Checking 줄이 여러 개 표시될 수 있습니다. Finished로 시작하는 마지막 줄이 표시되면 종속성과 생성된 파서가 함께 성공적으로 컴파일된 것입니다.
이제 프로그램에 도움말을 요청합니다. --는 Cargo 옵션과 프로그램에 전달할 인수를 구분합니다.
cargo run --quiet -- --help
출력에는 설명, 필수 <NAME> 값, 자동으로 생성된 도움말 및 버전 옵션이 포함됩니다.
Create a friendly greeting
Usage: hello-cli <NAME>
...
직접 작성한 것은 데이터 구조뿐이며, clap이 그 구조를 바탕으로 일관된 도움말과 인수 유효성 검사를 생성했습니다.
선택적 반복 플래그 추가
이 단계에서는 타입이 지정된 옵션을 추가하고, 파싱된 값을 간단한 반복문에서 사용합니다.
먼저 위치 인수로 이름 하나를 전달해 명령을 실행합니다.
cargo run --quiet -- Ada
Hello, Ada!
#[arg(...)] 속성이 없으므로 name: String은 필수 위치 값이 됩니다. 소스 파일을 다시 엽니다.
nano src/main.rs
Cli 내부의 두 번째 TODO 주석을 다음 코드로 바꿉니다.
/// Number of greetings to print
#[arg(short, long, default_value_t = 1)]
times: u8,
문서 주석은 도움말 텍스트가 됩니다. short는 -t를 만들고, long은 --times를 만들며, default_value_t = 1은 옵션을 생략했을 때 타입이 지정된 기본값을 제공합니다. 필드의 타입이 u8이므로 clap은 유효한 부호 없는 8비트 정수가 아닌 값도 거부합니다.
마지막 TODO 주석과 하나의 println! 줄을 다음 코드로 바꿉니다.
for _ in 0..cli.times {
println!("Hello, {}!", cli.name);
}
밑줄은 반복 횟수 자체를 의도적으로 사용하지 않는다는 뜻입니다. nano에서 저장하고 종료한 다음, 인사말을 세 번 출력합니다.
cargo run --quiet -- Ada --times 3
Hello, Ada!
Hello, Ada!
Hello, Ada!
잘못된 값도 입력해 봅니다.
cargo run --quiet -- Ada --times many
이 명령은 실패해야 합니다. clap은 many가 유효한 u8이 아니라는 오류를 출력하고, main이 잘못된 값을 사용하기 전에 0이 아닌 종료 상태로 종료합니다.
잠긴 종속성 그래프 확인 및 재사용
이 단계에서는 Cargo가 해결한 종속성 그래프를 확인하고, 네트워크에 연결하지 않아도 잠금 파일로 동일한 그래프를 재현할 수 있음을 검증합니다.
clap은 직접 종속성이지만, 자체적으로 여러 지원 crate를 사용합니다. 해결된 그래프의 첫 번째 수준을 표시합니다.
cargo tree --depth 1
정확한 패치 버전은 Cargo.toml의 호환성 요구 사항보다 최신일 수 있습니다. 중요한 구조는 다음과 같습니다.
hello-cli v0.1.0 (...)
└── clap v4...
생성된 잠금 파일을 엽니다.
nano Cargo.lock
Cargo.lock은 생성되는 데이터이므로 일반적으로 직접 편집하지 않습니다. Cargo가 전체 그래프에 대해 선택한 정확한 버전과 체크섬을 기록합니다. 이 CLI와 같은 애플리케이션에서는 팀원과 자동화된 빌드가 동일한 종속성 해결 결과를 재사용할 수 있도록 잠금 파일을 프로젝트에 함께 보관합니다. nano를 종료하려면 Ctrl+X를 누릅니다.
이제 기존 잠금 파일과 로컬 crate 캐시를 모두 사용하도록 요구합니다.
cargo check --locked --offline
--locked는 Cargo.lock을 변경하지 못하게 합니다. --offline은 네트워크 액세스를 차단합니다. 마지막에 Finished 줄이 표시되면 이미 다운로드된 종속성 그래프를 사용해 다른 버전을 조용히 다시 해결하지 않고 이 프로젝트를 확인할 수 있다는 뜻입니다.
요약
Cargo로 직접 crate 종속성을 추가하고 선택적 기능을 활성화했으며, 타입이 지정된 clap 파서를 derive하고 자동으로 생성된 도움말과 유효성 검사를 확인했습니다. 또한 Cargo.toml의 호환성 요구 사항과 Cargo.lock의 정확한 종속성 그래프를 구분하고, 잠긴 그래프가 오프라인에서도 작동함을 검증했습니다.


