一键导入
tokio
Rust 비동기 런타임 Tokio 핵심 패턴 및 API 가이드
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Rust 비동기 런타임 Tokio 핵심 패턴 및 API 가이드
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Spring Security 5.5.x + jjwt 0.10.7 레거시 JWT 인증 - WebSecurityConfigurerAdapter, OncePerRequestFilter, javax.servlet 환경
Spring Boot 3.x + Spring Security 6.x + jjwt 0.12.x 기반 모던 JWT 인증 패턴. SecurityFilterChain Bean, 람다 DSL, jakarta.servlet, Virtual Threads 적용
Unity 6 LTS 2D 모바일 게임용 uGUI 시스템 전문 스킬. Canvas/RectTransform/TextMeshPro, 모바일 UI 패턴(팝업·무한 스크롤·광고·IAP), 성능 최적화, UI Toolkit과의 선택 기준 포함.
아크라시아(akrasia, 자제력 없음) 학술 논쟁의 핵심 구도와 주요 연구자·문헌을 빠르게 파악할 수 있는 도메인 지식 스킬. 도덕윤리교육 전공 대학원생(석/박사)이 학위논문·KCI 투고·세미나 준비 시 고대–현대–한국 학계–도덕심리학 흐름을 한 번에 짚도록 구성. <example>사용자: "아리스토텔레스의 propeteia와 astheneia 구분을 인용하려는데 출처를 알려줘"</example> <example>사용자: "데이비슨이 의지박약을 어떻게 가능하다고 봤는지 핵심 논증을 정리해줘"</example> <example>사용자: "한국 도덕교육 학계에서 아크라시아 다룬 논문 있어?"</example>
아리스토텔레스 『니코마코스 윤리학』에서 akrasia(자제력없음)와 akolasia(무절제)의 5축 차이를 정밀하게 정리한 학위논문 자료 스킬. NE VII.4 1147b20-1148b14, VII.8 1150b29-1151a28, III.10-12 1117b23-1119b18 절별 분해와 표준 학자 해석(Bostock, Broadie-Rowe, Pakaluk, Hursthouse, Charles 등)을 포함. 도덕교육 적용을 위한 두 상태 차이의 함의 및 한국어 번역어 처리 권장안 제공. <example>사용자: "akrates와 akolastos를 prohairesis 측면에서 어떻게 구분해야 하나요?"</example> <example>사용자: "NE VII.4의 ἁπλῶς akrasia가 akolasia와 어떻게 갈라지는지 절별 분해해주세요"</example> <example>사용자: "Hursthouse의 연속체 모델을 도덕교육 적용 절에서 어떻게 활용할 수 있나요?"</example>
한국 위기 대응 자원(자살·자해·정신건강·여성·청소년·노인·다문화) 핫라인과 앱·챗봇 안전 가드 응답 패턴 종합. 꿈 해몽·정신건강 앱 등 자가 진단/감정 콘텐츠 도메인에서 위험 신호 포착 시 안전한 자원 안내 문구를 작성할 때 참조. <example>사용자: "꿈 해몽 앱에 위기 안내 문구를 어떻게 넣을까?"</example> <example>사용자: "한국에서 자살예방 핫라인 번호가 어떻게 바뀌었지?"</example> <example>사용자: "정신건강 챗봇 안전 가드 응답 템플릿을 짜줘"</example>
| name | tokio |
| description | Rust 비동기 런타임 Tokio 핵심 패턴 및 API 가이드 |
소스: https://tokio.rs/ , https://docs.rs/tokio/latest/tokio/ 검증일: 2026-06-20
Tokio 1.x (LTS). Cargo.toml에서 features 명시 필수:
[dependencies]
tokio = { version = "1", features = ["full"] }
"full" = macros, rt-multi-thread, io-util, net, time, sync, fs, signal 등 전체 feature 활성화.
필요한 feature만 선택 가능: ["rt", "macros", "time"] 등.
async fn main()을 런타임 위에서 실행하는 매크로.
#[tokio::main]
async fn main() {
println!("Hello from Tokio!");
}
멀티스레드 런타임 (기본값):
#[tokio::main(flavor = "multi_thread", worker_threads = 4)]
async fn main() { /* ... */ }
싱글스레드 런타임 (경량 환경용):
#[tokio::main(flavor = "current_thread")]
async fn main() { /* ... */ }
주의:
#[tokio::main]은macros+rtfeature가 필수다. 기본 flavor(multi-thread) 사용 시에는rt-multi-thread도 추가로 필요하다.flavor = "current_thread"지정 시에는rt-multi-thread없이도 동작한다.
매크로 없이 수동 구성:
fn main() {
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
println!("manual runtime");
});
}
Rust의 Future는 lazy - .await하거나 spawn해야 실행된다.
async fn fetch_data() -> String {
// 비동기 작업
"data".to_string()
}
#[tokio::main]
async fn main() {
let result = fetch_data().await;
println!("{}", result);
}
여러 Future 동시 실행:
use tokio::join;
async fn task_a() -> u32 { 1 }
async fn task_b() -> u32 { 2 }
#[tokio::main]
async fn main() {
// 두 태스크를 동시에 실행하고 모두 완료될 때까지 대기
let (a, b) = tokio::join!(task_a(), task_b());
println!("{} {}", a, b); // 1 2
}
먼저 완료되는 Future 선택:
use tokio::select;
#[tokio::main]
async fn main() {
tokio::select! {
val = task_a() => println!("a: {}", val),
val = task_b() => println!("b: {}", val),
}
// 먼저 완료된 브랜치만 실행, 나머지는 drop
}
독립 태스크를 런타임에 스케줄링. JoinHandle을 반환한다.
#[tokio::main]
async fn main() {
let handle = tokio::spawn(async {
// 독립 태스크
42
});
let result = handle.await.unwrap(); // JoinHandle<T> -> Result<T, JoinError>
println!("{}", result); // 42
}
핵심 제약:
spawn에 전달하는 Future는 Send + 'static 이어야 한다.move 클로저 또는 Arc 사용.let data = String::from("hello");
tokio::spawn(async move {
println!("{}", data); // data 소유권 이동
});
태스크 취소: JoinHandle::abort() 호출 시 태스크가 다음 .await 지점에서 취소된다.
let handle = tokio::spawn(async { /* ... */ });
handle.abort();
// handle.await -> Err(JoinError::Cancelled)
비동기 코드에서 안전한 뮤텍스. .lock().await로 잠금 획득.
use std::sync::Arc;
use tokio::sync::Mutex;
#[tokio::main]
async fn main() {
let data = Arc::new(Mutex::new(0));
let data_clone = data.clone();
let handle = tokio::spawn(async move {
let mut lock = data_clone.lock().await;
*lock += 1;
});
handle.await.unwrap();
println!("{}", *data.lock().await); // 1
}
주의: 잠금 구간이 짧고
.await를 포함하지 않는다면std::sync::Mutex가 더 효율적이다.tokio::sync::Mutex는 잠금 구간에서.await가 필요할 때 사용한다.
읽기 다수 / 쓰기 단독 접근.
use tokio::sync::RwLock;
let lock = RwLock::new(5);
// 읽기 (동시 여러 개 가능)
let r = lock.read().await;
println!("{}", *r);
drop(r);
// 쓰기 (단독)
let mut w = lock.write().await;
*w += 1;
mpsc (multi-producer, single-consumer):
use tokio::sync::mpsc;
let (tx, mut rx) = mpsc::channel::<String>(32); // 버퍼 크기 32
tokio::spawn(async move {
tx.send("hello".to_string()).await.unwrap();
});
while let Some(msg) = rx.recv().await {
println!("{}", msg);
}
oneshot (일회성 응답):
use tokio::sync::oneshot;
let (tx, rx) = oneshot::channel::<u32>();
tokio::spawn(async move {
tx.send(42).unwrap();
});
let value = rx.await.unwrap(); // 42
broadcast (multi-producer, multi-consumer):
use tokio::sync::broadcast;
let (tx, mut rx1) = broadcast::channel::<String>(16);
let mut rx2 = tx.subscribe();
tx.send("msg".to_string()).unwrap();
// rx1, rx2 모두 "msg" 수신
watch (최신 값 관찰):
use tokio::sync::watch;
let (tx, mut rx) = watch::channel("initial");
tx.send("updated").unwrap();
println!("{}", *rx.borrow_and_update()); // "updated"
주의: 내부적으로 블로킹 I/O를 별도 스레드 풀(
spawn_blocking)에서 실행한다.fsfeature 필요.
use tokio::fs;
#[tokio::main]
async fn main() -> std::io::Result<()> {
// 파일 쓰기
fs::write("hello.txt", b"Hello, Tokio!").await?;
// 파일 읽기
let contents = fs::read_to_string("hello.txt").await?;
println!("{}", contents);
// 파일 삭제
fs::remove_file("hello.txt").await?;
Ok(())
}
스트림 읽기/쓰기 (AsyncReadExt, AsyncWriteExt):
use tokio::fs::File;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
let mut file = File::create("output.txt").await?;
file.write_all(b"line 1\n").await?;
file.flush().await?;
let mut file = File::open("output.txt").await?;
let mut contents = String::new();
file.read_to_string(&mut contents).await?;
디렉토리 조작:
fs::create_dir_all("path/to/dir").await?;
let mut entries = fs::read_dir(".").await?;
while let Some(entry) = entries.next_entry().await? {
println!("{}", entry.file_name().to_string_lossy());
}
use tokio::time::{sleep, Duration};
sleep(Duration::from_secs(1)).await;
println!("1초 경과");
Future에 시간 제한을 건다. 초과 시 Err(Elapsed) 반환.
use tokio::time::{timeout, Duration};
match timeout(Duration::from_secs(5), some_async_fn()).await {
Ok(result) => println!("완료: {:?}", result),
Err(_) => println!("타임아웃 초과"),
}
주기적 실행.
use tokio::time::{interval, Duration};
let mut interval = interval(Duration::from_millis(500));
loop {
interval.tick().await;
println!("500ms마다 실행");
}
주의:
interval의 첫tick()은 즉시 완료된다. 지연 후 시작하려면interval_at을 사용한다.
use tokio::time::{interval_at, Instant, Duration};
let start = Instant::now() + Duration::from_secs(1);
let mut interval = interval_at(start, Duration::from_millis(500));
| 실수 | 해결 |
|---|---|
spawn에 Send가 아닌 값 전달 | Arc로 감싸거나 spawn_local 사용 |
tokio::sync::Mutex 잠금 구간에서 .await 없이 사용 | std::sync::Mutex로 교체 (성능 향상) |
block_on 안에서 block_on 호출 | 런타임 중첩 불가 - 구조 재설계 필요 |
#[tokio::test] 미사용 | 비동기 테스트에는 #[tokio::test] 매크로 필요 |
| feature flag 누락 | 컴파일 에러 시 Cargo.toml features 확인 |