IT/Rust 기초 완전 정복

Rust 웹 프로그래밍과 프레임워크 (24)

지식 발전소 2024. 4. 25. 21:29
728x90
반응형
반응형

1

 

소개

안녕하세요? 오늘은 Rust 웹 프로그래밍과 프레임워크에 대해 자세히 알아보겠습니다. 웹 개발은 현대 소프트웨어 엔지니어링의 핵심 분야 중 하나입니다. 우리는 웹 애플리케이션을 통해 정보를 공유하고, 서비스를 제공하며, 비즈니스를 수행합니다. 이렇게 중요한 웹 개발 영역에서 Rust 프로그래밍 언어가 주목받는 이유는 무엇일까요?

 

Rust는 시스템 프로그래밍 언어로 잘 알려져 있지만, 웹 개발에도 적합한 특성을 가지고 있습니다. 우선, Rust는 메모리 안전성을 보장하면서도 성능이 뛰어납니다. 이를 통해 보안성 높고 효율적인 웹 애플리케이션을 구축할 수 있습니다. 또한, Rust의 동시성 모델은 웹 서버와 같이 동시에 많은 요청을 처리해야 하는 환경에 적합합니다. 이 외에도 Rust는 패키지 관리, 크로스 플랫폼 지원, 우수한 도구 생태계 등의 장점을 제공합니다.

 

이번 장에서는 Rust를 이용한 웹 프로그래밍의 기초부터 프레임워크 활용까지 살펴볼 것입니다. 이를 통해 보안성과 성능을 모두 잡을 수 있는 Rust 웹 개발 능력을 기를 수 있을 것입니다. 지금부터 함께 시작해볼까요?

Rust 웹 프로그래밍 기초

웹 프로그래밍을 시작하기 위해서는 HTTP(Hypertext Transfer Protocol) 프로토콜에 대한 이해가 필수적입니다. HTTP는 웹 상에서 데이터를 주고받는 규약으로, 클라이언트와 서버 간의 요청과 응답으로 이루어집니다. 클라이언트가 서버에 요청을 보내면, 서버는 적절한 응답을 제공합니다.

 

Rust에서 HTTP 통신을 구현하기 위해서는 표준 라이브러리 std::net 모듈을 사용할 수 있습니다. 이 모듈에는 TCP/UDP 소켓 프로그래밍을 위한 기능이 포함되어 있습니다. 아래 예제는 간단한 HTTP 서버를 구현한 것입니다.

use std::net::TcpListener;
use std::io::prelude::*;

fn main() {
    let listener = TcpListener::bind("127.0.0.1:8000").unwrap();

    for stream in listener.incoming() {
        let mut stream = stream.unwrap();

        let mut buffer = [0; 1024];
        stream.read(&mut buffer).unwrap();

        let response = "HTTP/1.1 200 OK\r\nContent-Length: 12\r\n\r\nHello World!";
        stream.write(response.as_bytes()).unwrap();
        stream.flush().unwrap();
    }
}

이 예제에서는 TcpListener를 생성하여 127.0.0.1:8000 주소의 포트를 열고, 클라이언트 요청을 기다립니다. 요청이 오면 stream.read(&mut buffer)를 통해 요청 본문을 읽어들이고, 간단한 "Hello World!" 응답을 stream.write를 통해 보냅니다.

이처럼 Rust의 표준 라이브러리를 사용하면 저수준의 네트워크 프로그래밍이 가능하지만, 실제 웹 애플리케이션 개발에는 부족한 점이 있습니다. HTTP 메시지 파싱, 라우팅, 미들웨어, 세션 관리 등의 기능이 필요한데, 이를 직접 구현하기에는 많은 노력이 듭니다.

 

이러한 문제를 해결하기 위해 Rust 생태계에서는 웹 프레임워크를 제공하고 있습니다. 대표적인 웹 프레임워크로는 Actix Web, Rocket, Warp 등이 있습니다. 이번에는 가장 인기 있는 Actix Web 프레임워크에 대해 자세히 알아보겠습니다.

Actix Web 프레임워크 시작하기

Actix Web은 액터 모델을 기반으로 한 고성능 웹 프레임워크입니다. 액터 모델은 병렬 처리를 위한 설계 패턴으로, 효율적인 동시성 프로그래밍을 돕습니다. Actix Web은 이 모델을 활용하여 비동기 논블로킹 I/O를 제공하며, 작은 메모리 풋프린트 뛰어난 성능을 자랑합니다.

Actix Web을 시작하기 위해서는 먼저 프로젝트를 생성해야 합니다. 이를 위해 cargo 도구를 사용할 수 있습니다.

cargo new actix-app --bin

생성된 디렉터리로 이동하여 Cargo.toml 파일을 열고, actix-web  actix-rt 의존성을 추가합니다.

[dependencies]
actix-web = "4"
actix-rt = "2"

이제 main.rs 파일에서 간단한 웹 애플리케이션을 작성해보겠습니다.

use actix_web::{get, App, HttpResponse, HttpServer, Responder};

#[get("/")]
async fn hello() -> impl Responder {
    HttpResponse::Ok().body("Hello World!")
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| App::new().service(hello))
        .bind("127.0.0.1:8000")?
        .run()
        .await
}

이 코드에서는 hello 핸들러 함수를 정의하고, 이를 App 인스턴스에 서비스로 등록합니다. HttpServer를 생성하여 127.0.0.1:8000 주소에 바인딩하고 실행합니다.

 

cargo run을 통해 애플리케이션을 실행하면, http://127.0.0.1:8000/에 접속하여 "Hello World!"를 확인할 수 있습니다.

이처럼 Actix Web은 간결하고 직관적인 API를 제공하여 빠르게 웹 애플리케이션을 구축할 수 있습니다. 하지만 실제 프로덕션 레벨의 애플리케이션을 만들기 위해서는 더 많은 기능이 필요합니다.

Actix Web 핵심 기능 살펴보기

이번에는 Actix Web의 주요 기능들을 살펴보겠습니다.

라우팅

Actix Web에서는 /, /users, /users/{id} 등의 경로를 정의하고, 각 경로에 대한 핸들러를 등록할 수 있습니다. 이를 통해 HTTP 메서드에 따라 다른 핸들러를 호출할 수 있습니다.

use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder};

#[get("/")]
async fn hello() -> impl Responder {
    HttpResponse::Ok().body("Hello World!")
}

#[post("/users")]
async fn create_user(user: web::Json<User>) -> impl Responder {
    // 사용자 생성 로직
    HttpResponse::Ok().json(user.into_inner())
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .service(hello)
            .service(create_user)
    })
    .bind("127.0.0.1:8000")?
    .run()
    .await
}

이 예제에서는 GET /에 대한 hello 핸들러와 POST /users에 대한 create_user 핸들러를 정의하고 있습니다. create_user에서는 web::Json 추출기를 사용하여 요청 본문의 JSON 데이터를 User 구조체로 deserialize 합니다.

미들웨어

미들웨어(Middleware)는 요청과 응답을 가로채어 특정 작업을 수행할 수 있는 핸들러입니다. Actix Web에서는 로깅, 인증, 압축, CORS 등 다양한 미들웨어를 제공합니다.

use actix_web::{middleware, web, App, HttpServer};

async fn logging(req: actix_web::dev::ServiceRequest, _: actix_web::dev::ServiceResponse) {
    println!("{} {}", req.method(), req.path());
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .wrap(middleware::Logger::default())
            .service(
                web::resource("/")
                    .route(web::get().to(|| async { "Hello World!" }))
                    .route(web::method(http::Method::GET).to(logging)),
            )
    })
    .bind("127.0.0.1:8000")?
    .run()
    .await
}

이 예제에서는 middleware::Logger를 사용하여 요청에 대한 로깅을 수행하고 있습니다. 또한 / 경로에 대한 GET 요청에 대해 logging 미들웨어를 등록하여 요청 메서드와 경로를 출력합니다.

정적 파일 서빙

웹 애플리케이션에서는 HTML, CSS, JavaScript 등의 정적 파일을 제공해야 합니다. Actix Web에서는 actix-files 미들웨어를 사용하여 이를 쉽게 구현할 수 있습니다.

use actix_files as fs;
use actix_web::{web, App, HttpServer};

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new().service(
            web::scope("/static")
                .service(fs::Files::new("/static", "static/").show_files_listing()),
        )
    })
    .bind("127.0.0.1:8000")?
    .run()
    .await
}

이 예제에서는 /static 경로 아래에 있는 정적 파일을 static/ 디렉터리에서 제공합니다. show_files_listing() 메서드를 호출하면 디렉터리 목록도 확인할 수 있습니다.

JSON 처리

현대 웹 애플리케이션에서는 JSON이 주요 데이터 교환 형식으로 사용됩니다. Actix Web에서는 serde 라이브러리와 연동하여 JSON 직렬화/비직렬화를 쉽게 처리할 수 있습니다.

use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder};
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
struct User {
    id: u64,
    name: String,
    email: String,
}

#[post("/users")]
async fn create_user(user: web::Json<User>) -> impl Responder {
    HttpResponse::Ok().json(user.into_inner())
}

#[get("/users/{id}")]
async fn get_user(path: web::Path<u64>) -> impl Responder {
    let id = path.into_inner();
    let user = User {
        id,
        name: "John Doe".to_string(),
        email: "john@example.com".to_string(),
    };
    HttpResponse::Ok().json(user)
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .service(create_user)
            .service(get_user)
    })
    .bind("127.0.0.1:8000")?
    .run()
    .await
}

이 예제에서는 User 구조체를 정의하고, serde의 Serialize와 Deserialize 트레이트를 구현했습니다. create_user 핸들러에서는 요청 본문의 JSON 데이터를 User 구조체로 deserialize하고, get_user 핸들러에서는 User 인스턴스를 JSON으로 serialize하여 응답합니다.

데이터베이스 연동

웹 애플리케이션에서는 데이터베이스와의 연동이 필수적입니다. Actix Web에서는 ORM(Object-Relational Mapping) 라이브러리인 SQLx를 활용할 수 있습니다. SQLx는 Rust에서 비동기 PostgreSQL, MySQL, SQLite 클라이언트 라이브러리입니다.

use actix_web::{get, web, App, HttpResponse, HttpServer, Responder};
use sqlx::postgres::PgPoolOptions;
use std::env;

#[get("/users")]
async fn get_users(pool: web::Data<sqlx::PgPool>) -> impl Responder {
    let users = sqlx::query!("SELECT id, name, email FROM users")
        .fetch_all(&**pool)
        .await
        .unwrap();

    HttpResponse::Ok().json(users)
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
    let pool = PgPoolOptions::new()
        .max_connections(5)
        .connect(&database_url)
        .await
        .unwrap();

    HttpServer::new(move || {
        App::new()
            .app_data(web::Data::new(pool.clone()))
            .service(get_users)
    })
    .bind("127.0.0.1:8000")?
    .run()
    .await
}

이 예제에서는 PostgreSQL 데이터베이스와 연동하여 users 테이블의 데이터를 조회하는 get_users 핸들러를 정의했습니다. PgPoolOptions를 사용하여 데이터베이스 연결 풀을 생성하고, web::Data를 통해 애플리케이션 데이터로 등록합니다. 핸들러에서는 SQL 쿼리를 작성하고 fetch_all 메서드를 호출하여 결과를 가져옵니다.

SQLx는 비동기 코드 작성을 지원하며, Rust의 강력한 타입 시스템과 결합하여 타입 안전성을 보장합니다. 또한 커넥션 풀링을 통해 효율적인 데이터베이스 연결 관리가 가능합니다.

웹소켓

많은 웹 애플리케이션에서는 서버와 클라이언트 간의 실시간 양방향 통신이 필요합니다. Actix Web에서는 웹소켓을 통해 이를 구현할 수 있습니다.

use actix::*;
use actix_web::{web, App, Error, HttpRequest, HttpResponse, HttpServer};
use actix_web_actors::ws;

struct ServerWebSocket {
    heartbeat: Addr<Heartbeater>,
}

impl Actor for ServerWebSocket {
    type Context = ws::WebsocketContext<Self>;
}

impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for ServerWebSocket {
    fn handle(&mut self, msg: Result<ws::Message, ws::ProtocolError>, ctx: &mut Self::Context) {
        match msg {
            Ok(ws::Message::Ping(msg)) => ctx.pong(&msg),
            Ok(ws::Message::Text(text)) => ctx.text(text),
            Ok(ws::Message::Binary(bin)) => ctx.binary(bin),
            Ok(ws::Message::Close(reason)) => ctx.close(reason),
            _ => (),
        }
    }
}

async fn ws_route(req: HttpRequest, stream: web::Payload) -> Result<HttpResponse, Error> {
    ws::start(ServerWebSocket::default(), &req, stream)
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| App::new().service(web::resource("/ws/").route(web::get().to(ws_route))))
        .bind("127.0.0.1:8000")?
        .run()
        .await
}

이 예제에서는 ServerWebSocket 액터를 정의하고, 웹소켓 핸들러인 ws_route를 통해 연결을 처리합니다. ws::start 함수를 호출하여 웹소켓 연결을 시작하고, StreamHandler 트레이트를 구현하여 메시지를 처리합니다.

웹소켓을 사용하면 채팅, 실시간 알림, 협업 도구, 게임 등 다양한 애플리케이션을 구축할 수 있습니다.

Rust 웹 프로그래밍 도구 및 패키지

Rust 웹 프로그래밍을 위한 도구와 패키지 생태계도 살펴보겠습니다.

도구

  • Cargo: Rust의 공식 패키지 매니저로, 의존성 관리, 빌드, 테스트, 문서화 등의 기능을 제공합니다.
  • Rust Analyzer: Rust IDE 환경을 위한 언어 서버로, 자동 완성, 리팩토링, 진단 등의 기능을 제공합니다.
  • Trunk: Vite와 유사한 웹 애플리케이션 번들러로, Rust로 작성된 웹 애플리케이션을 위한 개발 서버와 빌드 파이프라인을 제공합니다.
  • wasm-pack: WebAssembly 패키지를 생성하고 NPM에 배포하기 위한 도구입니다.
  • cargo-watch: 파일 변경을 감지하고 자동으로 재빌드하는 도구입니다.

패키지

  • actix-web: 우리가 살펴본 것처럼, 강력하고 고성능의 웹 프레임워크입니다.
  • sqlx: 비동기 PostgreSQL, MySQL, SQLite 클라이언트 라이브러리입니다.
  • serde: 직렬화/비직렬화를 위한 프레임워크입니다.
  • tokio: 비동기 프로그래밍을 위한 런타임입니다.
  • reqwest: isomorphic HTTP 클라이언트로, 클라이언트와 서버 모두에서 사용 가능합니다.
  • warp: 간단하고 모듈식인 또 다른 웹 프레임워크입니다.
  • yew: 모던 Rust + WebAssembly 프레임워크로, React와 유사한 컨셉을 제공합니다.

이 외에도 다양한 라이브러리와 프레임워크가 있으며, crates.io에서 확인할 수 있습니다.

Rust 웹 프로그래밍의 장점

마지막으로 Rust를 웹 프로그래밍에 사용하는 장점을 정리해보겠습니다.

  • 메모리 안전성: Rust의 소유권 개념과 빌림 규칙을 통해 메모리 안전성을 보장합니다. 이를 통해 보안성이 높은 웹 애플리케이션을 구축할 수 있습니다.
  • 성능: Rust는 시스템 프로그래밍 언어 수준의 높은 성능을 제공합니다. 이는 높은 트래픽을 처리해야 하는 웹 애플리케이션에 적합합니다.
  • 동시성 모델: Rust의 동시성 모델은 웹 서버와 같이 많은 요청을 동시에 처리해야 하는 환경에 매우 적합합니다.
  • 패키지 관리 및 생태계: cargo와 crates.io를 통해 패키지 관리와 의존성 관리가 용이하며, 다양한 라이브러리와 도구가 제공됩니다.
  • 크로스 플랫폼 지원: Rust는 크로스 플랫폼 컴파일을 지원하므로, 다양한 환경에서 웹 애플리케이션을 배포할 수 있습니다.
  • WebAssembly 지원: Rust는 WebAssembly를 공식적으로 지원하므로, 브라우저에서 실행되는 웹 애플리케이션 개발에 적합합니다.

이처럼 Rust는 웹 프로그래밍에 매력적인 언어입니다. 안전성과 성능을 모두 잡을 수 있으며, 강력한 생태계와 크로스 플랫폼 지원을 제공합니다. Rust로 웹 애플리케이션을 개발해보시기 바랍니다.

참고 자료

[1] The Rust Programming Language Book - Rust 공식 문서

[2] Actix Web - Actix Web 프레임워크 공식 사이트

[3] SQLx - SQLx 라이브러리 GitHub 저장소

[4] Rust Web Programming: The Awesome Alternative - Rust 웹 프로그래밍 블로그 포스트

[5] Why You Should Use Rust for Web Development - Rust 웹 개발 장점에 대한 블로그 포스트

[6] Awesome Rust Web - Rust 웹 관련 라이브러리 및 리소스 모음

 

 

 

한 고대 문서 이야기

여기 한 고대 문서가 있습니다. 이 문서는 B.C. 1,500년 부터 A.D 100년까지 약 1,600 여 년 동안 기록되었습니다. 이 문서의 저자는 약 40 명입니다. 이 문서의 고대 사본은 25,000 개가 넘으나, 사본간 오

gospel79.tistory.com

 

유튜브 프리미엄 월 1만원 할인받고 월 4000원에 이용하는 방법

올해 5월부터 월 8000원 정도이던 유튜브 프리미엄 요금이 15000원 정도로 인상됩니다. 각종 OTT 서비스, ChatGPT 같은 서비스들이 늘어나다보니 이런 거 몇 개만 이용하더라도 월 이용요금이 5만원을

stock79.tistory.com

 

"이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다."

728x90
반응형