Step 02: 비동기 프로그래밍 기초
0 studying now
foundations 60 min
비동기 프로그래밍 기초
async/await를 배우고 비동기 코드를 작성합니다.
Execute this step
Run from project root:
cargo runStep 2: 비동기 프로그래밍 기초
학습 목표
- async/await 문법 배우기
- 비동기와 동기의 차이 이해하기
- tokio로 비동기 코드 실행하기
핵심 개념
1. 동기 vs 비동기
동기 (Synchronous):
1fn fetch_data() -> String { 2 // 네트워크 요청 (블로킹) 3 thread::sleep(Duration::from_secs(2)); 4 "데이터".to_string() 5} 6 7fn main() { 8 let data = fetch_data(); // 2초 대기 9 println!("{}", data); 10}
비동기 (Asynchronous):
1async fn fetch_data() -> String { 2 // 네트워크 요청 (논블로킹) 3 tokio::time::sleep(Duration::from_secs(2)).await; 4 "데이터".to_string() 5} 6 7#[tokio::main] 8async fn main() { 9 let data = fetch_data().await; // 다른 작업 가능 10 println!("{}", data); 11}
2. async 함수
async fn은 Future를 반환하는 함수입니다.
1// 이 함수는 2async fn hello() -> String { 3 "Hello".to_string() 4} 5 6// 실제로는 이렇게 동작 7fn hello() -> impl Future<Output = String> { 8 // ... 9}
3. .await
.await는 Future가 완료될 때까지 기다립니다.
1#[tokio::main] 2async fn main() { 3 let future = hello(); // Future 생성 (아직 실행 안 함) 4 let result = future.await; // 실행하고 결과 기다림 5 println!("{}", result); 6}
4. 여러 Future 동시 실행
1use tokio::join; 2 3#[tokio::main] 4async fn main() { 5 let (result1, result2, result3) = join!( 6 async_task_1(), 7 async_task_2(), 8 async_task_3(), 9 ); 10}
Library를 비동기로 변환
src/lib.rs에 비동기 메서드 추가:
1use tokio::sync::RwLock; 2use std::sync::Arc; 3 4// 비동기에서 안전한 Library 5pub struct AsyncLibrary { 6 library: Arc<RwLock<Library>>, 7} 8 9impl AsyncLibrary { 10 pub fn new() -> Self { 11 AsyncLibrary { 12 library: Arc::new(RwLock::new(Library::new())), 13 } 14 } 15 16 pub async fn add_book(&self, title: String, author: String, category: Category) -> u32 { 17 let mut lib = self.library.write().await; 18 lib.add_book(title, author, category) 19 } 20 21 pub async fn find_book(&self, id: u32) -> Option<Book> { 22 let lib = self.library.read().await; 23 lib.find_book(id).cloned() 24 } 25 26 pub async fn list_all(&self) -> Vec<Book> { 27 let lib = self.library.read().await; 28 lib.list_all().into_iter().cloned().collect() 29 } 30 31 pub async fn borrow_book(&self, id: u32, borrower: String, due_date: String) -> Result<(), String> { 32 let mut lib = self.library.write().await; 33 lib.borrow_book(id, borrower, due_date) 34 } 35 36 pub async fn return_book(&self, id: u32) -> Result<(), String> { 37 let mut lib = self.library.write().await; 38 lib.return_book(id) 39 } 40}
Arc와 RwLock
- Arc (Atomic Reference Counting): 여러 스레드에서 안전하게 공유
- RwLock (Read-Write Lock): 여러 읽기 또는 하나의 쓰기
1let lib = Arc::new(RwLock::new(Library::new())); 2 3// 읽기 (여러 개 가능) 4let reader1 = lib.read().await; 5let reader2 = lib.read().await; 6 7// 쓰기 (하나만 가능) 8let writer = lib.write().await;
비동기 예제 코드
src/main.rs 또는 examples/async_demo.rs:
1use book_manager::{AsyncLibrary, Category}; 2use tokio::time::{sleep, Duration}; 3 4#[tokio::main] 5async fn main() { 6 println!("=== 비동기 도서 관리 시스템 ===\n"); 7 8 let library = AsyncLibrary::new(); 9 10 // 비동기로 책 추가 11 println!("책 추가 중..."); 12 let id1 = library.add_book( 13 "러스트 프로그래밍".to_string(), 14 "스티브".to_string(), 15 Category::Science, 16 ).await; 17 println!("책 추가 완료 (ID: {})", id1); 18 19 let id2 = library.add_book( 20 "해리포터".to_string(), 21 "J.K. 롤링".to_string(), 22 Category::Fiction, 23 ).await; 24 println!("책 추가 완료 (ID: {})", id2); 25 26 println!(); 27 28 // 동시에 여러 작업 실행 29 println!("동시에 2권 대여 처리..."); 30 let (result1, result2) = tokio::join!( 31 library.borrow_book(id1, "김철수".to_string(), "2026-02-20".to_string()), 32 library.borrow_book(id2, "이영희".to_string(), "2026-02-22".to_string()), 33 ); 34 35 match result1 { 36 Ok(_) => println!("✓ 책 {} 대여 완료", id1), 37 Err(e) => println!("✗ 책 {} 대여 실패: {}", id1, e), 38 } 39 40 match result2 { 41 Ok(_) => println!("✓ 책 {} 대여 완료", id2), 42 Err(e) => println!("✗ 책 {} 대여 실패: {}", id2, e), 43 } 44 45 println!(); 46 47 // 비동기로 전체 목록 조회 48 println!("전체 도서 목록:"); 49 let books = library.list_all().await; 50 for book in books { 51 println!(" {}", book); 52 } 53 54 println!(); 55 56 // 비동기 딜레이 시뮬레이션 57 println!("3초 후 반납..."); 58 sleep(Duration::from_secs(3)).await; 59 60 library.return_book(id1).await.ok(); 61 println!("✓ 책 {} 반납 완료", id1); 62 63 // 최종 상태 64 println!("\n최종 도서 목록:"); 65 let books = library.list_all().await; 66 for book in books { 67 println!(" {}", book); 68 } 69}
비동기 태스크 생성
1use tokio::task; 2 3#[tokio::main] 4async fn main() { 5 let library = AsyncLibrary::new(); 6 7 // 백그라운드 태스크 생성 8 let handle = task::spawn(async move { 9 println!("백그라운드 작업 시작"); 10 tokio::time::sleep(Duration::from_secs(2)).await; 11 println!("백그라운드 작업 완료"); 12 }); 13 14 println!("메인 작업 계속 진행"); 15 16 // 백그라운드 태스크 완료 대기 17 handle.await.unwrap(); 18}
체크리스트
- [ ] async fn을 정의하고 사용했습니다
- [ ] .await로 Future를 실행했습니다
- [ ] tokio::main 매크로를 사용했습니다
- [ ] Arc와 RwLock으로 공유 상태를 관리했습니다
- [ ] tokio::join!으로 동시 실행했습니다
다음 단계
Step 3에서는 Actix-web으로 REST API 서버를 만듭니다!