모듈 가져오기와 가시성
러스트에서 모듈은 코드를 체계적으로 분리하고 재사용하기 쉽게 만드는 핵심 요소다. 모듈을 사용하면 서로 다른 영역에 존재하는 함수, 구조체, 열거형, 상수 등을 분리해둘 수 있고, 이를 가져와서 사용할 때는 경로(path) 개념과 가시성(visibility) 규칙이 적용된다. 이 과정에서 mod, use, pub 등의 키워드를 주로 활용한다. 코드를 관리하기 위해 하나의 파일 안에 여러 모듈을 둘 수도 있고, 여러 파일로 분산해 작성할 수도 있다. 이러한 구조적 설계를 올바르게 이해하면 대규모 프로젝트에서도 코드 충돌을 최소화하면서 명확하게 구성할 수 있다.
모듈 선언은 보통 mod 키워드를 사용하여 이뤄진다. 예를 들어 한 파일 내부에서 mod network와 같이 선언하면 network라는 하위 모듈이 정의된다. 그리고 이 모듈 내의 항목들(함수, 구조체 등)은 디폴트로 현재 모듈 범위 안에서만 접근 가능하다. 만약 모듈 내부의 함수나 구조체를 외부 모듈에서 호출하거나 인스턴스화하고 싶다면, 해당 항목을 pub 키워드로 공개해야 한다. 또한 모듈 간 경로는 절대 경로와 상대 경로로 나눌 수 있다. 예컨대, 최상위 크레이트로부터 지정하고 싶다면 crate::network::connect와 같은 절대 경로를 사용할 수 있고, 현재 모듈에서 상위 모듈로 올라가거나 옆에 있는 다른 모듈의 항목을 참조할 때는 super::something이나 self::something 혹은 super::super와 같은 상대 경로 접근을 활용한다.
가시성은 pub 키워드로 공개 범위를 조절할 수 있다. 가장 단순하게는 pub fn example() {}처럼 선언하면 다른 모듈에서 example() 함수를 부를 수 있다. 이때 pub 키워드를 붙이지 않으면 기본적으로 해당 항목은 현재 모듈 내에서만 접근할 수 있는 사적(private) 상태가 된다. 러스트는 좀 더 세부적인 가시성 범위를 지정하기 위해 pub(crate)나 pub(in some::path) 같은 문법도 제공한다. 예를 들어 pub(crate)로 선언하면 동일한 크레이트 내의 어느 모듈에서도 그 항목에 접근할 수 있다. 반면 pub(in some::path) 방식은 특정 모듈 경로 내부에서만 공개하고, 그 범위를 벗어나면 숨길 수 있다. 이처럼 세분화된 제어를 통해 실제 필요에 맞게 접근 범위를 한정하여 모듈 간 결합도를 낮출 수 있다.
모듈에서 정의된 항목을 재노출(re-export)하고 싶은 경우에는 pub use 구문을 많이 활용한다. 예컨대 어떤 하위 모듈에 있는 함수를 상위 모듈의 다른 이름으로 공개하고 싶다면 다음과 같이 작성할 수 있다.
pub use crate::network::connect as public_connect;이처럼 pub use를 이용하면 하위 모듈의 구현을 상위 모듈에서 명시적으로 노출하고 재정의된 경로를 제공할 수 있다. 내부 구조 변경 시 외부에 노출되는 인터페이스를 최소화하면서 모듈 구조를 유연하게 바꿀 수 있도록 도와준다.
파일 분할을 통한 모듈화에서는 mod my_module; 선언이 현재 파일에서 하위 모듈을 참조함을 의미하고, 러스트는 통상적으로 같은 디렉터리 내의 my_module.rs 파일이나 my_module/mod.rs 파일을 찾아서 그 안의 내용을 읽어온다. 예시로 src/main.rs에서 mod network;를 선언해두면, 컴파일러는 src/network.rs 파일을 자동으로 찾는다. 그리고 실제 사용 시에는 network::connect()처럼 모듈 경로를 타고 들어가서 함수를 호출할 수 있다. 만약 network 모듈 내에 pub fn connect()가 아니라 일반 fn connect()라고만 되어 있다면, 해당 함수는 network 모듈 내부에서만 접근 가능하다. 이렇게 다른 파일로 분리해 두면 모듈 단위로 컴파일되고 테스트 작성도 독립적으로 진행할 수 있어 유지보수가 한결 수월해진다.
use 키워드는 모듈 이름이나 경로를 가져와 현재 스코프로 단축하는 역할을 한다. 만약 crate::network::connect 함수를 자주 쓴다면, 매번 풀 경로로 호출하는 대신 다음과 같이 use 키워드를 이용할 수 있다.
use crate::network::connect;
fn main() {
connect();
}이렇게 두면 connect() 함수의 경로를 풀어서 반복 선언할 필요가 줄어든다. 물론 모듈 전체를 가져오거나 구조체, 열거형 멤버 등을 함께 가져올 수도 있다. 그러나 필요 이상의 심볼을 한 번에 스코프에 풀어놓으면 가독성 면에서 불리해질 수 있으므로 적절한 범위 내에서 쓰는 것이 좋다.
구현 관점에서 모듈 시스템을 이해할 때 가장 중요한 점은 러스트가 기본적으로 '은닉'을 원칙으로 삼는다는 것이다. 즉, 각 모듈은 스스로 관리하는 영역이 있으며, 이를 다른 모듈에서 사용하기 위해서는 가시성 키워드를 통해 명시적으로 공개해야 한다. 이러한 구조로 인해 의도치 않은 네임 충돌이나 API 혼선을 피하면서도, 필요한 것만 안전하게 외부에 내보낼 수 있다. 모듈 가져오기와 가시성 설정을 적절히 활용하면 프로젝트 전반을 더 유연하고 확장 가능하게 만들 수 있다.
Last updated