Rust 애플리케이션을 배포하는 방법은 무엇인가요?
Rust는 채택자에게 제공하는 다양한 기능으로 인해 4년 이상 StackOverflow 개발자 설문조사에서 가장 많은 찬사를 받은 언어입니다.
Mozilla는 안정적이고 성능이 뛰어나며 개발자 친화적인 Rust를 만들었습니다. Rust는 개발자가 언어의 주요 타깃인 C++ 및 C와 유사한 구문을 가지고 있습니다.
Rust는 또한 다른 언어를 사용하는 개발자가 직면하는 관련 함정을 피하는 모델을 통해 메모리 안전성과 동시성에 중점을 둡니다.
이 글에서는 Rust에서 API를 빌드하여 이점을 활용하는 방법을 배웁니다. Back4app의 무료 컨테이너화 서비스에서 Rust 애플리케이션을 제작, 컨테이너화 및 배포하는 과정을 통해 배우게 됩니다.
Rust 사용의 장점
프로젝트에 Rust를 사용하면 많은 이점을 얻을 수 있습니다. 다음은 몇 가지 중요한 이점입니다:
제로 비용 추상화
Rust는 추가적인 런타임 비용을 부과하지 않고 높은 수준의 추상화를 제공합니다. 이는 코드에서 사용하는 추상화(함수, 이터레이터 또는 제네릭)로 인해 프로그램이 느려지지 않는다는 것을 의미합니다.
Rust 컴파일러는 수동으로 컴파일된 로우레벨 코드에 대한 추상화를 최적화합니다. Rust는 표현력이 풍부한 저수준의 세분화된 성능 제어 사이의 간극을 메워줍니다.
동시 프로그램에서 메모리 안전을 위한 두려움 없는 동시성 접근 방식
Rust는 안전성과 효율성을 특징으로 하는 동시성에 대해 ‘두려움 없는 접근 방식’을 취합니다. Rust의 동시성 모델은 컴파일 시 데이터 경합을 방지하기 위해 소유권 모델과 타입 검사를 활용합니다.
이 기능을 사용하면 교착 상태 및 경쟁 조건과 같은 공유 상태 동시성의 단점 없이 멀티스레드 앱을 작성할 수 있습니다.
고급 유형 시스템 및 소유권 모델
Rust의 유형 시스템과 소유권 규칙은 메모리 안전성을 강화하는 데 도움이 되는 고유한 기능입니다.
소유권 모델은 차용 검사기를 사용하여 각 데이터에 단일 소유자가 있는지 확인하고 수명 주기를 관리하여 매달린 포인터 및 메모리 누수와 같은 문제를 방지합니다.
플랫폼 간 호환성 및 통합
크로스 플랫폼 애플리케이션을 구축하려는 경우 Rust는 훌륭한 선택입니다. 코드를 한 번 작성하면 기존 코드베이스를 크게 변경하지 않고도 여러 플랫폼에서 컴파일할 수 있습니다.
Rust는 다른 프로그래밍 언어, 특히 C와 잘 통합되어 웹 어셈블리 및 임베디드 시스템 작업에 적합한 언어입니다.
Rust 사용의 한계
Rust로 프로덕션급 앱을 빌드하는 동안 몇 가지 문제가 발생할 수 있습니다.
여기에는 Rust의 가파른 학습 곡선, 메모리 및 기타 검사로 인한 긴 컴파일 시간, 새롭고 작은 에코시스템 등이 포함될 수 있습니다.
Rust로 프로덕션급 제품을 빌드할 때 발생할 수 있는 몇 가지 단점이 있습니다. 다음은 몇 가지 단점입니다:
Rust 프로그래밍의 가파른 학습 곡선
다른 인기 언어(Go, Python, JavaScript 등)에 비해 Rust를 마스터하고 이 언어로 프로덕션급 애플리케이션을 구축하는 데는 상당한 시간이 걸립니다.
그렇다고 해서 Rust 사용을 주저할 필요는 없습니다. Rust를 마스터하면 앱 빌드 및 배포를 매우 생산적으로 수행할 수 있으며 Rust 사용의 모든 이점을 누릴 수 있습니다.
Rust 프로그램은 컴파일 시간이 길다
컴파일 시 메모리 및 동시성 검사와 다른 여러 요인이 결합되어 Rust 프로그램의 컴파일 시간이 길어집니다.
애플리케이션의 규모에 따라 컴파일 시간이 길어지면 개발 또는 프로덕션 단계에서 병목 현상이 발생할 수 있습니다.
Rust는 더 작은 라이브러리 생태계를 가지고 있습니다.
Rust는 다른 인기 언어에 비해 비교적 새로운 언어이며, 사용할 수 있는 라이브러리 생태계도 제한적입니다.
많은 라이브러리(상자)가 아직 프로덕션 버전으로 제공 중이며, AreWeWebYet과 같은 웹 사이트에서 Rust에서 웹 앱을 빌드하는 데 사용할 수 있는 프로덕션 지원 상자에 대한 개요를 확인할 수 있습니다.
Rust 배포 옵션
Rust는 이미 널리 채택되고 있으므로 앱에 대해 선택할 수 있는 배포 옵션이 다양합니다.
대부분의 Rust 배포 옵션은 IaaS 또는 CaaS 기반 플랫폼입니다. 프로젝트 사양에 따라 하나를 선택할 수 있습니다.
AWS와 같은 서비스형 인프라(IaaS)
서비스형 인프라(IaaS) 제공업체는 클라우드의 가상 머신에서 실행되는 앱을 배포하고 관리할 수 있는 인프라를 제공합니다.
IaaS 플랫폼이 제공하는 서비스를 사용하여 Linux, Windows, macOS 및 기타 Rust 지원 운영 체제를 실행하는 가상 머신에 Rust 앱을 배포할 수 있습니다.
다음은 인기 있는 IaaS 플랫폼 목록입니다:
- Amazon Web Services
- Digital Ocean
- Google Cloud
- Linode
- Microsoft Azure
Back4app 컨테이너와 같은 서비스형 컨테이너화
컨테이너화 서비스(CaaS) 제공업체는 컨테이너화 기술을 통해 앱 배포를 용이하게 할 수 있도록 도와줍니다.
컨테이너화를 지원하는 플랫폼에 애플리케이션을 배포하려면 애플리케이션과 모든 종속 요소를 격리된 컨테이너에 번들로 묶어야 합니다.
컨테이너는 격리되어 이동이 가능하지만, CaaS 제공업체의 기능 범위 내에서 작업해야 합니다.
일부 IaaS 제공업체는 CaaS 기능을 제공합니다. 또한 유연한 CaaS 기능만 단독으로 제공하는 플랫폼도 있습니다.
다음은 몇 가지 CaaS 플랫폼 목록입니다:
- Oracle Container Service
- Back4app
- Mirantix
- Docker Enterprise
Rust 애플리케이션 배포 프로세스
이 섹션에서는 Rust 앱을 Back4app의 CaaS 플랫폼에 배포하는 방법에 대해 설명합니다.
Back4app이란 무엇인가요?
Back4app은 모바일, 웹 및 기타 애플리케이션 유형을 위한 모든 유형의 백엔드 서비스를 만들고 배포하는 데 활용할 수 있는 클라우드 플랫폼입니다.
Back4app은 플랫폼에서 앱 배포를 간소화하는 데 사용할 수 있는 AI 에이전트를 제공합니다. 이를 사용하여 GitHub 리포지토리를 관리하고, 클라우드에 코드를 쉽게 배포하고, 실행 중인 앱을 관리할 수 있습니다.
Back4app의 백엔드 서버에서 사용자 지정 컨테이너는 CaaS 기능을 통해 배포 및 실행할 수 있습니다.
컨테이너 이미지를 사용하면 서버 아키텍처를 유지 관리할 걱정 없이 앱의 로직을 확장할 수 있습니다.
구축 및 배포
이 튜토리얼을 따라하려면 컴퓨터에 Rust가 설치되어 있어야 합니다. 사용 가능한 다양한 설치 옵션은 Rust 설치 페이지에서 확인할 수 있습니다.
Rust를 설치했으면 터미널 세션을 생성하고 다음 명령을 실행하여 새 Rust 프로젝트를 초기화합니다.
mkdir back4app-rust-deployment && cd back4app-rust-deployment && cargo init
명령을 실행하면 방금 만든 새 디렉터리에 cargo.toml
파일이 표시됩니다. 이 cargo.toml
파일을 사용하여 종속성을 관리합니다.
그런 다음, 앱을 빌드할 때 이러한 종속성을 설치하려면 Cargo.toml
파일의 [dependencies]
섹션에 이러한 지시어를 추가하세요.
[dependencies]
actix-web = "4.0"
serde = { version = "1.0", features = ["derive"] }
serde_derive = { version = "1.0" }
serde_json = "1.0"
lazy_static = "1.4"
actix-web
상자는 라우팅 및 기타 HTTP 관련 함수를 제공하고, serde
, serde_derive
및 serde_json
상자는 다양한 JSON 연산을 위한 함수를 제공하며, lazy_static
상자는 런타임에 API의 인메모리 데이터 저장소를 제공합니다.
main.rs]()
파일 상단에 이러한 가져오기를 추가합니다:
use serde::{Serialize, Deserialize};
use actix_web::{web, App, HttpServer, HttpResponse, Error};
use std::sync::Mutex;
extern crate lazy_static;
use lazy_static::lazy_static;
구조체를 사용하여 필요한 필드에 따라 API의 데이터 구조를 정의할 수 있습니다. 다음은 ID, 사용자 이름, 이메일이 있는 사람을 나타내는 구조입니다.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Person {
pub id: i32,
pub username: String,
pub email: String,
}
파생(직렬화, 역직렬화, 디버그, 복제)]
는 Person
구조체에 액세스하고 그 함수를 사용하기 위한 serde_derive
상자의 구현입니다.
다음은 lazy_static
상자를 사용하여 Person
구조체 유형에 따라 API의 인메모리 데이터 저장소를 설정하는 방법입니다:
lazy_static! {
static ref DATA_STORE: Mutex<Vec<Person>> = Mutex::new(Vec::new());
}
People 구조체에 대해 느리게 초기화되고 스레드에 안전한 공유 저장소를 만들었습니다. 핸들러 함수에서 이를 사용하여 데이터를 저장하고 검색할 것입니다.
POST 핸들러 함수
POST 요청 핸들러 함수는 Person
구조체의 JSON 표현을 입력으로 받습니다. 그런 다음 요청 시 클라이언트에 HTTP 응답과 오류를 반환합니다.
이 코드 블록을 [main.rs]()
파일에 추가하여 POST 요청 핸들러 기능을 구현하세요.
async fn create_person(new_person: web::Json<Person>) -> Result<HttpResponse, Error> {
let mut data_store = DATA_STORE.lock().unwrap();
let new_id = data_store.len() as i32 + 1;
let mut person = new_person.into_inner();
person.id = new_id;
data_store.push(person.clone());
Ok(HttpResponse::Ok().json(person))
}
create_person은
공유 데이터 저장소에 액세스하여 Person
구조체에 대한 새 ID를 생성하고 JSON 표현인 Person을
구조체로 변환하여 데이터 저장소에 푸시하는 비동기 함수입니다.
요청이 성공하면 이 함수는 200 상태 코드와 함께 데이터베이스에 입력된 데이터를 클라이언트에 제공합니다.
GET 핸들러 함수
여기서 GET 핸들러 함수는 데이터 저장소의 모든 데이터를 읽고 클라이언트에 JSON으로 반환하는 것을 보여줍니다.
이 코드 블록을 프로젝트에 추가하여 GET 핸들러 함수를 구현하세요.
async fn get_people() -> Result<HttpResponse, Error> {
let data_store = DATA_STORE.lock().unwrap();
let people: Vec<Person> = data_store.clone();
Ok(HttpResponse::Ok().json(people))
}
get_people
함수는 데이터 저장소에 액세스하여 콘텐츠를 클라이언트에 응답으로 쓰는 비동기 함수입니다.
요청이 성공하면 함수는 데이터 저장소에 있는 모든 데이터와 함께 200
상태 코드로 클라이언트에 응답합니다.
PUT 핸들러 함수
PUT 요청 핸들러 함수는 객체의 필드를 기반으로 데이터 저장소의 항목을 업데이트해야 합니다.
API에 PUT 핸들러 함수를 구현하는 방법은 다음과 같습니다:
async fn update_person(
id: web::Path<i32>,
person_update: web::Json<Person>,
) -> Result<HttpResponse, Error> {
let mut data_store = DATA_STORE.lock().unwrap();
if let Some(person) = data_store.iter_mut().find(|p| p.id == *id) {
*person = person_update.into_inner();
Ok(HttpResponse::Ok().json("Person updated successfully"))
} else {
Ok(HttpResponse::NotFound().json("Person not found"))
}
}
update_person
함수는 요청에서 ID와 새 항목을 가져옵니다. 그런 다음 데이터 저장소를 탐색하여 해당 항목이 존재하는 경우 새 항목으로 바꿉니다.
삭제 핸들러 함수
DELETE 요청 함수는 하나의 인수를 받는데, 요청된 요청의 ID 필드입니다. 함수가 실행되면 데이터 저장소에서 해당 ID를 가진 항목이 삭제됩니다.
이 DELETE 핸들러 함수 구현을 프로그램에 추가하세요.
// DELETE
pub async fn delete_person(id: web::Path<i32>) -> Result<HttpResponse, Error> {
let mut data_store = DATA_STORE.lock().unwrap();
if let Some(index) = data_store.iter().position(|p| p.id == *id) {
data_store.remove(index);
Ok(HttpResponse::Ok().json("Deleted successfully"))
} else {
Ok(HttpResponse::NotFound().json("Person not found"))
}
}
delete_person
함수는 데이터 저장소에서 지정된 ID를 가진 항목을 삭제합니다. 작업 상태에 따라 이 함수는 클라이언트에 문자열과 상태 코드를 반환합니다.
핸들러 함수를 경로에 매핑하기
엔드포인트를 정의한 후에는 핸들러 함수 기능에 액세스하기 위해 핸들러 함수에 대한 경로를 매핑해야 합니다.
핸들러 함수에 경로를 할당하는 방법은 다음과 같습니다:
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.route("/person", web::post().to(create_person))
.route("/people", web::get().to(get_people))
.route("/person/{id}", web::put().to(update_person))
.route("/person/{id}", web::delete().to(delete_person))
})
.bind("0.0.0.0:8000")?
.run()
.await
}
주요
기능은 핸들러 함수에 경로를 매핑한 후 서버를 설정하는 비동기 함수입니다.
HttpServer::new
함수는 HTTP 서버를 인스턴스화하고, App::new()
함수는 새 앱 인스턴스를 생성하며, route
함수는 경로를 핸들러 함수에 매핑합니다.
바인드
함수는 새 앱의 주소를 지정하고 실행
함수는 앱을 실행합니다.
Docker로 Rust 앱 컨테이너화하기
Docker는 시장에서 가장 인기 있는 컨테이너화 기술입니다. 휴대성을 위해 Docker로 Rust 앱을 컨테이너화하여 몇 번의 클릭만으로 Back4app에 배포할 수 있습니다.
이 명령을 실행하여 프로젝트에 새 Docker파일을 생성합니다:
touch Dockerfile
도커파일을 열고 이 빌드 지침을 도커파일에 추가합니다:
# Use Rust Nightly as the base image
FROM rustlang/rust:nightly
# Set the working directory inside the container
WORKDIR /usr/src/myapp
# Copy the current directory contents into the container
COPY . .
# Build the application
RUN cargo build --release
# Expose port 8000
EXPOSE 8000
# Define the command to run the application
CMD ["./target/release/back4app-rust-deployment"]
이 지침은 Docker로 Rust 애플리케이션을 컨테이너화하기 위한 기본 이미지와 빌드 지침을 지정합니다.
다음은 도커파일의 내용을 분석한 것입니다:
FROM rustlang/rust:nightly
지시어는 Docker파일의 기본 이미지를 지정합니다. Docker는 리포지토리에서 이 이미지를 가져와서 프로그램을 빌드합니다.WORKDIR /usr/src/myapp
지시어는 컨테이너 내부에서 애플리케이션의 작업 디렉터리를 설정합니다.COPY ..
지시어는 작업 디렉토리의 모든 내용을 컨테이너의 현재 작업 디렉토리에 복사합니다.RUN cargo build --release
지시문은 컨테이너에서 애플리케이션을 빌드하는 명령을 실행합니다.EXPOSE 8000
지시어는 들어오는 요청에 대해 컨테이너의 포트8000을
노출합니다.CMD ["./target/release/back4app-rust-deployment"]
는 프로그램(빌드 작업의 실행 파일)을 실행합니다.
Docker파일을 작성했으면 Back4app의 컨테이너 서비스에 컨테이너를 배포할 수 있습니다.
Back4app에 컨테이너 배포하기
컨테이너를 배포하려면 Back4app에서 계정을 만들어야 합니다.
Back4app 계정을 만드는 단계는 다음과 같습니다.
- Back4app 웹사이트 방문하기
- 페이지 오른쪽 상단의 가입 버튼을 클릭합니다.
- 가입 양식을 작성하여 제출하면 계정을 만들 수 있습니다.
이제 Back4app 계정을 성공적으로 만들었으면 로그인하고 랜딩 페이지 오른쪽 상단에 있는 새 앱
버튼을 클릭합니다.
앱을 빌드하는 방법을 선택할 수 있는 옵션이 표시됩니다. 서비스형 컨테이너
옵션을 선택합니다.
이제 Github 계정을 Back4app 계정에 연결하고 계정 또는 특정 프로젝트의 리포지토리에 대한 액세스를 구성합니다.
배포하려는 애플리케이션(이 튜토리얼에 있는 애플리케이션)을 선택하고 선택을 클릭합니다.
선택을 클릭하면 브랜치 이름, 루트 디렉터리 및 환경 변수를 포함하여 앱에 대한 정보를 입력할 수 있는 페이지로 이동합니다.
배포 프로세스가 자동으로 시작됩니다,
Back4app AI 에이전트로 배포하기
개발 워크플로우를 강화하기 위해 아래 이미지에서 볼 수 있듯이 Back4app AI 에이전트를 사용하여 앱을 배포할 수도 있습니다:
이 링크( )를 따라 GitHub 계정에 Back4app 컨테이너 앱을 설치한 다음 위 이미지의 단계에 따라 구성합니다.
앱 설정이 완료되면 AI 에이전트를 사용하여 앱 배포를 진행할 수 있습니다.
제공된 링크를 따라 앱의 배포 진행 상황을 모니터링하세요.
결론
Back4app에서 Docker 컨테이너화된 Rust 애플리케이션을 빌드하고 배포하는 방법을 배웠습니다.
Back4app에 앱을 배포하면 백엔드 인프라 관리를 간소화할 수 있습니다.
Back4App은 데이터 관리, 애플리케이션 확장, 성능 모니터링을 위한 강력한 도구를 제공합니다.
서버 관리보다는 훌륭한 애플리케이션을 구축하고자 하는 개발자에게 탁월한 선택입니다.