Actix Web - State

가으라늘·2023년 1월 28일
0

Actix Web

목록 보기
3/3

이 글은 Acticx Web의 공식문서에 있는 내용을 따라하여 웹 앱 제작을 연습하는 글입니다.

State

애플리케이션의 State는 같은 scope 내의 모든 route와 resource 내에서 공유된다. State는 web::Data\<T\>를 통해 접근할 수 있다. T는 state의 타입이다. State는 middleware에서도 접근할 수 있다.

struct AppState {
    app_name: String,
}

#[get("/")] // get 방식
async fn hello(data: web::Data<AppState>) -> String {
    let app_name = &data.app_name;
    format!("Hello {app_name}!")
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .app_data(web::Data::new(AppState {
                app_name: String::from("Actix Web"),
            }))
            .service(hello)
    })
    .bind(("127.0.0.1", 8080))?
    .run()
    .await
}

String 타입의 app_name이라는 필드를 가지는 AppState라는 구조체를 만든다. hello 함수는 web::Data 타입의 data란 매개변수를 가진다. web::Data를 통해서 AppState에 접근할 수 있다. 앱을 시작할 때 state를 전달한다.

localhost:8080에 접속하면 hello에서 AppState를 넘겨받아 Hello Actix Web를 출력하는 것을 볼 수 있다.

Shared Mutable State

HttpServer는 애플리케이션 인스턴스가 아닌 애플리케이션 팩토리를 허용한다. HttpServer는 각 쓰레드마다 애플리케이션 인스턴스를 구성한다. 그렇기에 애플리케이션 데이터를 여러번 구성해야 한다. 서로 다른 쓰레드 간에 데이터를 공유하기 위해서는 Send + Sync와 같은, 공유 가능한 오브젝트를 사용해야 한다.

이해가 잘 안된다. HttpServer가 쓰레드 당 하나의 인스턴스만 생성한다는 것인가? 그리고 쓰레드마다 인스턴스를 생성하기 때문에 애플리케이션 데이터는 여러번 생성된다는 뜻인 것일까...

내부적으로, web::DataArc를 사용한다. 두 개의 Arc가 만들어지는 걸 피하기 위해서, App::app_data()를 이용해서 데이터를 등록하기 전에 먼저 데이터를 만들어야 한다.

web::DataArc를 사용하는데, 두개의 Arc가 생성되면 안 되는 것 같다. Arc가 무엇인지 찾아보니 Atomic Reference Counting, 줄여서 Arc라고 불리는 타입인 것 같다. Arc에 대해선 추후에 더 자세히 공부해야겠다.

use actix_web::{get, web, App, HttpServer };
use std::sync::Mutex;

struct AppStateWithCounter {
    counter: Mutex<i32>, // 쓰레드 간에 안전하게 변경하기 위해 Mutex 사용
}

#[get("/")] // get 방식
async fn index(data: web::Data<AppStateWithCounter>) -> String {
    let mut counter = data.counter.lock().unwrap(); // counter의 MutexGuard를 획득
    *counter += 1; // MutexGuard 내부의 counter에 접근

    format!("Request number: {counter}")
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    // HttpServer::new closure 외부에서 만들어진 web::Data
    let counter = web::Data::new(AppStateWithCounter {
        counter: Mutex::new(0),
    });

    HttpServer::new(move || {
        // closure 내부로 counter 이동
        App::new()
            .app_data(counter.clone()) // 만들어진 데이터를 등록
            .service(index)
    })
    .bind(("127.0.0.1", 8080))?
    .run()
    .await
}

localhost:8080에 접속해보면 새로고침을 할 때마다 계속 숫자가 증가하는 것을 볼 수 있다.

공식문서에서는 이렇게 얘기하고 있다.

핵심 요소

  • closure 내부에서 초기화 되어 HttpServer::new로 전달되는 State는 worker thread에 지역적이며, 수정될 경우 de-sync된다.
  • 전역적으로 공유되는 state를 만들고 싶을 경우, HttpServer::new에 전달되는 closure의 외부에서 만들어져서 이동/복제 되어야 한다.

솔직히 이해 안되는 것들이 투성이다. Rust와 Actix를 계속 공부하면서 깨져나간 뒤에 다시 이 글을 볼 때 이해할 수 있을 것 같다.

참고자료

https://actix.rs/docs/application#state

profile
생각 없는 개발자

0개의 댓글