DaleSchool

코드 정리하기: 모듈과 크레이트

중급15분

학습 목표

  • mod로 모듈을 정의하고 use로 가져올 수 있다
  • pub 키워드로 공개 범위를 제어할 수 있다
  • Cargo.toml에 외부 크레이트를 추가하고 사용할 수 있다
  • crates.io에서 필요한 크레이트를 찾을 수 있다

동작하는 코드

코드가 점점 길어지면, 하나의 파일에 모든 걸 넣기 어려워져요. 모듈(module) 로 코드를 정리할 수 있어요.

mod math {
    pub fn add(a: i32, b: i32) -> i32 {
        a + b
    }

    pub fn multiply(a: i32, b: i32) -> i32 {
        a * b
    }

    fn secret_formula(x: i32) -> i32 {
        x * 42  // 비공개 함수!
    }
}

fn main() {
    let sum = math::add(3, 5);
    let product = math::multiply(4, 6);
    println!("3 + 5 = {sum}");
    println!("4 x 6 = {product}");
    // math::secret_formula(1); // 에러! 비공개
}

mod math로 모듈을 만들고, math::add()처럼 경로를 써서 호출해요.

직접 수정하기

  1. math::secret_formula(1) 주석을 풀어보세요. 어떤 에러가 나나요?
  2. secret_formulapub을 붙이면 에러가 사라지나요?

"왜?" — pub과 비공개 기본값

Rust에서 모듈 안의 항목은 기본적으로 비공개예요. 외부에서 사용하려면 pub을 명시적으로 붙여야 해요.

이건 소유권 철학과 같은 맥락이에요 — 안전이 기본값이에요. 필요한 것만 공개하고, 나머지는 숨기는 게 좋은 습관이에요.

| 키워드 | 의미 | | ------ | ------------------------------------- | | (없음) | 비공개 — 같은 모듈 안에서만 접근 가능 | | pub | 공개 — 외부에서도 접근 가능 |

use — 경로를 짧게

math::add() 대신 더 짧게 쓰고 싶으면 use를 사용해요.

mod math {
    pub fn add(a: i32, b: i32) -> i32 {
        a + b
    }

    pub fn multiply(a: i32, b: i32) -> i32 {
        a * b
    }
}

use math::add;
use math::multiply;

fn main() {
    println!("3 + 5 = {}", add(3, 5));
    println!("4 x 6 = {}", multiply(4, 6));
}

여러 개를 한 번에 가져올 수도 있어요.

use math::{add, multiply};

모듈을 파일로 분리하기

코드가 더 커지면 모듈을 별도 파일로 분리해요. 실제 프로젝트에서 가장 많이 쓰는 패턴이에요.

src/
├── main.rs
└── math.rs

src/math.rs:

pub fn add(a: i32, b: i32) -> i32 {
    a + b
}

pub fn multiply(a: i32, b: i32) -> i32 {
    a * b
}

src/main.rs:

mod math;  // math.rs 파일을 모듈로 가져옴

fn main() {
    println!("3 + 5 = {}", math::add(3, 5));
}

mod math;만 쓰면 Rust가 알아서 math.rs 파일을 찾아서 연결해줘요.

외부 크레이트 사용하기

모듈 07에서 rand 크레이트를 사용한 적 있죠? 외부 크레이트는 crates.io에서 찾을 수 있어요.

cargo add rand

이 명령어가 Cargo.toml[dependencies]에 자동으로 추가해줘요.

[dependencies]
rand = "0.8"

그 다음 use로 가져와서 사용하면 돼요.

use rand::Rng;

fn main() {
    let mut rng = rand::thread_rng();
    let number: i32 = rng.gen_range(1..=100);
    println!("랜덤 숫자: {number}");
}

표준 라이브러리 모듈

Rust에는 자주 쓰는 기능이 표준 라이브러리(std) 에 들어있어요. 별도 설치 없이 use로 가져오면 돼요.

use std::collections::HashMap;

fn main() {
    let mut scores = HashMap::new();
    scores.insert("민수", 95);
    scores.insert("지수", 88);

    for (name, score) in &scores {
        println!("{name}: {score}점");
    }
}

자주 쓰는 표준 라이브러리 모듈이에요.

| 모듈 | 용도 | | ------------------ | -------------------- | | std::io | 입출력 | | std::collections | HashMap, BTreeMap 등 | | std::fs | 파일 읽기/쓰기 | | std::fmt | 포맷팅 |

더 알아보기: crates.io에서 크레이트 찾기

crates.io는 Rust 패키지 저장소예요. 수만 개의 크레이트가 있어요.

인기 있는 크레이트 몇 가지를 소개할게요.

| 크레이트 | 용도 | | --------- | ------------------------------ | | serde | JSON 등 데이터 직렬화/역직렬화 | | tokio | 비동기 런타임 | | clap | 커맨드라인 인자 파싱 | | reqwest | HTTP 요청 |

크레이트를 고를 때는 다운로드 수최근 업데이트 날짜를 확인하세요. 활발히 관리되는 크레이트를 사용하는 게 좋아요.

더 알아보기: 모듈 경로와 crate 키워드

모듈 경로에는 두 가지 시작점이 있어요.

// 절대 경로: crate부터 시작
crate::math::add(1, 2);

// 상대 경로: 현재 위치부터
math::add(1, 2);

// 부모 모듈 참조
super::some_function();

crate는 현재 프로젝트의 루트를 가리키는 키워드예요. 큰 프로젝트에서 경로가 복잡해지면 절대 경로가 명확해요.

연습 1. geometry 모듈을 만들고, circle_arearectangle_area 함수를 추가해보세요.

mod geometry {
    // circle_area: 반지름을 받아서 원의 넓이 반환
    // rectangle_area: 가로, 세로를 받아서 직사각형 넓이 반환
    // 둘 다 pub으로!
    // 힌트: PI는 std::f64::consts::PI
}

fn main() {
    println!("원: {:.2}", geometry::circle_area(5.0));
    println!("사각형: {:.2}", geometry::rectangle_area(4.0, 6.0));
}

연습 2. use를 사용해서 아래 코드를 더 짧게 바꿔보세요.

fn main() {
    let mut map = std::collections::HashMap::new();
    map.insert("a", 1);
    map.insert("b", 2);

    let mut set = std::collections::HashSet::new();
    set.insert("x");
    set.insert("y");

    println!("{:?}", map);
    println!("{:?}", set);
}

힌트: use std::collections::{HashMap, HashSet};

Q1. Rust에서 모듈 안의 함수는 기본적으로?

  • A) 공개(public)이다
  • B) 비공개(private)이다
  • C) 읽기 전용이다
  • D) 상수(const)이다

Q2. 외부 크레이트를 프로젝트에 추가하는 명령어는?

  • A) cargo install 크레이트
  • B) cargo add 크레이트
  • C) cargo get 크레이트
  • D) cargo import 크레이트

Q3. 아래 코드에서 에러가 나는 이유는?

mod secrets {
    fn hidden() -> i32 { 42 }
}

fn main() {
    println!("{}", secrets::hidden());
}
  • A) mod 문법이 잘못되었다
  • B) hidden 함수가 비공개이다
  • C) 반환 타입이 잘못되었다
  • D) secrets 모듈이 존재하지 않는다