1
프로젝트 구조 이해하기
이번에는 Rust 프로젝트의 구조와 모듈화 방법에 대해 알아보겠습니다. 프로젝트의 구조를 잘 이해하고 모듈화를 효과적으로 하면 코드 관리와 재사용성이 높아져 생산성 향상을 기대할 수 있습니다.
Rust 프로젝트는 기본적으로 Cargo라는 빌드 도구와 패키지 매니저에 의해 관리됩니다. Cargo를 사용하면 프로젝트 생성, 종속성 관리, 빌드, 테스트 등의 작업을 편리하게 수행할 수 있습니다.
Cargo로 프로젝트 생성하기
Cargo를 이용하면 간단한 명령어로 프로젝트를 생성할 수 있습니다.
cargo new my_project
이 명령을 실행하면 my_project 디렉터리와 기본 파일들이 생성됩니다. 주요 파일의 역할은 다음과 같습니다.
- Cargo.toml: 프로젝트 메타데이터와 종속성 정보를 저장하는 매니페스트 파일
- src/main.rs: 프로젝트의 진입점인 main 함수가 포함된 소스 파일
- src/lib.rs: 라이브러리 코드를 작성하는 소스 파일
프로젝트 디렉터리 구조는 다음과 같습니다.
my_project/
├── Cargo.lock
├── Cargo.toml
├── src/
│ ├── lib.rs
│ └── main.rs
└── target/
target 디렉터리는 빌드 결과물이 저장되는 곳입니다. Cargo.lock 파일은 종속성 버전 정보를 저장합니다.
모듈과 파일 구조
Rust에서 모듈은 코드를 구조화하고 범위를 제어하는 방법입니다. 모듈을 통해 코드를 논리적으로 구분할 수 있으며, 은닉화와 재사용성을 높일 수 있습니다.
src/main.rs와 src/lib.rs 파일은 각각 이진 실행 파일과 라이브러리를 만들기 위한 모듈입니다. 이 외에도 필요에 따라 다른 모듈을 추가할 수 있습니다.
예를 들어, 프로젝트에 utils 모듈을 추가해 보겠습니다.
// src/lib.rs
pub mod utils;
// src/utils.rs
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
src/lib.rs 파일에서 pub mod utils; 라인을 추가하여 utils 모듈을 선언합니다. 그런 다음 src/utils.rs 파일을 생성하고 모듈 내용을 작성합니다.
모듈 안의 항목을 외부에 노출하려면 pub 키워드를 사용해야 합니다. 이렇게 하면 add 함수를 라이브러리 외부에서 사용할 수 있습니다.
모듈은 디렉터리 구조와도 매핑될 수 있습니다. 예를 들어, utils 모듈 안에 math 하위 모듈을 추가해 보겠습니다.
my_project/
├── ...
└── src/
├── lib.rs
├── main.rs
├── utils/
│ ├── math.rs
│ └── mod.rs
└── ...
utils/mod.rs 파일에서 하위 모듈을 선언합니다.
// src/utils/mod.rs
pub mod math;
그리고 utils/math.rs 파일에 모듈 내용을 작성합니다.
// src/utils/math.rs
pub fn square(x: i32) -> i32 {
x * x
}
이제 utils::math 모듈의 square 함수를 사용할 수 있습니다.
// src/main.rs
use my_project::utils::math;
fn main() {
println!("2^2 = {}", math::square(2));
}
이처럼 Rust에서는 모듈을 효과적으로 구조화하여 코드를 관리할 수 있습니다. 모듈화를 통해 코드 재사용성과 유지보수성이 높아집니다.
모듈 시스템 이해하기
Rust의 모듈 시스템은 코드를 구조화하고 범위를 제어하는 강력한 도구입니다. 모듈 시스템을 효과적으로 활용하면 코드 재사용성과 유지보수성이 크게 향상됩니다. 이번에는 Rust의 모듈 시스템에 대해 더 자세히 알아보겠습니다.
모듈과 크레이트
Rust에서 크레이트(crate)는 라이브러리나 실행 파일을 만드는 최소 단위입니다. 크레이트는 하나 이상의 모듈로 구성됩니다.
모듈은 네임스페이스를 제공하여 코드를 구조화하고 범위를 제어합니다. 모듈은 다른 모듈 내부에 중첩될 수 있으며, 이를 통해 계층적인 구조를 만들 수 있습니다.
// 최상위 모듈
mod utils {
// 하위 모듈
mod math {
// 함수
pub fn square(x: i32) -> i32 {
x * x
}
}
// 함수
pub fn greet(name: &str) {
println!("Hello, {}!", name);
}
}
// 최상위 모듈에서 utils::math::square 함수 사용
fn main() {
println!("2^2 = {}", utils::math::square(2));
}
이 예제에서 utils는 최상위 모듈이며, math는 utils 모듈 내부의 하위 모듈입니다. square 함수는 math 모듈 내에 정의되어 있습니다.
모듈 가시성 제어
모듈의 항목을 외부에 노출하려면 pub 키워드를 사용해야 합니다. 가시성이 제한된 항목은 외부에서 접근할 수 없습니다.
mod utils {
pub mod math {
pub fn square(x: i32) -> i32 {
x * x
}
fn cube(x: i32) -> i32 {
x * x * x
}
}
pub fn greet(name: &str) {
println!("Hello, {}!", name);
}
}
fn main() {
println!("2^2 = {}", utils::math::square(2)); // OK
println!("2^3 = {}", utils::math::cube(2)); // Error: cube 함수는 private
}
이 예제에서 math::cube 함수는 pub로 표시되지 않았기 때문에 외부에서 접근할 수 없습니다.
모듈 내에서는 자신의 모든 항목에 접근할 수 있지만, 다른 모듈의 private 항목에는 접근할 수 없습니다. 이를 통해 캡슐화와 정보 은닉이 가능해집니다.
모듈 경로와 use 문
모듈 항목에 접근하려면 경로 표기법을 사용해야 합니다. 예를 들어, utils::math::square(2)는 utils 모듈 내부의 math 모듈에 정의된 square 함수를 호출합니다.
use 문을 사용하면 모듈 항목을 간편하게 가져올 수 있습니다.
use utils::math::square;
fn main() {
println!("2^2 = {}", square(2)); // utils::math::square 함수를 직접 호출
}
use 문을 통해 모듈 항목을 가져오면 경로 표기법 없이 직접 사용할 수 있습니다.
중첩된 모듈의 경우 다음과 같이 use 문을 작성할 수 있습니다.
use utils::{
math::{square, cube},
greet,
};
fn main() {
println!("2^2 = {}", square(2));
println!("2^3 = {}", cube(2));
greet("Alice");
}
이처럼 use 문을 활용하면 코드를 간결하게 유지할 수 있습니다.
모듈 트레이트 구현
Rust에서 트레이트는 공유 동작을 정의하는 인터페이스 역할을 합니다. 모듈 내에서 트레이트를 구현할 수 있습니다.
trait Greet {
fn greet(&self) -> String;
}
mod person {
use super::Greet;
pub struct Person {
pub name: String,
}
impl Greet for Person {
fn greet(&self) -> String {
format!("Hello, {}!", self.name)
}
}
}
fn main() {
let alice = person::Person { name: "Alice".to_string() };
println!("{}", alice.greet());
}
이 예제에서 person 모듈은 Greet 트레이트를 구현합니다. use super::Greet; 문을 사용하여 상위 모듈에 정의된 Greet 트레이트를 가져옵니다.
person 모듈 외부에서는 Person 구조체와 Greet 트레이트 구현을 사용할 수 있습니다.
이처럼 모듈을 사용하면 코드를 효과적으로 구조화하고 캡슐화할 수 있습니다. 코드 재사용성과 유지보수성이 향상되며, 대규모 프로젝트를 더욱 체계적으로 관리할 수 있습니다.
크레이트와 종속성 관리
크레이트(crate)는 Rust에서 컴파일 단위를 나타내는 용어입니다. 크레이트는 바이너리 실행 파일이나 라이브러리가 될 수 있습니다.
이번에는 크레이트와 종속성 관리에 대해 알아보겠습니다. Rust 프로젝트에서 외부 라이브러리를 사용하거나 자신의 라이브러리를 다른 프로젝트에서 사용하려면 크레이트와 종속성 관리를 이해해야 합니다.
크레이트 종류
Rust에는 세 가지 종류의 크레이트가 있습니다.
- 바이너리 크레이트(Binary Crate): 실행 파일을 생성하는 크레이트입니다. src/main.rs 파일을 포함합니다.
- 라이브러리 크레이트(Library Crate): 다른 프로젝트에서 사용할 수 있는 라이브러리를 생성하는 크레이트입니다. src/lib.rs 파일을 포함합니다.
- 프로시저 매크로 크레이트(Procedural Macro Crate): 컴파일 시간에 코드를 생성하는 매크로를 정의하는 크레이트입니다.
대부분의 경우 바이너리 크레이트와 라이브러리 크레이트를 주로 사용합니다.
Cargo.toml과 종속성 관리
Cargo.toml은 Rust 프로젝트의 매니페스트 파일로, 프로젝트 정보와 종속성 정보를 포함합니다. 이 파일을 통해 크레이트 간의 종속성을 관리할 수 있습니다.
[package]
name = "my_project"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2021"
[dependencies]
rand = "0.8.5"
regex = "1.6.0"
[dependencies] 섹션에 종속성을 추가할 수 있습니다. 예제에서는 rand와 regex 크레이트를 종속성으로 추가했습니다.
Cargo는 의존성 해결 알고리즘을 사용하여 호환 가능한 버전의 종속성을 자동으로 다운로드합니다. 다운로드된 종속성은 Cargo.lock 파일에 기록됩니다.
크레이트 공개와 crates.io
crates.io는 Rust 커뮤니티에서 운영하는 공신, crates.io는 Rust 커뮤니티에서 운영하는 공개 크레이트 레지스트리입니다. 여기에 자신의 크레이트를 공개하면 다른 개발자들이 쉽게 사용할 수 있습니다.
크레이트를 crates.io에 공개하려면 다음 단계를 따르면 됩니다.
- crates.io 계정 생성
- Cargo.toml 파일에 필수 메타데이터 추가 (설명, 라이선스, 저장소 URL 등)
- 프로젝트 루트에서 cargo login 명령을 실행하여 인증
- cargo publish 명령을 실행하여 크레이트 공개
예를 들어, my_utils 라이브러리 크레이트를 공개하려면 Cargo.toml 파일을 다음과 같이 수정합니다.
[package]
name = "my_utils"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2021"
description = "A collection of utility functions"
license = "MIT OR Apache-2.0"
repository = "https://github.com/your-username/my_utils"
[dependencies]
# ...
[dev-dependencies]
# ...
cargo publish 명령을 실행하면 crates.io에 my_utils 크레이트가 공개됩니다.
다른 Rust 프로젝트에서 이 크레이트를 사용하려면 Cargo.toml에 종속성을 추가하기만 하면 됩니다.
[dependencies]
my_utils = "0.1.0"
Cargo는 crates.io에서 my_utils 크레이트를 다운로드하여 프로젝트에 포함시킵니다.
공개된 크레이트는 semantic versioning을 따릅니다. 버전 번호는 주버전.부버전.수정버전 형식을 가지며, 크레이트의 변경 사항에 따라 버전이 업데이트됩니다.
- 주버전 번호는 호환성 없는 API 변경 시 증가합니다.
- 부버전 번호는 하위 호환 기능이 추가될 때 증가합니다.
- 수정버전 번호는 호환성 있는 버그 수정 시 증가합니다.
crates.io는 Rust 생태계의 핵심 요소입니다. 다양한 라이브러리를 공유하고 재사용할 수 있어 개발 생산성이 크게 향상됩니다. 또한 docs.rs와 연동되어 크레이트 문서를 쉽게 확인할 수 있습니다.
작업 공간과 멀티 크레이트 프로젝트
대규모 프로젝트에서는 하나의 크레이트로는 부족할 수 있습니다. 이럴 때 작업 공간(Workspace) 기능을 활용하면 여러 크레이트로 구성된 프로젝트를 관리할 수 있습니다.
이번에는 작업 공간과 멀티 크레이트 프로젝트 구조에 대해 알아보겠습니다. 작업 공간을 효과적으로 활용하면 대규모 프로젝트를 체계적으로 구조화할 수 있어 생산성과 유지보수성이 높아집니다.
작업 공간 이해하기
작업 공간은 여러 크레이트가 하나의 프로젝트로 구성된 구조입니다. 작업 공간에는 다음과 같은 장점이 있습니다.
- 크레이트 간 의존성을 쉽게 관리할 수 있습니다.
- 크레이트를 독립적으로 개발하고 테스트할 수 있습니다.
- 크레이트를 개별적으로 공개할 수 있습니다.
작업 공간은 Cargo의 cargo new 명령으로 생성할 수 있습니다.
cargo new --lib my_workspace
my_workspace 디렉터리가 생성되고, 그 안에 Cargo.toml과 src 디렉터리가 포함됩니다.
작업 공간에 새로운 크레이트를 추가하려면 Cargo.toml 파일에 다음과 같이 작성합니다.
[workspace]
members = [
"my_crate",
]
그런 다음 cargo new 명령으로 새로운 크레이트를 생성합니다.
cargo new --lib my_crate
이렇게 하면 my_workspace 디렉터리 내에 my_crate 디렉터리가 생성됩니다. 여기에 새 크레이트의 소스 코드와 Cargo.toml 파일이 포함됩니다.
멀티 크레이트 프로젝트 구조화하기
작업 공간에서는 크레이트 간의 의존성을 Cargo.toml 파일에 명시해야 합니다. 예를 들어, my_crate가 my_workspace의 라이브러리 크레이트에 의존한다고 가정해 보겠습니다.
# my_crate/Cargo.toml
[dependencies]
my_workspace = { path = "../my_workspace" }
path 키워드를 사용하여 의존성 크레이트의 경로를 지정합니다. 이렇게 하면 my_crate에서 my_workspace의 코드를 사용할 수 있습니다.
// my_crate/src/lib.rs
extern crate my_workspace;
use my_workspace::utils;
pub fn my_function() {
utils::do_something();
}
멀티 크레이트 프로젝트에서는 크레이트를 논리적으로 구조화하는 것이 중요합니다. 예를 들어, 다음과 같이 구조화할 수 있습니다.
- my_workspace: 공유 유틸리티 코드를 포함하는 라이브러리 크레이트
- my_app: 애플리케이션 로직을 포함하는 바이너리 크레이트
- my_utils: 추가적인 유틸리티 코드를 포함하는 라이브러리 크레이트
이렇게 구조화하면 각 크레이트의 역할과 책임이 명확해지므로 코드 관리와 유지보수가 용이해집니다.
작업 공간 명령과 테스트
작업 공간에서는 특정 크레이트에 대해 명령을 실행할 수 있습니다. 예를 들어, cargo build --package my_crate은 my_crate만 빌드합니다.
cargo test를 실행하면 작업 공간의 모든 크레이트에 대해 테스트가 실행됩니다. 특정 크레이트에 대해서만 테스트를 실행하려면 --package 플래그를 사용합니다.
cargo test --package my_crate
이처럼 작업 공간을 사용하면 대규모 프로젝트를 효율적으로 관리할 수 있습니다. 크레이트 간의 의존성을 명확히 할 수 있고, 개별 크레이트를 독립적으로 개발하고 테스트할 수 있습니다.
모듈화 기법과 접근 제어
효과적인 모듈화와 접근 제어는 대규모 프로젝트에서 매우 중요합니다. 코드를 모듈로 구조화하고 적절한 수준의 가시성을 제공하면 코드 재사용성과 유지보수성이 높아집니다.
이번에는 Rust에서 모듈화와 접근 제어를 효과적으로 하는 기법에 대해 알아보겠습니다. 모듈화와 접근 제어를 통해 코드를 체계적으로 구조화하고 정보 은닉을 구현할 수 있습니다.
모듈화 기법
Rust에서는 모듈을 계층적으로 중첩할 수 있습니다. 이를 통해 코드를 논리적으로 구조화할 수 있습니다.
mod utils {
pub mod math {
pub fn square(x: i32) -> i32 {
x * x
}
fn cube(x: i32) -> i32 {
x * x * x
}
}
pub fn greet(name: &str) {
println!("Hello, {}!", name);
}
}
fn main() {
println!("2^2 = {}", utils::math::square(2));
utils::greet("Alice");
}
이 예제에서 utils 모듈은 math 하위 모듈과 greet 함수를 포함합니다. math 모듈 내부에는 square와 cube 함수가 있습니다.
모듈 내부의 항목을 외부에 노출하려면 pub 키워드를 사용해야 합니다. math::cube 함수는 pub로 표시되지 않았기 때문에 외부에서 접근할 수 없습니다.
use 문을 사용하면 모듈 항목을 편리하게 가져올 수 있습니다.
use utils::math::square;
fn main() {
println!("2^2 = {}", square(2));
}
이렇게 하면 square 함수를 직접 호출할 수 있어 코드가 간결해집니다.
모듈 파일 분리와 모듈 트리
모듈을 파일로 분리하면 코드 구조화에 도움이 됩니다. 예를 들어, utils 모듈을 utils.rs 파일로 분리할 수 있습니다.
src/
├── lib.rs
├── main.rs
└── utils/
├── math.rs
└── mod.rs
utils/mod.rs 파일에서 math 모듈을 선언합니다.
// utils/mod.rs
pub mod math;
pub fn greet(name: &str) {
println!("Hello, {}!", name);
}
utils/math.rs 파일에 math 모듈의 내용을 작성합니다.
// utils/math.rs
pub fn square(x: i32) -> i32 {
x * x
}
fn cube(x: i32) -> i32 {
x * x * x
}
이제 math 모듈의 square 함수를 다음과 같이 사용할 수 있습니다.
// src/main.rs
use utils::math::square;
fn main() {
println!("2^2 = {}", square(2));
}
이처럼 모듈을 파일로 분리하면 코드 구조가 더욱 명확해지고, 유지보수성이 높아집니다.
접근 제어
접근 제어는 모듈 항목의 가시성을 제어하는 방법입니다. Rust에서는 public 과 private 두 가지 접근 수준을 지원합니다.
- pub: 외부에서 접근 가능
- private (기본값): 외부에서 접근 불가능
mod utils {
pub mod math {
pub fn square(x: i32) -> i32 {
x * x
}
fn cube(x: i32) -> i32 {
x * x * x
}
}
pub fn greet(name: &str) {
println!("Hello, {}!", name);
}
fn secret() {
println!("This is a secret!");
}
}
fn main() {
utils::greet("Alice");
println!("2^2 = {}", utils::math::square(2));
utils::secret(); // Error: secret 함수는 private이므로 접근 불가능
}
이처럼 접근 제어를 사용하면 모듈의 내부 구현을 숨기고 필요한 부분만 노출할 수 있습니다. 이를 정보 은닉(Information Hiding) 이라고 합니다. 정보 은닉은 캡슐화와 모듈 간 의존성 감소에 도움이 됩니다.
모듈화와 접근 제어를 효과적으로 활용하면 코드를 체계적으로 구조화할 수 있습니다. 이를 통해 코드 재사용성과 유지보수성이 높아지며, 대규모 프로젝트를 보다 효율적으로 관리할 수 있습니다.
참고 자료
- The Rust Programming Language by Steve Klabnik and Carol Nichols
- Rust by Example
- Cargo Book
한 고대 문서 이야기
여기 한 고대 문서가 있습니다. 이 문서는 B.C. 1,500년 부터 A.D 100년까지 약 1,600 여 년 동안 기록되었습니다. 이 문서의 저자는 약 40 명입니다. 이 문서의 고대 사본은 25,000 개가 넘으나, 사본간 오
gospel79.tistory.com
유튜브 프리미엄 월 1만원 할인받고 월 4000원에 이용하는 방법
올해 5월부터 월 8000원 정도이던 유튜브 프리미엄 요금이 15000원 정도로 인상됩니다. 각종 OTT 서비스, ChatGPT 같은 서비스들이 늘어나다보니 이런 거 몇 개만 이용하더라도 월 이용요금이 5만원을
stock79.tistory.com
"이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다."
'IT > Rust 기초 완전 정복' 카테고리의 다른 글
| Rust 메모리 및 동시성 안전성 (30) (1) | 2024.04.25 |
|---|---|
| Rust 성능 최적화 기법 (29) (0) | 2024.04.25 |
| Rust 컴파일러와 빌드 도구 이해하기 (26) (1) | 2024.04.25 |
| Rust 데이터베이스 연동 방법 (25) (1) | 2024.04.25 |
| Rust 패키지 관리자 활용법 (27) (0) | 2024.04.25 |
댓글