🦀
모던 Rust
모던 · 선수: 에러 처리 1~4강
19. 모듈 시스템·Cargo·crates.io
Rust 프로젝트가 커지면 코드를 모듈로 나누고 외부 크레잇을 끌어다 씁니다. 이 강의에서 mod 키워드와 파일 분할 규칙, pub 가시성, use 경로, Cargo.toml 의 의존성 명시, 그리고 crates.io 에서 패키지를 찾고 추가하는 흐름까지 한 번에 정리합니다.
RustmoduleCargocratecrates.iopubuse
소요 시간
⏱ 약 1.5시간
난이도
📊 중급
선수 조건
🎯 에러 처리 1~4강
결과물
Rust 프로젝트가 커지면 코드를 모듈로 나누고 외부 크레잇을 끌어다 씁니다. 이 강의에서 mod 키워드와 파일 분할 규칙, pub 가시성, use 경로, Cargo.toml 의 의존성 명시, 그리고 crates.io 에서 패키지를 찾고 추가하는 흐름까지 한 번에 정리합니다.
이 강의에서 배우는 것
- 1mod 로 코드를 모듈로 분할한다
- 2pub 키워드로 가시성을 제어한다
- 3use 로 경로를 짧게 줄인다
- 4Cargo.toml 의 [dependencies] 에 외부 크레잇을 추가한다
- 5cargo add / cargo search / cargo doc 으로 패키지 작업을 한다
소개
main.rs 한 파일에 모든 코드를 몰아 두면 100줄을 넘기는 순간 가독성이 빠르게 떨어집니다. Rust 는 한 파일 = 한 모듈 식의 간단한 규칙으로 코드 분할을 강제 없이 자연스럽게 풀어줍니다.
핵심 개념
1) crate / module 구분
- **crate** — Cargo.toml 한 개 단위 (== 패키지 = 빌드 단위)
- **module** — 한 crate 안의 네임스페이스 단위
- binary crate 의 root = `src/main.rs`, library crate 의 root = `src/lib.rs`
2) mod 와 파일 분할
rust
// src/main.rs
mod math; // math.rs 또는 math/mod.rs 를 찾음
fn main() {
println!("{}", math::add(2, 3));
}
// src/math.rs
pub fn add(a: i32, b: i32) -> i32 { a + b }3) pub — 가시성 제어
- 기본은 **private** (같은 모듈 안에서만)
- `pub` — 외부 공개
- `pub(crate)` — 같은 crate 안에서만 공개
- `pub(super)` — 부모 모듈에만
4) use — 경로 단축
rust
use std::collections::HashMap;
use std::io::{self, Read}; // self = io 자체, Read = trait
use crate::math::add as plus; // 별칭5) Cargo.toml 의존성
toml
[dependencies]
serde = { version = "1", features = ["derive"] }
tokio = { version = "1", features = ["full"] }
thiserror = "1"핵심 예제
디렉토리 구조 예:
bash
src/
├── main.rs
├── config.rs
└── api/
├── mod.rs (또는 src/api.rs)
├── user.rs
└── post.rsrust
// src/main.rs
mod config;
mod api;
fn main() {
let cfg = config::load();
api::user::create(&cfg);
}
// src/api/mod.rs (또는 src/api.rs + src/api/ 폴더)
pub mod user;
pub mod post;
// src/api/user.rs
use crate::config::Config;
pub fn create(cfg: &Config) { /* ... */ }외부 크레잇 추가·사용:
bash
cargo add serde --features derive
cargo add chrono
cargo search regex # crates.io 검색rust
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug)]
struct User { name: String, age: u32 }
fn main() {
let u = User { name: "홍길동".into(), age: 30 };
let json = serde_json::to_string(&u).unwrap();
println!("{}", json);
}자주 하는 실수
Q. mod 와 use 의 차이가 뭐죠?
A. **mod** 는 모듈을 "존재시키는" 선언, **use** 는 이미 존재하는 경로를 "짧게 가져오는" 선언입니다. mod foo; 를 안 쓰면 use crate::foo 도 못 합니다.
Q. pub 안 붙이면 같은 파일에서도 못 쓰나요?
A. 같은 모듈(=같은 파일·같은 mod 블록) 안에서는 pub 없이도 접근 가능. **외부 모듈에서 접근하려면 pub 필수**.
Q. Cargo.lock 은 git 에 올리나요?
A. **바이너리 crate 는 올리고, 라이브러리 crate 는 올리지 않습니다.** 바이너리는 재현 가능한 빌드가 중요하고, 라이브러리는 사용자의 lockfile 이 별도라 의미가 없습니다.
정리
- crate = 빌드 단위, module = 네임스페이스 단위
- mod 로 선언, 파일/폴더 규칙으로 자동 매핑
- pub / pub(crate) / pub(super) 로 가시성 제어
- use 로 경로 단축, Cargo.toml 에 의존성, cargo add 로 추가
과제
- main.rs + math.rs 로 분할하고 main 에서 math::add 호출
- src/api/ 폴더에 user.rs / post.rs 모듈을 만들고 main 에서 사용
- cargo add chrono 로 chrono 추가 후 현재 시각을 출력