Rust 에서 Cargo Workspaces 탐구

Beginner

This tutorial is from open-source community. Access the source code

소개

Cargo Workspaces에 오신 것을 환영합니다. 이 랩은 Rust Book의 일부입니다. LabEx 에서 Rust 기술을 연습할 수 있습니다.

이 랩에서는 Cargo 의 workspaces 기능을 살펴볼 것입니다. 이 기능은 패키지를 여러 라이브러리 크레이트로 분할하여 함께 개발되는 여러 관련 패키지를 관리하는 데 도움이 됩니다.

Cargo Workspaces

12 장에서 우리는 바이너리 크레이트와 라이브러리 크레이트를 포함하는 패키지를 만들었습니다. 프로젝트가 발전함에 따라 라이브러리 크레이트가 계속 커지고 패키지를 여러 라이브러리 크레이트로 더 분할하고 싶을 수 있습니다. Cargo 는 함께 개발되는 여러 관련 패키지를 관리하는 데 도움이 되는 workspaces라는 기능을 제공합니다.

Workspace 생성하기

workspace는 동일한 Cargo.lock 및 출력 디렉토리를 공유하는 패키지 집합입니다. workspace 를 사용하여 프로젝트를 만들어 보겠습니다. workspace 의 구조에 집중할 수 있도록 간단한 코드를 사용하겠습니다. workspace 를 구성하는 방법에는 여러 가지가 있으므로 일반적인 방법 중 하나만 보여드리겠습니다. 바이너리 하나와 라이브러리 두 개를 포함하는 workspace 를 만들 것입니다. 주요 기능을 제공하는 바이너리는 두 라이브러리에 의존합니다. 한 라이브러리는 add_one 함수를 제공하고 다른 라이브러리는 add_two 함수를 제공합니다. 이 세 개의 크레이트는 동일한 workspace 의 일부가 됩니다. 먼저 workspace 를 위한 새 디렉토리를 만듭니다.

mkdir add
cd add

다음으로, add 디렉토리에서 전체 workspace 를 구성할 Cargo.toml 파일을 만듭니다. 이 파일에는 [package] 섹션이 없습니다. 대신, 바이너리 크레이트가 있는 패키지의 경로를 지정하여 workspace 에 멤버를 추가할 수 있도록 하는 [workspace] 섹션으로 시작합니다. 이 경우 해당 경로는 adder입니다.

파일 이름: Cargo.toml

[workspace]

members = [
    "adder",
]

다음으로, add 디렉토리 내에서 cargo new를 실행하여 adder 바이너리 크레이트를 만듭니다.

$ cargo new adder
     Created binary (application) `adder` package

이 시점에서 cargo build를 실행하여 workspace 를 빌드할 수 있습니다. add 디렉토리의 파일은 다음과 같아야 합니다.

├── Cargo.lock
├── Cargo.toml
├── adder
│   ├── Cargo.toml
│   └── src
│       └── main.rs
└── target

workspace 는 컴파일된 아티팩트가 배치될 최상위 레벨에 하나의 target 디렉토리를 갖습니다. adder 패키지 자체에는 target 디렉토리가 없습니다. adder 디렉토리 내부에서 cargo build를 실행하더라도 컴파일된 아티팩트는 add/adder/target이 아닌 add/target에 배치됩니다. Cargo 는 workspace 에서 크레이트가 서로 의존하도록 설계되었기 때문에 workspace 에서 target 디렉토리를 이렇게 구성합니다. 각 크레이트가 자체 target 디렉토리를 갖는다면, 각 크레이트는 아티팩트를 자체 target 디렉토리에 배치하기 위해 workspace 의 다른 각 크레이트를 다시 컴파일해야 합니다. 하나의 target 디렉토리를 공유함으로써 크레이트는 불필요한 재빌드를 피할 수 있습니다.

Workspace 에서 두 번째 패키지 생성하기

다음으로, workspace 에 다른 멤버 패키지를 생성하고 add_one이라고 부르겠습니다. 최상위 Cargo.toml을 변경하여 members 목록에 add_one 경로를 지정합니다.

파일 이름: Cargo.toml

[workspace]

members = [
    "adder",
    "add_one",
]

그런 다음 add_one이라는 새 라이브러리 크레이트를 생성합니다.

$ cargo new add_one --lib
Created library $(add_one) package

add 디렉토리에는 이제 다음과 같은 디렉토리와 파일이 있어야 합니다.

├── Cargo.lock
├── Cargo.toml
├── add_one
│   ├── Cargo.toml
│   └── src
│       └── lib.rs
├── adder
│   ├── Cargo.toml
│   └── src
│       └── main.rs
└── target

add_one/src/lib.rs 파일에 add_one 함수를 추가해 보겠습니다.

파일 이름: add_one/src/lib.rs

pub fn add_one(x: i32) -> i32 {
    x + 1
}

이제 바이너리가 있는 adder 패키지가 라이브러리가 있는 add_one 패키지에 의존하도록 할 수 있습니다. 먼저 adder/Cargo.tomladd_one에 대한 경로 종속성을 추가해야 합니다.

파일 이름: adder/Cargo.toml

[dependencies]
add_one = { path = "../add_one" }

Cargo 는 workspace 의 크레이트가 서로 의존한다고 가정하지 않으므로 종속성 관계를 명시적으로 지정해야 합니다.

다음으로, adder 크레이트에서 (add_one 크레이트에서) add_one 함수를 사용해 보겠습니다. adder/src/main.rs 파일을 열고 맨 위에 use 줄을 추가하여 새 add_one 라이브러리 크레이트를 범위 내로 가져옵니다. 그런 다음 Listing 14-7 과 같이 main 함수를 변경하여 add_one 함수를 호출합니다.

파일 이름: adder/src/main.rs

use add_one;

fn main() {
    let num = 10;
    println!(
        "Hello, world! {num} plus one is {}!",
        add_one::add_one(num)
    );
}

Listing 14-7: adder 크레이트에서 add_one 라이브러리 크레이트 사용하기

최상위 add 디렉토리에서 cargo build를 실행하여 workspace 를 빌드해 보겠습니다!

$ cargo build
   Compiling add_one v0.1.0 (file:///projects/add/add_one)
   Compiling adder v0.1.0 (file:///projects/add/adder)
    Finished dev [unoptimized + debuginfo] target(s) in 0.68s

add 디렉토리에서 바이너리 크레이트를 실행하려면 -p 인자와 패키지 이름을 사용하여 cargo run으로 workspace 에서 실행하려는 패키지를 지정할 수 있습니다.

$ cargo run -p adder
    Finished dev [unoptimized + debuginfo] target(s) in 0.0s
     Running `target/debug/adder`
Hello, world! 10 plus one is 11!

이것은 add_one 크레이트에 의존하는 adder/src/main.rs의 코드를 실행합니다.

Workspace 에서 외부 패키지에 의존하기

workspace 에는 각 크레이트의 디렉토리에 Cargo.lock이 있는 대신 최상위 레벨에 하나의 Cargo.lock 파일만 있다는 점에 유의하십시오. 이렇게 하면 모든 크레이트가 모든 종속성의 동일한 버전을 사용하도록 보장됩니다. rand 패키지를 adder/Cargo.tomladd_one/Cargo.toml 파일에 추가하면 Cargo 는 둘 다 rand의 한 버전으로 확인하고 해당 내용을 하나의 Cargo.lock에 기록합니다. workspace 의 모든 크레이트가 동일한 종속성을 사용하도록 하면 크레이트가 항상 서로 호환됩니다. add_one 크레이트에서 rand 크레이트를 사용할 수 있도록 rand 크레이트를 add_one/Cargo.toml 파일의 [dependencies] 섹션에 추가해 보겠습니다.

파일 이름: add_one/Cargo.toml

[dependencies]
rand = "0.8.5"

이제 add_one/src/lib.rs 파일에 use rand;를 추가할 수 있으며, add 디렉토리에서 cargo build를 실행하여 전체 workspace 를 빌드하면 rand 크레이트가 가져와 컴파일됩니다. 범위 내로 가져온 rand를 참조하지 않기 때문에 경고가 하나 표시됩니다.

$ cargo build
    Updating crates.io index
  Downloaded rand v0.8.5
   --snip--
   Compiling rand v0.8.5
   Compiling add_one v0.1.0 (file:///projects/add/add_one)
   Compiling adder v0.1.0 (file:///projects/add/adder)
    Finished dev [unoptimized + debuginfo] target(s) in 10.18s

최상위 Cargo.lock에는 이제 rand에 대한 add_one의 종속성에 대한 정보가 포함되어 있습니다. 그러나 rand가 workspace 어딘가에서 사용되더라도 rand를 해당 Cargo.toml 파일에도 추가하지 않으면 workspace 의 다른 크레이트에서 사용할 수 없습니다. 예를 들어, adder 패키지의 adder/src/main.rs 파일에 use rand;를 추가하면 오류가 발생합니다.

$ cargo build
   --snip--
   Compiling adder v0.1.0 (file:///projects/add/adder)
error[E0432]: unresolved import `rand`
 --> adder/src/main.rs:2:5
  |
2 | use rand;
  |     ^^^^ no external crate `rand`

이 문제를 해결하려면 adder 패키지의 Cargo.toml 파일을 편집하고 rand가 해당 패키지의 종속성임을 나타냅니다. adder 패키지를 빌드하면 Cargo.lock에서 adder의 종속성 목록에 rand가 추가되지만, rand의 추가 복사본은 다운로드되지 않습니다. Cargo 는 rand 패키지를 사용하는 workspace 의 모든 패키지의 모든 크레이트가 동일한 버전을 사용하도록 보장하여 공간을 절약하고 workspace 의 크레이트가 서로 호환되도록 합니다.

Workspace 에 테스트 추가하기

또 다른 개선 사항으로, add_one 크레이트 내에서 add_one::add_one 함수의 테스트를 추가해 보겠습니다.

파일 이름: add_one/src/lib.rs

pub fn add_one(x: i32) -> i32 {
    x + 1
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        assert_eq!(3, add_one(2));
    }
}

이제 최상위 add 디렉토리에서 cargo test를 실행합니다. 이와 같이 구조화된 workspace 에서 cargo test를 실행하면 workspace 의 모든 크레이트에 대한 테스트가 실행됩니다.

[object Object]

출력의 첫 번째 섹션은 add_one 크레이트의 it_works 테스트가 통과했음을 보여줍니다. 다음 섹션은 adder 크레이트에서 테스트가 0 개 발견되었음을 보여주고, 마지막 섹션은 add_one 크레이트에서 문서 테스트가 0 개 발견되었음을 보여줍니다.

또한 -p 플래그를 사용하고 테스트하려는 크레이트의 이름을 지정하여 최상위 디렉토리에서 workspace 의 특정 크레이트에 대한 테스트를 실행할 수도 있습니다.

[object Object]

이 출력은 cargo testadd_one 크레이트에 대한 테스트만 실행하고 adder 크레이트 테스트는 실행하지 않았음을 보여줍니다.

workspace 의 크레이트를 https://crates.io에 게시하는 경우 workspace 의 각 크레이트를 별도로 게시해야 합니다. cargo test와 마찬가지로 -p 플래그를 사용하고 게시하려는 크레이트의 이름을 지정하여 workspace 의 특정 크레이트를 게시할 수 있습니다.

추가 연습을 위해 add_one 크레이트와 유사한 방식으로 이 workspace 에 add_two 크레이트를 추가하십시오!

프로젝트가 커짐에 따라 workspace 를 사용하는 것을 고려하십시오. workspace 는 하나의 큰 코드 덩어리보다 이해하기 쉽고 작은 개별 구성 요소를 제공합니다. 또한 크레이트를 workspace 에 유지하면 크레이트가 동시에 자주 변경되는 경우 크레이트 간의 조정을 더 쉽게 할 수 있습니다.

요약

축하합니다! Cargo Workspaces 랩을 완료했습니다. LabEx 에서 더 많은 랩을 연습하여 기술을 향상시킬 수 있습니다.