Crates.io 에 크레이트 게시하기

Beginner

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

소개

crates.io 에 크레이트 게시하기에 오신 것을 환영합니다. 이 랩은 Rust Book의 일부입니다. LabEx 에서 Rust 기술을 연습할 수 있습니다.

이 랩에서는 오픈 소스 코드를 배포하는 크레이트 레지스트리인 crates.io 에 크레이트를 게시하는 방법을 논의하여 사람들이 패키지를 더 쉽게 찾고 사용할 수 있도록 할 것입니다.

crates.io 에 크레이트 게시하기

우리는 프로젝트의 종속성으로 https://crates.io의 패키지를 사용했지만, 자신의 패키지를 게시하여 다른 사람들과 코드를 공유할 수도 있습니다. https://crates.io의 크레이트 레지스트리는 패키지의 소스 코드를 배포하므로 주로 오픈 소스 코드를 호스팅합니다.

Rust 와 Cargo 는 게시된 패키지를 사람들이 더 쉽게 찾고 사용할 수 있도록 하는 기능을 가지고 있습니다. 다음에서 이러한 기능 중 일부에 대해 이야기하고 패키지를 게시하는 방법을 설명하겠습니다.

유용한 문서 주석 작성하기

패키지를 정확하게 문서화하면 다른 사용자가 패키지를 사용해야 하는 방법과 시기를 알 수 있으므로 문서 작성에 시간을 투자할 가치가 있습니다. 3 장에서 두 개의 슬래시, //를 사용하여 Rust 코드를 주석 처리하는 방법을 논의했습니다. Rust 에는 HTML 문서를 생성하는 문서 주석이라고 편리하게 알려진 특정 종류의 문서 주석도 있습니다. HTML 은 크레이트가 구현된 방식이 아닌 크레이트를 사용하는 방법에 관심 있는 프로그래머를 위해 공개 API 항목에 대한 문서 주석의 내용을 표시합니다.

문서 주석은 두 개의 슬래시 대신 세 개의 슬래시, ///를 사용하며 텍스트 서식을 지정하기 위해 Markdown 표기법을 지원합니다. 문서화하려는 항목 바로 앞에 문서 주석을 배치합니다. 목록 14-1 은 my_crate라는 크레이트의 add_one 함수에 대한 문서 주석을 보여줍니다.

파일 이름: src/lib.rs

/// 주어진 숫자에 1 을 더합니다.
///
/// ## 예시
///
/// ```
/// let arg = 5;
/// let answer = my_crate::add_one(arg);
///
/// assert_eq!(6, answer);
/// ```rust
pub fn add_one(x: i32) -> i32 {
    x + 1
}

목록 14-1: 함수에 대한 문서 주석

여기서는 add_one 함수가 수행하는 작업에 대한 설명을 제공하고, Examples라는 제목으로 섹션을 시작한 다음 add_one 함수를 사용하는 방법을 보여주는 코드를 제공합니다. cargo doc를 실행하여 이 문서 주석에서 HTML 문서를 생성할 수 있습니다. 이 명령은 Rust 와 함께 배포된 rustdoc 도구를 실행하고 생성된 HTML 문서를 target/doc 디렉토리에 넣습니다.

편의상 cargo doc --open을 실행하면 현재 크레이트의 문서 (및 크레이트의 모든 종속성에 대한 문서) 에 대한 HTML 이 빌드되고 웹 브라우저에서 결과가 열립니다. add_one 함수로 이동하면 그림 14-1 과 같이 문서 주석의 텍스트가 렌더링되는 방식을 볼 수 있습니다.

그림 14-1: add_one 함수에 대한 HTML 문서

일반적으로 사용되는 섹션

목록 14-1 에서 ## Examples Markdown 제목을 사용하여 "Examples"라는 제목의 HTML 섹션을 만들었습니다. 다음은 크레이트 작성자가 문서에서 일반적으로 사용하는 다른 섹션입니다.

  • Panics (패닉): 문서화되는 함수가 패닉할 수 있는 시나리오. 프로그램이 패닉되지 않도록 하려는 함수의 호출자는 이러한 상황에서 함수를 호출하지 않도록 해야 합니다.
  • Errors (오류): 함수가 Result를 반환하는 경우, 발생할 수 있는 오류 종류와 해당 오류가 반환될 수 있는 조건을 설명하면 호출자가 다양한 종류의 오류를 다른 방식으로 처리하는 코드를 작성하는 데 도움이 될 수 있습니다.
  • Safety (안전성): 함수를 호출하는 것이 unsafe한 경우 (19 장에서 안전하지 않음에 대해 논의), 함수가 안전하지 않은 이유와 함수가 호출자에게 유지하도록 기대하는 불변성을 설명하는 섹션이 있어야 합니다.

대부분의 문서 주석에는 이러한 모든 섹션이 필요하지 않지만, 이는 사용자가 알고 싶어할 코드의 측면을 상기시켜주는 좋은 체크리스트입니다.

테스트로서의 문서 주석

문서 주석에 예제 코드 블록을 추가하면 라이브러리 사용 방법을 시연하는 데 도움이 되며, 이렇게 하면 추가적인 보너스가 있습니다. cargo test를 실행하면 문서의 코드 예제가 테스트로 실행됩니다! 예제가 있는 문서보다 더 좋은 것은 없습니다. 그러나 코드가 문서 작성 이후 변경되어 작동하지 않는 예제보다 더 나쁜 것은 없습니다. 목록 14-1 의 add_one 함수에 대한 문서를 사용하여 cargo test를 실행하면 테스트 결과에 다음과 같은 섹션이 표시됩니다.

   Doc-tests my_crate

running 1 test
test src/lib.rs - add_one (line 5) ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0
filtered out; finished in 0.27s

이제 함수 또는 예제를 변경하여 예제의 assert_eq!가 패닉되도록 하고 cargo test를 다시 실행하면 문서 테스트가 예제와 코드가 서로 동기화되지 않음을 감지하는 것을 볼 수 있습니다!

포함된 항목에 주석 달기

doc 주석 //!는 주석을 따르는 항목이 아닌 주석을 포함하는 항목에 문서를 추가합니다. 일반적으로 크레이트 루트 파일 (src/lib.rs가 관례) 또는 모듈 내에서 이러한 doc 주석을 사용하여 크레이트 또는 모듈 전체를 문서화합니다.

예를 들어, add_one 함수를 포함하는 my_crate 크레이트의 목적을 설명하는 문서를 추가하려면 목록 14-2 에 표시된 것처럼 //!로 시작하는 문서 주석을 src/lib.rs 파일의 시작 부분에 추가합니다.

파일 이름: src/lib.rs

//! ## My Crate
//!
//! `my_crate` 는 특정 계산을 더 편리하게 수행하기 위한 유틸리티 모음입니다.

/// 주어진 숫자에 1 을 더합니다.
--snip--

목록 14-2: 전체 my_crate 크레이트에 대한 문서

//!로 시작하는 마지막 줄 뒤에는 코드가 없습니다. 주석을 /// 대신 //!로 시작했기 때문에 이 주석을 따르는 항목이 아닌 이 주석을 포함하는 항목을 문서화하고 있습니다. 이 경우 해당 항목은 크레이트 루트인 src/lib.rs 파일입니다. 이러한 주석은 전체 크레이트를 설명합니다.

cargo doc --open을 실행하면 그림 14-2 에 표시된 것처럼 이러한 주석이 my_crate에 대한 문서의 첫 페이지에 크레이트의 공개 항목 목록 위에 표시됩니다.

그림 14-2: 전체 크레이트를 설명하는 주석을 포함한 my_crate에 대한 렌더링된 문서

항목 내의 문서 주석은 특히 크레이트와 모듈을 설명하는 데 유용합니다. 컨테이너의 전반적인 목적을 설명하여 사용자가 크레이트의 구성을 이해하도록 돕는 데 사용하십시오.

pub use를 사용하여 편리한 공개 API 내보내기

크레이트를 게시할 때 공개 API 의 구조는 중요한 고려 사항입니다. 크레이트를 사용하는 사람들은 당신보다 구조에 익숙하지 않으며, 크레이트에 큰 모듈 계층 구조가 있는 경우 사용하려는 부분을 찾는 데 어려움을 겪을 수 있습니다.

7 장에서 pub 키워드를 사용하여 항목을 공개하는 방법과 use 키워드를 사용하여 항목을 범위로 가져오는 방법을 다루었습니다. 그러나 크레이트를 개발하는 동안 당신에게 적합한 구조가 사용자에게는 그다지 편리하지 않을 수 있습니다. 여러 수준을 포함하는 계층 구조로 구조체를 구성할 수 있지만, 계층 구조 깊숙한 곳에서 정의한 타입을 사용하려는 사람은 해당 타입이 존재한다는 것을 찾는 데 어려움을 겪을 수 있습니다. 또한 use my_crate::some_module::another_module::UsefulType; 대신 use my_crate::UsefulType;를 입력해야 하는 것에 짜증을 낼 수도 있습니다.

좋은 소식은 다른 라이브러리에서 사용하기에 구조가 편리하지 않은 경우 내부 구성을 재정렬할 필요가 없다는 것입니다. 대신 pub use를 사용하여 항목을 다시 내보내어 개인 구조와 다른 공개 구조를 만들 수 있습니다. 재내보내기는 한 위치의 공개 항목을 다른 위치에서 공개적으로 만들며, 마치 다른 위치에서 정의된 것처럼 작동합니다.

예를 들어, 예술적 개념을 모델링하기 위한 art라는 라이브러리를 만들었다고 가정해 보겠습니다. 이 라이브러리 내에는 PrimaryColorSecondaryColor라는 두 개의 열거형을 포함하는 kinds 모듈과 mix라는 함수를 포함하는 utils 모듈이 있습니다 (목록 14-3 참조).

파일 이름: src/lib.rs

//! ## Art
//!
//! 예술적 개념을 모델링하기 위한 라이브러리입니다.

pub mod kinds {
    /// RYB 색상 모델에 따른 기본 색상입니다.
    pub enum PrimaryColor {
        Red,
        Yellow,
        Blue,
    }

    /// RYB 색상 모델에 따른 보조 색상입니다.
    pub enum SecondaryColor {
        Orange,
        Green,
        Purple,
    }
}

pub mod utils {
    use crate::kinds::*;

    /// 두 개의 기본 색상을 동일한 양으로 결합하여
    /// 보조 색상을 만듭니다.
    pub fn mix(
        c1: PrimaryColor,
        c2: PrimaryColor,
    ) -> SecondaryColor {
        --snip--
    }
}

목록 14-3: kindsutils 모듈로 구성된 art 라이브러리

그림 14-3 은 cargo doc에서 생성된 이 크레이트에 대한 문서의 첫 페이지가 어떻게 표시되는지 보여줍니다.

그림 14-3: kindsutils 모듈을 나열하는 art에 대한 문서의 첫 페이지

PrimaryColorSecondaryColor 타입은 첫 페이지에 나열되지 않으며, mix 함수도 마찬가지입니다. kindsutils를 클릭해야 볼 수 있습니다.

이 라이브러리에 의존하는 다른 크레이트는 art에서 항목을 범위로 가져오는 use 문이 필요하며, 현재 정의된 모듈 구조를 지정합니다. 목록 14-4 는 art 크레이트에서 PrimaryColormix 항목을 사용하는 크레이트의 예제를 보여줍니다.

파일 이름: src/main.rs

use art::kinds::PrimaryColor;
use art::utils::mix;

fn main() {
    let red = PrimaryColor::Red;
    let yellow = PrimaryColor::Yellow;
    mix(red, yellow);
}

목록 14-4: 내부 구조가 내보내진 art 크레이트의 항목을 사용하는 크레이트

art 크레이트를 사용하는 목록 14-4 의 코드 작성자는 PrimaryColorkinds 모듈에 있고 mixutils 모듈에 있다는 것을 알아내야 했습니다. art 크레이트의 모듈 구조는 이를 사용하는 사람보다 art 크레이트에서 작업하는 개발자에게 더 관련이 있습니다. 내부 구조는 art 크레이트를 사용하는 방법을 이해하려는 사람에게 유용한 정보를 포함하지 않지만, 사용자가 어디를 찾아야 하는지 알아내야 하고 use 문에서 모듈 이름을 지정해야 하므로 혼란을 야기합니다.

공개 API 에서 내부 구성을 제거하기 위해 목록 14-3 의 art 크레이트 코드를 수정하여 목록 14-5 에 표시된 것처럼 최상위 수준에서 항목을 다시 내보내기 위해 pub use 문을 추가할 수 있습니다.

파일 이름: src/lib.rs

//! ## Art
//!
//! 예술적 개념을 모델링하기 위한 라이브러리입니다.

pub use self::kinds::PrimaryColor;
pub use self::kinds::SecondaryColor;
pub use self::utils::mix;

pub mod kinds {
    --snip--
}

pub mod utils {
    --snip--
}

목록 14-5: 항목을 다시 내보내기 위해 pub use 문 추가

cargo doc가 이 크레이트에 대해 생성하는 API 문서는 이제 그림 14-4 에 표시된 것처럼 첫 페이지에 다시 내보내기를 나열하고 연결하여 PrimaryColorSecondaryColor 타입과 mix 함수를 더 쉽게 찾을 수 있도록 합니다.

그림 14-4: 다시 내보내기를 나열하는 art에 대한 문서의 첫 페이지

art 크레이트 사용자는 목록 14-4 에 표시된 것처럼 목록 14-3 의 내부 구조를 계속 보고 사용할 수 있으며, 목록 14-6 에 표시된 것처럼 목록 14-5 의 더 편리한 구조를 사용할 수도 있습니다.

파일 이름: src/main.rs

use art::mix;
use art::PrimaryColor;

fn main() {
    --snip--
}

목록 14-6: art 크레이트에서 다시 내보낸 항목을 사용하는 프로그램

많은 중첩된 모듈이 있는 경우 pub use를 사용하여 최상위 수준에서 타입을 다시 내보내는 것은 크레이트를 사용하는 사람들의 경험에 상당한 차이를 만들 수 있습니다. pub use의 또 다른 일반적인 사용 사례는 현재 크레이트에서 종속성의 정의를 다시 내보내어 해당 크레이트의 정의를 크레이트의 공개 API 의 일부로 만드는 것입니다.

유용한 공개 API 구조를 만드는 것은 과학보다는 예술에 가깝고, 사용자를 위해 가장 잘 작동하는 API 를 찾기 위해 반복할 수 있습니다. pub use를 선택하면 크레이트를 내부적으로 구조화하는 방식에 유연성을 제공하고 내부 구조를 사용자에게 제공하는 것과 분리할 수 있습니다. 설치한 크레이트의 일부 코드를 살펴보고 내부 구조가 공개 API 와 다른지 확인하십시오.

Crates.io 계정 설정

크레이트를 게시하기 전에 https://crates.io에서 계정을 만들고 API 토큰을 받아야 합니다. 이렇게 하려면 https://crates.io의 홈페이지를 방문하여 GitHub 계정을 통해 로그인하십시오. (GitHub 계정은 현재 필수 사항이지만, 사이트에서 향후 다른 계정 생성 방법을 지원할 수 있습니다.) 로그인한 후 *https://crates.io/me*에서 계정 설정을 방문하여 API 키를 검색하십시오. 그런 다음 API 키를 사용하여 cargo login 명령을 실행합니다.

cargo login abcdefghijklmnopqrstuvwxyz012345

이 명령은 Cargo 에 API 토큰을 알리고 로컬로 ~/.cargo/credentials에 저장합니다. 이 토큰은 비밀이므로 다른 사람과 공유하지 마십시오. 어떤 이유로든 다른 사람과 공유하는 경우, 해당 토큰을 해지하고 https://crates.io에서 새 토큰을 생성해야 합니다.

새 크레이트에 메타데이터 추가하기

게시하려는 크레이트가 있다고 가정해 보겠습니다. 게시하기 전에 크레이트의 Cargo.toml 파일의 [package] 섹션에 몇 가지 메타데이터를 추가해야 합니다.

크레이트에는 고유한 이름이 필요합니다. 로컬에서 크레이트를 작업하는 동안 원하는 대로 크레이트의 이름을 지정할 수 있습니다. 그러나 https://crates.io의 크레이트 이름은 선착순으로 할당됩니다. 크레이트 이름이 사용되면 다른 사람은 해당 이름으로 크레이트를 게시할 수 없습니다. 크레이트를 게시하기 전에 사용하려는 이름을 검색하십시오. 이름이 사용된 경우 다른 이름을 찾아 Cargo.toml 파일의 [package] 섹션에서 name 필드를 편집하여 게시할 새 이름을 사용해야 합니다.

파일 이름: Cargo.toml

[package]
name = "guessing_game"

고유한 이름을 선택했더라도 이 시점에서 cargo publish를 실행하여 크레이트를 게시하면 경고와 오류가 발생합니다.

$ cargo publish
    Updating crates.io index
warning: manifest has no description, license, license-file, documentation,
homepage or repository.
See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata
for more info.
--snip--
error: failed to publish to registry at https://crates.io

Caused by:
  the remote server responded with an error: missing or empty metadata fields:
description, license. Please see https://doc.rust-
lang.org/cargo/reference/manifest.html for how to upload metadata

이것은 몇 가지 중요한 정보가 누락되었기 때문에 오류가 발생합니다. 설명과 라이선스는 필수이므로 사람들이 크레이트가 무엇을 하는지, 어떤 조건으로 사용할 수 있는지 알 수 있습니다. Cargo.toml에서 크레이트가 검색 결과에 표시되므로 한두 문장으로 된 설명을 추가합니다. license 필드의 경우 라이선스 식별자 값을 제공해야 합니다. *http://spdx.org/licenses*의 Linux Foundation 의 Software Package Data Exchange (SPDX) 는 이 값에 사용할 수 있는 식별자를 나열합니다. 예를 들어, MIT 라이선스를 사용하여 크레이트에 라이선스를 부여했음을 지정하려면 MIT 식별자를 추가합니다.

파일 이름: Cargo.toml

[package]
name = "guessing_game"
license = "MIT"

SPDX 에 나타나지 않는 라이선스를 사용하려면 해당 라이선스의 텍스트를 파일에 넣고, 해당 파일을 프로젝트에 포함한 다음, license 키를 사용하는 대신 license-file을 사용하여 해당 파일의 이름을 지정해야 합니다.

프로젝트에 적합한 라이선스에 대한 지침은 이 책의 범위를 벗어납니다. Rust 커뮤니티의 많은 사람들은 MIT OR Apache-2.0의 이중 라이선스를 사용하여 Rust 와 동일한 방식으로 프로젝트에 라이선스를 부여합니다. 이 관행은 OR로 구분된 여러 라이선스 식별자를 지정하여 프로젝트에 여러 라이선스를 가질 수도 있음을 보여줍니다.

고유한 이름, 버전, 설명 및 라이선스가 추가된 게시 준비가 된 프로젝트의 Cargo.toml 파일은 다음과 같습니다.

파일 이름: Cargo.toml

[package]
name = "guessing_game"
version = "0.1.0"
edition = "2021"
description = "A fun game where you guess what number the
computer has chosen."
license = "MIT OR Apache-2.0"

[dependencies]

*https://doc.rust-lang.org/cargo*의 Cargo 문서는 다른 사람이 크레이트를 더 쉽게 발견하고 사용할 수 있도록 지정할 수 있는 다른 메타데이터를 설명합니다.

Crates.io 에 게시하기

이제 계정을 만들고, API 토큰을 저장하고, 크레이트의 이름을 선택하고, 필요한 메타데이터를 지정했으므로 게시할 준비가 되었습니다! 크레이트를 게시하면 특정 버전이 다른 사용자가 사용할 수 있도록 https://crates.io에 업로드됩니다.

게시는 영구적이므로 주의하십시오. 버전은 절대 덮어쓸 수 없으며 코드를 삭제할 수 없습니다. Crates.io 의 주요 목표 중 하나는 https://crates.io의 크레이트에 의존하는 모든 프로젝트의 빌드가 계속 작동하도록 코드의 영구 보관소 역할을 하는 것입니다. 버전 삭제를 허용하면 해당 목표를 달성할 수 없습니다. 그러나 게시할 수 있는 크레이트 버전 수에는 제한이 없습니다.

cargo publish 명령을 다시 실행합니다. 이제 성공해야 합니다.

$ cargo publish
    Updating crates.io index
   Packaging guessing_game v0.1.0 (file:///projects/guessing_game)
   Verifying guessing_game v0.1.0 (file:///projects/guessing_game)
   Compiling guessing_game v0.1.0
(file:///projects/guessing_game/target/package/guessing_game-0.1.0)
    Finished dev [unoptimized + debuginfo] target(s) in 0.19s
   Uploading guessing_game v0.1.0 (file:///projects/guessing_game)

축하합니다! 이제 Rust 커뮤니티와 코드를 공유했으며, 누구나 크레이트를 프로젝트의 종속성으로 쉽게 추가할 수 있습니다.

기존 크레이트의 새 버전 게시하기

크레이트를 변경하고 새 버전을 출시할 준비가 되면 Cargo.toml 파일에 지정된 version 값을 변경하고 다시 게시합니다. 변경 사항의 종류에 따라 적절한 다음 버전 번호를 결정하기 위해 http://semver.org의 Semantic Versioning 규칙을 사용합니다. 그런 다음 cargo publish를 실행하여 새 버전을 업로드합니다.

cargo yank 를 사용하여 Crates.io 에서 버전 사용 중단

크레이트의 이전 버전을 제거할 수는 없지만, 향후 프로젝트에서 해당 버전을 새 종속성으로 추가하는 것을 방지할 수 있습니다. 이는 크레이트 버전이 어떤 이유로든 손상된 경우에 유용합니다. 이러한 상황에서 Cargo 는 크레이트 버전의 yank 를 지원합니다.

버전을 yank하면 새 프로젝트가 해당 버전에 종속되는 것을 방지하는 동시에, 이에 종속된 모든 기존 프로젝트는 계속 사용할 수 있습니다. 본질적으로 yank 는 Cargo.lock이 있는 모든 프로젝트가 중단되지 않으며, 생성될 모든 향후 Cargo.lock 파일이 yank 된 버전을 사용하지 않음을 의미합니다.

크레이트의 버전을 yank 하려면, 이전에 게시한 크레이트의 디렉토리에서 cargo yank를 실행하고 yank 하려는 버전을 지정합니다. 예를 들어, guessing_game 버전 1.0.1 이라는 크레이트를 게시했고 이를 yank 하려는 경우, guessing_game 프로젝트 디렉토리에서 다음을 실행합니다.

$ cargo yank --vers 1.0.1
Updating crates.io index
Yank guessing_game@1.0.1

명령에 --undo를 추가하면 yank 를 실행 취소하고 프로젝트가 다시 버전에 종속되도록 허용할 수도 있습니다.

$ cargo yank --vers 1.0.1 --undo
Updating crates.io index
Unyank guessing_game@1.0.1

Yank 는 코드를 삭제하지 않습니다. 예를 들어, 실수로 업로드된 비밀을 삭제할 수 없습니다. 그런 경우, 즉시 해당 비밀을 재설정해야 합니다.

요약

축하합니다! Crates.io 에 크레이트 게시하기 랩을 완료했습니다. LabEx 에서 더 많은 랩을 연습하여 기술을 향상시킬 수 있습니다.