Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

15. 크레이트 아키텍처 및 API 설계 🟡

학습 목표:

  • 모듈 구조의 표준 관례와 **재내보내기(Re-export)**를 이용한 API 정제법을 익힙니다.
  • 세련된 크레이트 제작을 위한 공개 API 체크리스트를 학습합니다.
  • impl Into, AsRef, Cow를 활용한 편리한(Ergonomic) 인자 전달 패턴을 배웁니다.
  • "검증하지 말고 파싱하라(Parse, don't validate)" 원칙과 TryFrom의 가치를 이해합니다.
  • 피처 플래그(Feature flags), 조건부 컴파일, 워크스페이스 구조를 마스터합니다.

모듈 구조와 가시성 가이드

성공적인 크레이트는 내부 구조가 복잡하더라도 외부에는 깔끔한 인터페이스를 노출합니다.

  • lib.rs: 크레이트의 뿌리입니다. 내부 모듈들을 선언하고, 사용자가 필요한 타입들만 pub use로 재내보내어 API를 큐레이션합니다.
  • 가시성 제어: pub(crate)를 활용해 크레이트 내부에서는 자유롭게 접근하되, 외부에는 노출되지 않도록 경계를 설정하세요.

공개 API 설계 체크리스트

  1. 빌림(Reference)으로 받고 소유권(Owned)으로 반환: fn process(s: &str) -> String
  2. impl Trait 활용: 인자 타입이 명확할 때는 제네릭보다 impl Read처럼 표현하는 것이 가독성에 좋습니다.
  3. 패닉 대신 Result: 실패 가능성이 있는 모든 API는 호출자가 처리 방식을 결정할 수 있게 하세요.
  4. 표준 트레이트 구현: Debug, Clone, Default 등을 기본적으로 제공하세요.
  5. 불가능한 상태를 타입으로 방지: 타입 상태(Type-state) 패턴을 적극 활용하세요.
  6. #[must_use] 사용: 반환값이 무시되면 안 되는 중요한 함수나 객체에 표시하세요.
  7. #[non_exhaustive] 사용: 공개 열거형에 사용해 나중에 항목이 추가되어도 하위 호환성이 깨지지 않게 하세요.

편리한 인자 패턴: impl Into, AsRef, Cow

사용자가 호출할 때 .to_string()이나 .as_ref()를 남발하지 않도록 설계하세요.

#![allow(unused)]
fn main() {
// ❌ 불편한 방식: 사용자가 직접 String으로 변환해야 함
fn connect(host: String) { ... }

// ✅ 편리한 방식: &str, String 모두 그대로 전달 가능
fn connect(host: impl Into<String>) {
    let host = host.into(); // 내부에서 변환
}
}
  • AsRef<T>: 데이터를 읽기만 할 때 사용하세요.
  • Cow<'a, T>: 평소에는 빌려 쓰다가 수정이 필요할 때만 비로소 복제(Clone)하고 싶을 때 사용하세요.

검증하지 말고 파싱하라 (Parse, don't validate)

데이터가 유효한지 체크만 하고 다시 원시 타입(String, i32 등)으로 들고 다니지 마세요. 유효성 검사가 완료된 정보만 담을 수 있는 **전용 타입(Newtype)**으로 파싱하세요. 일단 해당 타입의 객체가 생성되었다면, 이후 코드에서는 다시 검증할 필요 없이 안전하게 사용할 수 있습니다.


피처 플래그와 조건부 컴파일

사용자가 필요한 기능만 선택해서 빌드할 수 있게 하여 바이너리 크기와 의존성을 줄이세요. Cargo.toml[features] 섹션과 #[cfg(feature = "...")] 속성을 활용합니다.


📝 연습 문제: 크레이트 API 리팩토링 ★★ (~30분)

원시 타입 사용이 남발된(Stringly-typed) API를 TryFrom, 뉴타입, 빌더 패턴을 사용하여 리팩토링해 보세요. 특히 호스트 이름, 포트(1~65535) 등이 파싱 단계에서 완벽히 검증되도록 설계해 보세요.


📌 요약

  • API는 유연하게 받고(Into/AsRef), 구체적으로 반환하세요.
  • **#[non_exhaustive]**는 라이브러리 유지보수성을 위한 필수 도구입니다.
  • 타입 시스템 자체가 보증서가 되도록 설계하세요(Parse, don't validate).
  • 공급망 보안: cargo auditcargo deny를 정기적으로 실행하여 안전한 의존성을 관리하세요.