[Rust 시리즈 12편] 모듈과 패키지 구조 이해하기

English version

10편 이후부터는 프로젝트가 빠르게 비대해집니다. 함수와 구조체가 한 파일에 모여 있으면 ownership 규칙을 추적하기 어려워지므로, Rust가 제공하는 모듈·패키지 시스템으로 코드를 분해하는 훈련이 필요합니다.

전체 그림: crate, package, module

  • package: Cargo.toml이 위치한 최상위 단위로, 하나 이상의 crate를 포함합니다. 대부분의 입문 예제는 하나의 binary crate만 포함합니다.
  • crate: 컴파일러가 독립적으로 빌드하는 단위입니다. src/main.rs는 binary crate, src/lib.rs는 library crate의 진입점이 됩니다.
  • module (mod): crate 내부에서 코드를 논리적으로 그룹화하는 단위입니다. 파일이나 mod 블록으로 선언할 수 있습니다.

한 줄 그림으로 보면 아래와 같습니다.

package 안에 하나 이상 crate가 있고, 각 crate 안에 여러 module이 들어갑니다.

단일 파일 모듈 선언

처음에는 src/main.rs 안에서 mod 블록을 사용해 작은 단위부터 나눌 수 있습니다.

mod auth {
    pub fn login(user: &str) {
        println!("{} 로그인", user);
    }
}

fn main() {
    auth::login("yuna");
}
  • mod auth { ... }auth라는 모듈을 만듭니다.
  • 모듈 밖에서 사용할 함수는 pub으로 공개해야 합니다.
  • :: 연산자를 사용해 모듈 경로를 따라 접근합니다.

파일로 모듈 분리하기

코드가 길어지면 파일 분리가 필요합니다. Rust는 mod foo; 선언을 만나면 foo.rs 또는 foo/mod.rs 파일을 찾습니다.

src/
├── main.rs
├── auth.rs
└── user/
    ├── mod.rs
    └── profile.rs

main.rs에서 아래와 같이 선언합니다.

mod auth;
mod user;

fn main() {
    auth::login("yuna");
    user::profile::show("yuna");
}

user/mod.rs에서는 하위 모듈을 다시 선언합니다.

pub mod profile;

pub fn list() {
    println!("사용자 목록");
}
  • 디렉터리 기반 모듈은 mod.rs (새로운 에디션에서는 mod.rs 대신 user.rs + user/profile.rs로도 구성 가능) 파일을 통해 하위 모듈을 노출합니다.
  • pub mod profile;profile.rs 파일을 포함시키고 외부에서도 접근할 수 있게 만듭니다.

즉, user/mod.rs 안에 pub mod profile;이 있으면 Rust는 보통 user/profile.rs 파일을 찾는다고 이해하면 됩니다.

use로 경로 단축하기

긴 경로를 반복하지 않으려면 use를 활용합니다.

use crate::auth::login;
use crate::user::profile::show;

fn main() {
    login("yuna");
    show("yuna");
}
  • crate::는 현재 crate의 루트를 가리킵니다.
  • super::는 한 단계 상위 모듈을, self::는 현재 모듈을 가리킵니다.
  • use crate::user::{profile, list};처럼 중괄호를 사용하면 동일한 경로에서 여러 항목을 불러올 수 있습니다.

참고로 use는 경로를 짧게 쓰게 해 주는 가져오기일 뿐, 다른 모듈에 다시 공개하는 것은 아닙니다. 외부에 다시 노출하려면 pub use를 사용합니다.

공개 범위 설계하기

Rust 모듈 시스템의 핵심은 "무엇을 공개할지" 스스로 설계하는 것입니다.

  • pub(crate)으로 동일 crate 내부에만 공개할 수 있습니다.
  • pub(super)는 상위 모듈까지만 공개합니다.
  • 내부 구현(예: 데이터베이스 어댑터)은 비공개로 두고, 필요한 API만 pub으로 외부에 노출하세요.

Cargo workspace 살짝 맛보기

프로젝트가 커지면 여러 crate를 묶는 workspace가 필요할 수 있습니다. 이번 편에서는 구조만 가볍게 살펴봅니다.

[workspace]
  • 루트 Cargo.tomlworkspace를 선언하고, 각 멤버 crate는 자신의 Cargo.toml을 가집니다.
  • cargo build -p app처럼 특정 crate만 빌드할 수 있어 모듈 경계가 더 명확해집니다.

실습 과제

  1. auth, user, cli 세 모듈을 만들고, use를 활용해 경로를 간결하게 정리해 보세요.
  2. pub(crate)pub을 비교하며, 외부에 노출해도 되는 함수와 그렇지 않은 함수를 구분해 보세요.
  3. src/lib.rs를 만들고 간단한 library crate를 작성한 뒤, src/main.rs에서 use my_crate::... 형태로 불러오세요.

완료 기준

  • crate/package/module 관계를 말로 설명할 수 있다.
  • 파일 기반 모듈 분리를 직접 구성해 봤다.
  • use와 공개 범위를 조합해 API 표면을 정리해 봤다.

다음 편에서는 구조화된 코드 위에 제네릭과 트레이트를 얹어, 반복되는 로직을 타입 시스템 수준에서 재사용하는 방법을 살펴봅니다.

💬 댓글

이 글에 대한 의견을 남겨주세요