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

Control Scope and Privacy with Modules

이 절에서는 모듈과 모듈 시스템의 다른 구성 요소들을 다룹니다. 항목에 이름을 붙이는 경로(paths), 경로를 스코프로 가져오는 use 키워드, 그리고 항목을 공개로 만드는 pub 키워드가 그것입니다. 또한 as 키워드, 외부 패키지, 글롭 연산자도 함께 설명합니다.

모듈 치트 시트

모듈과 경로의 세부로 들어가기 전에, 여기서는 모듈, 경로, use 키워드, pub 키워드가 컴파일러 안에서 어떻게 동작하는지와 대부분의 개발자가 코드를 어떻게 정리하는지를 빠르게 훑어볼 수 있는 참조를 제공합니다. 이 장 전체에서 이 규칙들을 예제로 하나씩 살펴보겠지만, 여기 내용은 모듈이 어떻게 동작하는지 상기할 때 훌륭한 요약표가 됩니다.

  • 크레이트 루트에서 시작하기: 크레이트를 컴파일할 때 컴파일러는 먼저 크레이트 루트 파일(보통 라이브러리 크레이트는 src/lib.rs, 바이너리 크레이트는 src/main.rs) 에서 컴파일할 코드를 찾습니다.
  • 모듈 선언하기: 크레이트 루트 파일에서 새 모듈을 선언할 수 있습니다. 예를 들어 mod garden; 으로 “garden” 모듈을 선언한다고 합시다. 그러면 컴파일러는 다음 위치에서 그 모듈 코드를 찾습니다.
    • 세미콜론 대신 mod garden 뒤에 오는 중괄호 안 인라인 코드
    • src/garden.rs 파일
    • src/garden/mod.rs 파일
  • 하위 모듈 선언하기: 크레이트 루트가 아닌 어떤 파일에서도 하위 모듈을 선언할 수 있습니다. 예를 들어 src/garden.rs 에서 mod vegetables; 를 선언할 수 있습니다. 그러면 컴파일러는 부모 모듈 이름을 딴 디렉터리 안에서 하위 모듈 코드를 다음 위치에서 찾습니다.
    • 세미콜론 대신 mod vegetables 뒤에 오는 중괄호 안 인라인 코드
    • src/garden/vegetables.rs 파일
    • src/garden/vegetables/mod.rs 파일
  • 모듈 안 코드에 대한 경로: 어떤 모듈이 크레이트 일부가 되면, 같은 크레이트의 다른 곳 어디서든(공개 범위 규칙이 허용하는 한) 그 코드에 대한 경로를 사용해 접근할 수 있습니다. 예를 들어 garden의 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 이고, 다음 내용을 담고 있습니다.

Filename: 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 에서 찾은 코드를 포함하라고 알려 줍니다. 그 파일의 내용은 다음과 같습니다.

Filename: src/garden.rs
pub mod vegetables;

여기서 pub mod vegetables;src/garden/vegetables.rs 의 코드도 포함하라는 뜻입니다. 그 파일의 코드는 다음과 같습니다.

#[derive(Debug)]
pub struct Asparagus {}

이제 이 규칙들의 세부로 들어가 실제로 어떻게 동작하는지 살펴봅시다!

관련 코드를 모듈로 묶기

모듈(modules) 은 크레이트 안의 코드를 읽기 쉽고 재사용하기 쉽게 정리하게 해 줍니다. 또한 모듈은 항목의 공개 범위(privacy) 를 제어할 수 있게 해 줍니다. 모듈 안의 코드는 기본적으로 비공개이기 때문입니다. 비공개 항목은 외부에서 쓸 수 없는 내부 구현 세부입니다. 우리는 모듈과 그 안의 항목들을 공개로 만들 수도 있는데, 그러면 외부 코드가 그것들을 사용하고 의존할 수 있게 됩니다.

예제로, 레스토랑 기능을 제공하는 라이브러리 크레이트를 하나 작성해 봅시다. 여기서는 레스토랑 구현 자체보다 코드 조직에 집중하기 위해, 함수 시그니처만 정의하고 본문은 비워 두겠습니다.

레스토랑 업계에서는 보통 레스토랑의 일부를 프런트 오브 하우스(front of house), 다른 일부를 백 오브 하우스(back of house)라고 부릅니다. 프런트 오브 하우스 는 손님이 있는 공간입니다. 호스트가 손님을 자리로 안내하고, 서버가 주문과 계산을 받고, 바텐더가 음료를 만드는 곳이 여기에 포함됩니다. 백 오브 하우스 는 셰프와 요리사가 주방에서 일하고, 식기 세척 담당이 정리하고, 매니저가 행정 업무를 보는 공간입니다.

우리 크레이트도 이런 식으로 구성하려면, 기능을 중첩된 모듈들로 조직하면 됩니다. cargo new restaurant --lib 를 실행해 restaurant 라는 새 라이브러리를 만드세요. 그다음 src/lib.rs 에 목록 7-1의 코드를 넣어 몇 개의 모듈과 함수 시그니처를 정의합니다. 이 코드는 프런트 오브 하우스 부분에 해당합니다.

Filename: src/lib.rs
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() {}
    }
}
Listing 7-1: A front_of_house module containing other modules that then contain functions

모듈은 mod 키워드와 모듈 이름(여기서는 front_of_house)을 써서 정의합니다. 그 뒤 중괄호 안에 모듈 본문이 들어갑니다. 모듈 안에는 hosting, serving 처럼 다른 모듈도 넣을 수 있습니다. 또한 구조체, enum, 상수, 트레이트, 그리고 목록 7-1의 함수처럼 다른 항목 정의도 담을 수 있습니다.

모듈을 사용하면 서로 관련된 정의를 함께 묶고, 왜 서로 관련되어 있는지도 이름으로 표현할 수 있습니다. 이 코드를 사용하는 프로그래머는 모든 정의를 다 읽어야 하는 대신 그룹을 따라가며 코드를 탐색할 수 있어서, 자신에게 필요한 정의를 더 쉽게 찾을 수 있습니다. 또한 이 코드에 새 기능을 추가하는 프로그래머도 프로그램을 정리된 상태로 유지하려면 코드를 어디에 넣어야 하는지 쉽게 알 수 있습니다.

앞에서 src/main.rssrc/lib.rs크레이트 루트 라고 부른다고 했습니다. 이런 이름이 붙은 이유는, 이 두 파일 중 어느 하나의 내용이든 크레이트의 모듈 구조 맨 위에 있는 crate 라는 이름의 모듈을 이루기 때문입니다. 이 구조를 모듈 트리 라고 합니다.

목록 7-2는 목록 7-1 구조에 대한 모듈 트리를 보여 줍니다.

crate
 └── front_of_house
     ├── hosting
     │   ├── add_to_waitlist
     │   └── seat_at_table
     └── serving
         ├── take_order
         ├── serve_order
         └── take_payment
Listing 7-2: The module tree for the code in Listing 7-1

이 트리는 어떤 모듈이 다른 모듈 안에 중첩되는지 보여 줍니다. 예를 들어 hostingfront_of_house 안에 들어 있습니다. 또한 어떤 모듈은 서로 형제(siblings) 인데, 이는 같은 모듈 안에 정의되었다는 뜻입니다. hostingservingfront_of_house 안에 함께 정의된 형제 모듈입니다. 모듈 A가 모듈 B 안에 들어 있다면, A를 B의 자식(child) 이라고 하고 B를 A의 부모(parent) 라고 합니다. 그리고 전체 모듈 트리는 암묵적인 crate 모듈 아래에 뿌리를 두고 있다는 점에 주목하세요.

모듈 트리는 컴퓨터의 파일시스템 디렉터리 트리를 떠올리게 할 수도 있습니다. 그리고 그 비유는 아주 적절합니다! 파일시스템에서 디렉터리로 파일을 정리하듯, 우리는 모듈을 사용해 코드를 정리합니다. 또한 디렉터리 안 파일을 찾아야 하듯, 모듈도 찾아낼 수 있는 방법이 필요합니다.