모듈을 여러 파일로 분리하기
지금까지 이 장의 예제들은 여러 모듈을 한 파일 안에 정의했습니다. 하지만 모듈이 커지면, 코드를 더 쉽게 탐색할 수 있도록 정의를 별도 파일로 옮기고 싶어질 수 있습니다.
예를 들어 여러 레스토랑 모듈이 있던 목록 7-17의 코드부터 시작해 봅시다. 이번에는 모든 모듈을 크레이트 루트 파일 안에 두지 않고, 각 모듈을 파일로 분리해 보겠습니다. 이 경우 크레이트 루트 파일은 src/lib.rs 이지만, 크레이트 루트가 src/main.rs 인 바이너리 크레이트에도 같은 절차를 적용할 수 있습니다.
먼저 front_of_house 모듈을 자체 파일로 분리합니다. front_of_house 모듈의
중괄호 안 코드를 제거하고 mod front_of_house; 선언만 남겨, src/lib.rs 가
목록 7-21의 코드만 담도록 만듭니다. 물론 목록 7-22의 src/front_of_house.rs
파일을 만들기 전까지는 이 코드는 컴파일되지 않습니다.
mod front_of_house;
pub use crate::front_of_house::hosting;
pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
}
front_of_house 모듈 선언그다음 중괄호 안에 있던 코드를 목록 7-22처럼 src/front_of_house.rs 라는 새 파일로
옮깁니다. 크레이트 루트에서 front_of_house 라는 이름의 모듈 선언을 봤기 때문에,
컴파일러는 이 파일에서 모듈 코드를 찾습니다.
pub mod hosting {
pub fn add_to_waitlist() {}
}
front_of_house 모듈 정의주의할 점은, 모듈 트리 안에서 어떤 파일을 mod 선언으로 한 번만 불러오면
충분하다는 것입니다. 컴파일러가 그 파일이 프로젝트 일부라는 것을 알고(그리고 mod
문이 어디에 있는지 덕분에 모듈 트리 안의 어느 위치에 속하는지도 알고) 나면, 프로젝트의
다른 파일에서는 “모듈 트리의 항목을 경로로 가리키기” 절에서
설명한 것처럼 선언된 위치를 가리키는 경로로 그 코드에 접근하면 됩니다. 다시 말해
mod 는 다른 언어에서 보았을 수도 있는 “include” 연산이 아닙니다.
이제 hosting 모듈도 별도 파일로 분리해 봅시다. 다만 이번 과정은 조금 다릅니다.
hosting 은 루트 모듈의 자식이 아니라 front_of_house 의 자식 모듈이기 때문입니다.
따라서 hosting 파일은 모듈 트리에서 그 조상 이름을 딴 새 디렉터리, 여기서는
src/front_of_house 안에 두게 됩니다.
hosting 을 옮기기 시작하려면, src/front_of_house.rs 를 hosting 모듈 선언만
남도록 바꿉니다.
pub mod hosting;
그다음 src/front_of_house 디렉터리와 hosting.rs 파일을 만들어 hosting 모듈에
있던 정의를 넣습니다.
pub fn add_to_waitlist() {}
만약 hosting.rs 를 src 디렉터리에 그냥 두었다면, 컴파일러는 그 코드를 크레이트
루트에서 선언된 hosting 모듈의 코드라고 기대했을 것입니다. front_of_house
자식으로 선언된 hosting 모듈의 코드라고는 보지 않습니다. 즉 어떤 모듈의 코드를
찾을 때 어떤 파일을 살펴볼지에 대한 컴파일러 규칙 덕분에, 디렉터리와 파일 구조가 모듈
트리와 더 가깝게 맞춰지게 됩니다.
대체 파일 경로
지금까지는 러스트 컴파일러가 사용하는 가장 관용적인 파일 경로를 설명했지만,
러스트는 예전 스타일의 파일 경로도 여전히 지원합니다. 크레이트 루트에서
front_of_house 라는 모듈이 선언되면, 컴파일러는 그 모듈 코드를 다음 두 곳에서
찾습니다.
- src/front_of_house.rs (우리가 다룬 방식)
- src/front_of_house/mod.rs (예전 스타일이지만 여전히 지원되는 경로)
front_of_house 의 자식 모듈인 hosting 에 대해서는 다음 두 경로를 찾습니다.
- src/front_of_house/hosting.rs (우리가 다룬 방식)
- src/front_of_house/hosting/mod.rs (예전 스타일이지만 여전히 지원되는 경로)
같은 모듈에 대해 두 스타일을 모두 사용하면 컴파일 오류가 납니다. 프로젝트 안에서 모듈마다 스타일을 섞어 쓰는 것은 허용되지만, 프로젝트를 탐색하는 사람에게는 다소 혼란스러울 수 있습니다.
mod.rs 파일명을 사용하는 스타일의 가장 큰 단점은, 프로젝트 안에 mod.rs 라는 이름의 파일이 너무 많이 생길 수 있어서, 에디터에서 동시에 여러 개를 열어 두면 헷갈릴 수 있다는 점입니다.
이제 각 모듈의 코드를 별도 파일로 옮겼지만, 모듈 트리는 그대로 유지됩니다.
정의가 다른 파일에 있더라도 eat_at_restaurant 안의 함수 호출은 수정 없이 그대로
동작합니다. 이 기법을 사용하면 모듈이 커질 때 새 파일로 옮겨 가며 구조를 유지할 수
있습니다.
또한 src/lib.rs 안의 pub use crate::front_of_house::hosting 문도 바뀌지 않았고,
use 는 어떤 파일이 크레이트 일부로 컴파일되는지에는 아무 영향도 주지 않는다는 점에도
주목하세요. 모듈을 선언하는 것은 mod 키워드이며, 러스트는 모듈과 같은 이름의 파일을
찾아 그 코드를 해당 모듈에 넣습니다.
정리
러스트는 패키지를 여러 크레이트로 나누고, 하나의 크레이트도 여러 모듈로 나눌 수
있게 해 줍니다. 그래서 한 모듈에 정의된 항목을 다른 모듈에서 참조할 수 있습니다.
이때 절대 경로나 상대 경로를 사용할 수 있고, use 문을 사용하면 같은 스코프 안에서
더 짧은 경로를 반복해서 사용할 수 있습니다. 모듈 코드는 기본적으로 private 이지만,
pub 키워드를 붙여 정의를 public 하게 만들 수 있습니다.
다음 장에서는, 이렇게 잘 정리한 코드 안에서 사용할 수 있는 표준 라이브러리의 몇 가지 컬렉션 자료구조를 살펴보겠습니다.