C.W.K.
Stream
Lesson 04 of 06 · published

이벤트: emit & listen

~13 min · tauri, events, ipc, frontend

Level 0웹 관광객
0 XP0/56 lessons0/13 achievements
0/100 XP to next level100 XP to go0% complete
"command는 질문과 답이야. 이벤트는 아무도 안 물어본 공지야."

pull이 아니라 push

가끔 코어한텐 프론트엔드가 요청 안 한 소식이 있어: 다운로드 끝남, 감시하던 파일 바뀜, 타이머 틱, 트레이 메뉴 클릭됨. 그걸 command로 폴링하는 건 낭비야. 대신 코어가 이벤트를 emit하고 프론트엔드가 listen해. app.emit("name", payload)가 모든 웹뷰에 방송하고, 프론트엔드의 listen("name", handler)(@tauri-apps/api/event에서)가 각각을 payload랑 받아. payload는 직렬화 가능한 값 뭐든, command 데이터처럼 serde로 인코딩돼.

방송 vs 타깃

emit은 모두한테 보내. 창이 여럿이고 하나만 들어야 하면 emit_to("window-label", "name", payload)가 특정 웹뷰를 노려. 이벤트는 반대 방향으로도 흘러 — 프론트엔드가 emit하고 Rust가 listen할 수 있어 — 근데 프론트엔드→코어 요청엔 보통 command를 원해(반환값이랑 에러 처리를 얻으니까). 이벤트는 던지고 잊는 공지에 남겨둬.

리스너는 항상 정리해

listen은 리스너를 제거하는 함수를 반환해. 컴포넌트에선 effect에서 등록하고 cleanup에서 그 unlisten 함수를 불러 — 안 그러면 컴포넌트 마운트할 때마다 중복 핸들러가 쌓여, 고전적인 메모리 누수 + 이중 발화 버그야. 딱 한 번만 처리하고 싶은 이벤트엔 once를 써.

Code

코어에서 emit·rust
use tauri::{AppHandle, Manager, Emitter};
use serde::Serialize;

#[derive(Clone, Serialize)]
struct Progress { done: u32, total: u32 }

fn report(app: &AppHandle) {
    // "progress"를 듣는 모든 웹뷰에 방송.
    let _ = app.emit("progress", Progress { done: 7, total: 10 });
    // 또는 label로 창 하나를 노림:
    let _ = app.emit_to("main", "progress", Progress { done: 7, total: 10 });
}
프론트엔드에서 listen (cleanup 포함)·tsx
import { listen } from "@tauri-apps/api/event";
import { useEffect, useState } from "react";

function ProgressBar() {
  const [p, setP] = useState({ done: 0, total: 0 });
  useEffect(() => {
    // listen은 unlisten 함수로 resolve돼 — cleanup에서 불러.
    const stop = listen<{ done: number; total: number }>("progress", (e) =>
      setP(e.payload),
    );
    return () => { stop.then((un) => un()); };
  }, []);
  return <progress value={p.done} max={p.total} />;
}

External links

Exercise

AppHandle 레슨의 백그라운드 스레드가 매초 증가하는 숫자로 'tick' 이벤트를 emit하게 만들어. 프론트엔드에서 그걸 listen하고 최신 값을 렌더링해 — 제대로 cleanup하면서. 그다음 일부러 cleanup을 빼고 컴포넌트가 remount되면 무슨 일이 나는지 봐(React devtools 켜거나 토글): 두 배가 된 핸들러가 레슨이야.
Hint
Rust: 루프에서 handle.emit("tick", n). React: const stop = listen('tick', ...); return () => stop.then(u => u()). 누수 보려면 return cleanup을 주석 처리하고 컴포넌트를 remount해.

Progress

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

댓글 0

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

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