본문 바로가기
C.W.K.
Stream
Lesson 05 of 06 · published

Channel<T>로 스트리밍

~13 min · tauri, channel, streaming, ipc

Level 0웹 관광객
0 XP0/56 lessons0/13 achievements
0/100 XP to next level100 XP to go0% complete
"이벤트는 건물 전체에 나가는 안내방송이야. 채널은 통화 하나를 위해 깔아둔 전용선이고."

command 하나가 할 말이 많을 때

command는 딱 한 번 값을 돌려줘. 근데 어떤 일은 갱신을 줄줄이 만들어내. 받은 바이트를 계속 알려주는 파일 다운로드, 진행률을 뱉는 긴 작업, 토큰을 하나씩 내놓는 생성기 같은 것들. 이벤트로 방송할 수도 있지만, 그러면 사방의 리스너가 다 듣게 되고 어느 갱신이 어느 호출 것이었는지 네가 맞춰야 해. Tauri의 Channel<T>가 이걸 깔끔하게 풀어 — invoke 한 번에 딱 묶인, 타입 있고 순서 있는 스트림이야.

어떻게 엮이나

프론트엔드가 Channel을 만들고, onmessage 핸들러를 정하고, command 인자로 넘겨. Rust command는 Channel<T> 파라미터로 그걸 받아서 원하는 만큼 .send(payload)를 불러. send 할 때마다 프론트엔드 핸들러가 순서대로 실행돼. command가 반환하면 그 호출의 스트림도 끝나고. 이벤트 이름이 겹칠 일도, 어느 응답이 어느 요청 건지 맞춰볼 일도 없어 — 채널 자체가 그 짝이니까.

Channel이냐 이벤트냐, 아니면 아예 밖이냐

범위를 보고 골라. 이벤트는 전역이야. 누구나 들을 수 있고, 앱 전체에 걸린 소식에 좋아. Channel은 요청 하나에 묶여. 호출한 쪽 하나만 듣고, 작업의 진행 상황에 딱 맞지. 그리고 스트림이 아예 다른 프로세스에서 올 때 — 네 Tauri 코어가 아니라 — 둘 다 안 맞아. 그땐 진짜 네트워크 전송을 꺼내야 해. Cinder는 생성된 이미지를 별개 brain 프로세스에서 WebSocket으로 받아와. 만드는 쪽이 Tauri 코어 안에 없거든. 같은 '스트리밍'이란 말인데 데이터가 태어나는 자리에 따라 정답이 셋인 거야.

Code

Channel로 진행률을 흘려보내는 command·rust
use tauri::ipc::Channel;
use serde::Serialize;

#[derive(Clone, Serialize)]
struct Tick { step: u32, total: u32 }

// command가 Channel을 받아서 거기로 스트리밍해.
#[tauri::command]
async fn run_job(on_event: Channel<Tick>) -> Result<(), String> {
    for step in 1..=10 {
        tokio::time::sleep(std::time::Duration::from_millis(200)).await;
        on_event.send(Tick { step, total: 10 }).map_err(|e| e.to_string())?;
    }
    Ok(()) // 반환하면 이 호출의 스트림 종료
}
프론트엔드에서 스트림 받기·tsx
import { invoke, Channel } from "@tauri-apps/api/core";

type Tick = { step: number; total: number };

const channel = new Channel<Tick>();
channel.onmessage = (msg) => {
  console.log(`step ${msg.step}/${msg.total}`);
};

// 채널을 인자로 넘김; 갱신이 onmessage로 순서대로 도착.
await invoke("run_job", { onEvent: channel });

External links

Exercise

Channel로 진행 갱신 5개를 사이사이 짧게 쉬면서 흘려보내는 command를 짜고, channel.onmessage로 프론트엔드에 살아 움직이는 진행바를 그려. 그다음 언제 대신 이벤트를 쓰고 언제 WebSocket이 필요한지 각각 한 문장으로 말해봐 — 셋을 범위 순으로 늘어놓을 수 있다는 걸 보여주는 거야.
Hint
Rust는 루프에서 tokio sleep을 두고 on_event.send(...). 프론트엔드는 new Channel()을 만들고 onmessage를 정하고 { onEvent }로 넘겨. 이벤트는 전역이나 앱 전체 소식용이고, WebSocket은 다른 프로세스나 원격 서버에서 오는 데이터용이야.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.