Docs Menu
Docs Home
/ /

MongoDB 와 Rocket 통합

이 가이드 에서는 Rocket을 웹 프레임워크 로 사용하는 Rust 웹 애플리케이션 만드는 방법을 학습 수 있습니다. Rocket은 안전하고 유형이 안전한 웹 애플리케이션을 만들 수 있는 Rust 용 프레임워크 입니다.

이 튜토리얼의 애플리케이션 다음과 같은 계층으로 구성되어 있습니다.

  • 데이터베이스 계층: MongoDB 데이터 저장 및 검색을 제공합니다.

  • 애플리케이션 계층: Rocket은 HTTP 요청, 라우팅 및 로직 처리 처리합니다.

  • 프레젠테이션 계층: HTML 템플릿은 웹 페이지에 레스토랑 데이터를 렌더링합니다.

MongoDB Rust 및 Rocket과 통합하면 Rust의 메모리 안전성 및 성능 특성과 MongoDB의 유연한 문서 모델 함께 사용할 수 있습니다. Rocket의 유형 시스템은 API 엔드포인트에 대한 컴파일 타임을 보장 하며, MongoDB의 문서 저장 Serde와 같은 Rust의 직렬화 라이브러리와 함께 효율적으로 작동합니다.

Rust, Rocket, MongoDB 의 조합은 고성능, 유형 안전성, 확장성 필요한 애플리케이션을 지원합니다. 따라서 이 스택 처리량이 많은 API, 마이크로서비스 또는 엄격한 성능 보장 필요한 시스템과 같은 실제 애플리케이션에 적합하도록 잘 설계되었습니다.

이 튜토리얼에서는 Rust 와 Rocket을 사용하는 웹 애플리케이션 빌드 방법을 설명합니다. 애플리케이션 샘플 레스토랑 데이터에 액세스하고, 데이터를 쿼리하고, 스타일이 적용된 HTML 템플릿을 통해 데이터를 표시합니다. 이 튜토리얼에는 MongoDB Atlas 에서 호스팅되는 MongoDB cluster 에 연결하고 데이터베이스 의 데이터에 액세스하고 표시하는 방법에 대한 지침도 포함되어 있습니다.

Rocket 없이 Rust 운전자 사용하여 MongoDB 에 연결하려는 경우 Rust 드라이버 빠른 시작 튜토리얼을 참조하세요.

이 섹션의 단계에 따라 프로젝트 종속성을 설치하고, Atlas cluster 만들고, 애플리케이션 구조를 설정하다 .

1

빠른 시작 애플리케이션 만들려면 개발 환경에 다음 소프트웨어를 설치하세요.

전제 조건
참고 사항

rustup를 사용하여 안정적인 최신 버전을 설치합니다.

코드 편집기

터미널 앱 및 셸

MacOS 사용자의 경우 터미널 또는 유사한 앱을 사용하세요. Windows 사용자의 경우 PowerShell을 사용하세요.

2

MongoDB Atlas 는 MongoDB 배포를 호스팅하는 완전 관리형 클라우드 데이터베이스 서비스입니다. MongoDB deployment 없는 경우,MongoDB 시작하기 튜토리얼을 완료하여 무료로 MongoDB cluster 생성할 수 있습니다( 크레딧 카드 필요 없음). MongoDB 시작하기 튜토리얼에서는 sample_restaurants 이 튜토리얼에서 사용되는 데이터베이스 포함하여 샘플 데이터 세트를 클러스터 에 로드하는 방법도 보여줍니다.

MongoDB cluster 에 연결하려면 연결 URI를 사용해야 합니다. 연결 URI를 조회 방법을 학습 MongoDB 시작하기 튜토리얼의 연결 문자열 추가하기 섹션을 참조하세요.

중요

연결 string 을 안전한 위치 에 저장합니다.

3

터미널에서 다음 명령을 실행하여 새 Rust 프로젝트 만듭니다.

cargo new rocket-quickstart
cd rocket-quickstart

이렇게 하면 기본값 디렉토리 구조를 사용하여 rocket-quickstart 이라는 새 Rust 프로젝트 생성됩니다.

4

Cargo.toml 파일 로 이동하여 해당 내용을 다음 코드로 바꿉니다.

Rocket-quickstart/Cargo.toml
[package]
name = "rocket-quickstart"
version = "0.1.0"
edition = "2024"
[dependencies]
rocket = {version = "0.5.0", features = ["json"]}
serde = "1.0.136"
dotenv = "0.15.0"
tokio = {version = "1.32.0", features = ["full"]}
rocket_dyn_templates = { version = "0.1.0", features = ["handlebars"] }
[dependencies.mongodb]
version = "3.4.1"

rocket-quickstart 디렉토리 에서 다음 명령을 실행 하여 종속성을 설치합니다.

cargo build

이 명령은 다음 종속성을 설치합니다.

  • Rocket, JSON 지원 프로젝트의 웹 프레임워크

  • 데이터 직렬화를 위한Serde

  • MongoDB Rust 운전자, MongoDB 작업용

  • 비동기 런타임Tokio

  • Rocket_dyn_ Templates, 핸들바 템플릿 렌더링용

5

이 애플리케이션 에 필요한 디렉토리와 파일을 만들려면 rocket-quickstart 디렉토리 에서 다음 명령을 실행 .

mkdir -p src/models src/repository src/api templates static
touch src/models/restaurant.rs src/repository/mongodb_repo.rs src/api/restaurant_api.rs
touch src/models.rs src/repository.rs src/api.rs
touch templates/index.hbs static/style.css

이 명령은 목적에 따라 파일을 서로 다른 디렉토리로 그룹화하는 Rust의 모듈식 아키텍처 패턴 따르도록 프로젝트 구성합니다. 명령을 실행 하면 다음과 같은 디렉토리가 생성됩니다.

  • src/models: 레스토랑 데이터를 모델링하기 위한 데이터 구조를 포함합니다.

  • src/repository: 데이터베이스 액세스 로직을 포함합니다.

  • src/api: HTTP 엔드포인트 핸들러를 포함합니다.

  • templates: 데이터 렌더링을 위한 핸들바 템플릿 파일 포함합니다.

  • static: 스타일링을 위한 CSS 파일 포함합니다.

또한 이 명령은 각 src/ 디렉토리 에 대한 모듈 선언 파일을 생성하며, 이 파일에는 해당 디렉토리에서 사용 가능한 모듈이 나열되어 있으며 이를 통해 애플리케이션 전체에서 액세스 할 수 있습니다.

프로젝트 구조 및 종속성을 설정한 후 이 섹션의 단계에 따라 MongoDB 에 연결하고 데이터 모델을 설정하다 합니다.

1

rocket-quickstart 디렉토리 에서 다음 명령을 실행 하여 MONGO_URI 환경 변수를 연결 URI로 설정합니다.

export MONGO_URI="<connection URI>"

자리 표시자를 <connection URI> 이전 단계에서저장한 연결 URI로 바꿉니다.

2

src/models/restaurant.rs 파일 로 이동하여 다음 코드를 붙여넣습니다.

Rocket-quickstart/src/models/restaurant.rs
use serde::{Deserialize, Serialize};
use mongodb::bson::oid::ObjectId;
#[derive(Debug, Serialize, Deserialize)]
pub struct Restaurant {
#[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
pub id: Option<ObjectId>,
pub name: Option<String>,
pub borough: Option<String>,
pub cuisine: Option<String>,
pub address: Option<Address>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Address {
pub building: Option<String>,
pub street: Option<String>,
pub zipcode: Option<String>,
}

이 파일 sample_restaurants.restaurants 샘플 컬렉션 의 데이터를 나타내는 RestaurantAddress 모델을 정의합니다.

프로젝트 파일에서 모델을 사용하려면 restaurant.rs 파일 에 해당하는 restaurant 모듈을 등록해야 합니다. src/models.rs 파일 로 이동하여 다음 코드를 붙여넣습니다.

Rocket-quickstart/src/models.rs
pub mod restaurant;
pub use restaurant::*;
3

src/repository/mongodb_repo.rs 파일 로 이동하여 다음 코드를 붙여넣습니다.

Rocket-quickstart/src/ 리포지토리/mongodb_repo.rs
use std::env;
use mongodb::{
bson::{extjson::de::Error, doc},
Client, Collection
};
use crate::models::Restaurant;
pub struct MongoRepo {
col: Collection<Restaurant>,
}
impl MongoRepo {
pub async fn init() -> Self {
dotenv::dotenv().ok();
let uri = match env::var("MONGO_URI") {
Ok(v) => v.to_string(),
Err(_) => format!("Error loading env variable"),
};
let client = Client::with_uri_str(uri).await.unwrap();
let db = client.database("sample_restaurants");
let col: Collection<Restaurant> = db.collection("restaurants");
MongoRepo { col }
}
pub async fn get_all_restaurants(&self) -> Result<Vec<Restaurant>, Error> {
let mut cursor = self
.col
.find(doc! {})
.await
.map_err(|e| Error::DeserializationError { message: e.to_string() })?;
let mut restaurants: Vec<Restaurant> = Vec::new();
while cursor.advance().await.map_err(|e| Error::DeserializationError { message: e.to_string() })? {
match cursor.deserialize_current() {
Ok(restaurant) => restaurants.push(restaurant),
Err(e) => {
// Skips documents that can't be deserialized and logs the error
eprintln!("Warning: Skipping document due to deserialization error: {}", e);
continue;
}
}
}
Ok(restaurants)
}
pub async fn get_filtered_restaurants(&self) -> Result<Vec<Restaurant>, Error> {
let filter = doc! {
"borough": "Queens",
"name": { "$regex": "Moon", "$options": "i" }
};
let mut cursor = self
.col
.find(filter)
.await
.map_err(|e| Error::DeserializationError { message: e.to_string() })?;
let mut restaurants: Vec<Restaurant> = Vec::new();
while cursor.advance().await.map_err(|e| Error::DeserializationError { message: e.to_string() })? {
match cursor.deserialize_current() {
Ok(restaurant) => restaurants.push(restaurant),
Err(e) => {
// Skips documents that can't be deserialized and logs the error
eprintln!("Warning: Skipping document due to deserialization error: {}", e);
continue;
}
}
}
Ok(restaurants)
}
}

이 파일 sample_restaurants 데이터베이스 의 restaurants 컬렉션 에 액세스합니다. 그런 다음 컬렉션 문서를 조회 다음과 같은 쿼리 메서드를 정의합니다.

  • get_all_restaurants(): restaurants 컬렉션 의 모든 문서를 조회합니다.

  • get_filtered_restaurants(): restaurants 컬렉션 에서 borough 값이 "Queens" 이고 name 값이 "Moon"인 문서를 검색합니다. $options 값은 이 name 쿼리 대소문자를 구분하지 않도록 지정합니다.

이 코드에는 역직렬화할 수 없는 문서를 건너뛰는 로직도 포함되어 있습니다. 이렇게 하면 문서에 Restaurant 모델에 정의된 각 필드 포함되어 있지 않을 때 오류가 발생하지 않습니다.

그런 다음 src/repository.rs에 모듈을 등록합니다.

Rocket-quickstart/src/ 리포지토리.rs
pub mod mongodb_repo;
pub use mongodb_repo::*;

데이터 계층을 설정한 후 이 섹션의 단계에 따라 사용자 인터페이스에 대한 Rocket API 핸들러 및 템플릿을 생성합니다.

1

src/api/restaurant_api.rs 파일 로 이동하여 다음 코드를 붙여넣습니다.

Rocket-quickstart/src/api/restaurant_api.rs
use rocket::{State, serde::json::Json, get, http::Status};
use crate::{models::Restaurant, repository::MongoRepo};
#[get("/restaurants")]
pub async fn get_all_restaurants(db: &State<MongoRepo>) -> Result<Json<Vec<Restaurant>>, Status> {
let restaurants = db.get_all_restaurants().await;
match restaurants {
Ok(restaurants) => Ok(Json(restaurants)),
Err(_) => Err(Status::InternalServerError),
}
}
#[get("/restaurants/browse")]
pub async fn get_filtered_restaurants(db: &State<MongoRepo>) -> Result<Json<Vec<Restaurant>>, Status> {
let restaurants = db.get_filtered_restaurants().await;
match restaurants {
Ok(restaurants) => Ok(Json(restaurants)),
Err(_) => Err(Status::InternalServerError),
}
}

이 코드는 /restaurants/restaurants/browse 엔드포인트를 생성합니다. 이러한 엔드포인트는 레스토랑 정보를 동적으로 로드하고 표시하기 위해 프론트엔드 JavaScript 코드에서 사용할 쿼리 메서드의 JSON 데이터를 제공합니다.

그런 다음 src/api.rs에 API 모듈을 등록합니다.

Rocket-quickstart/src/api.rs
pub mod restaurant_api;
pub use restaurant_api::*;
2

src/main.rs 파일 로 이동하여 다음 코드를 붙여넣습니다.

Rocket-quickstart/src/main.rs
mod api;
mod models;
mod repository;
#[macro_use]
extern crate rocket;
use rocket::{get, fs::FileServer, response::content::RawHtml};
use rocket_dyn_templates::Template;
use api::restaurant_api::{get_all_restaurants, get_filtered_restaurants};
use repository::MongoRepo;
#[get("/")]
async fn index() -> RawHtml<String> {
let template_content = std::fs::read_to_string("templates/index.hbs").unwrap_or_else(|_| {
r#"<!DOCTYPE html>
<html><head><title>Error</title></head>
<body><h1>Template not found</h1></body></html>"#.to_string()
});
RawHtml(template_content)
}
#[get("/browse")]
async fn browse() -> RawHtml<String> {
let template_content = std::fs::read_to_string("templates/index.hbs").unwrap_or_else(|_| {
r#"<!DOCTYPE html>
<html><head><title>Error</title></head>
<body><h1>Template not found</h1></body></html>"#.to_string()
});
RawHtml(template_content)
}
#[rocket::launch]
async fn rocket() -> _ {
let db = MongoRepo::init().await;
rocket::build()
.manage(db)
.mount("/", routes![index, browse, get_all_restaurants, get_filtered_restaurants])
.mount("/static", FileServer::from("static/"))
.attach(Template::fairing())
}

이 파일 Rocket 웹 서버 설정하고 다음 경로를 구성합니다.

  • /: 모든 레스토랑을 표시하는 메인 인덱스 페이지의 형식을 지정하는 HTML 코드를 반환합니다.

  • /browse: 필터링된 레스토랑 페이지의 형식을 지정하는 HTML 코드를 반환합니다.

3

templates/index.hbs 파일 로 이동하여 다음 코드를 붙여넣습니다.

Rocket-quickstart/ Templates/ 인덱스.hbs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Restaurant Explorer</title>
<link rel="stylesheet" href="/static/style.css">
</head>
<body>
<div class="container">
<header>
<h1>Restaurant Directory</h1>
</header>
<div class="controls">
<a href="/" class="btn" id="all-btn">All Restaurants</a>
<a href="/browse" class="btn" id="browse-btn">Browse Filtered</a>
</div>
<div id="restaurants-container">
<div class="loading">Loading restaurants...</div>
</div>
</div>
<script>
// Sets the active button based on the current path
const path = window.location.pathname;
if (path === '/browse') {
document.getElementById('browse-btn').classList.add('active');
} else {
document.getElementById('all-btn').classList.add('active');
}
// Loads the restaurant documents
async function loadRestaurants() {
try {
const apiEndpoint = path === '/browse' ? '/restaurants/browse' : '/restaurants';
const response = await fetch(apiEndpoint);
const restaurants = await response.json();
displayRestaurants(restaurants);
} catch (error) {
document.getElementById('restaurants-container').innerHTML =
'<div class="error">Error loading restaurants. Please try again later.</div>';
}
}
// Displays restaurants in a grid format
function displayRestaurants(restaurants) {
const container = document.getElementById('restaurants-container');
if (restaurants.length === 0) {
container.innerHTML = '<div class="error">No restaurants found.</div>';
return;
}
const restaurantsHTML = restaurants.map(function(restaurant) {
return '<div class="restaurant-card">' +
'<div class="restaurant-name">' + (restaurant.name || 'Unknown Name') + '</div>' +
(restaurant.cuisine ? '<div class="cuisine-tag">' + restaurant.cuisine + '</div>' : '') +
(restaurant.borough ? '<div class="restaurant-info"><strong>Borough:</strong> ' + restaurant.borough + '</div>' : '') +
(restaurant.restaurant_id ? '<div class="restaurant-info"><strong>ID:</strong> ' + restaurant.restaurant_id + '</div>' : '') +
(restaurant.address ?
'<div class="address">' +
(restaurant.address.building ? restaurant.address.building + ' ' : '') +
(restaurant.address.street ? restaurant.address.street : '') +
(restaurant.address.zipcode ? ', ' + restaurant.address.zipcode : '') +
'</div>' : '') +
'</div>';
}).join('');
container.innerHTML = '<div class="restaurants-grid">' + restaurantsHTML + '</div>';
}
loadRestaurants();
</script>
</body>
</html>

핸들바 템플릿은 JavaScript 사용하여 API 엔드포인트에서 레스토랑 데이터를 동적으로 로드하고 대화형 그리드 레이아웃으로 레스토랑을 표시하는 반응형 웹 인터페이스를 만듭니다.

4

static/style.css에 다음 코드를 추가합니다.

Rocket-quickstart/static/style.css
/* Modern, clean styling for the restaurant app */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
line-height: 1.6;
color: #333;
background: rgb(198, 240, 209);
min-height: 100vh;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
header {
text-align: center;
margin-bottom: 40px;
color: rgb(0, 0, 0);
}
h1 {
font-size: 3rem;
margin-bottom: 10px;
}
.subtitle {
font-size: 1.2rem;
opacity: 0.9;
}
.controls {
display: flex;
justify-content: center;
gap: 20px;
margin-bottom: 30px;
}
.btn {
background: white;
color: #0d8958;
border: none;
padding: 12px 24px;
border-radius: 25px;
font-weight: 600;
text-decoration: none;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(0,0,0,0.3);
}
.btn.active {
background: #0d8958;
color: white;
}
.restaurants-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
gap: 20px;
margin-top: 20px;
}
.restaurant-card {
background: white;
border-radius: 15px;
padding: 20px;
box-shadow: 0 8px 25px rgba(0,0,0,0.1);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.restaurant-card:hover {
transform: translateY(-5px);
box-shadow: 0 12px 35px rgba(0,0,0,0.15);
}
.restaurant-name {
font-size: 1.4rem;
font-weight: 700;
color: #000000;
margin-bottom: 10px;
}
.restaurant-info {
margin-bottom: 8px;
}
.restaurant-info strong {
color: #555;
}
.cuisine-tag {
display: inline-block;
background: #0d8958;
color: white;
padding: 4px 12px;
border-radius: 15px;
font-size: 0.85rem;
font-weight: 600;
margin-top: 10px;
}
.address {
color: #666;
font-style: italic;
margin-top: 8px;
}
.loading {
text-align: center;
color: white;
font-size: 1.2rem;
margin: 40px 0;
}
.error {
text-align: center;
color: #ff6b6b;
background: white;
padding: 20px;
border-radius: 10px;
margin: 20px 0;
}
@media (max-width: 768px) {
.container {
padding: 10px;
}
h1 {
font-size: 2rem;
}
.controls {
flex-direction: column;
align-items: center;
}
.restaurants-grid {
grid-template-columns: 1fr;
}
}

이 CSS 파일 웹 페이지에 표시되는 레스토랑 데이터의 스타일을 지정합니다.

마지막으로 이 섹션의 단계에 따라 웹 애플리케이션 실행 하고 브라우저 인터페이스를 사용하여 레스토랑 데이터를 탐색합니다.

1

프로젝트 디렉토리 로 이동하여 다음 명령을 실행 .

cargo run

성공적인 하면 명령 출력은 다음 예시 와 유사합니다.

Configured for debug.
>> address: 127.0.0.1
>> port: 8000
...
Routes:
>> (index) GET /
>> (browse) GET /browse
>> (get_all_restaurants) GET /restaurants
>> (get_filtered_restaurants) GET /restaurants/browse
>> (FileServer: static/) GET /static/<path..> [10]
Fairings:
>> Templating (ignite, liftoff, request)
>> Shield (liftoff, response, singleton)
Shield:
>> X-Frame-Options: SAMEORIGIN
>> Permissions-Policy: interest-cohort=()
>> X-Content-Type-Options: nosniff
Templating:
>> directory: templates
>> engines: ["hbs"]
Rocket has launched from http://127.0.0.1:8000
2

127.0.0.1브라우저에서 http://:8000 를 엽니다. 초기 방문 페이지에는 컬렉션 의 모든 레스토랑이 표시됩니다.sample_restaurants.restaurants

모든 레스토랑이 표시되는 랜딩 페이지

그런 다음 Browse Filtered 버튼을 클릭하여 이름에 "Moon" 가 포함된 퀸즈의 레스토랑을 확인합니다.

필터링된 레스토랑을 표시하는 웹 페이지
3

터미널에서 다음 명령을 실행 하여 기본 API 엔드포인트를 직접 테스트할 수도 있습니다.

curl http://127.0.0.1:8000/restaurants
curl http://127.0.0.1:8000/restaurants/browse

이러한 엔드포인트는 웹 인터페이스에 표시되는 것과 동일한 데이터를 JSON 문서 형식으로 반환합니다.

빠른 시작 튜토리얼을 완료한 것을 축하합니다! 이 단계를 완료하면 MongoDB deployment 에 연결하고, 샘플 레스토랑 데이터에 대한 쿼리를 실행하고, 로컬로 호스팅되는 웹 인터페이스에 결과를 표시하는 Rust and Rocket 웹 애플리케이션 이 생성됩니다.

Rust, Rocket 및 MongoDB 에 대해 자세히 학습 다음 리소스를 참조하세요.

돌아가기

소스 보기

이 페이지의 내용