Step 00: 프로젝트 준비 및 의존성 추가
0 studying now
prep 20 min
프로젝트 준비 및 의존성 추가
초급 코드를 가져오고 웹 서버 의존성을 추가합니다.
Execute this step
Run from project root:
cargo runStep 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)를 배우고 커스텀 트레이트를 구현합니다.