0Pricing
Learn Rust Coding · 강의

데이터베이스 통합(SQLx/Diesel)

SQLx나 Diesel과 같은 인기 ORM 또는 쿼리 빌더를 사용해 Rust 웹 서비스를 데이터베이스와 통합합니다.

데이터베이스 통합(SQLx/Diesel)은(는) CoddyKit의 무료 Learn Rust Coding 강의입니다. 이것은 3개 중 2번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Learn Rust Coding 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Learn Rust Coding 강의에는 총 3개의 강의가 포함되어 있습니다.

이 강의의 일부는 아직 번역되지 않았으며 영어로 표시됩니다.

Why Databases for Web?

Web services often need to store and retrieve data persistently. Think about user profiles, product catalogs, or blog posts – this data needs to live somewhere beyond your program's memory.

Databases provide a structured way to manage this data, ensuring it's safe, consistent, and accessible. Integrating your Rust web service with a database is a core task for most real-world applications.

SQLx: Your Async DB Friend

In Rust, we have excellent tools for database interaction. Two popular choices are SQLx and Diesel.

  • SQLx: An asynchronous, pure Rust SQL crate with compile-time checked queries. It's often preferred for async web services built with frameworks like Tokio.
  • Diesel: A powerful ORM (Object-Relational Mapper) that provides a more abstract way to interact with your database, mapping Rust structs directly to database tables. It's typically synchronous.

For this lesson, we'll focus on SQLx due to its strong async support, which aligns well with modern Rust web service development.

SQLx Setup: Dependencies

Before writing code, you need to add sqlx and tokio to your project's Cargo.toml. tokio provides the async runtime needed by sqlx.

For example, to use SQLite (an easy file-based database for local development) with SQLx, your Cargo.toml might look like this:

[dependencies] sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "sqlite"] } tokio = { version = "1", features = ["full"] }

You'll also typically use a DATABASE_URL environment variable to configure your database connection string.

Connect to Your First DB

Let's establish a connection to an in-memory SQLite database. SQLx uses connection pools, which manage multiple database connections efficiently to handle concurrent requests in your web service.

Try running this example:

use sqlx::SqlitePool;
use tokio;

#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
    // Use an in-memory SQLite database for simplicity
    let database_url = "sqlite::memory:"; 
    let pool = SqlitePool::connect(&database_url).await?;
    println!("Successfully connected to the database!");

    // In a real web app, 'pool' would be passed around or stored in app state.

    Ok(())
}

Structuring Data with Tables

Before we can store data, we need to define our database schema using DDL (Data Definition Language). Let's create a simple tasks table.

The sqlx::query! macro allows you to embed SQL directly into your Rust code. It even performs compile-time checks on your SQL!

use sqlx::{SqlitePool, query};
use tokio;

#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
    let database_url = "sqlite::memory:";
    let pool = SqlitePool::connect(&database_url).await?;

    // Create a 'tasks' table if it doesn't exist
    query!(
        "CREATE TABLE IF NOT EXISTS tasks (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            description TEXT NOT NULL,
            completed BOOLEAN NOT NULL DEFAULT FALSE
        )"
    ).execute(&pool).await?;

    println!("Table 'tasks' created successfully!");
    Ok(())
}

Adding New Records

Now that we have a table, let's add some data! We'll use the INSERT statement with parameter binding. This protects against SQL injection and makes your queries safer.

Notice how ? placeholders are used for parameters, and values are passed to .bind().

use sqlx::{SqlitePool, query};
use tokio;

#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
    let database_url = "sqlite::memory:";
    let pool = SqlitePool::connect(&database_url).await?;

    query!(
        "CREATE TABLE IF NOT EXISTS tasks (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            description TEXT NOT NULL,
            completed BOOLEAN NOT NULL DEFAULT FALSE
        )"
    ).execute(&pool).await?;

    // Insert a new task
    let description = "Learn SQLx";
    let completed = false;
    query!(
        "INSERT INTO tasks (description, completed) VALUES (?, ?)",
        description,
        completed
    ).execute(&pool).await?;

    println!("Task '{}' inserted successfully!", description);
    Ok(())
}

Retrieving Data with SELECT

To fetch data from our tasks table, we use the SELECT statement. SQLx provides methods like .fetch_all() or .fetch_one() to get results.

You can iterate over the returned rows and access columns by name or index.

use sqlx::{SqlitePool, query};
use tokio;

#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
    let database_url = "sqlite::memory:";
    let pool = SqlitePool::connect(&database_url).await?;

    query!(
        "CREATE TABLE IF NOT EXISTS tasks (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            description TEXT NOT NULL,
            completed BOOLEAN NOT NULL DEFAULT FALSE
        )"
    ).execute(&pool).await?;

    query!(
        "INSERT INTO tasks (description, completed) VALUES (?, ?)",
        "Learn SQLx", false
    ).execute(&pool).await?;
    query!(
        "INSERT INTO tasks (description, completed) VALUES (?, ?)",
        "Build a web app", false
    ).execute(&pool).await?;

    // Fetch and print all tasks
    println!("\n--- All Tasks ---");
    let rows = query!("SELECT id, description, completed FROM tasks")
        .fetch_all(&pool).await?;

    for row in rows {
        println!("ID: {}, Desc: {}, Done: {}", 
            row.id, row.description, row.completed);
    }
    Ok(())
}

Type-Safe Data with Structs

Manually extracting data from rows can be error-prone. SQLx's query_as! macro, combined with #[derive(FromRow)], lets you map query results directly into Rust structs!

This provides strong type safety and makes your code much cleaner and easier to work with.

use sqlx::{FromRow, SqlitePool, query, query_as};
use tokio;

#[derive(Debug, FromRow)]
struct Task {
    id: i64,
    description: String,
    completed: bool,
}

#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
    let database_url = "sqlite::memory:";
    let pool = SqlitePool::connect(&database_url).await?;

    query!(
        "CREATE TABLE IF NOT EXISTS tasks (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            description TEXT NOT NULL,
            completed BOOLEAN NOT NULL DEFAULT FALSE
        )"
    ).execute(&pool).await?;

    query!(
        "INSERT INTO tasks (description, completed) VALUES (?, ?)",
        "Learn SQLx", false
    ).execute(&pool).await?;

    // Fetch tasks directly into our Task struct
    println!("\n--- Tasks as Structs ---");
    let tasks = query_as::<_, Task>("SELECT id, description, completed FROM tasks")
        .fetch_all(&pool).await?;

    for task in tasks {
        println!("{:?}", task);
    }
    Ok(())
}

Modifying & Deleting Records

Updating and deleting data follows a similar pattern to inserting. You use UPDATE and DELETE SQL statements with parameter binding.

  • UPDATE: Changes existing rows based on a WHERE clause.
  • DELETE: Removes rows based on a WHERE clause.

Always be careful with WHERE clauses in UPDATE and DELETE to avoid unintended data loss!

use sqlx::{SqlitePool, query};
use tokio;

#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
    let database_url = "sqlite::memory:";
    let pool = SqlitePool::connect(&database_url).await?;

    query!(
        "CREATE TABLE IF NOT EXISTS tasks (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            description TEXT NOT NULL,
            completed BOOLEAN NOT NULL DEFAULT FALSE
        )"
    ).execute(&pool).await?;

    query!(
        "INSERT INTO tasks (description, completed) VALUES (?, ?)",
        "Finish lesson", false
    ).execute(&pool).await?;
    let task_id_to_update = 1;

    // Update a task
    query!(
        "UPDATE tasks SET completed = ? WHERE id = ?",
        true,
        task_id_to_update
    ).execute(&pool).await?;
    println!("Task {} updated.", task_id_to_update);

    // Delete a task (uncomment to run, careful with IDs after update)
    // let task_id_to_delete = 1;
    // query!(
    //     "DELETE FROM tasks WHERE id = ?",
    //     task_id_to_delete
    // ).execute(&pool).await?;
    // println!("Task {} deleted.", task_id_to_delete);

    Ok(())
}

Test Your DB Skills

Which of the following statements are TRUE regarding SQLx in Rust web services?

DB Integration: Key Takeaways

You've taken a significant step in building robust Rust web services by learning database integration!

  • Persistence: Databases are vital for storing application data.
  • SQLx: An async, compile-time checked SQL crate, great for web services.
  • Connection Pools: Essential for efficient and concurrent database access.
  • CRUD: We covered creating (INSERT), retrieving (SELECT), updating (UPDATE), and deleting (DELETE) data.
  • Type Safety: query_as! and #[derive(FromRow)] enable safe and ergonomic data mapping.

Next, you can explore integrating these database operations into actual web routes!

자주 묻는 질문

“데이터베이스 통합(SQLx/Diesel)” 강의는 무료인가요?

네 — “데이터베이스 통합(SQLx/Diesel)” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Learn Rust Coding 강의 전체를 잠금 해제할 수 있습니다. Learn Rust Coding 강의에는 총 3개의 강의가 포함되어 있습니다.

“데이터베이스 통합(SQLx/Diesel)”에서 뭘 배우나요?

SQLx나 Diesel과 같은 인기 ORM 또는 쿼리 빌더를 사용해 Rust 웹 서비스를 데이터베이스와 통합합니다. 브라우저에서 직접 실행하는 실습 코드로 Learn Rust Coding을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Learn Rust Coding을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Learn Rust Coding은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 3개 중 2번째 강의입니다.

“데이터베이스 통합(SQLx/Diesel)” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Learn Rust Coding 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Learn Rust Coding 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. Actix-web/Rocket으로 REST API 만들기
  2. 데이터베이스 통합(SQLx/Diesel)
  3. 인증과 권한 부여
← Learn Rust Coding(으)로 돌아가기