Step 00: 프로젝트 준비 및 의존성 추가
prep 20 min

프로젝트 준비 및 의존성 추가

초급 코드를 가져오고 웹 서버 의존성을 추가합니다.

Execute this step

Run from project root:
cargo run

Step 0: 프로젝트 준비 및 의존성 추가

학습 목표

  • 웹 서버에 필요한 크레이트 추가하기
  • 코드를 라이브러리와 바이너리로 분리하기
  • 모듈 시스템 이해하기

프로젝트 구조

초급에서는 모든 코드가 main.rs에 있었지만, 중급에서는 분리합니다:

1book_manager/
2├── Cargo.toml
3└── src/
4    ├── lib.rs      # 라이브러리 코드 (Book, Library 등)
5    ├── main.rs     # CLI 바이너리
6    └── web.rs      # 웹 서버 (나중에 추가)

Cargo.toml 수정

1[package]
2name = "book_manager"
3version = "0.2.0"
4edition = "2021"
5
6[dependencies]
7# 웹 서버
8actix-web = "4"
9tokio = { version = "1", features = ["full"] }
10
11# JSON 직렬화
12serde = { version = "1", features = ["derive"] }
13serde_json = "1"
14
15# 날짜/시간
16chrono = { version = "0.4", features = ["serde"] }

핵심 개념

1. 크레이트 (Crate)

  • actix-web: 고성능 비동기 웹 프레임워크
  • tokio: 비동기 런타임
  • serde: 직렬화/역직렬화 라이브러리
  • chrono: 날짜/시간 처리

2. 모듈 시스템

lib.rs - 재사용 가능한 라이브러리 코드:

1// src/lib.rs
2use serde::{Serialize, Deserialize};
3use std::collections::HashMap;
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
6pub enum Category {
7    Fiction,
8    NonFiction,
9    Science,
10    History,
11}
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
14pub enum BookStatus {
15    Available,
16    Borrowed { by: String, due_date: String },
17    Reserved { by: String },
18    Lost,
19}
20
21#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct Book {
23    pub id: u32,
24    pub title: String,
25    pub author: String,
26    pub status: BookStatus,
27    pub category: Category,
28}
29
30#[derive(Debug)]
31pub struct Library {
32    books: HashMap<u32, Book>,
33    next_id: u32,
34}
35
36// impl 블록들...

main.rs - CLI 바이너리:

1// src/main.rs
2use book_manager::{Library, Category};
3
4fn main() {
5    let mut library = Library::new();
6    // CLI 코드...
7}

3. derive 매크로

1#[derive(Debug, Clone, Serialize, Deserialize)]
2pub struct Book {
3    // ...
4}
  • Debug: {:?}로 출력 가능
  • Clone: .clone() 메서드 자동 구현
  • Serialize: JSON으로 변환 가능
  • Deserialize: JSON에서 파싱 가능

소스 코드

src/lib.rs (초급 코드를 옮기고 pub 추가):

1use serde::{Serialize, Deserialize};
2use std::collections::HashMap;
3
4#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5pub enum Category {
6    Fiction,
7    NonFiction,
8    Science,
9    History,
10}
11
12#[derive(Debug, Clone, Serialize, Deserialize)]
13pub enum BookStatus {
14    Available,
15    Borrowed { by: String, due_date: String },
16    Reserved { by: String },
17    Lost,
18}
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct Book {
22    pub id: u32,
23    pub title: String,
24    pub author: String,
25    pub status: BookStatus,
26    pub category: Category,
27}
28
29impl Book {
30    pub fn new(id: u32, title: String, author: String, category: Category) -> Book {
31        Book {
32            id,
33            title,
34            author,
35            status: BookStatus::Available,
36            category,
37        }
38    }
39    
40    pub fn display(&self) {
41        print!("[{}] {} - {} ", self.id, self.title, self.author);
42        match &self.status {
43            BookStatus::Available => println!("(대여 가능)"),
44            BookStatus::Borrowed { by, due_date } => {
45                println!("(대여 중: {} - 반납일: {})", by, due_date)
46            }
47            BookStatus::Reserved { by } => println!("(예약됨: {})", by),
48            BookStatus::Lost => println!("(분실)"),
49        }
50    }
51    
52    pub fn borrow_book(&mut self, borrower: String, due_date: String) -> bool {
53        match self.status {
54            BookStatus::Available => {
55                self.status = BookStatus::Borrowed { by: borrower, due_date };
56                true
57            }
58            _ => false,
59        }
60    }
61    
62    pub fn return_book(&mut self) {
63        self.status = BookStatus::Available;
64    }
65}
66
67#[derive(Debug)]
68pub struct Library {
69    books: HashMap<u32, Book>,
70    next_id: u32,
71}
72
73impl Library {
74    pub fn new() -> Library {
75        Library {
76            books: HashMap::new(),
77            next_id: 1,
78        }
79    }
80    
81    pub fn add_book(&mut self, title: String, author: String, category: Category) -> u32 {
82        let id = self.next_id;
83        let book = Book::new(id, title, author, category);
84        self.books.insert(id, book);
85        self.next_id += 1;
86        id
87    }
88    
89    pub fn find_book(&self, id: u32) -> Option<&Book> {
90        self.books.get(&id)
91    }
92    
93    pub fn find_book_mut(&mut self, id: u32) -> Option<&mut Book> {
94        self.books.get_mut(&id)
95    }
96    
97    pub fn list_all(&self) -> Vec<&Book> {
98        self.books.values().collect()
99    }
100    
101    pub fn borrow_book(&mut self, id: u32, borrower: String, due_date: String) -> Result<(), String> {
102        match self.find_book_mut(id) {
103            Some(book) => {
104                if book.borrow_book(borrower, due_date) {
105                    Ok(())
106                } else {
107                    Err("이미 대여 중입니다".to_string())
108                }
109            }
110            None => Err("책을 찾을 수 없습니다".to_string()),
111        }
112    }
113    
114    pub fn return_book(&mut self, id: u32) -> Result<(), String> {
115        match self.find_book_mut(id) {
116            Some(book) => {
117                book.return_book();
118                Ok(())
119            }
120            None => Err("책을 찾을 수 없습니다".to_string()),
121        }
122    }
123}

src/main.rs (CLI 유지):

1use book_manager::{Library, Category};
2
3fn main() {
4    println!("=== 도서 관리 시스템 CLI ===\n");
5    
6    let mut library = Library::new();
7    
8    // 샘플 데이터
9    library.add_book("러스트 프로그래밍".to_string(), "스티브".to_string(), Category::Science);
10    library.add_book("해리포터".to_string(), "J.K. 롤링".to_string(), Category::Fiction);
11    
12    for book in library.list_all() {
13        book.display();
14    }
15}

의존성 설치 및 실행

cargo build
cargo run

체크리스트

  • [ ] Cargo.toml에 의존성을 추가했습니다
  • [ ] 코드를 lib.rs와 main.rs로 분리했습니다
  • [ ] pub 키워드로 공개 API를 만들었습니다
  • [ ] derive 매크로를 사용했습니다
  • [ ] 프로젝트가 정상적으로 빌드됩니다

다음 단계

Step 1에서는 트레이트(Traits)를 배우고 커스텀 트레이트를 구현합니다.

Did you find this helpful? Give it a cheer!