프로젝트가 커지면 커질수록 방대해지는 코드를 효율적으로 관리하는 것은 아주 중요합니다. Rust에서 이러한 코드 조직화를 다루기 위해 제공하는 크레이트 / 패키지 / 모듈 개념에 대한 내용을 정리해 보겠습니다.
main 함수를 포함하고 있어야 한다. → 여태껏 만들어 본 모든 크레이트는 바이너리 크레이트였음.main 함수를 가지고 있지 않고, 실행파일의 형태로 컴파일되지 않는다.src/lib.rs 가 크레이트 루트 & Binary crate → src/main.rs 가 크레이트 루트.Cargo.toml 파일 포함cargo new 명령어 → 패키지를 만든다.$ cargo new my-project
Created binary (application) `my-project` package
$ ls my-project
Cargo.toml
src
$ ls my-project/src
main.rsmy-project라는 이름의 바이너리 크레이트만으로 구성되어 있습니다. 만약 어떤 패키지가 src/main.rs와 src/lib.rs를 가지고 있다면 해당 패키지는 패키지와 같은 이름의 바이너리, 라이브러리 크레이트를 포함하게 됩니다. (즉, 한 패키지에 크레이트 2개!) src/bin 디렉터리 내에 파일을 배치하면 각각의 파일이 바이너리 크레이트가 되어, 여러 바이너리 크레이트를 패키지에 포함할 수 있습니다.https://doc.rust-kr.org/ch07-02-defining-modules-to-control-scope-and-privacy.html#모듈-치트-시트
mod garden;이라는 코드로 ‘garden’ 모듈을 선언할 수 있습니다. 컴파일러는 아래의 장소에서 이 모듈의 코드가 있는지 찾는다.mod garden 뒤에 세미콜론 대신 중괄호를 써서 안쪽에 코드를 적은 인라인mod vegetables; 를 선언할 수 있다.) → 아래의 장소에서 찾는다.mod vegetables 뒤에 세미콜론 대신 중괄호를 써서 안쪽에 코드를 적은 인라인Asparagus 타입은 crate::garden::vegetables::Asparagus로 찾아 쓸 수 있습니다.mod 대신 pub mod를 써서 선언하세요. 공개 모듈의 아이템들을 공개하려면 마찬가지로 그 선언 앞에 pub을 붙이세요.use 키워드: 어떤 스코프 내에서 use 키워드는 긴 경로의 반복을 줄이기 위한 어떤 아이템으로의 단축경로를 만들어 줍니다. crate::garden::vegetables::Asparagus를 참조할 수 있는 모든 스코프에서 use crate::garden::vegetables::Asparagus;로 단축경로를 만들 수 있으며, 그 이후부터는 스코프에서 이 타입을 사용하려면 Asparagus만 작성해주면 됩니다. 위의 규칙들을 보여주는 backyard라는 이름의 바이너리 크레이트를 만들어 보았습니다. 디렉터리명 또한 backyard로서, 아래의 파일들과 디렉터리들로 구성되어 있습니다.
backyard
├── Cargo.lock
├── Cargo.toml
└── src
├── garden
│ └── vegetables.rs
├── garden.rs
└── main.rs
지금의 경우 크레이트 루트 파일은 src/main.rs이고, 내용은 아래와 같습니다:
파일명: src/main.rs
use crate::garden::vegetables::Asparagus;
pub mod garden;
fn main() {
let plant = Asparagus {};
println!("I'm growing {:?}!", plant);
}
pub mod garden; 라인이 컴파일러에게 src/garden.rs에 있는 코드를 포함할 것을 알려주고, src/garden.rs는 아래와 같습니다:
파일명: src/garden.rs
pub mod vegetables;
여기 pub mod vegetables;은 src/garden/vegetables.rs의 코드 또한 포함되어야 함을 의미합니다. 해당 파일의 코드는 아래와 같습니다:
#[derive(Debug)]
pub struct Asparagus {}
이제 위 규칙들의 세부 사항으로 넘어가서 실제로 해보면서 확인합시다!
mod front_of_house {
mod hosting {
fn add_to_waitlist() {}
fn seat_at_table() {}
}
mod serving {
fn take_order() {}
fn serve_order() {}
fn take_payment() {}
}
}
→ 모듈 예시!
mod 키워드와 모듈 이름(위의 경우 front_of_house)을 지정하여 모듈을 정의합니다.hosting, serving 모듈처럼, 모듈 내에는 다른 모듈을 넣을 수 있습니다.위 예시를 트리 구조로 표현하면,
crate
└── front_of_house
├── hosting
│ ├── add_to_waitlist
│ └── seat_at_table
└── serving
├── take_order
├── serve_order
└── take_payment
crate라는 모듈이 암묵적으로 위치한다는 점을 기억해 두세요.crate라는 이름을 갖는 일종의 모듈로 형성되기 때문입니다.경로는 두 가지 형태가 존재합니다.
crate 리터럴로부터 시작됩니다.self, super 혹은 현재 모듈 내의 식별자를 사용합니다.절대 경로, 상대 경로 뒤에는 ::으로 구분된 식별자가 하나 이상 따라옵니다.
mod front_of_house {
mod hosting {
fn add_to_waitlist() {}
}
}
pub fn eat_at_restaurant() {
// 절대 경로
crate::front_of_house::hosting::add_to_waitlist();
// 상대 경로
front_of_house::hosting::add_to_waitlist();
}
→ 이 코드는 에러가 발생합니다! 에러 메시지는 hosting 모듈이 비공개 (private) 라는 내용입니다. hosting 모듈과 add_to_waitlist 함수의 경로를 정확히 명시했지만, 해당 영역은 비공개 영역이기 때문에 러스트가 접근을 허용하지 않습니다.
pub 키워드를 사용하여 자식 모듈의 내부 구성 요소를 공개 (public) 함으로써 외부의 상위 모듈로 노출할 방법을 제공합니다.mod front_of_house {
pub mod hosting {
fn add_to_waitlist() {}
}
}
pub fn eat_at_restaurant() {
// 절대 경로
crate::front_of_house::hosting::add_to_waitlist();
// 상대 경로
front_of_house::hosting::add_to_waitlist();
}
→ 앞의 코드와 비교했을 때, hosting 모듈에 pub 키워드를 추가했습니다. 하지만 이 코드 또한 에러가 발생합니다. 왜 그런 걸까요?
hosting 모듈에 pub을 붙여서 모듈이 공개되었으므로, 이제 front_of_house 에 접근할 수 있다면 hosting 에도 접근할 수 있게 되었지만, hosting 모듈의 내용은 여전히 비공개입니다. 모듈을 공개했다고 해서 모듈의 내용까지 공개되지는 않습니다.
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
pub fn eat_at_restaurant() {
// 절대 경로
crate::front_of_house::hosting::add_to_waitlist();
// 상대 경로
front_of_house::hosting::add_to_waitlist();
}
→ 이렇게 add_to_waitlist 함수도 정의에 pub 키워드를 추가해서 공개해야 합니다. 이제서야 우리는 코드를 컴파일할 수 있게 되었습니다!
추가적으로,
pub 키워드로 공개해주지 않아도 접근할 수 있습니다.pub 키워드로 외부에 공개되었다면, “해당 대상의 부모에게 접근할 수 있을 때, 해당 대상에게도 접근할 수 있다” 라고 핵심을 정리할 수 있겠습니다.super 로 시작하면 현재 모듈 혹은 크레이트 루트 대신 자기 부모 모듈부터 시작되는 경로를 만들 수 있습니다.pub를 쓰면 구조체는 공개되지만, 구조체의 필드는 비공개로 유지됩니다.use 키워드를 한번 사용하여 어떤 경로의 단축경로 (shortcut) 를 만들 수 있고, 그러면 스코프 안쪽 어디서라도 짧은 이름을 사용할 수 있습니다.use 키워드와 경로를 작성하는 건 파일 시스템에서 심볼릭 링크 (symbolic link) 를 생성하는 것과 유사합니다.use 키워드로 가져온 경우도 다른 경로와 마찬가지로 비공개 규칙이 적용됩니다.mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
use crate::front_of_house::hosting;
pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
}
→ eat_at_restaurant 함수 내에서 add_to_waitlist 함수를 hosting::add_to_waitlist 경로만으로 호출하는 예제입니다.
use가 사용된 특정한 스코프에서만 단축경로가 만들어진다는 점을 주의하세요. use 구문으로 만들어진 단축 경로가 적용되지 않습니다.https://doc.rust-kr.org/ch07-00-managing-growing-projects-with-packages-crates-and-modules.html