Rust에서 Lambda 함수 핸들러 정의
참고
Rust 런타임 클라이언트
Lambda 함수의 핸들러는 이벤트를 처리하는 함수 코드의 메서드입니다. 함수가 간접 호출되면 Lambda는 핸들러 메서드를 실행합니다. 함수는 핸들러가 응답을 반환하거나 종료하거나 제한 시간이 초과될 때까지 실행됩니다.
이 페이지에서는 프로젝트 초기화, 이름 지정 규칙, 모범 사례를 포함하여 Rust에서 Lambda 함수 핸들러를 사용하는 방법을 설명합니다. 이 페이지에는 주문에 대한 정보를 가져와서 텍스트 파일 영수증을 생성하고 해당 파일을 Amazon Simple Storage Service(S3) 버킷에 넣는 Rust Lambda 함수의 예제도 포함되어 있습니다. 함수를 작성한 후 배포하는 방법에 대한 자세한 내용은 .zip 파일 아카이브를 사용하여 Rust Lambda 함수 배포 섹션을 참조하세요.
주제
Rust 핸들러 프로젝트 설정
Rust에서 Lambda 함수를 사용할 경우 프로세스에는 코드를 작성한 후 컴파일하고, 컴파일된 아티팩트를 Lambda에 배포하는 작업이 포함됩니다. Rust에서 Lambda 핸들러 프로젝트를 설정하는 가장 간단한 방법은 Rust용 AWS Lambda 런타임lambda_runtime
)입니다.
다음 명령을 사용하여 Rust용 AWS Lambda 런타임을 설치합니다.
cargo install cargo-lambda
cargo-lambda
를 성공적으로 설치한 후 다음 명령을 사용하여 새 Rust Lambda 함수 핸들러 프로젝트를 초기화합니다.
cargo lambda new example-rust
이 명령을 실행하면 명령줄 인터페이스(CLI)가 Lambda 함수에 대해 다음과 같은 몇 가지 질문을 합니다.
-
HTTP 함수 - API Gateway 또는 함수 URL을 통해 함수를 간접적으로 호출하려는 경우 예라고 답합니다. 그렇지 않으면 아니요라고 답합니다. 이 페이지의 예제 코드에서는 사용자 지정 JSON 이벤트로 함수를 간접적으로 호출하므로 아니요라고 답합니다.
-
이벤트 유형 - 사전 정의된 이벤트 셰이프를 사용하여 함수를 간접적으로 호출하려는 경우 올바른 예상 이벤트 유형을 선택합니다. 나머지 경우에는 이 옵션 필드를 비워둡니다. 이 페이지의 예제 코드에서는 사용자 지정 JSON 이벤트로 함수를 간접적으로 호출하므로 이 옵션을 비워 둡니다.
명령이 성공적으로 실행되면 프로젝트의 기본 디렉터리를 입력합니다.
cd example-rust
이 명령은 src
디렉터리에 generic_handler.rs
파일과 main.rs
파일을 생성합니다. generic_handler.rs
를 사용하여 일반 이벤트 핸들러를 사용자 지정할 수 있습니다. main.rs
파일에는 기본 애플리케이션 로직이 들어있습니다. Cargo.toml
파일에는 패키지에 대한 메타데이터와 외부 종속성 목록이 들어 있습니다.
예제 Rust Lambda 함수 코드
다음 예제 Rust Lambda 함수 코드는 주문에 대한 정보를 입력으로 가져와서 텍스트 파일 영수증을 생성하고 해당 파일을 Amazon S3 버킷에 넣습니다.
예 main.rs
Lambda 함수
use aws_sdk_s3::{Client, primitives::ByteStream}; use lambda_runtime::{run, service_fn, Error, LambdaEvent}; use serde::{Deserialize, Serialize}; use serde_json::Value; use std::env; #[derive(Deserialize, Serialize)] struct Order { order_id: String, amount: f64, item: String, } async fn function_handler(event: LambdaEvent<Value>) -> Result<String, Error> { let payload = event.payload; // Deserialize the incoming event into Order struct let order: Order = serde_json::from_value(payload)?; let bucket_name = env::var("RECEIPT_BUCKET") .map_err(|_| "RECEIPT_BUCKET environment variable is not set")?; let receipt_content = format!( "OrderID: {}\nAmount: ${:.2}\nItem: {}", order.order_id, order.amount, order.item ); let key = format!("receipts/{}.txt", order.order_id); let config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await; let s3_client = Client::new(&config); upload_receipt_to_s3(&s3_client, &bucket_name, &key, &receipt_content).await?; Ok("Success".to_string()) } async fn upload_receipt_to_s3( client: &Client, bucket_name: &str, key: &str, content: &str, ) -> Result<(), Error> { client .put_object() .bucket(bucket_name) .key(key) .body(ByteStream::from(content.as_bytes().to_vec())) // Fixed conversion .content_type("text/plain") .send() .await?; Ok(()) } #[tokio::main] async fn main() -> Result<(), Error> { run(service_fn(function_handler)).await }
main.rs
파일은 다음 코드 섹션을 포함하고 있습니다.
-
use
문: Lambda 함수에 필요한 Rust 크레이트와 메서드를 가져오는 데 사용합니다. -
#[derive(Deserialize, Serialize)]
: 이 Rust 구조체에 예상 입력 이벤트의 셰이프를 정의합니다. -
async fn function_handler(event: LambdaEvent<Value>) -> Result<String, Error>
: 기본 애플리케이션 논리를 포함한 기본 핸들러 메서드입니다. -
async fn upload_receipt_to_s3 (...)
: 기본function_handler
메서드에서 참조하는 도우미 메서드입니다. -
#[tokio::main]
: Rust 프로그램의 진입점을 표시하는 매크로입니다. 또한 Tokio 런타임을 설정하여 main()
메서드가async
/await
를 사용하고 비동기적으로 실행될 수 있도록 합니다. -
async fn main() -> Result<(), Error>
:main()
함수는 코드의 진입점입니다. 그 안에서function_handler
를 주요 핸들러 메서드로 지정합니다.
다음 Cargo.toml
파일은 이 함수와 함께 제공됩니다.
[package] name = "example-rust" version = "0.1.0" edition = "2024" [dependencies] aws-config = "1.5.18" aws-sdk-s3 = "1.78.0" lambda_runtime = "0.13.0" serde = { version = "1", features = ["derive"] } serde_json = "1" tokio = { version = "1", features = ["full"] }
이 함수가 제대로 작동하려면 실행 역할이 s3:PutObject
작업을 허용해야 합니다. 또한 RECEIPT_BUCKET
환경 변수를 정의해야 합니다. 성공적으로 간접 호출되면 Amazon S3 버킷은 영수증 파일을 포함합니다.
Rust 핸들러에 유효한 클래스 정의
대부분의 경우 Rust에서 정의하는 Lambda 핸들러 서명의 형식은 다음과 같습니다.
async fn function_handler(event: LambdaEvent<T>) -> Result<U, Error>
이 핸들러의 경우
-
이 핸들러의 이름은
function_handler
입니다. -
핸들러에 대한 단일 입력은 이벤트이며
LambdaEvent<T>
유형입니다.-
LambdaEvent
는lambda_runtime
크레이트에서 제공되는 래퍼입니다. 이 래퍼를 사용하면 간접 호출의 요청 ID 등의 Lambda 관련 메타데이터가 포함된 컨텍스트 객체에 액세스할 수 있습니다. -
T
는 역직렬화된 이벤트 유형입니다. 예를 들어, 이는 핸들러가 모든 일반 JSON 입력을 받을 수 있도록 하는serde_json::Value
일 수 있습니다. 또는 함수가 사전 정의된 특정 입력 유형을 예상하는 경우ApiGatewayProxyRequest
와 같은 유형일 수 있습니다.
-
-
핸들러의 반환 유형은
Result<U, Error>
입니다.-
U
는 역직렬화된 출력 유형입니다.U
는 Lambda가 반환 값을 JSON으로 변환할 수 있도록serde::Serialize
특성을 구현해야 합니다. 예를 들어U
는Serialize
를 구현하는 한String
,serde_json::Value
또는 사용자 지정 구조체와 같은 단순한 유형일 수 있습니다. 코드가 Ok(U) 문에 도달하면 실행이 성공한 것이며 함수는U
유형의 값을 반환합니다. -
코드에 오류(예:
Err(Error)
)가 발생하면 함수는 Amazon CloudWatch에 오류를 기록하고Error
유형의 오류 응답을 반환합니다.
-
이 예제에서 핸들러 서명은 다음과 같습니다.
async fn function_handler(event: LambdaEvent<Value>) -> Result<String, Error>
다른 유효한 핸들러 서명은 다음과 같은 특징이 있습니다.
-
LambdaEvent
래퍼 생략 -LambdaEvent
를 생략하면 함수 내의 Lambda 컨텍스트 객체에 대한 액세스 권한을 잃게 됩니다. 다음은 이러한 유형의 서명 예입니다.async fn handler(event: serde_json::Value) -> Result<String, Error>
-
입력으로 단위 유형 사용 - Rust의 경우 단위 유형을 사용하여 빈 입력을 나타낼 수 있습니다. 주기적으로 예약된 간접 호출이 있는 함수에 주로 사용됩니다. 다음은 이러한 유형의 서명 예입니다.
async fn handler(_: ()) -> Result<Value, Error>
핸들러 이름 지정 규칙
Rust의 Lambda 핸들러에는 엄격한 이름 지정 제한이 없습니다. 핸들러에 어떤 이름이든 사용할 수 있지만 Rust에서는 함수 이름을 일반적으로 snake_case
로 지정합니다.
이 예제와 같은 소규모 애플리케이션의 경우 단일 main.rs
파일을 사용하여 모든 코드를 포함할 수 있습니다. 대규모 프로젝트의 경우 main.rs
에는 함수에 대한 진입점이 포함되어야 하지만 코드를 논리적 모듈로 구분하기 위한 추가 파일이 있을 수 있습니다. 예를 들어, 다음과 같은 파일 구조가 있을 수 있습니다.
/example-rust │── src/ │ ├── main.rs # Entry point │ ├── handler.rs # Contains main handler │ ├── services.rs # [Optional] Back-end service calls │ ├── models.rs # [Optional] Data models │── Cargo.toml
입력 이벤트 객체 정의 및 액세스
JSON은 Lambda 함수의 가장 일반적인 표준 입력 형식입니다. 이 예제에서 함수는 다음과 유사한 입력을 예상합니다.
{ "order_id": "12345", "amount": 199.99, "item": "Wireless Headphones" }
Rust에서는 구조체에 예상 입력 이벤트의 셰이프를 정의할 수 있습니다. 이 예제에서는 Order
를 나타내는 다음 구조체를 정의합니다.
#[derive(Deserialize, Serialize)] struct Order { order_id: String, amount: f64, item: String, }
이 구조체는 예상 입력 형태와 일치합니다. 이 예제에서 #[derive(Deserialize, Serialize)]
매크로는 직렬화 및 역직렬화를 위한 코드를 자동으로 생성합니다. 즉, serde_json::from_value()
메서드를 사용하여 일반 입력 JSON 유형을 구조체로 역직렬화할 수 있습니다. 이는 핸들러의 처음 몇 줄에 설명되어 있습니다.
async fn function_handler(event: LambdaEvent<Value>) -> Result<String, Error> { let payload = event.payload; // Deserialize the incoming event into Order struct let order: Order = serde_json::from_value(payload)?; ... }
그런 다음, 객체의 필드에 액세스할 수 있습니다. 예를 들어 order.order_id
는 원래 입력에서 order_id
의 값을 검색합니다.
사전 정의된 입력 이벤트 유형
aws_lambda_events
크레이트에는 사전 정의된 입력 이벤트 유형이 많이 있습니다. 다음 가져오기를 포함하여 API Gateway로 함수를 간접적으로 호출하려는 경우를 예로 들 수 있습니다.
use aws_lambda_events::event::apigw::ApiGatewayProxyRequest;
그런 다음 기본 핸들러가 다음 서명을 사용하는지 확인합니다.
async fn handler(event: LambdaEvent<ApiGatewayProxyRequest>) -> Result<String, Error> { let body = event.payload.body.unwrap_or_default(); ... }
기타 사전 정의된 입력 이벤트 유형에 대한 자세한 내용은 aws_lambda_events 크레이트
Lambda 컨텍스트 객체 액세스 및 사용
Lambda 컨텍스트 객체에는 호출, 함수, 실행 환경에 관한 정보가 포함되어 있습니다. Rust에서 LambdaEvent
래퍼에는 컨텍스트 객체가 포함됩니다. 예를 들어 컨텍스트 객체를 사용하여 다음 코드로 현재 간접 호출의 요청 ID를 검색할 수 있습니다.
async fn function_handler(event: LambdaEvent<Value>) -> Result<String, Error> { let request_id = event.context.request_id; ... }
컨텍스트 객체에 대한 자세한 내용은 Lambda 컨텍스트 객체를 사용하여 Rust 함수 정보 검색 섹션을 참조하세요.
핸들러에서 AWS SDK for Rust 사용
종종 Lambda 함수를 사용하여 다른 AWS 리소스와 상호 작용하거나 업데이트하는 경우가 있습니다. 이러한 리소스와 상호 작용하는 가장 간단한 방법은 AWS SDK for Rust을 사용하는 것입니다.
함수에 SDK 종속성을 추가하려면 Cargo.toml
파일에 추가합니다. 함수에 필요한 라이브러리만 추가하는 것이 좋습니다. 이전의 예제 코드에서는 aws_sdk_s3::Client
를 사용했습니다. Cargo.toml
파일에서 [dependencies]
섹션 아래에 다음 줄을 추가하여 이 종속성을 추가할 수 있습니다.
aws-sdk-s3 = "1.78.0"
참고
이는 최신 버전이 아닐 수 있습니다. 애플리케이션에 적합한 버전을 선택합니다.
그런 다음 코드에서 직접 종속성을 가져옵니다.
use aws_sdk_s3::{Client, primitives::ByteStream};
이렇게 하면 예제 코드가 다음과 같이 Amazon S3 클라이언트를 초기화합니다.
let config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await; let s3_client = Client::new(&config);
SDK 클라이언트를 초기화한 후에는 이를 사용하여 다른 AWS 서비스와 상호 작용할 수 있습니다. 예제 코드는 upload_receipt_to_s3
도우미 함수에서 Amazon S3 PutObject
API를 직접적으로 호출합니다.
환경 변수에 액세스
핸들러 코드에서 env::var
메서드를 사용하여 모든 환경 변수를 참조할 수 있습니다. 이 예제에서는 다음 코드 줄을 사용하여 정의된 RECEIPT_BUCKET
환경 변수를 참조합니다.
let bucket_name = env::var("RECEIPT_BUCKET") .map_err(|_| "RECEIPT_BUCKET environment variable is not set")?;
공유 상태 사용
Lambda 함수의 핸들러 코드와 무관한 공유 변수를 선언할 수 있습니다. 이러한 변수는 함수가 이벤트를 수신하기 전에 초기화 단계에서 상태 정보를 로드하는 데 도움이 될 수 있습니다. 예를 들어 이 페이지의 코드를 수정하여 Amazon S3 클라이언트를 초기화할 때 공유 상태를 사용하도록 main
함수와 핸들러 서명을 업데이트할 수 있습니다.
async fn function_handler(client: &Client, event: LambdaEvent<Value>) -> Result<String, Error> { ... upload_receipt_to_s3(client, &bucket_name, &key, &receipt_content).await?; ... } ... #[tokio::main] async fn main() -> Result<(), Error> { let shared_config = aws_config::from_env().load().await; let client = Client::new(&shared_config); let shared_client = &client; lambda_runtime::run(service_fn(move |event: LambdaEvent<Request>| async move { handler(&shared_client, event).await })) .await
Rust Lambda 함수의 코드 모범 사례
Lambda 함수를 구축할 때 코딩 모범 사례를 사용하려면 다음 목록의 지침을 준수하세요.
-
핵심 로직에서 Lambda 핸들러를 분리합니다. 이를 통해 단위 테스트를 수행할 수 있는 더 많은 함수를 만들 수 있습니다.
-
종속성의 복잡성을 최소화합니다. 실행 환경 시작 시 빠르게 로드되는 더 단순한 프레임워크가 권장됩니다.
-
배포 패키지 크기를 런타임 필요에 따라 최소화합니다. 이렇게 하면 호출 전에 배포 패키지를 다운로드하고 압축을 풀 때 걸리는 시간이 단축됩니다.
-
실행 환경 재사용을 활용하여 함수 성능을 향상시킵니다. 함수 핸들러 외부에서 SDK 클라이언트 및 데이터베이스 연결을 초기화하고 정적 자산을
/tmp
디렉토리에 로컬로 캐시합니다. 동일한 함수 인스턴스에서 처리하는 후속 간접 호출은 이러한 리소스를 재사용할 수 있습니다. 이를 통해 함수 실행 시간을 줄여 비용을 절감합니다.호출에서 발생할 수 있는 데이터 유출을 방지하려면 실행 환경을 사용하여 사용자 데이터, 이벤트 또는 보안과 관련된 기타 정보를 저장하지 마세요. 함수가 핸들러 내부 메모리에 저장할 수 없는 변경 가능한 상태에 의존하는 경우 각 사용자에 대해 별도의 함수 또는 별도의 함수 버전을 생성하는 것이 좋습니다.
-
연결 유지 지시문을 사용하여 지속적인 연결을 유지하세요. Lambda는 시간이 지남에 따라 유휴 연결을 제거합니다. 함수를 호출할 때 유휴 연결을 재사용하려고 하면 연결 오류가 발생합니다. 지속적인 연결을 유지하려면 런타임과 관련된 연결 유지 지시문을 사용하세요. 예를 들어, Node.js에서 연결 유지를 이용해 연결 재사용을 참조하세요.
-
환경 변수를 사용하여 함수에 운영 파라미터를 전달합니다. 예를 들어, Amazon S3 버킷에 기록하는 경우 기록하고 있는 버킷 이름을 하드 코딩하는 대신 환경 변수로 구성합니다.
-
Lambda 함수에서 함수가 자기 자신을 간접적으로 간접 호출하거나 함수를 다시 간접적으로 간접 호출할 수 있는 프로세스를 시작하는 재귀적 간접 호출을 사용하지 마세요. 리커시브 코드를 사용할 경우, 의도하지 않은 함수 호출이 증가하고 비용이 상승할 수 있습니다. 의도치 않게 간접 호출이 대량으로 발생하는 경우 함수의 예약된 동시성을 즉시
0
으로 설정하여 코드를 업데이트하는 동안 해당 함수에 대한 모든 간접 호출을 제한합니다. -
Lambda 함수 코드에는 문서화되지 않은 비공개 API를 사용하지 마세요. AWS Lambda 관리형 런타임의 경우, Lambda는 주기적으로 보안 및 기능 업데이트를 Lambda의 내부 API에 적용합니다. 이러한 내부 API 업데이트는 이전 버전과 호환되지 않으므로 함수가 이러한 비공개 API에 종속성을 갖는 경우 호출 실패와 같은 의도하지 않은 결과를 초래할 수 있습니다. 공개적으로 사용 가능한 API의 목록은 API 레퍼런스를 참조하세요.
-
멱등성 코드를 작성합니다. 함수에 멱등성 코드를 작성하면 중복 이벤트가 동일한 방식으로 처리됩니다. 코드는 이벤트를 올바르게 검증하고 중복 이벤트를 정상적으로 처리해야 합니다. 자세한 내용은 멱등성 Lambda 함수를 만들려면 어떻게 해야 합니까?
단원을 참조하십시오.