---
title: 상태 관리
sidebar:
  order: 1
i18nReady: true
---

Tauri 애플리케이션에서는 종종 애플리케이션의 현재 상태를 추적하거나 애플리케이션과 관련된 다양한 것들의 라이프사이클을 관리해야 합니다. Tauri에서는 [`Manager`] API를 사용하여 애플리케이션의 상태를 관리하고, 명령이 호출되었을 때의 상태를 읽기 위한 간단한 방법을 제공합니다.

다음은 간단한 예입니다:

```rust
use tauri::{Builder, Manager};

struct AppData {
  welcome_message: &'static str,
}

fn main() {
  Builder::default()
    .setup(|app| {
      app.manage(AppData {
        welcome_message: "Welcome to Tauri!",
      });
      Ok(())
    })
    .run(tauri::generate_context!())
    .unwrap();
}
```

[`Manager`] 트레이트를 구현하는 모든 유형(예: [`App`] 인스턴스)은 나중에 해당 상태에 액세스할 수 있습니다:

```rust
let data = app.state::<AppData>();
```

각 명령의 상태 액세스를 포함하여 자세한 내용은 [상태 액세스](#상태-액세스) 섹션을 참조하십시오.

## 가변성

Rust에서는 여러 스레드 간에 공유되는 값이나 소유권이 [`Arc`](또는 Tauri의 [`State`])와 같은 공유 포인터를 통해 제어되는 값을 직접 변경할 수 없습니다. 그렇게 하면 (예: 두 개의 쓰기가 동시에 발생하는 등) 데이터 경합이 발생할 수 있기 때문입니다.

이를 피하기 위해 [**내부 가변성**](https://doc.rust-kr.org/ch15-05-interior-mutability.html)이라는 개념을 사용합니다. 예를 들어, 표준 라이브러리의 [`Mutex`](뮤텍스)는 "상태를 래핑"하는 데 사용할 수 있습니다. 이를 통해 값을 변경해야 할 때 잠그고 변경이 끝나면 잠금을 해제합니다.

> > > 《번역 주》 **Mutex** "Mutex"는 "mutual exclusion"(상호 배제)의 약자로, 어떤 경우에도 하나의 스레드에만 데이터에 대한 액세스를 허용하는 메커니즘입니다. "`Mutex` 뮤텍스"에 대한 자세한 설명은 [Rust Book 16.3](https://doc.rust-kr.org/ch16-03-shared-state.html)을 참조하십시오.

```rust
use std::sync::Mutex;

use tauri::{Builder, Manager};

#[derive(Default)]
struct AppState {
  counter: u32,
}

fn main() {
  Builder::default()
    .setup(|app| {
      app.manage(Mutex::new(AppState::default()));
      Ok(())
    })
    .run(tauri::generate_context!())
    .unwrap();
}
```

"뮤텍스"를 잠그면 상태를 변경할 수 있습니다:

```rust
let state = app.state::<Mutex<AppState>>();

// 뮤텍스를 잠그고 가변 액세스를 얻습니다:
let mut state = state.lock().unwrap();

// 상태를 변경합니다:
state.counter += 1;
```

범위가 끝나거나 `MutexGuard`가 삭제되면 "뮤텍스"는 자동으로 잠금 해제되어 애플리케이션의 다른 부분이 내부 데이터에 액세스하고 해당 데이터를 변경할 수 있게 됩니다.

### 비동기 뮤텍스 사용 기회

> > > 《번역 주》 **Tokio** "Tokio"는 Rust용 비동기 런타임 라이브러리입니다.

[Tokio의 `Mutex`](https://docs.rs/tokio/latest/tokio/sync/struct.Mutex.html#which-kind-of-mutex-should-you-use) 문서에서 인용하면, 다음과 같이 Tokio가 제공하는 것과 같은 비동기 뮤텍스 대신 표준 라이브러리의 [`Mutex`]를 사용해도 괜찮은 경우가 많습니다:

> 일반적으로 믿어지는 것과는 반대로, 비동기 코드에서 표준 라이브러리의 일반 Mutex를 사용해도 문제없으며, 종종 그것이 선호됩니다. ... 비동기 뮤텍스의 주요 사용 사례는 데이터베이스 연결과 같은 IO 리소스에 대한 공유 가변 액세스를 제공하는 것입니다.

이 두 `Mutex` 간의 트레이드오프(일장일단)를 이해하려면 각각의 위 링크된 문서를 충분히 읽어보는 것이 좋습니다. 비동기 뮤텍스가 필요하게 될 _수도 있는_ 이유 중 하나는 "대기 지점"을 넘어 `MutexGuard`를 유지해야 하는 경우입니다.

### `Arc`는 필요한가?

> > > 《번역 주》 **Arc** "[구조체 Arc](https://doc.rust-kr.org/rust-by-example/std/arc.html)"(Struct `Arc`). Atomically Reference Counted(원자적 참조 카운트)의 약자.

Rust에서는 여러 스레드 간에 값의 소유권을 공유하기 위해 [`Arc`]가 일반적으로 사용됩니다(보통 `Arc<Mutex<T>>` 형태로 [`Mutex`]와 함께 사용됩니다). 그러나 [`State`]에 저장된 것에 대해서는 [`Arc`]를 사용할 필요가 없습니다. 왜냐하면 Tauri가 대신 이를 실행하기 때문입니다.

`State`의 수명 요구 사항으로 인해 "상태"를 새 스레드로 이동할 수 없는 경우, 대신 `AppHandle`을 해당 새 스레드로 이동하여 아래 "[Manager 트레이트를 사용하여 상태에 액세스하기](#manager-트레이트를-사용하여-상태에-액세스하기)" 항목에서 보여주는 것처럼 "상태"를 가져올 수 있습니다. `AppHandle`은 이러한 유스케이스(사용 사례)에서 의도적으로 복제가 저렴합니다.

> > > 《번역 주》 **의도적으로 복제가 저렴** 원문 deliberately cheap to clone. (문맥 불명: `AppHandle`에서 유사한 처리를 간편하게 수행할 수 있도록 한다는 의미?)

## 상태 액세스

### 명령 내 상태 액세스

```rust
#[tauri::command]
fn increase_counter(state: State<'_, Mutex<AppState>>) -> u32 {
  let mut state = state.lock().unwrap();
  state.counter += 1;
  state.counter
}
```

명령에 대한 자세한 내용은 "[프론트엔드에서 Rust 호출하기](/ko/develop/calling-rust/)" 장을 참조하십시오.

#### 비동기 명령

`async` 명령을 사용하고 Tokio의 비동기 [`Mutex`](https://docs.rs/tokio/latest/tokio/sync/struct.Mutex.html)를 사용하려면 다음과 같이 유사한 방법으로 설정하면 "상태"에 액세스할 수 있습니다.

```rust
#[tauri::command]
async fn increase_counter(state: State<'_, Mutex<AppState>>) -> Result<u32, ()> {
  let mut state = state.lock().await;
  state.counter += 1;
  Ok(state.counter)
}
```

비동기 명령을 사용하는 경우 반환 유형은 [`Result`]여야 한다는 점에 유의하십시오.

### [`Manager`] 트레이트를 사용하여 상태에 액세스하기

경우에 따라 다른 스레드 내나 `on_window_event`와 같은 이벤트 핸들러 내와 같이 명령 외부에서 "상태"에 액세스해야 할 수 있습니다. 이러한 경우 [`Manager`] 트레이트를 구현하는 유형(`AppHandle` 등)의 `state()` 메서드를 사용하여 상태를 가져올 수 있습니다:

```rust
use std::sync::Mutex;
use tauri::{Builder, Window, WindowEvent, Manager};

#[derive(Default)]
struct AppState {
  counter: u32,
}

// 이벤트 핸들러 내:
fn on_window_event(window: &Window, _event: &WindowEvent) {
    // 전역 상태를 가져올 수 있도록 앱에 대한 핸들을 가져옵니다.
    let app_handle = window.app_handle();
    let state = app_handle.state::<Mutex<AppState>>();

    // 뮤텍스를 잠그고 "상태"에 가변적으로 액세스합니다.
    let mut state = state.lock().unwrap();
    state.counter += 1;
}

fn main() {
  Builder::default()
    .setup(|app| {
      app.manage(Mutex::new(AppState::default()));
      Ok(())
    })
    .on_window_event(on_window_event)
    .run(tauri::generate_context!())
    .unwrap();
}
```

이 방법은 명령 주입(외부에서의 명령 실행)에 의존할 수 없는 경우에 유용합니다. 예를 들어, `AppHandle`을 사용하는 것이 더 간단한 스레드에 "상태"를 이동해야 하는 경우나 명령 컨텍스트 내에 존재하지 않는 경우 등입니다.

> > > 《번역 주》 **명령 주입에 의존할 수 없음** 원문은 cannot rely on command injection. (문맥 불명)
> > > 《번역 주》 **명령 컨텍스트** 원문 a command context. 명령 실행에 필요한 정보나 환경.

## 유형 불일치

:::caution
구조체 [`State`]의 매개변수에 잘못된 유형을 사용하면 "컴파일 시 오류"가 아니라 "런타임 패닉"이 발생합니다.

예를 들어, `State<'_, Mutex<AppState>>` 대신 `State<'_, AppState>`를 사용하면 해당 유형으로 관리되는 "상태"가 존재하지 않게 됩니다.
:::

필요한 경우 이 실수를 방지하기 위해 "상태"를 "[유형 별칭](https://doc.rust-kr.org/ch19-04-advanced-types.html)"으로 래핑할 수도 있습니다:

```rust
use std::sync::Mutex;

#[derive(Default)]
struct AppStateInner {
  counter: u32,
}

type AppState = Mutex<AppStateInner>;
```

단, 유형 별칭은 그대로 사용하고 [`Mutex`]로 다시 래핑하지 않도록 하십시오. 그렇지 않으면 동일한 문제를 일으킬 수 있습니다.

[`Manager`]: https://docs.rs/tauri/latest/tauri/trait.Manager.html
[`State`]: https://docs.rs/tauri/latest/tauri/struct.State.html
[`Mutex`]: https://doc.rust-lang.org/stable/std/sync/struct.Mutex.html
[`Arc`]: https://doc.rust-lang.org/stable/std/sync/struct.Arc.html
[`App`]: https://docs.rs/tauri/latest/tauri/struct.App.html
[`Result`]: https://doc.rust-lang.org/stable/std/result/index.html

<div style="text-align: right">
  【※ 이 한국어판은, 「Mar 28, 2025 영문판」에 근거하고 있습니다】
</div>
