

# Lambda 지속성 함수
<a name="durable-functions"></a>

Lambda 지속성 함수를 사용하면 중단이 발생해도 안정적인 진행 상황을 유지하면서 최대 1년 동안 실행할 수 있는 복원력이 뛰어난 다단계 애플리케이션 및 AI 워크플로를 빌드할 수 있습니다. 지속성 함수가 실행되면 이 전체 수명 주기를 지속성 실행이라고 합니다. 이 실행은 체크포인트를 사용하여 진행 상황을 추적하고 재생을 통해 실패로부터 자동으로 복구하며, 완료된 작업을 건너뛰면서 처음부터 다시 실행할 수 있습니다.

각 함수 내에서 지속성 작업을 기본 구성 요소로 사용합니다. 단계는 기본 제공 재시도 및 진행 상황 추적을 통해 비즈니스 로직을 실행하는 반면 대기는 컴퓨팅 요금이 발생하지 않도록 실행을 일시 중지하므로 휴먼 인 더 루프(human-in-the-loop) 워크플로 또는 외부 종속성 폴링과 같은 장기 실행 프로세스에 적합합니다. 주문 처리, 마이크로서비스 조정, 에이전틱 AI 애플리케이션 오케스트레이션 등에 있어 지속성 함수는 익숙한 프로그래밍 언어로 코드를 작성하는 동안 상태를 자동으로 유지하고 실패로부터 복구합니다.

## 주요 이점
<a name="durable-functions-benefits"></a>

**자연스럽게 복원력이 뛰어난 코드 작성:** 익숙한 프로그래밍 구조를 사용하면 실패를 자동으로 처리하는 코드를 작성할 수 있습니다. 내장된 체크포인트 지정, 투명한 재시도 및 자동 복구를 통해 비즈니스 로직을 깔끔하게 유지할 수 있습니다.

**사용한 만큼만 지불:** 대기 작업 중에 컴퓨팅 요금이 발생하지 않고 함수가 일시 중지됩니다. 몇 시간 또는 며칠을 기다려야 하는 장기 실행 워크플로의 경우 유휴 대기가 아닌 실제 처리 시간에 대해서만 비용을 지불합니다.

**운영 간소화:** Lambda의 서버리스 모델을 사용하면 인프라를 관리하지 않고도 0으로 규모 조정을 포함한 자동 조정을 수행할 수 있습니다. 지속성 함수는 상태 관리, 재시도 로직 및 실패 복구를 자동으로 처리하여 운영 오버헤드를 줄입니다.

## 지속성 함수를 사용하는 경우
<a name="durable-functions-use-cases"></a>

**단기 조정:** 실패 시 자동 롤백을 통해 여러 서비스에서 결제, 재고 및 배송을 조정합니다. 완료를 보장하면서 검증, 결제 권한 부여, 재고 할당 및 이행을 통해 주문을 처리합니다.

**안심할 수 있는 결제 처리:** 실패가 발생해도 트랜잭션 상태를 유지하고 재시도를 자동으로 처리하는 복원력이 뛰어난 결제 흐름을 빌드합니다. 여러 단계에서 전체 감사가 가능하도록 결제 공급자 간에 다단계 권한 부여, 사기 검사 및 결제를 조정합니다.

**신뢰할 수 있는 AI 워크플로 빌드:** 모델 직접 호출을 연결하고, 인적 피드백을 통합하고, 실패 발생 시 장기 실행 태스크를 결정론적으로 처리하는 다단계 AI 워크플로를 생성합니다. 일시 중지 후 자동으로 재개되며, 활성 실행 시간에 대해서만 비용을 지불합니다.

**복잡한 주문 이행 오케스트레이션:** 기본 제공되는 복원력으로 재고, 결제, 배송 및 알림 시스템에서 주문 처리를 조정합니다. 부분 실패를 자동으로 처리하고, 중단이 발생해도 주문 상태를 유지하며, 컴퓨팅 리소스를 소비하지 않고 외부 이벤트를 효율적으로 기다립니다.

**다단계 비즈니스 워크플로 자동화:** 며칠 또는 몇 주 동안 지속되는 직원 온보딩, 대출 승인 및 규정 준수 프로세스를 대상으로 신뢰할 수 있는 워크플로를 빌드합니다. 프로세스 상태 및 내역을 전체적으로 파악하면서 인적 승인, 시스템 통합 및 예약된 태스크 전반에 걸쳐 워크플로 상태를 유지 관리합니다.

### 지속성 함수와 Step Functions 비교
<a name="durable-functions-vs-step-functions"></a>

지속성 함수와 Step Functions 모두 자동 상태 관리를 통해 워크플로 오케스트레이션을 제공합니다. 주요 차이점은 실행 위치와 워크플로 정의 방법입니다.
+ **지속성 함수:** Lambda 내에서 실행, 표준 프로그래밍 언어를 사용, Lambda 환경 내에서 관리
+ **Step Functions:** 독립 실행형 서비스, 그래프 기반 DSL 또는 시각적 디자이너, 유지 관리가 필요 없는 완전관리형

지속성 함수는 Lambda에서 워크플로가 비즈니스 로직과 강하게 연결되는 애플리케이션 개발에 적합합니다. Step Functions는 시각적 디자인, 220개 이상의 AWS 서비스와의 네이티브 통합, 제로 유지 관리 인프라가 필요한 서비스 전반에 걸친 워크플로 오케스트레이션에 탁월합니다.

자세한 비교는 [지속성 함수 또는 Step Functions](durable-step-functions.md)를 참조하세요.

## 작동 방식
<a name="durable-functions-how-it-works"></a>

 지속성 함수는 일반적으로 지속성 실행이라고 하는 사용자 정의 일시 중지 지점을 통해 진행 상황을 추적하고 장기 실행 작업을 지원하는 체크포인트/재생 메커니즘을 사용하는 일반 Lambda 함수입니다. 일시 중지 또는 중단으로부터 함수가 재개되면 시스템이 재생을 수행합니다. 재생 중에 코드는 처음부터 실행되지만 완료된 작업을 다시 실행하는 대신 저장된 결과를 사용하여 완료된 체크포인트를 건너뜁니다. 이 재생 메커니즘은 장기 실행을 활성화하면서 일관성을 보장합니다.

애플리케이션에서 이 체크포인트-재생 메커니즘을 활용하기 위해 Lambda는 지속성 실행 SDK를 제공합니다. SDK는 체크포인트 및 재생 관리의 복잡성을 추상화하여 코드에 사용하는 지속성 작업이라고 하는 간단한 기본 요소를 노출합니다. SDK는 JavaScript, TypeScript, Python 및 Java(미리 보기)에서 사용할 수 있으며, 기존 Lambda 개발 워크플로와 원활하게 통합됩니다.

SDK를 사용하면 Lambda 이벤트 핸들러를 래핑한 다음 이벤트와 함께 DurableContext를 제공합니다. 이 컨텍스트를 사용하면 단계 및 대기와 같은 지속성 작업에 액세스할 수 있습니다. 함수 로직을 일반적인 순차적 코드로 작성하지만, 서비스를 직접 호출하는 대신 자동 체크포인트 지정 및 재시도를 위해 호출을 단계별로 래핑합니다. 실행을 일시 중지해야 하는 경우 대기를 추가하면 요금 발생 없이 함수가 일시 중지됩니다. SDK는 상태 관리 및 재생의 모든 복잡성을 처리하므로 코드를 깔끔하게 읽을 수 있습니다.

 ![\[Filter for Amazon Inspector results related to Lambda functions\]](http://docs.aws.amazon.com/ko_kr/lambda/latest/dg/images/how_durable_works.png) 

## 다음 단계
<a name="durable-functions-next-steps"></a>
+ [지속성 함수 시작하기](durable-getting-started.md)
+ [지속성 실행 SDK 살펴보기](durable-execution-sdk.md)
+ [지속성 함수 또는 Step Functions](durable-step-functions.md)
+ [지속성 함수 모니터링 및 디버깅](durable-monitoring.md)
+ [보안 및 권한 검토](durable-security.md)
+ [모범 사례 따르기](durable-best-practices.md)

# 기본 개념
<a name="durable-basic-concepts"></a>

Lambda는 JavaScript, TypeScript, Python에 대한 지속성 실행 SDK를 제공합니다. 이러한 SDK는 지속성 함수를 빌드하고, 진행 중 체크포인트를 지정하고, 재시도를 처리하고, 실행 흐름을 관리하는 데 필요한 기본 요소를 제공하는 기반입니다. 전체 SDK 설명서 및 예제는 GitHub의 [JavaScript/TypeScript SDK](https://github.com/aws/aws-durable-execution-sdk-js) 및 [Python SDK](https://github.com/aws/aws-durable-execution-sdk-python)를 참조하세요.

## 지속성 실행
<a name="durable-execution-concept"></a>

**지속성 실행**은 체크포인트 및 재생 메커니즘을 사용하여 비즈니스 로직 진행 상황을 추적하고, 실행을 일시 중지하고, 장애로부터 복구하는 Lambda 지속성 함수의 전체 수명 주기를 나타냅니다. 일시 중지 또는 중단 후에 함수가 재개되면 이전에 완료된 체크포인트가 재생되고 함수가 계속 실행됩니다.

수명 주기에는 특히 일시 중지 또는 장애 복구 후 실행을 완료하기 위해 여러 번의 Lambda 함수 간접 호출이 포함될 수 있습니다. 이 접근 방식을 사용하면 중단이 발생해도 안정적인 진행 상황을 유지하면서 함수를 장기간(최대 1년) 실행할 수 있습니다.

**재생 작동 방식**  
Lambda는 함수가 실행될 때 모든 지속성 작업(단계, 대기 및 기타 작업)에 대한 실행 로그를 유지합니다. 함수를 일시 중지해야 하거나 중단이 발생하면 Lambda는 이 체크포인트 로그를 저장하고 실행을 중지합니다. 재개할 시간이 되면 Lambda는 처음부터 함수를 다시 호출하고 체크포인트 로그를 재생하여 저장된 값을 완료된 작업으로 대체합니다. 즉, 코드는 다시 실행되지만 이전에 완료된 단계는 다시 실행되지 않습니다. 저장된 결과가 대신 사용됩니다.

이 재생 메커니즘은 지속성 함수를 이해하는 데 있어 필수적입니다. 코드는 재생 중에 결정론적이어야 합니다. 즉, 동일한 입력이 주어지면 동일한 결과를 생성해야 합니다. 재생 도중 다른 값을 생성하여 비결정론적 동작을 유발할 수 있으므로 단계를 벗어나는 부작용(예: 난수 생성 또는 현재 시간 가져오기)이 있는 작업 사용은 자제합니다.

## DurableContext
<a name="durable-context-concept"></a>

**DurableContext**는 지속성 함수가 수신하는 컨텍스트 객체입니다. 체크포인트를 생성하고 실행 흐름을 관리하는 단계 및 대기와 같은 지속성 작업에 대한 메서드를 제공합니다.

지속성 함수는 기본 Lambda 컨텍스트 대신 `DurableContext`를 수신합니다.

------
#### [ TypeScript ]

```
import {
  DurableContext,
  withDurableExecution,
} from "@aws/durable-execution-sdk-js";

export const handler = withDurableExecution(
  async (event: any, context: DurableContext) => {
    const result = await context.step(async () => {
      return "step completed";
    });
    return result;
  },
);
```

------
#### [ Python ]

```
from aws_durable_execution_sdk_python import (
    DurableContext,
    durable_execution,
    durable_step,
)

@durable_step
def my_step(step_context, data):
    # Your business logic
    return result

@durable_execution
def handler(event, context: DurableContext):
    result = context.step(my_step(event["data"]))
    return result
```

------

지속성 함수용 Python SDK는 동기식 메서드를 사용하며 `await`을 지원하지 않습니다. TypeScript SDK는 `async/await`을 사용합니다.

## 단계
<a name="steps-concept"></a>

**단계**는 기본 제공 재시도 및 자동 체크포인트 지정을 사용하여 비즈니스 로직을 실행합니다. 각 단계는 결과를 저장하고, 이를 통해 중단 후 완료된 모든 단계에서 함수가 재개될 수 있습니다.

------
#### [ TypeScript ]

```
// Each step is automatically checkpointed
const order = await context.step(async () => processOrder(event));
const payment = await context.step(async () => processPayment(order));
const result = await context.step(async () => completeOrder(payment));
```

------
#### [ Python ]

```
# Each step is automatically checkpointed
order = context.step(lambda: process_order(event))
payment = context.step(lambda: process_payment(order))
result = context.step(lambda: complete_order(payment))
```

------

## 대기 상태
<a name="wait-states-concept"></a>

**대기 상태**는 함수가 계속될 때까지 실행을 중지(및 충전 중지)하는 계획된 일시 중지입니다. 이 상태를 사용하여 기간, 외부 콜백 또는 특정 조건을 대기합니다.

------
#### [ TypeScript ]

```
// Wait for 1 hour without consuming resources
await context.wait({ seconds:3600 });

// Wait for external callback
const approval = await context.waitForCallback(
  async (callbackId) => sendApprovalRequest(callbackId)
);
```

------
#### [ Python ]

```
# Wait for 1 hour without consuming resources
context.wait(3600)

# Wait for external callback
approval = context.wait_for_callback(
    lambda callback_id: send_approval_request(callback_id)
)
```

------

함수에 대기가 발생하거나 일시 중지해야 하는 경우 Lambda는 체크포인트 로그를 저장하고 실행을 중지합니다. 재개할 시간이 되면 Lambda는 함수를 다시 호출하고 체크포인트 로그를 재생하여 저장된 값을 완료된 작업으로 대체합니다.

보다 복잡한 워크플로의 경우 지속성 Lambda 함수에는 동시 실행에 `parallel()`, 배열 처리에 `map()`, 중첩된 작업에 `runInChildContext()`, 폴링에 `waitForCondition()` 같은 고급 작업도 제공됩니다. 각 작업의 사용 시기에 대한 자세한 예제와 지침은 [예제](durable-examples.md)를 참조하세요.

## 기타 함수 간접 호출
<a name="invoke-concept"></a>

**간접 호출**을 사용하면 지속성 함수가 다른 Lambda 함수를 직접 호출하고 결과를 기다릴 수 있습니다. 호출 함수는 간접 호출된 함수가 실행되는 동안 일시 중지되어 결과 보존을 위한 체크포인트를 생성합니다. 이를 통해 특수 함수가 특정 태스크를 처리하는 모듈식 워크플로를 빌드할 수 있습니다.

`context.invoke()`를 사용하여 지속성 함수 내에서 다른 함수를 직접 호출합니다. 간접 호출에 체크포인트가 지정되므로 간접 호출된 함수가 완료된 후 함수가 중단되면 함수를 다시 간접 호출하지 않고 저장된 결과로 재개됩니다.

------
#### [ TypeScript ]

```
// Invoke another function and wait for result
const customerData = await context.invoke(
  'validate-customer',
  'arn:aws:lambda:us-east-1:123456789012:function:customer-service:1',
  { customerId: event.customerId }
);

// Use the result in subsequent steps
const order = await context.step(async () => {
  return processOrder(customerData);
});
```

------
#### [ Python ]

```
# Invoke another function and wait for result
customer_data = context.invoke(
    'arn:aws:lambda:us-east-1:123456789012:function:customer-service:1',
    {'customerId': event['customerId']},
    name='validate-customer'
)

# Use the result in subsequent steps
order = context.step(
    lambda: process_order(customer_data),
    name='process-order'
)
```

------

간접 호출된 함수는 지속성 또는 표준 Lambda 함수일 수 있습니다. 지속성 함수를 간접 호출하면 직접 호출 함수는 전체 지속성 실행이 완료될 때까지 기다립니다. 이 패턴은 각 함수가 특정 도메인을 처리하는 마이크로서비스 아키텍처에서 일반적이므로 재사용 가능한 특수 함수에서 복잡한 워크플로를 구성할 수 있습니다.

**참고**  
교차 계정 간접 호출은 지원되지 않습니다. 간접 호출된 함수는 직접 호출 함수와 동일한 AWS 계정에 있어야 합니다.

## 지속성 함수 구성
<a name="durable-configuration-basic"></a>

지속성 함수에는 실행 동작 및 데이터 보존을 제어하는 특정 구성 설정이 있습니다. 이러한 설정은 표준 Lambda 함수 구성과 별개이며 전체 지속성 실행 수명 주기에 적용됩니다.

**DurableConfig** 객체는 지속성 함수에 대한 구성을 정의합니다.

```
{
  "ExecutionTimeout": Integer,
  "RetentionPeriodInDays": Integer
}
```

### 실행 제한 시간
<a name="durable-execution-timeout"></a>

**실행 제한 시간**은 지속성 실행이 시작부터 완료까지 실행될 수 있는 시간을 제어합니다. 이는 단일 함수 간접 호출이 실행될 수 있는 기간을 제어하는 Lambda 함수 제한 시간과 다릅니다.

지속성 실행은 체크포인트, 대기 및 재생을 진행하면서 여러 Lambda 함수 간접 호출에 걸칠 수 있습니다. 실행 제한 시간은 개별 함수 간접 호출이 아닌 지속성 실행의 총 경과 시간에 적용됩니다.

**차이점 이해**  
Lambda 함수 제한 시간(최대 15분)은 함수의 각 개별 간접 호출을 제한합니다. 지속성 실행 제한 시간(최대 1년)은 실행이 시작되는 시점부터 완료, 실패 또는 제한 시간까지의 총 시간을 제한합니다. 이 기간 동안 함수는 단계를 처리하고, 대기하고, 실패로부터 복구할 때 여러 번 간접 호출될 수 있습니다.

예를 들어, 지속성 실행 제한 시간을 24시간으로 설정하고 Lambda 함수 제한 시간을 5분으로 설정한 경우 다음이 적용됩니다.
+ 각 함수 간접 호출은 5분 이내에 완료되어야 합니다.
+ 전체 지속성 실행은 최대 24시간 동안 실행될 수 있습니다.
+ 이 24시간 동안 함수가 여러 번 간접 호출될 수 있습니다.
+ 대기 작업은 Lambda 함수 제한 시간에 포함되지 않지만 실행 제한 시간에 포함됩니다.

Lambda 콘솔, AWS CLI 또는 AWS SAM을 사용하여 지속성 함수를 생성할 때 실행 제한 시간을 구성할 수 있습니다. Lambda 콘솔에서 함수를 선택하고, 구성과 지속성 실행을 선택합니다. 실행 제한 시간 값을 초 단위로 설정합니다(기본값: 86,400초/24시간, 최소: 60초, 최대: 31,536,000초/1년).

**참고**  
실행 제한 시간과 Lambda 함수 제한 시간은 서로 다른 설정입니다. Lambda 함수 제한 시간은 각 개별 간접 호출이 실행될 수 있는 시간을 제어합니다(최대 15분). 실행 제한 시간은 전체 지속성 실행의 총 경과 시간(최대 1년)을 제어합니다.

### 보존 기간
<a name="durable-retention-period"></a>

**보존 기간**은 지속성 실행이 완료된 후 Lambda가 실행 내역 및 체크포인트 데이터를 보존하는 기간을 제어합니다. 이 데이터에는 단계 결과, 실행 상태 및 전체 체크포인트 로그가 포함됩니다.

보존 기간이 만료된 후 Lambda는 실행 내역 및 체크포인트 데이터를 삭제합니다. 더 이상 실행 세부 정보를 검색하거나 실행을 재생할 수 없습니다. 보존 기간은 실행이 터미널 상태(SUCCEEDED, FAILED, STOPPED 또는 TIMED\$1OUT)에 도달하면 시작됩니다.

Lambda 콘솔, AWS CLI 또는 AWS SAM을 사용하여 지속성 함수를 생성할 때 보존 기간을 구성할 수 있습니다. Lambda 콘솔에서 함수를 선택하고, 구성과 지속성 실행을 선택합니다. 일 단위로 보존 기간 값을 설정합니다(기본값: 14일, 최소: 1일, 최대: 90일).

규정 준수 요구 사항, 디버깅 요구 사항 및 비용 고려 사항에 따라 보존 기간을 선택합니다. 보존 기간이 길수록 디버깅 및 감사에 더 많은 시간이 확보되지만 스토리지 비용은 증가합니다.

## 다음 사항도 참조하세요.
<a name="durable-basic-concepts-see-also"></a>
+ [지속성 함수 또는 Step Functions](durable-step-functions.md) - 지속성 함수와 Step Functions를 비교하여 각 접근 방식이 가장 효과적인 경우를 파악합니다.

# Lambda 지속성 함수 생성
<a name="durable-getting-started"></a>

Lambda 지속성 함수를 시작하려면 Lambda 콘솔을 사용하여 지속성 함수를 생성합니다. 몇 분 안에 단계와 대기를 사용하여 체크포인트 기반 실행을 시연하는 지속성 함수를 생성하고 배포할 수 있습니다.

자습서를 따라 진행하면서 `DurableContext` 객체를 사용하고, 단계를 따라 체크포인트를 생성하고, 대기를 통해 실행을 일시 중지하는 방법과 같은 기본적인 지속성 함수 개념을 학습합니다. 또한 대기 후 함수가 재개될 때 재생이 작동하는 방식도 알아봅니다.

쉽게 알 수 있도록 이 자습서에서는 Python 또는 Node.js 런타임을 사용하여 함수를 생성하는 방법을 보여줍니다. 이러한 해석된 언어를 사용하면 콘솔의 기본 제공 코드 편집기에서 함수 코드를 직접 편집할 수 있습니다.

Java(미리 보기)의 지속성 함수는 현재 컨테이너 이미지를 통해서만 배포할 수 있습니다. 컨테이너 이미지에서 지속성 함수를 생성하는 방법에 대한 자세한 내용은 [지속성 함수에 지원되는 런타임](durable-supported-runtimes.md) 또는 [코드형 인프라를 사용하여 Lambda 지속성 함수 배포](durable-getting-started-iac.md)를 참조하세요.

**참고**  
지속성 함수는 현재 Python, Node.js(JavaScript/TypeScript) 런타임 및 컨테이너 이미지(OCI)를 지원합니다(예: Java). 지원되는 런타임 버전 및 컨테이너 이미지 옵션의 전체 목록을 보려면 [지속성 함수에 지원되는 런타임](durable-supported-runtimes.md)을 참조하세요. Lambda에서의 컨테이너 이미지 사용에 대한 자세한 내용은 Lambda 개발자 안내서에서 [Lambda 컨테이너 이미지 생성](https://docs.aws.amazon.com/lambda/latest/dg/images-create.html)을 참조하세요.

**작은 정보**  
**서버리스 솔루션**을 빌드하는 방법을 알아보려면 [서버리스 개발자 안내서](https://docs.aws.amazon.com/serverless/latest/devguide/)를 확인하세요.

## 사전 조건
<a name="durable-getting-started-prerequisites"></a>

### AWS 계정에 가입
<a name="sign-up-for-aws"></a>

AWS 계정이 없는 경우 다음 절차에 따라 계정을 생성합니다.

**AWS 계정에 가입하려면**

1. [https://portal.aws.amazon.com/billing/signup](https://portal.aws.amazon.com/billing/signup)을 엽니다.

1. 온라인 지시 사항을 따르세요.

   등록 절차 중 전화 또는 텍스트 메시지를 받고 전화 키패드로 확인 코드를 입력하는 과정이 있습니다.

   *AWS 계정 루트 사용자*에 가입하면 AWS 계정루트 사용자가 만들어집니다. 루트 사용자에게는 계정의 모든 AWS 서비스및 리소스에 액세스할 권한이 있습니다. 보안 모범 사례는 사용자에게 관리 액세스 권한을 할당하고, 루트 사용자만 사용하여 [루트 사용자 액세스 권한이 필요한 작업](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_root-user.html#root-user-tasks)을 수행하는 것입니다.

가입 프로세스가 완료되면 AWS은 사용자에게 확인 이메일을 전송합니다. 언제든지 [https://aws.amazon.com/](https://aws.amazon.com/)으로 이동하고 **내 계정**을 선택하여 현재 계정 활동을 확인하고 계정을 관리할 수 있습니다.

### 관리자 액세스 권한이 있는 사용자 생성
<a name="create-an-admin"></a>

AWS 계정에 가입하고 AWS 계정 루트 사용자에 보안 조치를 한 다음, AWS IAM Identity Center을 활성화하고 일상적인 작업에 루트 사용자를 사용하지 않도록 관리 사용자를 생성합니다.

**귀하의 AWS 계정 루트 사용자보호**

1.  **루트 사용자**를 선택하고 AWS 계정 이메일 주소를 입력하여 [AWS Management Console](https://console.aws.amazon.com/)에 계정 소유자로 로그인합니다. 다음 페이지에서 비밀번호를 입력합니다.

   루트 사용자를 사용하여 로그인하는 데 도움이 필요하면 *AWS Sign-In사용 설명서*의 [루트 사용자로 로그인](https://docs.aws.amazon.com/signin/latest/userguide/console-sign-in-tutorials.html#introduction-to-root-user-sign-in-tutorial)을 참조하세요.

1. 루트 사용자의 다중 인증(MFA)을 활성화합니다.

   지침은 *IAM 사용 설명서*의 [AWS 계정루트 사용자용 가상 MFA 디바이스 활성화(콘솔)](https://docs.aws.amazon.com/IAM/latest/UserGuide/enable-virt-mfa-for-root.html)를 참조하세요.

**관리자 액세스 권한이 있는 사용자 생성**

1. IAM Identity Center를 활성화합니다.

   지침은 *AWS IAM Identity Center사용 설명서*의 [AWS IAM Identity Center설정](https://docs.aws.amazon.com//singlesignon/latest/userguide/get-set-up-for-idc.html)을 참조하세요.

1. IAM Identity Center에서 사용자에게 관리 액세스 권한을 부여합니다.

   IAM Identity Center 디렉터리를 ID 소스로 사용하는 방법에 대한 자습서는 *AWS IAM Identity Center사용 설명서*의 [기본 IAM Identity Center 디렉터리로 사용자 액세스 구성](https://docs.aws.amazon.com//singlesignon/latest/userguide/quick-start-default-idc.html)을 참조하세요.

**관리 액세스 권한이 있는 사용자로 로그인**
+ IAM IDentity Center 사용자로 로그인하려면 IAM Identity Center 사용자를 생성할 때 이메일 주소로 전송된 로그인 URL을 사용합니다.

  IAM Identity Center 사용자로 로그인하는 데 도움이 필요한 경우 *AWS Sign-In 사용 설명서*의 [AWS액세스 포털에 로그인](https://docs.aws.amazon.com/signin/latest/userguide/iam-id-center-sign-in-tutorial.html)을 참조하세요.

**추가 사용자에게 액세스 권한 할당**

1. IAM Identity Center에서 최소 권한 적용 모범 사례를 따르는 권한 세트를 생성합니다.

   지침은 *AWS IAM Identity Center 사용 설명서*의 [Create a permission set](https://docs.aws.amazon.com//singlesignon/latest/userguide/get-started-create-a-permission-set.html)를 참조하세요.

1. 사용자를 그룹에 할당하고, 그룹에 Single Sign-On 액세스 권한을 할당합니다.

   지침은 *AWS IAM Identity Center 사용 설명서*의 [그룹 추가](https://docs.aws.amazon.com//singlesignon/latest/userguide/addgroups.html)를 참조하세요.

## 콘솔로 Lambda 지속성 함수 생성
<a name="getting-started-create-durable-function"></a>

이 예제에서는 지속성 함수가 자동 체크포인트 지정을 사용하여 여러 단계를 통해 주문을 처리합니다. 함수는 주문 ID가 포함된 JSON 객체를 가져와 주문을 검증하고, 결제를 처리하고, 주문을 확인합니다. 각 단계는 자동으로 체크포인트가 지정되므로 함수가 중단되면 마지막으로 완료된 단계부터 재개됩니다.

또한 함수에서는 대기 작업을 보여주며, 외부 확인 대기를 시뮬레이션하기 위해 잠시 동안 실행을 일시 중지합니다.

**콘솔을 사용하여 지속성 함수 생성**

1. Lambda 콘솔의 [함수 페이지](https://console.aws.amazon.com/lambda/home#/functions)를 엽니다.

1. **함수 생성**을 선택합니다.

1. **새로 작성**을 선택합니다.

1. **기본 정보** 창의 **함수 이름**에 `myDurableFunction`을 입력하세요.

1. **런타임**에서 **Node.js 24** 또는 **Python 3.14**을 선택합니다.

1. **지속성 실행 활성화**를 선택합니다.

Lambda는 체크포인트 작업(`lambda:CheckpointDurableExecutions` 및 `lambda:GetDurableExecutionState`)에 대한 권한이 포함된 [실행 역할](lambda-intro-execution-role.md)을 사용하여 지속성 함수를 생성합니다.

**참고**  
Lambda 런타임에는 지속성 실행 SDK가 포함되어 있으므로 종속성을 패키징하지 않고도 지속성 함수를 테스트할 수 있습니다. 하지만 프로덕션용 배포 패키지에는 SDK를 포함하는 것이 좋습니다. 이를 통해 버전 일관성이 보장되고 함수에 영향을 미칠 수 있는 잠재적 런타임 업데이트를 방지할 수 있습니다.

콘솔에 내장된 코드 편집기를 사용하여 지속성 함수 코드를 추가합니다.

------
#### [ Node.js ]

**콘솔에서 코드 수정**

1. **코드** 탭을 선택합니다.

   콘솔의 기본 제공 코드 편집기에서 Lambda가 생성한 함수 코드를 볼 수 있습니다. 코드 편집기에 **index.mjs** 탭이 표시되지 않으면 다음 다이어그램과 같이 파일 탐색기에서 **index.mjs**를 선택합니다.  
![\[\]](http://docs.aws.amazon.com/ko_kr/lambda/latest/dg/images/durable-nodejs.png)

1. 다음 코드를 **index.mjs** 탭에 붙여 넣고 Lambda가 생성한 코드를 바꿉니다.

   ```
   import {
     DurableContext,
     withDurableExecution,
   } from "@aws/durable-execution-sdk-js";
   
   export const handler = withDurableExecution(
     async (event, context) => {
       const orderId = event.orderId;
       
       // Step 1: Validate order
       const validationResult = await context.step(async (stepContext) => {
         stepContext.logger.info(`Validating order ${orderId}`);
         return { orderId, status: "validated" };
       });
       
       // Step 2: Process payment
       const paymentResult = await context.step(async (stepContext) => {
         stepContext.logger.info(`Processing payment for order ${orderId}`);
         return { orderId, status: "paid", amount: 99.99 };
       });
       
       // Wait for 10 seconds to simulate external confirmation
       await context.wait({ seconds: 10 });
       
       // Step 3: Confirm order
       const confirmationResult = await context.step(async (stepContext) => {
         stepContext.logger.info(`Confirming order ${orderId}`);
         return { orderId, status: "confirmed" };
       });
           
       return {
         orderId: orderId,
         status: "completed",
         steps: [validationResult, paymentResult, confirmationResult]
       };
     }
   );
   ```

1. **배포** 섹션에서 **배포**를 선택하여 함수의 코드를 업데이트하세요.  
![\[\]](http://docs.aws.amazon.com/ko_kr/lambda/latest/dg/images/getting-started-tutorial/deploy-console.png)

**지속성 함수 코드 이해**  
다음 단계로 넘어가기 전에 함수 코드를 살펴보고 주요 지속성 함수 개념을 알아보겠습니다.
+ `withDurableExecution` 래퍼:

  지속성 함수는 `withDurableExecution`으로 래핑됩니다. 이 래퍼는 `DurableContext` 객체를 제공하고 체크포인트 작업을 관리하여 지속성 실행을 활성화합니다.
+ `DurableContext` 객체:

  함수는 표준 Lambda 컨텍스트 대신 `DurableContext`를 수신합니다. 이 객체는 체크포인트를 생성하는 `step()` 및 `wait()`과 같은 지속성 작업에 대한 메서드를 제공합니다.
+ 단계 및 체크포인트:

  각 `context.step()` 직접 호출 시 실행 전후에 체크포인트가 생성됩니다. 함수가 중단되면 마지막으로 완료된 체크포인트에서 재개됩니다. 함수는 완료된 단계를 다시 실행하지 않습니다. 대신 저장된 결과를 사용합니다.
+ 대기 작업:

  `context.wait()` 직접 호출은 컴퓨팅 리소스를 소비하지 않고 실행을 일시 중지합니다. 대기가 완료되면 Lambda는 함수를 다시 간접 호출하고 체크포인트 로그를 재생하여 저장된 값을 완료된 단계로 대체합니다.
+ 재생 메커니즘:

  대기 또는 중단 후 함수가 재개되면 Lambda는 처음부터 코드를 실행합니다. 하지만 완료된 단계는 다시 실행되지 않습니다. Lambda는 체크포인트 로그에서 결과를 재생합니다. 따라서 코드가 결정론적이어야 합니다.

------
#### [ Python ]

**콘솔에서 코드 수정**

1. **코드** 탭을 선택합니다.

   콘솔의 기본 제공 코드 편집기에서 Lambda가 생성한 함수 코드를 볼 수 있습니다. 코드 편집기에 **lambda\$1function.py** 탭이 표시되지 않으면 다음 다이어그램과 같이 파일 탐색기에서 **lambda\$1function.py**를 선택합니다.  
![\[\]](http://docs.aws.amazon.com/ko_kr/lambda/latest/dg/images/durable-python.png)

1. 다음 코드를 **lambda\$1function.py** 탭에 붙여 넣고 Lambda가 생성한 코드를 바꿉니다.

   ```
   from aws_durable_execution_sdk_python import (
       DurableContext,
       durable_execution,
       durable_step,
   )
   from aws_durable_execution_sdk_python.config import Duration
   
   @durable_step
   def validate_order(step_context, order_id):
       step_context.logger.info(f"Validating order {order_id}")
       return {"orderId": order_id, "status": "validated"}
   
   @durable_step
   def process_payment(step_context, order_id):
       step_context.logger.info(f"Processing payment for order {order_id}")
       return {"orderId": order_id, "status": "paid", "amount": 99.99}
   
   @durable_step
   def confirm_order(step_context, order_id):
       step_context.logger.info(f"Confirming order {order_id}")
       return {"orderId": order_id, "status": "confirmed"}
   
   @durable_execution
   def lambda_handler(event, context: DurableContext):
       order_id = event['orderId']
       
       # Step 1: Validate order
       validation_result = context.step(validate_order(order_id))
       
       # Step 2: Process payment
       payment_result = context.step(process_payment(order_id))
       
       # Wait for 10 seconds to simulate external confirmation
       context.wait(Duration.from_seconds(10))
       
       # Step 3: Confirm order
       confirmation_result = context.step(confirm_order(order_id))
           
       return {
           "orderId": order_id,
           "status": "completed",
           "steps": [validation_result, payment_result, confirmation_result]
       }
   ```

1. **배포** 섹션에서 **배포**를 선택하여 함수의 코드를 업데이트하세요.  
![\[\]](http://docs.aws.amazon.com/ko_kr/lambda/latest/dg/images/getting-started-tutorial/deploy-console.png)

**지속성 함수 코드 이해**  
다음 단계로 넘어가기 전에 함수 코드를 살펴보고 주요 지속성 함수 개념을 알아보겠습니다.
+ `@durable_execution` 데코레이터:

  핸들러 함수는 `@durable_execution`으로 데코레이션됩니다. 이 데코레이터는 `DurableContext` 객체를 제공하고 체크포인트 작업을 관리하여 지속성 실행을 활성화합니다.
+ `@durable_step` 데코레이터:

  각 단계 함수는 `@durable_step`으로 데코레이션됩니다. 이 데코레이터는 체크포인트를 생성하는 지속성 단계로 함수를 표시합니다.
+ `DurableContext` 객체:

  함수는 표준 Lambda 컨텍스트 대신 `DurableContext`를 수신합니다. 이 객체는 체크포인트를 생성하는 `step()` 및 `wait()`과 같은 지속성 작업에 대한 메서드를 제공합니다.
+ 단계 및 체크포인트:

  각 `context.step()` 직접 호출 시 실행 전후에 체크포인트가 생성됩니다. 함수가 중단되면 마지막으로 완료된 체크포인트에서 재개됩니다. 함수는 완료된 단계를 다시 실행하지 않습니다. 대신 저장된 결과를 사용합니다.
+ 대기 작업:

  `context.wait()` 직접 호출은 컴퓨팅 리소스를 소비하지 않고 실행을 일시 중지합니다. 대기가 완료되면 Lambda는 함수를 다시 간접 호출하고 체크포인트 로그를 재생하여 저장된 값을 완료된 단계로 대체합니다.
+ Python SDK는 동기식입니다.

  Python SDK는 `await`을 사용하지 않습니다. 모든 지속성 작업은 동기식 메서드 직접 호출입니다.

------

## 콘솔 코드 편집기를 사용하여 지속성 함수 간접 호출
<a name="get-started-invoke-durable-manually"></a>

명시적 버전이 지정(또는 게시)되지 않은 경우 콘솔은 `$LATEST` 버전 한정자를 사용하여 지속성 함수를 간접적으로 호출합니다. 하지만 코드를 결정론적으로 실행하려면 항상 안정적인 버전을 가리키는 정규화된 ARN을 사용해야 합니다.

**함수의 버전 게시**

1. **버전** 탭을 선택합니다.

1. [**새 버전 발행**]을 선택합니다.

1. **버전 설명**에 **Initial version**(선택 사항)을 입력합니다.

1. **게시**를 선택합니다.

1. Lambda는 함수 버전 1을 생성합니다. 이제 함수 ARN의 끝에 `:1`이 포함되어 버전 1임을 나타냅니다.

이제 함수로 전송할 테스트 이벤트를 생성합니다. 이벤트는 주문 ID가 포함된 JSON 형식의 문서입니다.

**테스트 이벤트 생성**

1. 콘솔 코드 편집기의 **테스트 이벤트** 섹션에서 **테스트 이벤트 생성**을 선택하세요.  
![\[\]](http://docs.aws.amazon.com/ko_kr/lambda/latest/dg/images/getting-started-tutorial/test-event.png)

1. **Event Name(이벤트 이름)**에 **myTestEvent**를 입력합니다.

1. **이벤트 JSON** 섹션에서 기본 JSON을 다음으로 바꾸세요.

   ```
   {
     "orderId": "order-12345"
   }
   ```

1. **저장**을 선택합니다.

**지속성 함수 테스트 및 실행 보기**

콘솔 코드 편집기의 **테스트 이벤트** 섹션에서 테스트 이벤트 옆에 있는 실행 아이콘을 선택하세요.

![\[\]](http://docs.aws.amazon.com/ko_kr/lambda/latest/dg/images/getting-started-tutorial/run-test-event.png)


지속성 함수 실행이 시작됩니다. 10초 대기가 포함되므로 최초 간접 호출이 빠르게 완료되고, 대기 기간 후에 함수가 재개됩니다. 실행 진행 상황은 **지속성 실행** 탭에서 확인할 수 있습니다.

**지속성 함수 실행 보기**

1. **지속성 실행** 탭을 선택합니다.

1. 목록에서 실행을 찾습니다. 실행에 현재 상태(실행 중, 성공 또는 실패)가 표시됩니다.

1. 실행 ID를 선택하여 다음을 포함한 세부 정보를 확인합니다.
   + 각 단계가 완료된 시간을 보여주는 실행 타임라인
   + 체크포인트 내역
   + 대기 기간
   + 단계 결과

CloudWatch Logs에서 함수의 로그를 보고 각 단계의 콘솔 출력을 볼 수도 있습니다.

**CloudWatch Logs에서 함수의 간접 호출 레코드 보기**

1. CloudWatch 콘솔에서 [로그 그룹 페이지](https://console.aws.amazon.com/cloudwatch/home#logs:)를 엽니다.

1. 함수에 대한 로그 그룹(`/aws/lambda/myDurableFunction`)을 선택합니다.

1. 아래로 스크롤하고 확인하고자 하는 함수 간접 호출의 **로그 스트림**을 선택합니다.  
![\[\]](http://docs.aws.amazon.com/ko_kr/lambda/latest/dg/images/log-stream.png)

   최초 실행 및 대기 후 재생을 포함하여 함수의 각 간접 호출에 대한 로그 항목이 표시될 것입니다.

**참고**  
`DurableContext`의 로거(예: `context.logger` 또는 `stepContext.logger`)를 사용하면 Lambda 콘솔의 지속성 실행 및 단계 뷰에도 로그가 표시됩니다. 이러한 로그는 로드하는 데 시간이 걸릴 수 있습니다.

## 정리
<a name="gettingstarted-durable-cleanup"></a>

지속성 함수 예제를 사용한 작업을 마치면 해당 함수를 삭제합니다. 함수의 로그를 저장하는 로그 그룹과 콘솔에서 생성된 [실행 역할](lambda-intro-execution-role.md)을 삭제할 수도 있습니다.

**Lambda 함수를 삭제하려면**

1. Lambda 콘솔의 [함수 페이지](https://console.aws.amazon.com/lambda/home#/functions)를 엽니다.

1. 생성한 함수를 선택합니다.

1. **작업**, **삭제**를 선택합니다.

1. 텍스트 입력 필드에 **confirm**를 입력하고 **Delete**(삭제)를 선택합니다.

**로그 그룹 삭제**

1. CloudWatch 콘솔에서 [로그 그룹 페이지](https://console.aws.amazon.com/cloudwatch/home#logs:)를 엽니다.

1. 함수의 로그 그룹(`/aws/lambda/myDurableFunction`)을 선택합니다.

1. **작업(Actions)**, **로그 그룹 삭제(Delete log group(s))**를 선택합니다.

1. **로그 그룹 삭제(Delete log group(s))** 대화 상자에서 **삭제(Delete)**를 선택합니다.

**실행 역할을 삭제하려면**

1. AWS Identity and Access Management(IAM) 콘솔의 [역할 페이지](https://console.aws.amazon.com/iam/home?#/roles)를 엽니다.

1. 함수의 실행 역할(예: `myDurableFunction-role-31exxmpl`)을 선택합니다.

1. **삭제**를 선택합니다.

1. **역할 삭제** 대화 상자에 역할 이름을 입력하고 **삭제**를 선택하세요.

## 추가 리소스 및 다음 단계
<a name="durable-getting-started-more-resources"></a>

콘솔을 사용하여 간단한 지속성 함수를 생성하고 테스트했으니, 이제 다음 단계를 수행합니다.
+ 분산 트랜잭션, 주문 처리, 인적 검토 워크플로 등 지속성 함수의 일반적인 사용 사례에 대해 알아봅니다. [예제](durable-examples.md)를 참조하세요.
+ CloudWatch 지표 및 실행 내역을 사용하여 지속성 함수 실행을 모니터링하는 방법을 이해합니다. [모니터링 및 디버깅](durable-monitoring.md)을 참조하세요.
+ 지속성 함수를 동기식 및 비동기식으로 간접 호출하고, 장기 실행을 관리하는 방법을 알아봅니다. [지속성 함수 간접 호출](durable-invoking.md)을 참조하세요.
+ 결정론적 코드 작성, 체크포인트 크기 관리, 비용 최적화 모범 사례를 따릅니다. [모범 사례](durable-best-practices.md)를 참조하세요.
+ 로컬 및 클라우드에서 지속성 함수를 테스트하는 방법을 알아봅니다. [지속성 함수 테스트](durable-testing.md)를 참조하세요.
+ 지속성 함수와 Step Functions를 비교하여 각 접근 방식이 가장 효과적인 경우를 파악합니다. [지속성 함수 또는 Step Functions](durable-step-functions.md)를 참조하세요.

# AWS CLI를 사용하여 Lambda 지속성 함수 배포 및 간접 호출
<a name="durable-getting-started-cli"></a>

AWS CLI를 사용하여 필수 명령으로 Lambda 지속성 함수를 생성 및 배포합니다. 이 접근 방식을 사용하면 배포 프로세스의 각 단계를 직접 제어할 수 있습니다.

## 사전 조건
<a name="durable-cli-prerequisites"></a>
+ AWS CLI를 설치하고 구성합니다. 지침은 [AWS CLI 설치](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html)를 참조하세요.
+ 함수 코드와 지속성 실행 SDK를 사용하여 배포 패키지를 생성합니다.
+ 체크포인트 권한을 가진 IAM 실행 역할을 생성합니다.

## 실행 역할 생성
<a name="durable-cli-create-role"></a>

기본 Lambda 실행 및 체크포인트 작업에 대한 권한이 있는 IAM 역할을 생성합니다.

**실행 역할을 만들려면**

1. Lambda의 역할 수임을 허용하는 신뢰 정책 문서를 생성합니다. `trust-policy.json`으로 저장합니다.

   ```
   {
     "Version": "2012-10-17",		 	 	 
     "Statement": [
       {
         "Effect": "Allow",
         "Principal": {
           "Service": "lambda.amazonaws.com"
         },
         "Action": "sts:AssumeRole"
       }
     ]
   }
   ```

1. 역할을 생성합니다.

   ```
   aws iam create-role \
     --role-name durable-function-role \
     --assume-role-policy-document file://trust-policy.json
   ```

1. 체크포인트 작업 및 기본 실행을 위한 지속성 실행 정책을 연결합니다.

   ```
   aws iam attach-role-policy \
     --role-name durable-function-role \
     --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicDurableExecutionRolePolicy
   ```

`AWSLambdaBasicDurableExecutionRolePolicy` 관리형 정책에는 체크포인트 작업(`lambda:CheckpointDurableExecutions` 및 `lambda:GetDurableExecutionState`)과 기본 Lambda 실행에 필요한 권한이 포함됩니다.

## 지속성 함수 생성
<a name="durable-cli-create-function"></a>

`--durable-config` 파라미터를 사용하여 지속성 함수를 생성합니다.

**지속성 함수 생성**

1. 종속성이 포함된 함수 코드를 .zip 파일로 패키징합니다.

   ```
   zip -r function.zip index.mjs node_modules/
   ```

1. 지속성 실행이 활성화된 함수를 생성합니다.

   ```
   aws lambda create-function \
     --function-name myDurableFunction \
     --runtime nodejs22.x \
     --role arn:aws:iam::123456789012:role/durable-function-role \
     --handler index.handler \
     --zip-file fileb://function.zip \
     --durable-config '{"ExecutionTimeout": 3600, "RetentionPeriodInDays": 7}'
   ```

**참고**  
함수를 생성할 때만 지속성 실행을 활성화할 수 있습니다. 기존 함수에서는 활성화할 수 없습니다.

**참고**  
현재 Java(미리 보기)의 지속성 함수는 현재 컨테이너 이미지를 통해서만 생성할 수 있습니다. 컨테이너 이미지에서 지속성 함수를 생성하는 방법에 대한 자세한 내용은 [지속성 함수에 지원되는 런타임](durable-supported-runtimes.md)을 참조하세요.

## 버전 게시
<a name="durable-cli-publish-version"></a>

지속성 함수는 `$LATEST` 버전 한정자를 사용하여 간접적으로 호출할 수 있지만, 코드를 결정론적으로 실행하려면 항상 안정적인 버전을 가리키는 정규화된 ARN을 사용해야 합니다.

```
aws lambda publish-version \
  --function-name myDurableFunction \
  --description "Initial version"
```

명령은 버전 ARN을 반환합니다. ARN 끝에 있는 버전 번호(예: `:1`)를 기록해 둡니다.

버전을 가리키는 별칭을 생성합니다(선택 사항).

```
aws lambda create-alias \
  --function-name myDurableFunction \
  --name prod \
  --function-version 1
```

## 지속성 함수 간접 호출
<a name="durable-cli-invoke"></a>

정규화된 ARN(버전 또는 별칭)을 사용하여 지속성 함수를 간접 호출합니다.

**참고**  
**멱등적 간접 호출:** 실패한 간접 호출을 재시도할 때 중복 실행을 방지하려면 최대 1회 실행 의미 체계를 보장하는 실행 이름을 제공할 수 있습니다. 자세한 내용은 [멱등성](durable-execution-idempotency.md)을 참조하세요.

**동기식 간접 호출**  
15분 이내에 완료되는 실행의 경우 동기식 간접 호출을 사용합니다.

```
aws lambda invoke \
  --function-name myDurableFunction:1 \
  --payload '{"orderId": "order-12345"}' \
  --cli-binary-format raw-in-base64-out \
  response.json
```

또는 별칭을 사용합니다.

```
aws lambda invoke \
  --function-name myDurableFunction:prod \
  --payload '{"orderId": "order-12345"}' \
  --cli-binary-format raw-in-base64-out \
  response.json
```

**비동기식 간접 호출**  
장기 실행의 경우 비동기식 간접 호출을 사용합니다.

```
aws lambda invoke \
  --function-name myDurableFunction:prod \
  --invocation-type Event \
  --payload '{"orderId": "order-12345"}' \
  --cli-binary-format raw-in-base64-out \
  response.json
```

비동기식 간접 호출을 사용하면 Lambda는 즉시 반환합니다. 함수는 백그라운드에서 계속 실행됩니다.

**참고**  
콘솔에서 프로토타입 생성 및 테스트를 위해 `$LATEST`를 사용할 수 있습니다. 프로덕션 워크로드의 경우 게시된 버전 또는 별칭을 사용합니다.

## 지속성 실행 관리
<a name="durable-cli-manage-executions"></a>

다음 명령을 사용하여 지속성 함수 실행을 관리하고 모니터링합니다.

**실행 나열**  
지속성 함수에 대한 모든 실행을 나열합니다.

```
aws lambda list-durable-executions-by-function \
  --function-name myDurableFunction
```

**실행 세부 정보 가져오기**  
특정 실행에 대한 세부 정보를 가져옵니다.

```
aws lambda get-durable-execution \
  --durable-execution-arn arn:aws:lambda:us-east-1:123456789012:function:myDurableFunction:my-function-version/durable-execution/my-execution-name/my-execution-id
```

**실행 내역 가져오기**  
실행에 대한 체크포인트 내역을 확인합니다.

```
aws lambda get-durable-execution-history \
  --durable-execution-arn arn:aws:lambda:us-east-1:123456789012:function:myDurableFunction:my-function-version/durable-execution/my-execution-name/my-execution-id
```

**실행 중지**  
지속성 실행을 중지합니다.

```
aws lambda stop-durable-execution \
  --durable-execution-arn arn:aws:lambda:us-east-1:123456789012:function:myDurableFunction:my-function-version/durable-execution/my-execution-name/my-execution-id
```

## 함수 코드 업데이트
<a name="durable-cli-update-function"></a>

지속성 함수 코드를 업데이트하고 새 버전을 게시합니다.

**새 버전 업데이트 및 게시**

1. 함수 코드를 업데이트합니다.

   ```
   aws lambda update-function-code \
     --function-name myDurableFunction \
     --zip-file fileb://function.zip
   ```

1. 업데이트가 완료될 때까지 대기합니다.

   ```
   aws lambda wait function-updated \
     --function-name myDurableFunction
   ```

1. 새 버전을 게시합니다.

   ```
   aws lambda publish-version \
     --function-name myDurableFunction \
     --description "Updated order processing logic"
   ```

1. 새 버전을 가리키도록 별칭을 업데이트합니다.

   ```
   aws lambda update-alias \
     --function-name myDurableFunction \
     --name prod \
     --function-version 2
   ```

**중요**  
진행 중인 실행에서 시작된 버전을 계속 사용합니다. 새 간접 호출은 업데이트된 별칭 버전을 사용합니다.

## 함수 로그 보기
<a name="durable-cli-view-logs"></a>

CloudWatch Logs에서 지속성 함수의 로그를 봅니다.

```
aws logs tail /aws/lambda/myDurableFunction --follow
```

특정 실행에 대해 로그를 필터링합니다.

```
aws logs filter-log-events \
  --log-group-name /aws/lambda/myDurableFunction \
  --filter-pattern "exec-abc123"
```

## 리소스 정리
<a name="durable-cli-cleanup"></a>

지속성 함수 및 관련 리소스를 삭제합니다.

```
# Delete the function
aws lambda delete-function --function-name myDurableFunction

# Delete the IAM role policies
aws iam detach-role-policy \
  --role-name durable-function-role \
  --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

aws iam detach-role-policy \
  --role-name durable-function-role \
  --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicDurableExecutionRolePolicy

# Delete the role
aws iam delete-role --role-name durable-function-role
```

## 다음 단계
<a name="durable-cli-next-steps"></a>

AWS CLI를 사용하여 지속성 함수를 배포하면 다음 작업을 할 수 있습니다.
+ `list-durable-executions` 및 `get-durable-execution` 명령을 사용하여 실행 모니터링
+ AWS CloudTrail 데이터 이벤트에서 체크포인트 작업 보기
+ 실행 실패 또는 장기 실행에 대한 CloudWatch 경보 설정
+ 쉘 스크립트 또는 CI/CD 파이프라인을 사용하여 배포 자동화

Lambda에 대해 사용 가능한 AWS CLI 명령에 대한 자세한 내용은 [AWS CLI 명령 레퍼런스](https://docs.aws.amazon.com/cli/latest/reference/lambda/index.html)를 참조하세요.

# 코드형 인프라를 사용하여 Lambda 지속성 함수 배포
<a name="durable-getting-started-iac"></a>

AWS CloudFormation, AWS CDK, AWS Serverless Application Model, Terraform과 같은 코드형 인프라(IaC) 도구를 사용하여 Lambda 지속성 함수를 배포할 수 있습니다. 이러한 도구를 사용하면 코드에서 함수, 실행 역할 및 권한을 정의하여 배포를 반복하고 버전을 관리할 수 있습니다.

3가지 도구 모두에서 다음을 수행해야 합니다.
+ 함수에서 지속성 실행 활성화
+ 실행 역할에 체크포인트 권한 부여
+ 버전 게시 또는 별칭 생성(지속성 함수에는 정규화된 ARN 필요)

## ZIP의 지속성 함수
<a name="durable-iac-zip"></a>

### AWS CloudFormation
<a name="durable-iac-cloudformation"></a>

CloudFormation을 사용하여 템플릿에서 지속성 함수를 정의합니다. 다음 예제에서는 필요한 권한을 가진 지속성 함수를 생성합니다.

```
AWSTemplateFormatVersion: '2010-09-09'
Description: Lambda durable function example

Resources:
  DurableFunctionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'		 	 	 
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicDurableExecutionRolePolicy

  DurableFunction:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: myDurableFunction
      Runtime: nodejs22.x
      Handler: index.handler
      Role: !GetAtt DurableFunctionRole.Arn
      Code:
        ZipFile: |
          // Your durable function code here
          export const handler = async (event, context) => {
            return { statusCode: 200 };
          };
      DurableConfig:
        ExecutionTimeout: 3600
        RetentionPeriodInDays: 7

  DurableFunctionVersion:
    Type: AWS::Lambda::Version
    Properties:
      FunctionName: !Ref DurableFunction
      Description: Initial version

  DurableFunctionAlias:
    Type: AWS::Lambda::Alias
    Properties:
      FunctionName: !Ref DurableFunction
      FunctionVersion: !GetAtt DurableFunctionVersion.Version
      Name: prod

Outputs:
  FunctionArn:
    Description: Durable function ARN
    Value: !GetAtt DurableFunction.Arn
  AliasArn:
    Description: Function alias ARN (use this for invocations)
    Value: !Ref DurableFunctionAlias
```

**템플릿을 배포하려면**

```
aws cloudformation deploy \
  --template-file template.yaml \
  --stack-name my-durable-function-stack \
  --capabilities CAPABILITY_IAM
```

### AWS CDK
<a name="durable-iac-cdk"></a>

AWS CDK를 사용하면 프로그래밍 언어로 인프라를 정의할 수 있습니다. 다음 예제에서는 TypeScript 및 Python을 사용하여 지속성 함수를 생성하는 방법을 보여줍니다.

------
#### [ TypeScript ]

```
import * as cdk from 'aws-cdk-lib';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as iam from 'aws-cdk-lib/aws-iam';
import { Construct } from 'constructs';

export class DurableFunctionStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // Create the durable function
    const durableFunction = new lambda.Function(this, 'DurableFunction', {
      runtime: lambda.Runtime.NODEJS_22_X,
      handler: 'index.handler',
      code: lambda.Code.fromAsset('lambda'),
      functionName: 'myDurableFunction',
      durableConfig: { executionTimeout: Duration.hours(1), retentionPeriod: Duration.days(30) },
    });

    // Create version and alias
    const version = durableFunction.currentVersion;
    const alias = new lambda.Alias(this, 'ProdAlias', {
      aliasName: 'prod',
      version: version,
    });

    // Output the alias ARN
    new cdk.CfnOutput(this, 'FunctionAliasArn', {
      value: alias.functionArn,
      description: 'Use this ARN to invoke the durable function',
    });
  }
}
```

------
#### [ Python ]

```
from aws_cdk import (
    Stack,
    aws_lambda as lambda_,
    aws_iam as iam,
    CfnOutput,
)
from constructs import Construct

class DurableFunctionStack(Stack):
    def __init__(self, scope: Construct, id: str, **kwargs):
        super().__init__(scope, id, **kwargs)

        # Create the durable function
        durable_function = lambda_.Function(
            self, 'DurableFunction',
            runtime=lambda_.Runtime.NODEJS_22_X,
            handler='index.handler',
            code=lambda_.Code.from_asset('lambda'),
            function_name='myDurableFunction',
            durable_execution={execution_timeout: Duration.hours(1), retention_period: Duration.days(30)}
        )

        # Add durable execution managed policy for checkpoint permissions
        durable_function.role.add_managed_policy(
            iam.ManagedPolicy.from_aws_managed_policy_name('service-role/AWSLambdaBasicDurableExecutionRolePolicy')
        )

        # Create version and alias
        version = durable_function.current_version
        alias = lambda_.Alias(
            self, 'ProdAlias',
            alias_name='prod',
            version=version
        )

        # Output the alias ARN
        CfnOutput(
            self, 'FunctionAliasArn',
            value=alias.function_arn,
            description='Use this ARN to invoke the durable function'
        )
```

------

**CDK 스택 배포**

```
cdk deploy
```

### AWS Serverless Application Model
<a name="durable-iac-sam"></a>

AWS SAM은 서버리스 애플리케이션의 CloudFormation 템플릿을 간소화합니다. 다음 템플릿은 AWS SAM을 사용하여 지속성 함수를 생성합니다.

```
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Lambda durable function with SAM

Resources:
  DurableFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: myDurableFunction
      Runtime: nodejs22.x
      Handler: index.handler
      CodeUri: ./src
      DurableConfig:
        ExecutionTimeout: 3600
        RetentionPeriodInDays: 7
      Policies:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicDurableExecutionRolePolicy
      AutoPublishAlias: prod

Outputs:
  FunctionArn:
    Description: Durable function ARN
    Value: !GetAtt DurableFunction.Arn
  AliasArn:
    Description: Function alias ARN (use this for invocations)
    Value: !Ref DurableFunction.Alias
```

**SAM 템플릿 배포**

```
sam build
sam deploy --guided
```

### Terraform
<a name="durable-iac-terraform"></a>

Terraform은 AWS 리소스를 지원하는 인기 있는 오픈 소스 IaC 도구입니다. 다음 예제에서는 Terraform에서 AWS 공급자 버전 6.25.0 이상을 사용하여 지속성 함수를 생성합니다.

```
terraform {
  required_version = ">= 1.0"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = ">= 6.25.0"
    }
  }
}

provider "aws" {
  region = "us-east-2"
}

# IAM Role for Lambda Function
resource "aws_iam_role" "lambda_role" {
  name = "durable-function-role"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Action = "sts:AssumeRole"
      Effect = "Allow"
      Principal = {
        Service = "lambda.amazonaws.com"
      }
    }]
  })
}

# Attach durable execution policy for checkpoint operations
resource "aws_iam_role_policy_attachment" "lambda_durable" {
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicDurableExecutionRolePolicy"
  role       = aws_iam_role.lambda_role.name
}

# Lambda Function with Durable Execution enabled
resource "aws_lambda_function" "durable_function" {
  filename      = "function.zip"
  function_name = "myDurableFunction"
  role          = aws_iam_role.lambda_role.arn
  handler       = "index.handler"
  runtime       = "nodejs22.x"
  timeout       = 30
  memory_size   = 512

  durable_config {
    execution_timeout = 900
    retention_period  = 7
  }
}

# Publish a version
resource "aws_lambda_alias" "prod" {
  name             = "prod"
  function_name    = aws_lambda_function.durable_function.function_name
  function_version = aws_lambda_function.durable_function.version
}

output "function_arn" {
  description = "ARN of the Lambda function"
  value       = aws_lambda_function.durable_function.arn
}

output "alias_arn" {
  description = "ARN of the function alias (use this for invocations)"
  value       = aws_lambda_alias.prod.arn
}
```

**Terraform을 사용하여 배포하려면**

```
terraform init
terraform plan
terraform apply
```

**참고**  
Lambda 지속성 함수에 대한 Terraform 지원에 AWS 공급자 버전 6.25.0 이상이 필요합니다. 이전 공급자 버전을 사용하는 경우 버전을 업데이트하세요.

## OCI 컨테이너 이미지의 지속성 함수
<a name="durable-iac-oci"></a>

컨테이너 이미지를 기반으로 지속성 함수를 생성할 수도 있습니다. 컨테이너 이미지를 빌드하는 방법에 대한 지침은 [지속성 함수에 지원되는 런타임](durable-supported-runtimes.md)을 참조하세요.

### AWS CDK
<a name="durable-iac-oci-cdk"></a>

AWS CDK를 사용하면 프로그래밍 언어로 인프라를 정의할 수 있습니다. 다음 예제에서는 컨테이너 이미지의 TypeScript를 사용하여 지속성 함수를 생성하는 방법을 보여줍니다.

```
import * as cdk from 'aws-cdk-lib';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as iam from 'aws-cdk-lib/aws-iam';
import { Construct } from 'constructs';

export class DurableFunctionStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // Create the durable function
    const durableFunction = new lambda.DockerImageFunction(this, 'DurableFunction', {
      code: lambda.DockerImageCode.fromImageAsset('./lambda', {
        platform: cdk.aws_ecr_assets.Platform.LINUX_AMD64,
      }),
      functionName: 'myDurableFunction',
      memorySize: 512,
      timeout: cdk.Duration.seconds(30),
      durableConfig: { executionTimeout: cdk.Duration.hours(1), retentionPeriod: cdk.Duration.days(30) },
    });

    // Create version and alias
    const version = durableFunction.currentVersion;
    const alias = new lambda.Alias(this, 'ProdAlias', {
      aliasName: 'prod',
      version: version,
    });

    // Output the alias ARN
    new cdk.CfnOutput(this, 'FunctionAliasArn', {
      value: alias.functionArn,
      description: 'Use this ARN to invoke the durable function',
    });
  }
}
```

**CDK 스택 배포**

```
cdk deploy
```

### AWS Serverless Application Model
<a name="durable-iac-oci-sam"></a>

AWS SAM은 서버리스 애플리케이션의 CloudFormation 템플릿을 간소화합니다. 다음 템플릿은 AWS SAM을 사용하여 지속성 함수를 생성합니다.

```
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Lambda durable function with SAM

Resources:
  DurableFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: myDurableFunction
      PackageType: Image
      ImageUri: ./src
      DurableConfig:
        ExecutionTimeout: 3600
        RetentionPeriodInDays: 7
      Policies:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicDurableExecutionRolePolicy
      AutoPublishAlias: prod
    Metadata:
      DockerTag: latest
      DockerContext: ./src
      Dockerfile: Dockerfile

Outputs:
  FunctionArn:
    Description: Durable function ARN
    Value: !GetAtt DurableFunction.Arn
  AliasArn:
    Description: Function alias ARN (use this for invocations)
    Value: !Ref DurableFunction.Alias
```

**SAM 템플릿 배포**

```
sam build
sam deploy --guided
```

## 일반적인 구성 패턴
<a name="durable-iac-common-patterns"></a>

사용하는 IaC 도구와 무관하게 지속성 함수에 대해 다음 패턴을 따릅니다.

**지속성 실행 활성화**  
함수의 `DurableConfig` 속성을 설정하여 지속성 실행을 활성화합니다. 이 속성은 함수 생성 시에만 사용할 수 있습니다. 기존 함수에서는 지속성 실행을 활성화할 수 없습니다.

**체크포인트 권한 부여**  
`AWSLambdaBasicDurableExecutionRolePolicy` 관리형 정책을 실행 역할에 연결합니다. 이 정책에는 필요한 `lambda:CheckpointDurableExecutions` 및 `lambda:GetDurableExecutionState` 권한이 포함되어 있습니다.

**정규화된 ARN 사용**  
함수의 버전 또는 별칭을 생성합니다. 지속성 함수는 간접 호출에 정규화된 ARN(버전 또는 별칭 포함)이 필요합니다. AWS SAM에서 `AutoPublishAlias`를 사용하거나 CloudFormation, AWS CDK 및 Terraform에서 명시적 버전을 생성합니다.

**패키지 종속성**  
배포 패키지에 지속성 실행 SDK를 포함합니다. Node.js의 경우 `@aws/durable-execution-sdk-js`를 설치합니다. Python의 경우 `aws-durable-execution-sdk-python`을 설치합니다.

## 다음 단계
<a name="durable-iac-next-steps"></a>

지속성 함수 배포 후에 다음을 진행합니다.
+ 정규화된 ARN(버전 또는 별칭)을 사용하여 함수 테스트
+ Lambda 콘솔의 지속성 실행 탭에서 실행 진행 상황 모니터링
+ AWS CloudTrail 데이터 이벤트에서 체크포인트 작업 보기
+ CloudWatch Logs에서 함수 출력 및 재생 동작 검토

IaC 도구를 사용하여 Lambda 함수를 배포하는 방법에 대한 자세한 내용은 다음 항목을 참조하세요.
+ [CloudFormation AWS::Lambda::Function 참조](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-function.html)
+ [AWS CDK Lambda 모듈 설명서](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_lambda-readme.html)
+ [AWS SAM 개발자 안내서](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html)

# 지속성 함수 또는 Step Functions
<a name="durable-step-functions"></a>

Lambda 지속성 함수와 AWS Step Functions를 모두 사용하면 자동 상태 관리 및 장애 복구를 통해 신뢰할 수 있는 워크플로 오케스트레이션이 가능합니다. 다양한 개발자 선호 및 아키텍처 패턴을 충족합니다. 지속성 함수는 Lambda 내 애플리케이션 개발에 최적화되어 있으며, Step Functions는 AWS 서비스 간 워크플로 오케스트레이션을 위해 개발되었습니다.

## 지속성 함수를 사용하는 경우
<a name="durable-sfn-when-durable"></a>

지속성 함수를 사용하는 경우:
+ 팀이 표준 프로그래밍 언어 및 익숙한 개발 도구를 선호하는 경우
+ 애플리케이션 로직이 주로 Lambda 함수 내에 있는 경우
+ 코드의 실행 상태를 세밀하게 제어하려는 경우
+ 워크플로와 비즈니스 로직을 강하게 결합하여 Lambda 중심 애플리케이션을 구축하는 경우
+ 코드와 시각적/JSON 디자이너 간에 전환하지 않고 빠르게 반복하려는 경우

## Step Functions를 사용해야 하는 경우
<a name="durable-sfn-when-step"></a>

Step Functions를 사용하는 경우:
+ 팀 간 가시성을 위해 시각적 워크플로 표현이 필요한 경우
+ 여러 AWS 서비스를 오케스트레이션하고 사용자 지정 SDK 코드 없이 네이티브 통합을 원하는 경우
+ 제로 유지 관리 인프라가 필요한 경우(패치, 런타임 업데이트 필요 없음)
+ 기술과 관련되지 않은 이해관계자가 워크플로 로직을 이해하고 검증해야 하는 경우

## 결정 프레임워크
<a name="durable-sfn-decision-framework"></a>

다음 질문을 사용하여 사용 사례에 적합한 서비스를 결정합니다.
+ **주요 초점은 무엇입니까?** Lambda 내 애플리케이션 개발 → 지속성 함수. AWS 간 워크플로 오케스트레이션 → Step Functions.
+ **선호하는 프로그래밍 모델은 무엇입니까?** 표준 프로그래밍 언어 → 지속성 함수. 그래프 기반 DSL 또는 시각적 디자이너 → Step Functions.
+ **관련된 AWS 서비스는 몇 개입니까?** 주로 Lambda → 지속성 함수. 여러 AWS 서비스 → Step Functions.
+ **어떤 개발 도구를 사용합니까?** Lambda 개발자 경험, LLM 에이전트를 사용한 IDE, 프로그래밍 언어별 유닛 테스트 프레임워크, AWS SAM, AWS CDK, AWS 도구 키트 → 지속성 함수. 시각적 워크플로 빌더, 워크플로 모델링을 위한 AWS CDK → Step Functions.
+ **누가 인프라를 관리합니까?** Lambda 내에서 유연성을 원함 → 지속성 함수. 완전관리형, 제로 유지 관리를 원함 → Step Functions.

## 기능 비교
<a name="durable-sfn-comparison"></a>

다음 표에서는 Step Functions 및 Lambda 지속성 함수의 주요 기능을 비교합니다.


| 기능 | AWS Step Functions | Lambda 지속성 함수 | 
| --- | --- | --- | 
| 기본 포커스 | AWS 간 워크플로 오케스트레이션 | Lambda 내 애플리케이션 개발 | 
| 서비스 유형 | 독립 실행형 전용 워크플로 서비스 | Lambda 내에서 실행 | 
| 프로그래밍 모델 | 그래프 기반, Amazon States Language DSL 또는 AWS CDK | 표준 프로그래밍 언어(JavaScript/TypeScript, Python) | 
| 개발 도구 | 콘솔/AWS 도구 키트 IDE 확장의 시각적 빌더, AWS CDK  | IDE 및 LLM 에이전트 내의 Lambda DX, 유닛 테스트 프레임워크, AWS SAM, AWS 도구 키트 IDE 확장 | 
| 통합 | 220개 이상의 AWS 서비스, 16,000개 API | Lambda 이벤트 중심 프로그래밍 모델 확장(이벤트 소스) | 
| 관리 | 완전관리형, 런타임에 구애받지 않음, 제로 유지 관리(패치, 런타임 업데이트 필요 없음) | Lambda 환경 내에서 관리됨 | 
| 최적의 용도 | 비즈니스 프로세스 및 IT 자동화, 데이터 처리, AI 워크플로 | 분산 트랜잭션, 상태 저장 애플리케이션 로직, 함수 오케스트레이션, 데이터 처리, AI 워크플로 | 

## 하이브리드 아키텍처
<a name="durable-sfn-hybrid"></a>

많은 애플리케이션에서 두 서비스를 모두 사용하면 이점을 얻을 수 있습니다. 일반적인 패턴은 Lambda 내의 애플리케이션 수준 로직에 지속성 함수를 사용하는 것이지만, Step Functions는 Lambda 함수 이외의 여러 AWS 서비스에서 상위 수준 워크플로를 조정합니다.

## 마이그레이션 고려 사항
<a name="durable-sfn-migration"></a>

**단순하게 시작하여 복잡하게 진화:** Lambda 중심 워크플로의 경우 지속성 함수로 시작합니다. 다중 서비스 오케스트레이션 또는 시각적 워크플로 설계가 필요한 경우 Step Functions를 추가합니다.

**기존 Step Functions 사용자:** 확립된 교차 서비스 워크플로를 위해 Step Functions를 유지합니다. 신뢰성이 필요한 새로운 Lambda 애플리케이션 로직에는 지속성 함수를 고려하세요.

## 관련 리소스
<a name="durable-sfn-related"></a>
+ [Lambda 지속성 함수](durable-functions.md)
+ [Step Functions로 Lambda 함수 오케스트레이션](with-step-functions.md)
+ [지속성 함수 시작하기](durable-getting-started.md)

# 예제 및 사용 사례
<a name="durable-examples"></a>

Lambda 지속성 함수를 사용하면 단계 및 대기와 같은 지속성 작업을 사용하여 내결함성 다단계 애플리케이션을 빌드할 수 있습니다. 자동 체크포인트 지정과 실패 후 처음부터 실행이 다시 시작되지만 완료된 체크포인트를 건너뛰는 체크포인트-재생 모델을 사용하면 함수가 실패로부터 복구되어 진행 상황 손실 없이 실행을 재개할 수 있습니다.

## 단기 내결함성 프로세스
<a name="durable-examples-short-lived"></a>

지속성 함수를 사용하여 일반적으로 몇 분 내에 완료되는 안정적인 작업을 빌드합니다. 이러한 프로세스는 장기 실행 워크플로보다 짧지만 분산 시스템에서 자동 체크포인트 지정 및 내결함성의 이점을 계속해서 활용할 수 있습니다. 지속성 함수를 사용하면 복잡한 오류 처리 또는 상태 관리 코드 없이 개별 서비스 직접 호출이 실패하더라도 다단계 프로세스가 성공적으로 완료될 수 있습니다.

일반적인 시나리오로는 호텔 예약 시스템, 레스토랑 예약 플랫폼, 차량 공유 운행 요청, 이벤트 티켓 구매, SaaS 구독 업그레이드 등이 있습니다. 이러한 시나리오는 공통의 특징, 즉 함께 완료해야 하는 여러 서비스 직접 호출, 일시적 실패에 대한 자동 재시도 필요성, 분산 시스템에서 일관된 상태를 유지해야 한다는 요구 사항을 공유합니다.

### 마이크로서비스 간 분산 트랜잭션
<a name="durable-examples-distributed-transactions"></a>

실패 시 자동 롤백을 통해 여러 서비스에서 결제, 재고 및 배송을 조정합니다. 각 서비스 작업은 단계로 래핑되므로 서비스가 실패할 경우 트랜잭션이 어느 시점에서든 복구될 수 있습니다.

------
#### [ TypeScript ]

```
import { DurableContext, withDurableExecution } from "@aws/durable-execution-sdk-js";

export const handler = withDurableExecution(
  async (event: any, context: DurableContext) => {
    const { orderId, amount, items } = event;
    
    // Reserve inventory across multiple warehouses
    const inventory = await context.step("reserve-inventory", async () => {
      return await inventoryService.reserve(items);
    });
    
    // Process payment
    const payment = await context.step("process-payment", async () => {
      return await paymentService.charge(amount);
    });
    
    // Create shipment
    const shipment = await context.step("create-shipment", async () => {
      return await shippingService.createShipment(orderId, inventory);
    });
    
    return { orderId, status: 'completed', shipment };
  }
);
```

------
#### [ Python ]

```
from aws_durable_execution_sdk_python import DurableContext, durable_execution

@durable_execution
def lambda_handler(event, context: DurableContext):
    order_id = event['orderId']
    amount = event['amount']
    items = event['items']
    
    # Reserve inventory across multiple warehouses
    inventory = context.step(
        lambda _: inventory_service.reserve(items),
        name='reserve-inventory'
    )
    
    # Process payment
    payment = context.step(
        lambda _: payment_service.charge(amount),
        name='process-payment'
    )
    
    # Create shipment
    shipment = context.step(
        lambda _: shipping_service.create_shipment(order_id, inventory),
        name='create-shipment'
    )
    
    return {'orderId': order_id, 'status': 'completed', 'shipment': shipment}
```

------

단계가 실패하면 함수는 마지막으로 성공한 체크포인트에서 자동으로 재시도합니다. 재고 예약은 결제 처리가 일시적으로 실패하더라도 유지됩니다. 함수가 재시도되면 완료된 재고 단계를 건너뛰고 결제 처리가 바로 진행됩니다. 이를 통해 중복 예약이 제거되고 분산 시스템에서 일관된 상태가 보장됩니다.

### 여러 단계를 통한 주문 처리
<a name="durable-examples-order-processing"></a>

자동 재시도와 복구를 활용하여 검증, 결제 권한 부여, 재고 할당, 이행을 통해 주문을 처리합니다. 각 단계에 체크포인트가 지정되어 개별 단계가 실패하고 재시도되더라도 순서는 진행됩니다.

------
#### [ TypeScript ]

```
import { DurableContext, withDurableExecution } from "@aws/durable-execution-sdk-js";

export const handler = withDurableExecution(
  async (event: any, context: DurableContext) => {
    const { orderId, customerId, items } = event;
    
    // Validate order details
    const validation = await context.step("validate-order", async () => {
      const customer = await customerService.validate(customerId);
      const itemsValid = await inventoryService.validateItems(items);
      return { customer, itemsValid };
    });
    
    if (!validation.itemsValid) {
      return { orderId, status: 'rejected', reason: 'invalid_items' };
    }
    
    // Authorize payment
    const authorization = await context.step("authorize-payment", async () => {
      return await paymentService.authorize(
        validation.customer.paymentMethod,
        calculateTotal(items)
      );
    });
    
    // Allocate inventory
    const allocation = await context.step("allocate-inventory", async () => {
      return await inventoryService.allocate(items);
    });
    
    // Fulfill order
    const fulfillment = await context.step("fulfill-order", async () => {
      return await fulfillmentService.createShipment({
        orderId,
        items: allocation.allocatedItems,
        address: validation.customer.shippingAddress
      });
    });
    
    return {
      orderId,
      status: 'completed',
      trackingNumber: fulfillment.trackingNumber
    };
  }
);
```

------
#### [ Python ]

```
from aws_durable_execution_sdk_python import DurableContext, durable_execution

@durable_execution
def lambda_handler(event, context: DurableContext):
    order_id = event['orderId']
    customer_id = event['customerId']
    items = event['items']
    
    # Validate order details
    def validate_order(_):
        customer = customer_service.validate(customer_id)
        items_valid = inventory_service.validate_items(items)
        return {'customer': customer, 'itemsValid': items_valid}
    
    validation = context.step(validate_order, name='validate-order')
    
    if not validation['itemsValid']:
        return {'orderId': order_id, 'status': 'rejected', 'reason': 'invalid_items'}
    
    # Authorize payment
    authorization = context.step(
        lambda _: payment_service.authorize(
            validation['customer']['paymentMethod'],
            calculate_total(items)
        ),
        name='authorize-payment'
    )
    
    # Allocate inventory
    allocation = context.step(
        lambda _: inventory_service.allocate(items),
        name='allocate-inventory'
    )
    
    # Fulfill order
    fulfillment = context.step(
        lambda _: fulfillment_service.create_shipment({
            'orderId': order_id,
            'items': allocation['allocatedItems'],
            'address': validation['customer']['shippingAddress']
        }),
        name='fulfill-order'
    )
    
    return {
        'orderId': order_id,
        'status': 'completed',
        'trackingNumber': fulfillment['trackingNumber']
    }
```

------

이 패턴을 활용하면 주문이 중간 상태에서 멈추지 않습니다. 검증에 실패하면 결제 권한 부여 전에 주문이 거부됩니다. 결제 권한 부여에 실패하면 재고가 할당되지 않습니다. 각 단계는 이전 단계를 기반으로 하고 자동 재시도 및 복구를 활용합니다.

**Note**  
조건부 검사 `if (!validation.itemsValid)`는 단계에서 벗어나며 재생 중에 다시 실행됩니다. 이는 결정론적이므로 안전합니다. 동일한 검증 객체는 항상 동일한 결과를 생성합니다.

## 장기 실행 프로세스
<a name="durable-examples-long-running"></a>

몇 시간, 며칠 또는 몇 주에 걸친 프로세스에 지속성 함수를 사용합니다. 대기 작업은 컴퓨팅 요금을 발생시키지 않고 실행을 일시 중지하므로 장기 실행 프로세스의 비용 효과성이 향상됩니다. 대기 기간 동안 함수는 실행을 중지하고 Lambda는 실행 환경을 재활용합니다. 재개할 때가 되면 Lambda는 함수를 다시 간접 호출하고 마지막 체크포인트에서 재생합니다.

이 실행 모델은 인적 결정, 외부 시스템 응답, 예약된 처리 기간 또는 시간 기반 지연을 기다리는 등 장기간 일시 중지해야 하는 프로세스에 적합한 지속성 함수를 제공합니다. 활성 컴퓨팅 시간에만 비용을 지불하고 대기 시간에는 비용을 지불하지 않습니다.

일반적인 시나리오로는 문서 승인 프로세스, 예약된 배치 처리, 며칠이 걸리는 온보딩 프로세스, 구독 평가판 프로세스, 지연 알림 시스템이 있습니다. 이러한 시나리오의 일반적인 특징으로는 몇 시간 또는 며칠 단위로 측정되는 장시간 대기, 해당 대기 시간 동안 실행 상태를 유지해야 하는 필요성, 유휴 컴퓨팅 시간에 대한 비용 지불을 금지하는 비용에 민감한 요구 사항 등이 있습니다.

### 휴먼 인 더 루프 승인
<a name="durable-examples-human-in-loop"></a>

실행 상태를 유지하면서 문서 검토, 승인 또는 결정을 위해 실행을 일시 중지합니다. 이 함수는 리소스를 소비하지 않고 외부 콜백을 대기하며, 승인을 받으면 자동으로 재개됩니다.

이 패턴은 사람의 판단이나 외부 검증이 필요한 프로세스에 반드시 필요합니다. 함수는 콜백 지점에서 일시 중지되어 대기 중에 컴퓨팅 요금이 발생하지 않습니다. 누군가 API를 통해 결정을 제출하면 Lambda는 함수를 다시 간접 호출하고 체크포인트에서 다시 재생하여 해당 승인 결과로 계속 진행합니다.

------
#### [ TypeScript ]

```
import { DurableContext, withDurableExecution } from "@aws/durable-execution-sdk-js";

export const handler = withDurableExecution(
  async (event: any, context: DurableContext) => {
    const { documentId, reviewers } = event;
    
    // Step 1: Prepare document for review
    const prepared = await context.step("prepare-document", async () => {
      return await documentService.prepare(documentId);
    });
    
    // Step 2: Request approval with callback
    const approval = await context.waitForCallback(
      "approval-callback",
      async (callbackId) => {
        await notificationService.sendApprovalRequest({
          documentId,
          reviewers,
          callbackId,
          expiresIn: 86400
        });
      },
      {
        timeout: { seconds: 86400 }
      }
    );
    
    // Function resumes here when approval is received
    if (approval?.approved) {
      const finalized = await context.step("finalize-document", async () => {
        return await documentService.finalize(documentId, approval.comments);
      });
      
      return {
        status: 'approved',
        documentId,
        finalizedAt: finalized.timestamp
      };
    }
    
    // Handle rejection
    await context.step("archive-rejected", async () => {
      await documentService.archive(documentId, approval?.reason);
    });
    
    return {
      status: 'rejected',
      documentId,
      reason: approval?.reason
    };
  }
);
```

------
#### [ Python ]

```
from aws_durable_execution_sdk_python import DurableContext, durable_execution, WaitConfig

@durable_execution
def lambda_handler(event, context: DurableContext):
    document_id = event['documentId']
    reviewers = event['reviewers']
    
    # Step 1: Prepare document for review
    prepared = context.step(
        lambda _: document_service.prepare(document_id),
        name='prepare-document'
    )
    
    # Step 2: Request approval with callback
    def send_approval_request(callback_id):
        notification_service.send_approval_request({
            'documentId': document_id,
            'reviewers': reviewers,
            'callbackId': callback_id,
            'expiresIn': 86400
        })
    
    approval = context.wait_for_callback(
        send_approval_request,
        name='approval-callback',
        config=WaitConfig(timeout=86400)
    )
    
    # Function resumes here when approval is received
    if approval and approval.get('approved'):
        finalized = context.step(
            lambda _: document_service.finalize(document_id, approval.get('comments')),
            name='finalize-document'
        )
        
        return {
            'status': 'approved',
            'documentId': document_id,
            'finalizedAt': finalized['timestamp']
        }
    
    # Handle rejection
    context.step(
        lambda _: document_service.archive(document_id, approval.get('reason') if approval else None),
        name='archive-rejected'
    )
    
    return {
        'status': 'rejected',
        'documentId': document_id,
        'reason': approval.get('reason') if approval else None
    }
```

------

콜백이 수신되고 함수가 재개되면 처음부터 재생됩니다. prepare-document 단계에서 체크포인트가 지정된 결과를 즉시 반환합니다. 또한 waitForCallback 작업은 다시 기다리는 대신 저장된 승인 결과를 즉시 반환합니다. 이후 실행은 finalize 또는 archive 단계로 계속됩니다.

### 다중 스테이지 데이터 파이프라인
<a name="durable-examples-data-pipelines"></a>

스테이지 간 체크포인트를 사용하는 추출, 변환 및 로드 단계를 통해 대규모 데이터세트를 처리합니다. 각 스테이지를 완료하는 데 몇 시간이 걸릴 수 있고, 파이프라인이 중단된 경우 체크포인트를 통해 어떤 스테이지에서도 재개될 수 있습니다.

이 패턴은 중간에 복구 지점이 있는 스테이지에서 데이터를 처리해야 하는 ETL 워크플로, 데이터 마이그레이션 또는 배치 처리 작업에 적합합니다. 스테이지가 실패하면 파이프라인은 처음부터 다시 시작하지 않고 마지막으로 완료된 스테이지에서 다시 시작됩니다. 대기 작업을 사용하여 스테이지 간에 일시 중지할 수도 있습니다. 즉, 속도 제한을 준수하거나, 다운스트림 시스템이 준비될 때까지 기다리거나, 사용량이 적은 시간에 처리를 예약할 수 있습니다.

------
#### [ TypeScript ]

```
import { DurableContext, withDurableExecution } from "@aws/durable-execution-sdk-js";

export const handler = withDurableExecution(
  async (event: any, context: DurableContext) => {
    const { datasetId, batchSize } = event;
    
    // Stage 1: Extract data from source
    const extracted = await context.step("extract-data", async () => {
      const records = await sourceDatabase.extractRecords(datasetId);
      return { recordCount: records.length, records };
    });
    
    // Wait 5 minutes to respect source system rate limits
    await context.wait({ seconds: 300 });
    
    // Stage 2: Transform data in batches
    const transformed = await context.step("transform-data", async () => {
      const batches = chunkArray(extracted.records, batchSize);
      const results = [];
      
      for (const batch of batches) {
        const transformed = await transformService.processBatch(batch);
        results.push(transformed);
      }
      
      return { batchCount: batches.length, results };
    });
    
    // Wait until off-peak hours (e.g., 2 AM)
    const now = new Date();
    const targetHour = 2;
    const msUntilTarget = calculateMsUntilHour(now, targetHour);
    await context.wait({ seconds: Math.floor(msUntilTarget / 1000) });
    
    // Stage 3: Load data to destination
    const loaded = await context.step("load-data", async () => {
      let loadedCount = 0;
      
      for (const result of transformed.results) {
        await destinationDatabase.loadBatch(result);
        loadedCount += result.length;
      }
      
      return { loadedCount };
    });
    
    // Stage 4: Verify and finalize
    const verified = await context.step("verify-pipeline", async () => {
      const verification = await destinationDatabase.verifyRecords(datasetId);
      await pipelineService.markComplete(datasetId, verification);
      return verification;
    });
    
    return {
      datasetId,
      recordsProcessed: extracted.recordCount,
      batchesProcessed: transformed.batchCount,
      recordsLoaded: loaded.loadedCount,
      verified: verified.success
    };
  }
);
```

------
#### [ Python ]

```
from aws_durable_execution_sdk_python import DurableContext, durable_execution
from datetime import datetime

@durable_execution
def lambda_handler(event, context: DurableContext):
    dataset_id = event['datasetId']
    batch_size = event['batchSize']
    
    # Stage 1: Extract data from source
    def extract_data(_):
        records = source_database.extract_records(dataset_id)
        return {'recordCount': len(records), 'records': records}
    
    extracted = context.step(extract_data, name='extract-data')
    
    # Wait 5 minutes to respect source system rate limits
    context.wait(300)
    
    # Stage 2: Transform data in batches
    def transform_data(_):
        batches = chunk_array(extracted['records'], batch_size)
        results = []
        
        for batch in batches:
            transformed = transform_service.process_batch(batch)
            results.append(transformed)
        
        return {'batchCount': len(batches), 'results': results}
    
    transformed = context.step(transform_data, name='transform-data')
    
    # Wait until off-peak hours (e.g., 2 AM)
    now = datetime.now()
    target_hour = 2
    ms_until_target = calculate_ms_until_hour(now, target_hour)
    context.wait(ms_until_target // 1000)
    
    # Stage 3: Load data to destination
    def load_data(_):
        loaded_count = 0
        
        for result in transformed['results']:
            destination_database.load_batch(result)
            loaded_count += len(result)
        
        return {'loadedCount': loaded_count}
    
    loaded = context.step(load_data, name='load-data')
    
    # Stage 4: Verify and finalize
    def verify_pipeline(_):
        verification = destination_database.verify_records(dataset_id)
        pipeline_service.mark_complete(dataset_id, verification)
        return verification
    
    verified = context.step(verify_pipeline, name='verify-pipeline')
    
    return {
        'datasetId': dataset_id,
        'recordsProcessed': extracted['recordCount'],
        'batchesProcessed': transformed['batchCount'],
        'recordsLoaded': loaded['loadedCount'],
        'verified': verified['success']
    }
```

------

각 스테이지는 단계로 래핑되어 중단될 경우 임의의 스테이지에서 파이프라인을 재개할 수 있는 체크포인트를 생성합니다. 추출과 변환 사이의 5분 대기는 컴퓨팅 리소스를 소비하지 않으면서 소스 시스템 속도 제한을 준수하고, 오전 2시까지 대기하면 사용량이 적은 시간에 비용이 많이 드는 로드 작업이 예약됩니다.

**Note**  
`new Date()` 직접 호출 및 `calculateMsUntilHour()` 함수는 단계를 벗어나며 재생 중에 다시 실행됩니다. 재생 간에 일관성이 있어야 하는 시간 기반 작업의 경우 단계 내의 타임스탬프를 계산하거나 대기 기간(체크포인트 지정됨)에만 사용합니다.

## 고급 패턴
<a name="durable-examples-advanced"></a>

지속성 함수를 사용하여 여러 지속성 작업, 병렬 실행, 배열 처리, 조건부 로직 및 폴링을 결합하는 복잡한 다단계 애플리케이션을 빌드합니다. 이러한 패턴을 사용하면 내결함성 및 자동 복구를 유지하면서 많은 태스크를 조정하는 정교한 애플리케이션을 빌드할 수 있습니다.

고급 패턴은 단순한 순차적 단계 이상입니다. `parallel()`로 동시에 작업을 실행하고, `map()`을 사용하여 배열을 처리하고, `waitForCondition()`을 사용하여 외부 조건을 기다리고, 이러한 기본 요소를 결합하여 신뢰할 수 있는 애플리케이션을 빌드할 수 있습니다. 각각의 지속성 작업은 자체 체크포인트를 생성하므로 중단된 경우 언제든지 애플리케이션을 복구할 수 있습니다.

### 사용자 온보딩 프로세스
<a name="durable-examples-user-onboarding"></a>

재시도 처리를 통해 사용자에게 등록, 이메일 확인, 프로필, 설정, 최초 구성을 안내합니다. 이 예제에서는 순차적 단계, 콜백 및 조건부 로직을 결합하여 전체 온보딩 프로세스를 생성합니다.

------
#### [ TypeScript ]

```
import { DurableContext, withDurableExecution } from "@aws/durable-execution-sdk-js";

export const handler = withDurableExecution(
  async (event: any, context: DurableContext) => {
    const { userId, email } = event;
    
    // Step 1: Create user account
    const user = await context.step("create-account", async () => {
      return await userService.createAccount(userId, email);
    });
    
    // Step 2: Send verification email
    await context.step("send-verification", async () => {
      return await emailService.sendVerification(email);
    });
    
    // Step 3: Wait for email verification (up to 48 hours)
    const verified = await context.waitForCallback(
      "email-verification",
      async (callbackId) => {
        await notificationService.sendVerificationLink({
          email,
          callbackId,
          expiresIn: 172800
        });
      },
      {
        timeout: { seconds: 172800 }
      }
    );
    
    if (!verified) {
      await context.step("send-reminder", async () => {
        await emailService.sendReminder(email);
      });
      
      return {
        status: "verification_timeout",
        userId,
        message: "Email verification not completed within 48 hours"
      };
    }
    
    // Step 4: Initialize user profile in parallel
    const setupResults = await context.parallel("profile-setup", [
      async (ctx: DurableContext) => {
        return await ctx.step("create-preferences", async () => {
          return await preferencesService.createDefaults(userId);
        });
      },
      
      async (ctx: DurableContext) => {
        return await ctx.step("setup-notifications", async () => {
          return await notificationService.setupDefaults(userId);
        });
      },
      
      async (ctx: DurableContext) => {
        return await ctx.step("create-welcome-content", async () => {
          return await contentService.createWelcome(userId);
        });
      }
    ]);
    
    // Step 5: Send welcome email
    await context.step("send-welcome", async () => {
      const [preferences, notifications, content] = setupResults.getResults();
      return await emailService.sendWelcome({
        email,
        preferences,
        notifications,
        content
      });
    });
    
    return {
      status: "onboarding_complete",
      userId,
      completedAt: new Date().toISOString()
    };
  }
);
```

------
#### [ Python ]

```
from aws_durable_execution_sdk_python import DurableContext, durable_execution, WaitConfig
from datetime import datetime

@durable_execution
def lambda_handler(event, context: DurableContext):
    user_id = event['userId']
    email = event['email']
    
    # Step 1: Create user account
    user = context.step(
        lambda _: user_service.create_account(user_id, email),
        name='create-account'
    )
    
    # Step 2: Send verification email
    context.step(
        lambda _: email_service.send_verification(email),
        name='send-verification'
    )
    
    # Step 3: Wait for email verification (up to 48 hours)
    def send_verification_link(callback_id):
        notification_service.send_verification_link({
            'email': email,
            'callbackId': callback_id,
            'expiresIn': 172800
        })
    
    verified = context.wait_for_callback(
        send_verification_link,
        name='email-verification',
        config=WaitConfig(timeout=172800)
    )
    
    if not verified:
        context.step(
            lambda _: email_service.send_reminder(email),
            name='send-reminder'
        )
        
        return {
            'status': 'verification_timeout',
            'userId': user_id,
            'message': 'Email verification not completed within 48 hours'
        }
    
    # Step 4: Initialize user profile in parallel
    def create_preferences(ctx: DurableContext):
        return ctx.step(
            lambda _: preferences_service.create_defaults(user_id),
            name='create-preferences'
        )
    
    def setup_notifications(ctx: DurableContext):
        return ctx.step(
            lambda _: notification_service.setup_defaults(user_id),
            name='setup-notifications'
        )
    
    def create_welcome_content(ctx: DurableContext):
        return ctx.step(
            lambda _: content_service.create_welcome(user_id),
            name='create-welcome-content'
        )
    
    setup_results = context.parallel(
        [create_preferences, setup_notifications, create_welcome_content],
        name='profile-setup'
    )
    
    # Step 5: Send welcome email
    def send_welcome(_):
        results = setup_results.get_results()
        preferences, notifications, content = results[0], results[1], results[2]
        return email_service.send_welcome({
            'email': email,
            'preferences': preferences,
            'notifications': notifications,
            'content': content
        })
    
    context.step(send_welcome, name='send-welcome')
    
    return {
        'status': 'onboarding_complete',
        'userId': user_id,
        'completedAt': datetime.now().isoformat()
    }
```

------

이 프로세스는 계정 생성 및 이메일 전송을 위해 체크포인트와 순차적 단계를 결합한 다음 최대 48시간 동안 일시 중지되어 리소스를 소비하지 않고 이메일 확인을 기다립니다. 조건부 로직은 확인 완료 또는 제한 시간 초과 여부에 따라 다양한 경로를 처리합니다. 프로필 설정 태스크는 병렬 작업을 사용하여 동시에 실행되어 총 실행 시간을 줄이고, 각 단계는 신뢰할 수 있는 온보딩을 위해 일시적 실패 시 자동으로 재시도됩니다.

### 함수 간 연쇄 간접 호출
<a name="durable-examples-chained-invocations"></a>

`context.invoke()`를 사용하여 지속성 함수 내에서 다른 Lambda 함수를 간접 호출합니다. 호출 함수는 간접 호출된 함수가 완료될 때까지 기다리는 동안 일시 중지되어 결과를 보존하는 체크포인트를 생성합니다. 간접 호출된 함수가 완료된 후 호출 함수가 중단되면 함수를 다시 간접 호출하지 않고 저장된 결과로 재개됩니다.

특정 도메인(고객 검증, 결제 처리, 재고 관리)을 처리하고 워크플로에서 조정해야 하는 특정 함수가 있는 경우 이 패턴을 사용합니다. 각 함수는 자체 로직을 유지하고 코드 중복을 방지하기 위해 여러 오케스트레이터 함수에 의해 간접 호출될 수 있습니다.

------
#### [ TypeScript ]

```
import { DurableContext, withDurableExecution } from "@aws/durable-execution-sdk-js";

// Main orchestrator function
export const handler = withDurableExecution(
  async (event: any, context: DurableContext) => {
    const { orderId, customerId } = event;
    
    // Step 1: Validate customer by invoking customer service function
    const customer = await context.invoke(
      "validate-customer",
      "arn:aws:lambda:us-east-1:123456789012:function:customer-service:1",
      { customerId }
    );
    
    if (!customer.isValid) {
      return { orderId, status: "rejected", reason: "invalid_customer" };
    }
    
    // Step 2: Check inventory by invoking inventory service function
    const inventory = await context.invoke(
      "check-inventory",
      "arn:aws:lambda:us-east-1:123456789012:function:inventory-service:1",
      { orderId, items: event.items }
    );
    
    if (!inventory.available) {
      return { orderId, status: "rejected", reason: "insufficient_inventory" };
    }
    
    // Step 3: Process payment by invoking payment service function
    const payment = await context.invoke(
      "process-payment",
      "arn:aws:lambda:us-east-1:123456789012:function:payment-service:1",
      {
        customerId,
        amount: inventory.totalAmount,
        paymentMethod: customer.paymentMethod
      }
    );
    
    // Step 4: Create shipment by invoking fulfillment service function
    const shipment = await context.invoke(
      "create-shipment",
      "arn:aws:lambda:us-east-1:123456789012:function:fulfillment-service:1",
      {
        orderId,
        items: inventory.allocatedItems,
        address: customer.shippingAddress
      }
    );
    
    return {
      orderId,
      status: "completed",
      trackingNumber: shipment.trackingNumber,
      estimatedDelivery: shipment.estimatedDelivery
    };
  }
);
```

------
#### [ Python ]

```
from aws_durable_execution_sdk_python import DurableContext, durable_execution

# Main orchestrator function
@durable_execution
def lambda_handler(event, context: DurableContext):
    order_id = event['orderId']
    customer_id = event['customerId']
    
    # Step 1: Validate customer by invoking customer service function
    customer = context.invoke(
        'arn:aws:lambda:us-east-1:123456789012:function:customer-service:1',
        {'customerId': customer_id},
        name='validate-customer'
    )
    
    if not customer['isValid']:
        return {'orderId': order_id, 'status': 'rejected', 'reason': 'invalid_customer'}
    
    # Step 2: Check inventory by invoking inventory service function
    inventory = context.invoke(
        'arn:aws:lambda:us-east-1:123456789012:function:inventory-service:1',
        {'orderId': order_id, 'items': event['items']},
        name='check-inventory'
    )
    
    if not inventory['available']:
        return {'orderId': order_id, 'status': 'rejected', 'reason': 'insufficient_inventory'}
    
    # Step 3: Process payment by invoking payment service function
    payment = context.invoke(
        'arn:aws:lambda:us-east-1:123456789012:function:payment-service:1',
        {
            'customerId': customer_id,
            'amount': inventory['totalAmount'],
            'paymentMethod': customer['paymentMethod']
        },
        name='process-payment'
    )
    
    # Step 4: Create shipment by invoking fulfillment service function
    shipment = context.invoke(
        'arn:aws:lambda:us-east-1:123456789012:function:fulfillment-service:1',
        {
            'orderId': order_id,
            'items': inventory['allocatedItems'],
            'address': customer['shippingAddress']
        },
        name='create-shipment'
    )
    
    return {
        'orderId': order_id,
        'status': 'completed',
        'trackingNumber': shipment['trackingNumber'],
        'estimatedDelivery': shipment['estimatedDelivery']
    }
```

------

각 간접 호출로 오케스트레이터 함수에 체크포인트가 생성됩니다. 고객 검증이 완료된 후 오케스트레이터가 중단되면 해당 체크포인트에서 저장된 고객 데이터로 재개되어 검증 간접 호출을 건너뜁니다. 이를 통해 다운스트림 서비스에 대한 중복 호출이 방지되고 중단 시에도 일관적인 실행이 보장될 수 있습니다.

간접 호출된 함수는 지속성 또는 표준 Lambda 함수일 수 있습니다. 지속성 함수를 간접 호출하면 대기 및 체크포인트가 있는 자체 다단계 워크플로가 있을 수 있습니다. 오케스트레이터는 전체 지속성 실행이 완료될 때까지 기다렸다가 최종 결과를 수신합니다.

**참고**  
교차 계정 간접 호출은 지원되지 않습니다. 간접 호출된 모든 함수는 직접 호출 함수와 동일한 AWS 계정에 있어야 합니다.

### 체크포인트를 사용한 배치 처리
<a name="durable-examples-batch-processing"></a>

실패 후 마지막으로 성공한 체크포인트에서 자동으로 복구하여 수백만 개의 레코드를 처리합니다. 이 예제에서는 지속성 함수가 `map()` 작업을 청킹 및 속도 제한과 결합하여 대규모 데이터 처리를 처리하는 방법을 보여줍니다.

------
#### [ TypeScript ]

```
import { DurableContext, withDurableExecution } from "@aws/durable-execution-sdk-js";

interface Batch {
  batchIndex: number;
  recordIds: string[];
}

export const handler = withDurableExecution(
  async (event: any, context: DurableContext) => {
    const { datasetId, batchSize = 1000 } = event;
    
    // Step 1: Get all record IDs to process
    const recordIds = await context.step("fetch-record-ids", async () => {
      return await dataService.getRecordIds(datasetId);
    });
    
    // Step 2: Split into batches
    const batches: Batch[] = [];
    for (let i = 0; i < recordIds.length; i += batchSize) {
      batches.push({
        batchIndex: Math.floor(i / batchSize),
        recordIds: recordIds.slice(i, i + batchSize)
      });
    }
    
    // Step 3: Process batches with controlled concurrency
    const batchResults = await context.map(
      "process-batches",
      batches,
      async (ctx: DurableContext, batch: Batch, index: number) => {
        const processed = await ctx.step(`batch-${batch.batchIndex}`, async () => {
          const results = [];
          for (const recordId of batch.recordIds) {
            const result = await recordService.process(recordId);
            results.push(result);
          }
          return results;
        });
        
        const validated = await ctx.step(`validate-${batch.batchIndex}`, async () => {
          return await validationService.validateBatch(processed);
        });
        
        return {
          batchIndex: batch.batchIndex,
          recordCount: batch.recordIds.length,
          successCount: validated.successCount,
          failureCount: validated.failureCount
        };
      },
      {
        maxConcurrency: 5
      }
    );
    
    // Step 4: Aggregate results
    const summary = await context.step("aggregate-results", async () => {
      const results = batchResults.getResults();
      const totalSuccess = results.reduce((sum, r) => sum + r.successCount, 0);
      const totalFailure = results.reduce((sum, r) => sum + r.failureCount, 0);
      
      return {
        datasetId,
        totalRecords: recordIds.length,
        batchesProcessed: batches.length,
        successCount: totalSuccess,
        failureCount: totalFailure,
        completedAt: new Date().toISOString()
      };
    });
    
    return summary;
  }
);
```

------
#### [ Python ]

```
from aws_durable_execution_sdk_python import DurableContext, durable_execution, MapConfig
from datetime import datetime
from typing import List, Dict

@durable_execution
def lambda_handler(event, context: DurableContext):
    dataset_id = event['datasetId']
    batch_size = event.get('batchSize', 1000)
    
    # Step 1: Get all record IDs to process
    record_ids = context.step(
        lambda _: data_service.get_record_ids(dataset_id),
        name='fetch-record-ids'
    )
    
    # Step 2: Split into batches
    batches = []
    for i in range(0, len(record_ids), batch_size):
        batches.append({
            'batchIndex': i // batch_size,
            'recordIds': record_ids[i:i + batch_size]
        })
    
    # Step 3: Process batches with controlled concurrency
    def process_batch(ctx: DurableContext, batch: Dict, index: int):
        batch_index = batch['batchIndex']
        
        def process_records(_):
            results = []
            for record_id in batch['recordIds']:
                result = record_service.process(record_id)
                results.append(result)
            return results
        
        processed = ctx.step(process_records, name=f'batch-{batch_index}')
        
        validated = ctx.step(
            lambda _: validation_service.validate_batch(processed),
            name=f'validate-{batch_index}'
        )
        
        return {
            'batchIndex': batch_index,
            'recordCount': len(batch['recordIds']),
            'successCount': validated['successCount'],
            'failureCount': validated['failureCount']
        }
    
    batch_results = context.map(
        process_batch,
        batches,
        name='process-batches',
        config=MapConfig(max_concurrency=5)
    )
    
    # Step 4: Aggregate results
    def aggregate_results(_):
        results = batch_results.get_results()
        total_success = sum(r['successCount'] for r in results)
        total_failure = sum(r['failureCount'] for r in results)
        
        return {
            'datasetId': dataset_id,
            'totalRecords': len(record_ids),
            'batchesProcessed': len(batches),
            'successCount': total_success,
            'failureCount': total_failure,
            'completedAt': datetime.now().isoformat()
        }
    
    summary = context.step(aggregate_results, name='aggregate-results')
    
    return summary
```

------

레코드는 메모리 또는 다운스트림 서비스 과부하가 발생하지 않도록 관리 가능한 배치로 분할된 다음 병렬 처리를 제어하는 `maxConcurrency`로 동시에 여러 배치를 처리합니다. 각 배치에는 자체 체크포인트가 있으므로 실패가 발생하면 모든 레코드를 재처리하는 대신 실패한 배치만 재시도합니다. 이 패턴은 처리에 시간이 걸릴 수 있는 ETL 작업, 데이터 마이그레이션 또는 대량 작업에 적합합니다.

## 다음 단계
<a name="durable-examples-next-steps"></a>
+ DurableContext, 단계 및 대기를 이해하기 위한 [기본 개념](durable-basic-concepts.md) 살펴보기
+ 결정론적 코드 작성 및 성능 최적화 [모범 사례](durable-best-practices.md) 검토
+ 로컬 및 클라우드에서 [지속성 함수 테스트](durable-testing.md) 방법 알아보기
+ 지속성 함수와 Step Functions를 비교하여 각 접근 방식이 가장 효과적인 경우를 파악합니다. [지속성 함수 또는 Step Functions](durable-step-functions.md)를 참조하세요.

# Lambda 지속성 함수의 보안 및 권한
<a name="durable-security"></a>

Lambda 지속성 함수에서 체크포인트 작업을 관리하기 위해 특정 IAM 권한이 필요합니다. 함수에 필요한 권한만 부여하여 최소 권한 원칙을 따르세요.

## 실행 역할 권한
<a name="durable-execution-role"></a>

지속성 함수의 실행 역할에는 체크포인트를 생성하고 실행 상태를 검색할 수 있는 권한이 필요합니다. 다음 정책에서는 필요한 최소 권한을 보여줍니다.

```
{
    "Version": "2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "lambda:CheckpointDurableExecution",
                "lambda:GetDurableExecutionState"
            ],
            "Resource": "arn:aws:lambda:region:account-id:function:function-name:*"
        }
    ]
}
```

콘솔을 사용하여 지속성 함수를 생성하면 Lambda는 이러한 권한을 실행 역할에 자동으로 추가합니다. AWS CLI 또는 AWS CloudFormation을 사용하여 함수를 생성하는 경우 실행 역할에 이러한 권한을 추가합니다.

**최소 권한 원칙**  
와일드카드를 사용하는 대신 `Resource` 요소의 범위를 특정 함수 ARN으로 지정합니다. 이렇게 하면 실행 역할이 필요한 함수에 대해서만 체크포인트 작업으로 제한됩니다.

**예제: 여러 함수에 대한 권한 범위**

```
{
    "Version": "2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "lambda:CheckpointDurableExecution",
                "lambda:GetDurableExecutionState"
            ],
            "Resource": [
                "arn:aws:lambda:us-east-1:123456789012:function:orderProcessor:*",
                "arn:aws:lambda:us-east-1:123456789012:function:paymentHandler:*"
            ]
        }
    ]
}
```

또는 Amazon CloudWatch Logs에 대한 기본 Lambda 실행 권한과 함께 필요한 지속성 실행 권한이 포함된 AWS 관리형 정책 `AWSLambdaBasicDurableExecutionRolePolicy`를 사용할 수 있습니다.

## 상태 암호화
<a name="durable-state-encryption"></a>

Lambda 지속성 함수는 AWS 소유 키를 무료로 사용하여 저장 시 암호화를 자동으로 활성화합니다. 각 함수 실행은 다른 실행에서 액세스할 수 없는 격리된 상태로 유지됩니다. 고객 관리형 키(CMK)는 지원되지 않습니다.

체크포인트 데이터에는 다음이 포함됩니다.
+ 단계 결과 및 반환 값
+ 실행 진행 상황 및 타임라인
+ 대기 상태 정보

Lambda가 체크포인트 데이터를 읽거나 쓸 때 TLS를 사용하여 모든 데이터가 전송 중 암호화됩니다.

### 사용자 지정 직렬 변환기 및 역직렬 변환기를 사용한 사용자 지정 암호화
<a name="durable-custom-encryption"></a>

중요한 보안 요구 사항의 경우 지속성 SDK를 사용하여 사용자 지정 직렬 변환기 및 역직렬 변환기(SerDer)를 사용하여 자체 암호화 및 복호화 메커니즘을 구현할 수 있습니다. 이 접근 방식을 사용하면 체크포인트 데이터를 보호하는 데 사용되는 암호화 키와 알고리즘을 완벽하게 제어할 수 있습니다.

**중요**  
사용자 지정 암호화를 사용하면 Lambda 콘솔 및 API 응답에서 작업 결과를 볼 수 없습니다. 체크포인트 데이터는 실행 내역에서 암호화되어 표시되고 복호화를 사용하지 않고는 검사할 수 없습니다.

함수의 실행 역할에는 사용자 지정 SerDer 구현에 사용되는 AWS KMS 키에 대한 `kms:Encrypt` 및 `kms:Decrypt` 권한이 필요합니다.

## CloudTrail 로깅
<a name="durable-cloudtrail-logging"></a>

Lambda는 체크포인트 작업을 AWS CloudTrail의 데이터 이벤트로 로깅합니다. CloudTrail을 사용하여 체크포인트 생성 시기를 감사하고, 실행 상태 변경을 추적하고, 지속성 실행 데이터에 대한 액세스를 모니터링할 수 있습니다.

체크포인트 작업은 CloudTrail 로그에 다음 이벤트 이름과 함께 표시됩니다.
+ `CheckpointDurableExecution` - 단계가 완료되고 체크포인트를 생성할 때 로깅됩니다.
+ `GetDurableExecutionState` - Lambda가 재생 중에 실행 상태를 검색할 때 로깅됩니다.

지속성 함수에 대한 데이터 이벤트 로깅을 활성화하려면 Lambda 데이터 이벤트를 로깅하도록 CloudTrail 추적을 구성합니다. 자세한 내용은 CloudTrail 사용 설명서의 [데이터 이벤트 로깅](https://docs.aws.amazon.com/awscloudtrail/latest/userguide/logging-data-events-with-cloudtrail.html)을 참조하세요.

**예: 체크포인트 작업에 대한 CloudTrail 로그 항목**

```
{
    "eventVersion": "1.08",
    "eventTime": "2024-11-16T10:30:45Z",
    "eventName": "CheckpointDurableExecution",
    "eventSource": "lambda.amazonaws.com",
    "requestParameters": {
        "functionName": "myDurableFunction",
        "executionId": "exec-abc123",
        "stepId": "step-1"
    },
    "responseElements": null,
    "eventType": "AwsApiCall"
}
```

## 크로스 계정 고려 사항
<a name="durable-cross-account-access"></a>

AWS 계정 간에 지속성 함수를 간접 호출하는 경우 호출 계정에 `lambda:InvokeFunction` 권한이 필요하지만 체크포인트 작업은 항상 함수 계정의 실행 역할을 사용합니다. 호출 계정은 체크포인트 데이터 또는 실행 상태에 직접 액세스할 수 없습니다.

이러한 격리를 통해 외부 계정에서 간접 호출된 경우에도 함수의 계정 내에서 체크포인트 데이터가 안전하게 유지됩니다.

## 상속된 Lambda 보안 기능
<a name="durable-inherited-security"></a>

지속성 함수는 VPC 연결, 환경 변수 암호화, Dead Letter Queue(DLQ), 예약된 동시성, 함수 URL, 코드 서명 및 규정 준수 인증(SOC, PCI DSS, HIPAA 등)을 포함하여 Lambda의 모든 보안, 거버넌스 및 규정 준수 기능을 상속합니다.

Lambda 보안 기능에 대한 자세한 내용은 Lambda 개발자 안내서의 [AWS Lambda의 보안](https://docs.aws.amazon.com/lambda/latest/dg/lambda-security.html)을 참조하세요. 지속성 함수에 대한 유일한 추가 보안 고려 사항은 이 안내서에서 설명하는 체크포인트 권한입니다.

# 지속성 실행 SDK
<a name="durable-execution-sdk"></a>

지속성 실행 SDK는 지속성 함수 빌드의 기반입니다. 진행 상태의 체크포인트를 지정하고, 재시도를 처리하고, 실행 흐름을 관리하는 데 필요한 기본 요소를 제공합니다. SDK는 체크포인트 관리 및 재생의 복잡성을 추상화하여 자동으로 내결함성이 되는 순차적 코드를 작성할 수 있습니다.

SDK는 JavaScript, TypeScript, Python 및 Java에 사용할 수 있습니다(미리 보기). 전체 API 설명서 및 예제는 GitHub의 [JavaScript/TypeScript SDK](https://github.com/aws/aws-durable-execution-sdk-js), [Python SDK](https://github.com/aws/aws-durable-execution-sdk-python) 및 [Java SDK](https://github.com/aws/aws-durable-execution-sdk-java)를 참조하세요.

## DurableContext
<a name="durable-sdk-context"></a>

SDK는 함수에 모든 지속성 작업을 노출하는 `DurableContext` 객체를 제공합니다. 이 컨텍스트는 표준 Lambda 컨텍스트를 대체하고 체크포인트 생성, 실행 흐름 관리 및 외부 시스템과의 조정을 위한 방법을 제공합니다.

SDK를 사용하려면 지속성 실행 래퍼로 Lambda 핸들러를 래핑합니다.

------
#### [ TypeScript ]

```
import { withDurableExecution, DurableContext } from '@aws/durable-execution-sdk-js';

export const handler = withDurableExecution(
  async (event: any, context: DurableContext) => {
    // Your function receives DurableContext instead of Lambda context
    // Use context.step(), context.wait(), etc.
    return result;
  }
);
```

------
#### [ Python ]

```
from aws_durable_execution_sdk_python import durable_execution, DurableContext

@durable_execution
def handler(event: dict, context: DurableContext):
    # Your function receives DurableContext
    # Use context.step(), context.wait(), etc.
    return result
```

------
#### [ Java (Preview) ]

```
import software.amazon.lambda.durable.DurableContext;
import software.amazon.lambda.durable.DurableHandler;

public class Handler extends DurableHandler<Object, String> {
    @Override
    public String handleRequest(Object input, DurableContext context) {
        // Your function receives DurableContext
        // Use context.step(), context.wait(), etc.
        return result;
    }
}
```

------

래퍼는 함수 간접 호출을 가로채고, 기존 체크포인트 로그를 로드하고, 재생 및 체크포인트를 관리하는 `DurableContext`를 제공합니다.

## SDK가 수행하는 작업
<a name="durable-sdk-what-it-does"></a>

SDK는 지속성 실행이 가능하도록 3가지 중요한 책임을 처리합니다.

**체크포인트 관리:** SDK는 함수가 지속성 작업을 실행할 때 체크포인트를 자동으로 생성합니다. 각 체크포인트에는 작업 유형, 입력 및 결과가 기록됩니다. 함수가 단계를 완료하면 SDK는 계속하기 전에 체크포인트를 유지합니다. 이를 통해 중단되더라도 완료된 작업에서 함수를 재개할 수 있습니다.

**재생 조정:** 일시 중지 또는 중단 후 함수가 재개되면 SDK가 재생을 수행합니다. 처음부터 코드를 실행하지만, 저장된 체크포인트 결과를 사용하여 완료된 작업을 다시 실행하지 않고 건너뜁니다. SDK를 통해 재생이 결정론적이게 됩니다. 입력 및 체크포인트 로그가 동일하면 함수가 동일한 결과를 생성합니다.

**상태 격리:** SDK는 비즈니스 로직과 별도로 실행 상태를 유지합니다. 각 지속성 실행에는 다른 실행이 액세스할 수 없는 자체 체크포인트 로그가 있습니다. SDK는 저장된 체크포인트 데이터를 암호화하고 재생 전반에서 상태를 일관적으로 유지합니다.

## 체크포인트 작동 방식
<a name="durable-sdk-how-checkpointing-works"></a>

지속성 작업을 직접 호출하면 SDK는 다음 순서를 따릅니다.

1. **기존 체크포인트 확인:** SDK는 이전 간접 호출에서 이 작업이 이미 완료되었는지 확인합니다. 체크포인트가 있는 경우 SDK는 작업을 다시 실행하지 않고 저장된 결과를 반환합니다.

1. **작업 실행:** 체크포인트가 없으면 SDK는 작업 코드를 실행합니다. 단계의 경우 이는 함수의 직접 호출을 의미합니다. 대기의 경우 작업 재개 예약을 의미합니다.

1. **체크포인트 생성:** 작업이 완료되면 SDK가 결과를 직렬화하고 체크포인트를 생성합니다. 체크포인트에는 작업 유형, 이름, 입력, 결과 및 타임스탬프가 포함됩니다.

1. **체크포인트 유지:** SDK는 Lambda 체크포인트 API를 직접 호출하여 체크포인트를 유지합니다. 이렇게 하면 실행을 계속하기 전에 체크포인트의 지속성이 유지됩니다.

1. **결과 반환:** SDK는 작업 결과를 코드에 반환하고, 이 결과는 다음 작업으로 이어집니다.

이 시퀀스를 통해 작업이 완료되면 결과가 안전하게 저장됩니다. 어느 시점에서든 함수가 중단되면 SDK는 마지막으로 완료된 체크포인트까지 재생할 수 있습니다.

## 재생 동작
<a name="durable-sdk-replay-behavior"></a>

일시 중지 또는 중단 후 함수가 재개되면 SDK가 재생을 수행합니다.

1. **체크포인트 로그 로드:** SDK는 Lambda에서 이 실행에 대한 체크포인트 로그를 검색합니다.

1. **처음부터 실행:** SDK는 일시 중지된 위치가 아닌 처음 위치에서 핸들러 함수를 간접 호출합니다.

1. **완료된 지속성 작업 건너뛰기:** 코드가 지속성 작업을 직접 호출할 때 SDK는 체크포인트 로그를 기준으로 각 작업을 확인합니다. 완료된 지속성 작업의 경우 SDK는 작업 코드를 실행하지 않고 저장된 결과를 반환합니다.
**참고**  
하위 컨텍스트의 결과가 최대 체크포인트 크기(256KB)보다 크면 재생 중에 컨텍스트의 코드가 다시 실행됩니다. 이를 통해 컨텍스트 내에서 실행된 지속성 작업의 대용량 결과를 구성할 수 있으며, 이는 체크포인트 로그에서 조회가 가능합니다. 따라서 컨텍스트 자체에서만 결정론적 코드를 실행해야 합니다. 결과 크기가 큰 하위 컨텍스트를 사용하는 경우, 단계 내에서 장기 실행 또는 비결정론적 작업을 수행하고 컨텍스트 자체에서 결과를 결합하는 단기 실행 태스크만 수행하는 것이 모범 사례입니다.

1. **중단 지점에서 재개:** SDK가 체크포인트 없이 작업에 도달하면 정상적으로 실행되고 지속성 작업이 완료되면 새 체크포인트를 생성합니다.

이 재생 메커니즘을 사용하려면 코드가 결정론적이어야 합니다. 입력 및 체크포인트 로그가 동일한 경우 함수는 동일한 시퀀스의 지속성 작업 직접 호출을 수행해야 합니다. SDK는 재생 중에 작업 이름과 유형이 체크포인트 로그와 일치하는지 확인하여 이를 적용합니다.

## 사용 가능한 지속성 작업
<a name="durable-sdk-operations"></a>

`DurableContext`는 다양한 조정 패턴에 대한 작업을 제공합니다. 각각의 지속성 작업은 체크포인트를 자동으로 생성하므로 언제든지 함수를 재개할 수 있습니다.

### 단계
<a name="durable-sdk-op-step"></a>

자동 체크포인트 지정 및 재시도를 사용하여 비즈니스 로직을 실행합니다. 외부 서비스를 직접 호출하거나, 계산을 수행하거나, 체크포인트를 지정해야 하는 로직을 실행하는 작업에는 단계를 사용합니다. SDK는 단계 전후에 체크포인트를 생성하여 재생할 결과를 저장합니다.

------
#### [ TypeScript ]

```
const result = await context.step('process-payment', async () => {
  return await paymentService.charge(amount);
});
```

------
#### [ Python ]

```
result = context.step(
    lambda _: payment_service.charge(amount),
    name='process-payment'
)
```

------
#### [ Java (Preview) ]

```
var result = context.step("process-payment", Payment.class, 
    () -> paymentService.charge(amount)
);
```

------

단계는 구성 가능한 재시도 전략, 실행 시맨틱(최대 1회 또는 최소 1회) 및 사용자 지정 직렬화를 지원합니다.

### 대기
<a name="durable-sdk-op-wait"></a>

컴퓨팅 리소스를 소비하지 않고 지정된 기간 동안 실행을 일시 중지합니다. SDK는 체크포인트를 생성하고, 함수 간접 호출을 종료하고, 재개를 예약합니다. 대기가 완료되면 Lambda는 함수를 다시 간접 호출하고 SDK는 계속하기 전에 대기 지점까지 재생합니다.

------
#### [ TypeScript ]

```
// Wait 1 hour without charges
await context.wait({ seconds: 3600 });
```

------
#### [ Python ]

```
# Wait 1 hour without charges
context.wait(3600)
```

------
#### [ Java (Preview) ]

```
// Wait 1 hour without charges
context.wait(Duration.ofHours(1));
```

------

### 콜백
<a name="durable-sdk-op-callback"></a>

콜백을 사용하면 함수가 일시 중지되고 외부 시스템에서 입력을 제공할 때까지 대기합니다. 콜백을 생성할 때 SDK는 고유한 콜백 ID를 생성하고 체크포인트를 생성합니다. 이후 함수는 컴퓨팅 요금을 발생시키지 않고 일시 중지(간접 호출 종료)합니다. 외부 시스템은 `SendDurableExecutionCallbackSuccess` 또는 `SendDurableExecutionCallbackFailure` Lambda API를 사용하여 콜백 결과를 제출합니다. 콜백이 제출되면 Lambda는 함수를 다시 간접 호출하고 SDK는 콜백 지점으로 재생하며 함수는 콜백 결과를 계속 진행합니다.

SDK는 콜백 작업과 관련하여 2가지 메서드를 제공합니다.

**createCallback:** 콜백을 생성하고 promise와 콜백 ID를 모두 반환합니다. Lambda API를 사용하여 결과를 제출하는 외부 시스템으로 콜백 ID를 보냅니다.

------
#### [ TypeScript ]

```
const [promise, callbackId] = await context.createCallback('approval', {
  timeout: { hours: 24 }
});

await sendApprovalRequest(callbackId, requestData);
const approval = await promise;
```

------
#### [ Python ]

```
callback = context.create_callback(
    name='approval',
    config=CallbackConfig(timeout_seconds=86400)
)

context.step(
    lambda _: send_approval_request(callback.callback_id),
    name='send_request'
)

approval = callback.result()
```

------
#### [ Java (Preview) ]

```
var config = CallbackConfig.builder(Duration.ofHours(24)).timeout()

var callback = context.createCallback("approval", String.class, config);

context.step("send-request", String.class, () -> {
    notificationService.sendApprovalRequest(callback.callbackId(), requestData);
    return "request-sent";
});

// Blocks until the callback finishes or times out
String approval = callback.get();
```

------

**waitForCallback:** 콜백 생성 및 제출을 하나의 작업으로 결합하여 콜백 처리를 간소화합니다. SDK는 콜백을 생성하고, 콜백 ID로 제출자 함수를 실행하고, 결과를 기다립니다.

------
#### [ TypeScript ]

```
const result = await context.waitForCallback(
  'external-api',
  async (callbackId, ctx) => {
    await submitToExternalAPI(callbackId, requestData);
  },
  { timeout: { minutes: 30 } }
);
```

------
#### [ Python ]

```
result = context.wait_for_callback(
    lambda callback_id: submit_to_external_api(callback_id, request_data),
    name='external-api',
    config=WaitForCallbackConfig(timeout_seconds=1800)
)
```

------
#### [ Java (Preview) ]

Java용 waitForCallback은 아직 개발 중입니다.

------

함수가 무기한 대기하지 않도록 제한 시간을 구성합니다. 콜백 제한 시간이 초과되면 SDK가 `CallbackError`를 발생시키고 함수가 제한 시간 사례를 처리할 수 있습니다. 장기 실행 콜백에 하트비트 제한 시간을 사용하여 외부 시스템의 응답 중지 시기를 감지합니다.

휴먼 인 더 루프 워크플로, 외부 시스템 통합, 웹후크 응답 또는 외부 입력을 위해 실행을 일시 중지해야 하는 시나리오에는 콜백을 사용합니다.

### 병렬 실행
<a name="durable-sdk-op-parallel"></a>

선택적 동시성 제어를 활용하여 여러 작업을 실행합니다. SDK는 병렬 실행을 관리하고, 각 작업에 대한 체크포인트를 생성하고, 완료 정책에 따라 실패를 처리합니다.

------
#### [ TypeScript ]

```
const results = await context.parallel([
  async (ctx) => ctx.step('task1', async () => processTask1()),
  async (ctx) => ctx.step('task2', async () => processTask2()),
  async (ctx) => ctx.step('task3', async () => processTask3())
]);
```

------
#### [ Python ]

```
results = context.parallel(
    lambda ctx: ctx.step(lambda _: process_task1(), name='task1'),
    lambda ctx: ctx.step(lambda _: process_task2(), name='task2'),
    lambda ctx: ctx.step(lambda _: process_task3(), name='task3')
)
```

------
#### [ Java (Preview) ]

Java용 Parallel은 아직 개발 중입니다.

------

`parallel`을 사용하여 독립적인 작업을 동시에 실행합니다.

### 맵
<a name="durable-sdk-op-map"></a>

선택적 동시성 제어를 사용하여 배열의 각 항목에 대해 동시에 작업을 실행합니다. SDK는 동시 실행을 관리하고, 각 작업에 대한 체크포인트를 생성하고, 완료 정책에 따라 실패를 처리합니다.

------
#### [ TypeScript ]

```
const results = await context.map(itemArray, async (ctx, item, index) =>
  ctx.step('task', async () => processItem(item, index))
);
```

------
#### [ Python ]

```
results = context.map(
    item_array,
    lambda ctx, item, index: ctx.step(
        lambda _: process_item(item, index),
        name='task'
    )
)
```

------
#### [ Java (Preview) ]

Java용 Map은 아직 개발 중입니다.

------

`map`을 사용하여 동시성 제어로 배열을 처리합니다.

### 하위 컨텍스트
<a name="durable-sdk-op-child-context"></a>

그룹화 작업에 필요한 격리된 실행 컨텍스트를 생성합니다. 하위 컨텍스트에는 자체 체크포인트 로그가 있으며 여러 개의 단계, 대기 및 기타 작업을 포함할 수 있습니다. SDK는 전체 하위 컨텍스트를 재시도 및 복구를 위한 단일 유닛으로 취급합니다.

하위 컨텍스트를 사용하여 복잡한 워크플로를 구성하거나, 하위 워크플로를 구현하거나, 함께 재시도해야 하는 작업을 격리할 수 있습니다.

------
#### [ TypeScript ]

```
const result = await context.runInChildContext(
  'batch-processing',
  async (childCtx) => {
    return await processBatch(childCtx, items);
  }
);
```

------
#### [ Python ]

```
result = context.run_in_child_context(
    lambda child_ctx: process_batch(child_ctx, items),
    name='batch-processing'
)
```

------
#### [ Java (Preview) ]

```
var result = context.runInChildContext(
    "batch-processing", 
    String.class, 
    childCtx -> process_batch(childCtx, items)
);
```

------

재생 메커니즘을 사용하려면 지속성 작업이 결정론적 순서에 따라 발생해야 합니다. 여러 하위 컨텍스트를 사용하면 여러 작업 스트림을 동시에 실행할 수 있으며, 결정론은 각 컨텍스트 내에 별도로 적용됩니다. 이를 통해 여러 개의 CPU 코어를 효율적으로 활용하는 고성능 함수를 구성할 수 있습니다.

예를 들어 2개의 하위 컨텍스트 A와 B를 시작한다고 가정합니다. 최초 간접 호출 시 컨텍스트 내의 단계는 A1, B1, B2, A2, A3 순서로 실행되고, 'A' 단계는 'B' 단계와 동시에 실행되었습니다. 재생 시 체크포인트 로그에서 결과가 검색되면 타이밍이 훨씬 빨라지고, B1, A1, A2, B2, A3 등의 다른 순서로 단계가 발생합니다. 'A' 단계가 올바른 순서(A1, A2, A3)로 발생했고 'B' 단계가 올바른 순서(B1, B2)로 발생했기 때문에 결정론의 필요성이 올바르게 충족되었습니다.

### 조건부 대기
<a name="durable-sdk-op-wait-condition"></a>

시도 사이에 자동 체크포인트 지정을 포함하는 조건에 대해 폴링합니다. SDK는 검사 함수를 실행하고, 결과가 포함된 체크포인트를 생성하고, 전략에 따라 대기하고, 조건이 충족될 때까지 반복합니다.

------
#### [ TypeScript ]

```
const result = await context.waitForCondition(
  async (state, ctx) => {
    const status = await checkJobStatus(state.jobId);
    return { ...state, status };
  },
  {
    initialState: { jobId: 'job-123', status: 'pending' },
    waitStrategy: (state) => 
      state.status === 'completed' 
        ? { shouldContinue: false }
        : { shouldContinue: true, delay: { seconds: 30 } }
  }
);
```

------
#### [ Python ]

```
result = context.wait_for_condition(
    lambda state, ctx: check_job_status(state['jobId']),
    config=WaitForConditionConfig(
        initial_state={'jobId': 'job-123', 'status': 'pending'},
        wait_strategy=lambda state, attempt: 
            {'should_continue': False} if state['status'] == 'completed'
            else {'should_continue': True, 'delay': 30}
    )
)
```

------
#### [ Java (Preview) ]

Java용 waitForCondition은 아직 개발 중입니다.

------

외부 시스템을 폴링하거나, 리소스가 준비될 때까지 대기하거나, 백오프를 사용하여 재시도를 구현하는 경우 `waitForCondition`을 사용합니다.

### 함수 간접 호출
<a name="durable-sdk-op-invoke"></a>

다른 Lambda 함수를 간접 호출하고 결과를 기다립니다. SDK는 체크포인트를 생성하고, 대상 함수를 간접 호출하고, 호출이 완료되면 함수를 재개합니다. 이를 통해 함수 구성 및 워크플로 분해가 활성화됩니다.

------
#### [ TypeScript ]

```
const result = await context.invoke(
  'invoke-processor',
  'arn:aws:lambda:us-east-1:123456789012:function:processor:1',
  { data: inputData }
);
```

------
#### [ Python ]

```
result = context.invoke(
    'arn:aws:lambda:us-east-1:123456789012:function:processor:1',
    {'data': input_data},
    name='invoke-processor'
)
```

------
#### [ Java (Preview) ]

```
var result = context.invoke(
    "invoke-processor", 
    "arn:aws:lambda:us-east-1:123456789012:function:processor:1",
    inputData,
    Result.class, 
    InvokeConfig.builder().build()
);
```

------

## 지속성 작업 측정 방법
<a name="durable-operations-checkpoint-consumption"></a>

`DurableContext`를 통해 직접 호출하는 각각의 지속성 작업은 실행 진행 상황을 추적하고 상태 데이터를 저장하기 위해 체크포인트를 생성합니다. 이러한 작업에는 사용량에 따라 요금이 부과되고, 체크포인트에는 데이터 쓰기 및 보존 비용에 영향을 미치는 데이터가 포함될 수 있습니다. 저장된 데이터에는 간접 호출 이벤트 데이터, 단계에서 반환된 페이로드, 콜백 완료 시 전달된 데이터가 포함됩니다. 지속성 작업의 측정 방식을 이해하면 실행 비용을 추정하고 워크플로를 최적화하는 데 도움이 됩니다. 요금에 대한 자세한 내용은 [Lambda 요금 페이지](https://aws.amazon.com/lambda/pricing/)를 참조하세요.

페이로드 크기는 지속성 작업에서 유지되는 직렬화된 데이터의 크기를 나타냅니다. 데이터는 바이트 단위로 측정되고 크기는 작업에서 사용하는 직렬 변환기에 따라 달라질 수 있습니다. 작업의 페이로드는 성공적인 완료의 결과 자체이거나 작업이 실패한 경우 직렬화된 오류 객체일 수 있습니다.

### 기본 작업
<a name="durable-operations-basic"></a>

기본 작업은 지속성 함수의 기본 구성 요소입니다.


| 연산 | 체크포인트 시점 | 작업 수 | 유지된 데이터 | 
| --- | --- | --- | --- | 
| Execution | 시작됨 | 1 | 입력 페이로드 크기 | 
| Execution | 완료(성공/실패/중지) | 0 | 출력 페이로드 크기 | 
| 단계 | 재시도/성공/실패 | 1 \$1 N회 재시도 | 각 시도에서 반환된 페이로드 크기 | 
| Wait | 시작됨 | 1 | 해당 사항 없음 | 
| WaitForCondition | 각 폴링 시도 | 1 \$1 N회 폴링 | 각 폴링 시도에서 반환된 페이로드 크기 | 
| 간접 호출 수준 재시도 | 시작됨 | 1 | 오류 객체의 페이로드 | 

### 콜백 작업
<a name="durable-operations-callbacks"></a>

콜백 작업을 사용하면 함수가 일시 중지되고 외부 시스템에서 입력을 제공할 때까지 대기합니다. 이러한 작업은 콜백이 생성 및 완료될 때 체크포인트를 생성합니다.


| 연산 | 체크포인트 시점 | 작업 수 | 유지된 데이터 | 
| --- | --- | --- | --- | 
| CreateCallback | 시작됨 | 1 | 해당 사항 없음 | 
| API 직접 호출을 통한 콜백 완료 | 완료됨 | 0 | 콜백 페이로드 | 
| WaitForCallback | 시작됨 | 3 \$1 N회 재시도(컨텍스트 \$1 콜백 \$1 단계) | 제출자 단계 시도로 반환된 페이로드와 콜백 페이로드 사본 2개 | 

### 복합 작업
<a name="durable-operations-compound"></a>

복합 작업은 여러 지속성 작업을 결합하여 병렬 실행, 배열 처리 및 중첩된 컨텍스트와 같은 복잡한 조정 패턴을 처리합니다.


| 연산 | 체크포인트 시점 | 작업 수 | 유지된 데이터 | 
| --- | --- | --- | --- | 
| Parallel | 시작됨 | 1 \$1 N개 브랜치(상위 컨텍스트 1개 \$1 하위 컨텍스트 N개) | 각 브랜치에서 반환된 페이로드 크기의 복사본 최대 2개와 각 브랜치의 상태 | 
| 지도 | 시작됨 | 1 \$1 N개 브랜치(상위 컨텍스트 1개 \$1 하위 컨텍스트 N개) | 각 반복에서 반환된 페이로드 크기의 복사본 최대 2개와 각 반복의 상태 | 
| promise 헬퍼 | 완료됨 | 1 | promise에서 반환된 페이로드 크기 | 
| RunInChildContext | 성공/실패 | 1 | 하위 컨텍스트에서 반환된 페이로드 크기 | 

`runInChildContext`의 컨텍스트나 복합 작업 내부에서 사용하는 컨텍스트의 경우 256KB 미만의 결과는 직접 체크포인트가 지정됩니다. 이보다 더 큰 결과는 저장되지 않고 컨텍스트의 작업을 다시 처리하여 재생 중에 재구성됩니다.

# 지속성 함수에 지원되는 런타임
<a name="durable-supported-runtimes"></a>

런타임 버전 유연성을 높이기 위해 선택한 관리형 런타임 및 OCI 컨테이너 이미지에 지속성 함수를 사용할 수 있습니다. 콘솔에서 직접 관리형 런타임을 사용하거나 infrastructure-as-code를 통해 프로그래밍 방식으로 Node.js와 Python용 지속성 함수를 생성할 수 있습니다. Java(미리 보기)의 지속성 함수는 현재 컨테이너 이미지를 통해서만 배포할 수 있습니다.

## Lambda 관리형 런타임
<a name="durable-managed-runtimes"></a>

다음 관리형 런타임은 Lambda 콘솔에서 함수를 생성하거나 `--durable-config '{"ExecutionTimeout": 3600, "RetentionPeriodInDays": 7}'` 파라미터와 함께 AWS CLI를 사용할 때 지속성 함수를 지원합니다. Lambda 런타임에 대한 자세한 내용은 [Lambda 런타임](lambda-runtimes.md)을 참조하세요.


| 언어 | 런타임 | 
| --- | --- | 
| Node.js | nodejs22.x | 
| Node.js | nodejs24.x | 
| Python | python3.13 | 
| Python | python3.14 | 

**참고**  
Lambda 런타임에는 테스트 및 개발을 위한 지속성 실행 SDK가 포함되어 있습니다. 하지만 프로덕션용 배포 패키지에는 SDK를 포함하는 것이 좋습니다. 이를 통해 버전 일관성이 보장되고 함수 동작에 영향을 미칠 수 있는 잠재적 런타임 업데이트를 방지할 수 있습니다.

### Node.js
<a name="durable-runtime-nodejs"></a>

Node.js 프로젝트에 SDK를 설치합니다.

```
npm install @aws/durable-execution-sdk-js
```

SDK는 JavaScript 및 TypeScript를 지원합니다. TypeScript 프로젝트의 경우 SDK에는 유형 정의가 포함됩니다.

### Python
<a name="durable-runtime-python"></a>

Python 프로젝트에 SDK를 설치합니다.

```
pip install aws-durable-execution-sdk-python
```

Python SDK는 동기식 메서드를 사용하며 `async/await`이 필요하지 않습니다.

### Java(미리 보기)
<a name="durable-runtime-java"></a>

`pom.xml`에 종속성 추가:

```
<dependency>
    <groupId>software.amazon.lambda.durable</groupId>
    <artifactId>aws-durable-execution-sdk-java</artifactId>
    <version>VERSION</version>
</dependency>
```

Python 프로젝트에서 SDK 설치:

```
mvn install
```

Java SDK의 미리 보기 버전을 사용할 수 있습니다. waitForCondition, waitForCallback, 병렬 및 맵 작업은 아직 개발 중입니다.

## 컨테이너 이미지
<a name="durable-container-images"></a>

컨테이너 이미지와 함께 지속성 함수를 사용하여 추가 런타임 버전 또는 사용자 지정 런타임 구성을 지원할 수 있습니다. 컨테이너 이미지를 사용하면 관리형 런타임으로 사용할 수 없는 런타임 버전을 사용하거나 런타임 환경을 사용자 지정할 수 있습니다.

컨테이너 이미지를 사용하여 지속성 함수를 생성합니다.

1. Lambda 기본 이미지를 기반으로 Dockerfile 생성

1. 컨테이너에 지속성 실행 SDK 설치

1. 컨테이너 이미지를 빌드하고 Amazon Elastic Container Registry에 푸시

1. 지속성 실행이 활성화된 컨테이너 이미지에서 Lambda 함수 생성

### 컨테이너 예제
<a name="durable-container-python"></a>

Dockerfile 생성:

------
#### [ Python ]

Python 3.11용 Dockerfile을 생성합니다.

```
FROM public.ecr.aws/lambda/python:3.11

# Copy requirements file
COPY requirements.txt ${LAMBDA_TASK_ROOT}/

# Install dependencies including durable SDK
RUN pip install -r requirements.txt

# Copy function code
COPY lambda_function.py ${LAMBDA_TASK_ROOT}/

# Set the handler
CMD [ "lambda_function.handler" ]
```

`requirements.txt` 파일을 생성합니다.

```
aws-durable-execution-sdk-python
```

------
#### [ Java (Preview) ]

Java 25용 Dockerfile 생성:

```
FROM --platform=linux/amd64 public.ecr.aws/lambda/java:25

# Install Maven
RUN dnf install -y maven

WORKDIR /var/task

# Copy Maven configuration and source code
COPY pom.xml .
COPY src ./src

# Build
RUN mvn clean package -DskipTests

# Move JAR to lib directory
RUN mv target/*.jar lib/

# Set the handler
CMD ["src.path.to.lambdaFunction::handler"]
```

------

이미지를 빌드하고 푸시합니다.

```
# Build the image
docker build -t my-durable-function .

# Tag for ECR
docker tag my-durable-function:latest 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-durable-function:latest

# Push to ECR
docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-durable-function:latest
```

지속성 실행이 활성화된 함수를 생성합니다.

```
aws lambda create-function \
  --function-name myDurableFunction \
  --package-type Image \
  --code ImageUri=123456789012.dkr.ecr.us-east-1.amazonaws.com/my-durable-function:latest \
  --role arn:aws:iam::123456789012:role/lambda-execution-role \
  --durable-config '{"ExecutionTimeout": 3600, "RetentionPeriodInDays": 7}'
```

Lambda에서의 컨테이너 이미지 사용에 대한 자세한 내용은 Lambda 개발자 안내서에서 [Lambda 컨테이너 이미지 생성](https://docs.aws.amazon.com/lambda/latest/dg/images-create.html)을 참조하세요.

## 런타임 고려 사항
<a name="durable-runtime-considerations"></a>

**SDK 버전 관리:** 배포 패키지 또는 컨테이너 이미지에 지속성 실행 SDK를 포함합니다. 이를 통해 함수가 특정 SDK 버전을 사용하게 되고 런타임 업데이트의 영향을 받지 않습니다. `package.json` 또는 `requirements.txt`에서 SDK 버전을 고정하여 업그레이드 시기를 제어합니다.

**런타임 업데이트:** AWS는 보안 패치 및 버그 수정을 포함하여 관리형 런타임을 업데이트합니다. 업데이트에는 새 SDK 버전이 포함될 수 있습니다. 예기치 않은 동작을 방지하려면 배포 패키지에 SDK를 포함하고 프로덕션에 배포하기 전에 철저히 테스트해야 합니다.

**컨테이너 이미지 크기:** 컨테이너 이미지의 압축되지 않은 최대 크기는 10GB입니다. 지속성 실행 SDK는 이미지에 최소 크기를 추가합니다. 다중 스테이지 빌드를 사용하고 불필요한 종속성을 제거하여 컨테이너를 최적화하세요.

**콜드 스타트 성능:** 컨테이너 이미지는 관리형 런타임보다 콜드 스타트 시간이 길 수 있습니다. 지속성 실행 SDK는 콜드 스타트 성능에 미치는 영향을 최소화합니다. 콜드 스타트 지연 시간이 애플리케이션에 있어 중요한 경우 프로비저닝 동시성을 사용합니다.

# 지속성 Lambda 함수 간접 호출
<a name="durable-invoking"></a>

지속성 Lambda 함수는 표준 Lambda 함수와 동일한 간접 호출 방법을 지원합니다. 지속성 함수를 동기식이나 비동기식으로, 또는 이벤트 소스 매핑을 통해 간접 호출할 수 있습니다. 간접 호출 프로세스는 표준 함수와 동일하지만 지속성 함수는 장기 실행 및 자동 상태 관리를 위한 추가 기능을 제공합니다.

## 간접 호출 방법
<a name="durable-invoking-methods"></a>

**동기식 간접 호출:** 지속성 함수를 간접 호출하고 응답을 기다립니다. 동기식 간접 호출은 Lambda에 의해 15분(또는 구성된 함수 및 실행 제한 시간에 따라 그 이하)으로 제한됩니다. 즉각적인 결과가 필요하거나 응답을 기대하는 API 및 서비스와 통합할 때는 동기식 간접 호출을 사용합니다. 호출자를 방해하지 않고 효율적인 계산을 위해 대기 작업을 사용할 수 있습니다. 간접 호출은 전체 지속성 실행이 완료될 때까지 기다립니다. 멱등성 실행 시작의 경우 [멱등성](durable-execution-idempotency.md)에 설명된 대로 실행 이름 파라미터를 사용합니다.

```
aws lambda invoke \
  --function-name my-durable-function:1 \
  --cli-binary-format raw-in-base64-out \
  --payload '{"orderId": "12345"}' \
  response.json
```

**비동기식 간접 호출:** 응답을 기다리지 않고 처리할 이벤트를 대기열에 등록합니다. Lambda는 이벤트를 대기열에 배치하고 즉시 반환합니다. 비동기식 간접 호출은 최대 1년의 실행 기간을 지원합니다. 실행 후 망각형 시나리오 또는 백그라운드에서 처리가 발생할 수 있는 경우 비동기식 간접 호출을 사용합니다. 멱등성 실행 시작의 경우 [멱등성](durable-execution-idempotency.md)에 설명된 대로 실행 이름 파라미터를 사용합니다.

```
aws lambda invoke \
  --function-name my-durable-function:1 \
  --invocation-type Event \
  --cli-binary-format raw-in-base64-out \
  --payload '{"orderId": "12345"}' \
  response.json
```

**이벤트 소스 매핑:** Amazon SQS, Kinesis 또는 DynamoDB와 같은 스트림 또는 대기열 기반 서비스에서 레코드를 사용할 수 있을 때 지속성 함수를 자동으로 간접 호출하도록 Lambda를 구성합니다. 이벤트 소스 매핑은 이벤트 소스를 폴링하고 배치 레코드가 있는 함수를 간접 호출합니다. 실행 기간 제한을 포함하여 지속성 함수와 함께 이벤트 소스 매핑을 사용하는 방법에 대한 자세한 내용은 [지속성 함수를 사용한 이벤트 소스 매핑](durable-invoking-esm.md)을 참조하세요.

각 간접 호출 방법에 대한 자세한 내용은 [동기식 간접 호출](invocation-sync.md) 및 [비동기식 간접 호출](invocation-async.md)을 참조하세요.

**참고**  
지속성 함수는 오류 처리에 Dead Letter Queue(DLQ)를 지원하지만 Lambda 대상은 지원하지 않습니다. 실패한 간접 호출에서 레코드를 캡처하도록 DLQ를 구성합니다.

## 정규화된 ARN 요구 사항
<a name="durable-invoking-qualified-arns"></a>

지속성 함수는 간접 호출에 정규화된 식별자가 필요합니다. 버전 번호, 별칭 또는 `$LATEST`를 사용하여 지속성 함수를 간접 호출해야 합니다. 정규화된 ARN 또는 함수 이름을 버전/별칭 접미사와 함께 사용할 수 있습니다. 정규화되지 않은 식별자(버전 또는 별칭 접미사 제외)는 사용할 수 없습니다.

**유효한 간접 호출:**

```
# Using full ARN with version number
arn:aws:lambda:us-east-1:123456789012:function:my-durable-function:1

# Using full ARN with alias
arn:aws:lambda:us-east-1:123456789012:function:my-durable-function:prod

# Using full ARN with $LATEST
arn:aws:lambda:us-east-1:123456789012:function:my-durable-function:$LATEST

# Using function name with version number
my-durable-function:1

# Using function name with alias
my-durable-function:prod
```

**유효하지 않은 간접 호출:**

```
# Unqualified ARN (not allowed)
arn:aws:lambda:us-east-1:123456789012:function:my-durable-function

# Unqualified function name (not allowed)
my-durable-function
```

이 요구 사항은 수명 주기 동안 지속성 실행이 일관되게 유지되도록 합니다. 지속성 실행이 시작되면 특정 함수 버전에 고정됩니다. 함수가 일시 중지되고 몇 시간 또는 며칠 후에 재개되면 Lambda는 실행을 시작한 것과 동일한 버전을 간접 호출하여 전체 워크플로에서 코드 일관성을 보장합니다.

**모범 사례**  
`$LATEST`가 아닌 프로덕션 지속성 함수에 번호가 지정된 버전 또는 별칭을 사용합니다. 번호가 지정된 버전은 변경할 수 없으며, 결정론적 재생을 지원합니다. 선택 사항인 별칭은 간접 호출 코드를 변경하지 않고 새 버전을 가리키도록 업데이트할 수 있는 안정적인 참조의 역할을 합니다. 별칭을 업데이트하면 새 실행은 새 버전을 사용하지만 진행 중인 실행은 원래 버전으로 계속됩니다. `$LATEST`를 사용하여 프로토타입을 생성하거나 개발 중에 배포 시간을 단축할 수 있습니다. 단, 실행 중에 기본 코드가 변경되면 실행이 올바르게 재생되지 않을 수 있으며, 실패할 수도 있습니다.

## 실행 수명 주기 이해
<a name="durable-invoking-execution-lifecycle"></a>

지속성 함수를 간접 호출하면 Lambda는 여러 함수 간접 호출에 걸쳐 지속성 실행을 생성합니다.

1. **최초 간접 호출:** 간접 호출 요청으로 새로운 지속성 실행이 생성됩니다. Lambda는 고유한 실행 ID를 할당하고 처리를 시작합니다.

1. **실행 및 체크포인트 지정:** 함수가 지속성 작업을 실행하면 SDK는 진행 상황을 추적하는 체크포인트를 생성합니다.

1. **일시 중지(필요한 경우):** 함수가 `wait` 또는 `waitForCallback` 같은 지속성 대기 또는 자동 단계 재시도를 사용하는 경우 Lambda는 실행을 일시 중지하고 컴퓨팅 시간 요금이 발생하지 않게 됩니다.

1. **재개:** 재개(재시도 후 포함)할 시간이 되면 Lambda는 함수를 다시 간접 호출합니다. SDK는 체크포인트 로그를 재생하고 실행이 일시 중지된 위치에서 계속합니다.

1. **완료:** 함수가 최종 결과를 반환하거나 처리되지 않은 오류를 발생시키면 지속성 실행이 완료됩니다.

동기식 간접 호출의 경우 호출자는 대기 작업을 포함하여 전체 지속성 실행이 완료될 때까지 기다립니다. 실행이 간접 호출 제한 시간(15분 이하)을 초과하면 간접 호출 제한 시간이 초과됩니다. 비동기식 간접 호출의 경우 Lambda는 즉시 반환하고 실행은 독립적으로 계속됩니다. 지속성 실행 API를 사용하여 실행 상태를 추적하고 최종 결과를 검색합니다.

## 애플리케이션 코드에서 간접 호출
<a name="durable-invoking-with-sdk"></a>

AWS SDK를 사용하여 애플리케이션 코드에서 지속성 함수를 간접 호출합니다. 간접 호출 프로세스는 표준 함수와 동일합니다.

------
#### [ TypeScript ]

```
import { LambdaClient, InvokeCommand } from '@aws-sdk/client-lambda';

const client = new LambdaClient({});

// Synchronous invocation
const response = await client.send(new InvokeCommand({
  FunctionName: 'arn:aws:lambda:us-east-1:123456789012:function:my-durable-function:1',
  Payload: JSON.stringify({ orderId: '12345' })
}));

const result = JSON.parse(Buffer.from(response.Payload!).toString());

// Asynchronous invocation
await client.send(new InvokeCommand({
  FunctionName: 'arn:aws:lambda:us-east-1:123456789012:function:my-durable-function:1',
  InvocationType: 'Event',
  Payload: JSON.stringify({ orderId: '12345' })
}));
```

------
#### [ Python ]

```
import boto3
import json

client = boto3.client('lambda')

# Synchronous invocation
response = client.invoke(
    FunctionName='arn:aws:lambda:us-east-1:123456789012:function:my-durable-function:1',
    Payload=json.dumps({'orderId': '12345'})
)

result = json.loads(response['Payload'].read())

# Asynchronous invocation
client.invoke(
    FunctionName='arn:aws:lambda:us-east-1:123456789012:function:my-durable-function:1',
    InvocationType='Event',
    Payload=json.dumps({'orderId': '12345'})
)
```

------

## 연쇄 간접 호출
<a name="durable-invoking-chained"></a>

지속성 함수는 `DurableContext`의 `invoke` 작업을 사용하여 다른 지속성 및 비지속성 함수를 간접 호출할 수 있습니다. 이렇게 하면 함수 직접 호출 시 간접 호출된 함수가 완료될 때까지 대기(일시 중지)하는 연쇄 간접 호출이 생성됩니다.

------
#### [ TypeScript ]

```
export const handler = withDurableExecution(
  async (event: any, context: DurableContext) => {
    // Invoke another durable function and wait for result
    const result = await context.invoke(
      'process-order',
      'arn:aws:lambda:us-east-1:123456789012:function:order-processor:1',
      { orderId: event.orderId }
    );
    
    return { statusCode: 200, body: JSON.stringify(result) };
  }
);
```

------
#### [ Python ]

```
@durable_execution
def handler(event, context: DurableContext):
    # Invoke another durable function and wait for result
    result = context.invoke(
        'arn:aws:lambda:us-east-1:123456789012:function:order-processor:1',
        {'orderId': event['orderId']},
        name='process-order'
    )
    
    return {'statusCode': 200, 'body': json.dumps(result)}
```

------

연쇄 간접 호출은 직접 호출 함수에 체크포인트를 생성합니다. 함수 직접 호출이 중단되면 함수를 다시 간접 호출하지 않고 간접 호출된 함수의 결과와 함께 체크포인트에서 재개됩니다.

**참고**  
교차 계정 연쇄 간접 호출은 지원되지 않습니다. 간접 호출된 함수는 직접 호출 함수와 동일한 AWS 계정에 있어야 합니다.

# 지속성 함수를 사용한 이벤트 소스 매핑
<a name="durable-invoking-esm"></a>

지속성 함수는 모든 Lambda 이벤트 소스 매핑에서 작동합니다. 표준 함수에 대해 구성하는 것과 동일한 방식으로 지속성 함수에 대한 이벤트 소스 매핑을 구성합니다. 이벤트 소스 매핑은 Amazon SQS, Kinesis 및 DynamoDB Streams와 같은 이벤트 소스를 자동으로 폴링하고, 레코드 배치를 사용하여 함수를 간접 호출합니다.

이벤트 소스 매핑은 복잡한 다단계 워크플로로 스트림 또는 대기열을 처리하는 지속성 함수에 유용합니다. 예를 들어 재시도, 외부 API 직접 호출 및 인적 승인으로 Amazon SQS 메시지를 처리하는 지속성 함수를 생성할 수 있습니다.

## 이벤트 소스 매핑이 지속성 함수를 간접 호출하는 방법
<a name="durable-esm-invocation-behavior"></a>

이벤트 소스 매핑은 지속성 함수를 동기식으로 간접 호출하고, 다음 배치를 처리하거나 레코드를 처리된 것으로 표시하기 전에 전체 지속성 실행이 완료될 때까지 기다립니다. 총 지속성 실행 시간이 15분을 초과하면 실행 제한 시간이 초과되고 실패합니다. 이벤트 소스 매핑은 제한 시간 예외를 수신하고 재시도 구성에 따라 처리합니다.

## 15분 실행 제한
<a name="durable-esm-duration-limit"></a>

이벤트 소스 매핑에 의해 지속성 함수가 간접 호출되는 경우 총 지속성 실행 기간은 15분을 초과할 수 없습니다. 이 제한은 개별 함수 간접 호출뿐만 아니라 시작부터 완료까지 지속되는 전체 실행에 적용됩니다.

이 15분 제한은 Lambda 함수 제한 시간(최대 15분)과 별개입니다. 함수 제한 시간은 각 개별 간접 호출이 실행될 수 있는 시간을 제어하는 반면, 지속성 실행 제한 시간은 실행 시작부터 완료까지의 총 경과 시간을 제어합니다.

**예제 시나리오:**
+ **유효:** 지속성 함수는 각각 2분 정도 걸리는 3개 단계로 Amazon SQS 메시지를 처리한 다음 최종 단계를 완료하기 전에 5분을 기다립니다. 총 실행 시간은 11분입니다. 이 작업의 총합은 15분 미만이기 때문에 작동합니다.
+ **유효하지 않음:** 지속성 함수가 Amazon SQS 메시지를 처리하고, 2분 후에 최초 처리를 완료한 다음 외부 콜백이 완료될 때까지 20분 동안 기다립니다. 총 실행 시간은 22분입니다. 15분 제한을 초과하고 실패합니다.
+ **유효하지 않음:** 지속성 함수가 Kinesis 레코드를 처리하고 단계 사이에 총 30분의 대기 작업이 포함됩니다. 각 개별 간접 호출이 빠르게 완료되더라도 총 실행 시간은 15분을 초과합니다.

**중요**  
이벤트 소스 매핑을 사용할 때 지속성 실행 제한 시간을 15분 이하로 구성하지 않으면 이벤트 소스 매핑 생성이 실패합니다. 워크플로에 더 긴 실행 시간이 필요한 경우 아래에서 설명하는 중간 함수 패턴을 사용합니다.

## 이벤트 소스 매핑 구성
<a name="durable-esm-configuration"></a>

Lambda 콘솔, AWS CLI 또는 AWS SDK를 사용하여 지속성 함수에 대한 이벤트 소스 매핑을 구성합니다. 모든 표준 이벤트 소스 매핑 속성은 지속성 함수에 적용됩니다.

```
aws lambda create-event-source-mapping \
  --function-name arn:aws:lambda:us-east-1:123456789012:function:my-durable-function:1 \
  --event-source-arn arn:aws:sqs:us-east-1:123456789012:my-queue \
  --batch-size 10 \
  --maximum-batching-window-in-seconds 5
```

지속성 함수에 대한 이벤트 소스 매핑을 구성할 때는 정규화된 ARN(버전 번호 또는 별칭 포함)을 사용해야 합니다.

## 이벤트 소스 매핑을 사용한 오류 처리
<a name="durable-esm-error-handling"></a>

이벤트 소스 매핑은 지속성 함수와 함께 작동하는 기본 오류 처리를 제공합니다.
+ **재시도 동작:** 최초 간접 호출이 실패하면 이벤트 소스 매핑이 재시도 구성에 따라 재시도됩니다. 요구 사항에 따라 최대 재시도 횟수 및 재시도 간격을 구성합니다.
+ **Dead Letter Queue(DLQ):** 모든 재시도 후 실패한 레코드를 캡처하도록 Dead Letter Queue(DLQ)를 구성합니다. 이를 통해 메시지가 손실되지 않고 실패한 레코드를 수동으로 검사할 수 있습니다.
+ **부분 배치 실패:** Amazon SQS 및 Kinesis의 경우 부분 배치 실패 보고를 사용하여 레코드를 개별적으로 처리하고 실패한 레코드만 재시도합니다.
+ **오류 시 bisect:** Kinesis 및 DynamoDB Streams의 경우 오류 시 bisect를 활성화하여 실패한 배치를 분할하고 문제가 있는 레코드를 격리합니다.

**참고**  
지속성 함수는 오류 처리에 Dead Letter Queue(DLQ)를 지원하지만 Lambda 대상은 지원하지 않습니다. 실패한 간접 호출에서 레코드를 캡처하도록 DLQ를 구성합니다.

이벤트 소스 매핑 오류 처리에 대한 자세한 내용은 [이벤트 소스 매핑](invocation-eventsourcemapping.md)을 참조하세요.

## 장기 실행 워크플로에 중간 함수 사용
<a name="durable-esm-intermediary-function"></a>

워크플로 완료에 15분 이상이 필요한 경우 이벤트 소스 매핑과 지속성 함수 사이에 중간 표준 Lambda 함수를 사용합니다. 중간 함수는 이벤트 소스 매핑에서 이벤트를 수신하고 지속성 함수를 비동기식으로 간접 호출하여 15분 실행 제한을 제거합니다.

이 패턴은 이벤트 소스 매핑의 동기식 간접 호출 모델을 지속성 함수의 장기 실행 모델에서 분리합니다. 이벤트 소스 매핑은 지속성 실행을 시작한 후 빠르게 반환되는 중간 함수를 호출합니다. 그런 다음 지속성 함수는 필요한 기간(최대 1년) 동안 독립적으로 실행됩니다.

### 아키텍처
<a name="durable-esm-intermediary-architecture"></a>

중간 함수 패턴은 다음 3가지 구성 요소를 사용합니다.

1. **이벤트 소스 매핑:** 이벤트 소스(Amazon SQS, Kinesis, DynamoDB Streams)를 폴링하고 레코드 배치와 동기식으로 중간 함수를 간접 호출합니다.

1. **중간 함수:** 이벤트 소스 매핑에서 이벤트를 수신하고, 필요한 경우 데이터를 검증 및 변환하고, 지속성 함수를 비동기식으로 간접 호출하는 표준 Lambda 함수입니다. 이 함수는 빠르게 완료되고(일반적으로 1초 미만) 이벤트 소스 매핑에 제어 권한을 반환합니다.

1. **지속성 함수:** 장기간 실행할 수 있는 복잡한 다단계 로직으로 이벤트를 처리합니다. 비동기식으로 간접 호출되므로 15분 제한이 적용되지 않습니다.

### 구현
<a name="durable-esm-intermediary-implementation"></a>

중간 함수는 이벤트 소스 매핑에서 전체 이벤트를 수신하고 지속성 함수를 비동기식으로 간접 호출합니다. 실행 이름 파라미터를 사용하여 멱등성 실행이 시작되도록 하면 이벤트 소스 매핑이 재시도되는 경우 중복 처리를 방지합니다.

------
#### [ TypeScript ]

```
import { LambdaClient, InvokeCommand } from '@aws-sdk/client-lambda';
import { SQSEvent } from 'aws-lambda';
import { createHash } from 'crypto';

const lambda = new LambdaClient({});

export const handler = async (event: SQSEvent) => {
  // Invoke durable function asynchronously with execution name
  await lambda.send(new InvokeCommand({
    FunctionName: 'arn:aws:lambda:us-east-1:123456789012:function:my-durable-function:1',
    InvocationType: 'Event',
    Payload: JSON.stringify({
      executionName: event.Name,
      event: event
    })
  }));
  
  return { statusCode: 200 };
};
```

------
#### [ Python ]

```
import boto3
import json
import hashlib

lambda_client = boto3.client('lambda')

def handler(event, context):  
    # Invoke durable function asynchronously with execution name
    lambda_client.invoke(
        FunctionName='arn:aws:lambda:us-east-1:123456789012:function:my-durable-function:1',
        InvocationType='Event',
        Payload=json.dumps({
            'executionName': execution_name,
            'event': event["name"]
        })
    )
    
    return {'statusCode': 200}
```

------

중간 함수 자체의 멱등성의 경우 [Powertools for AWS Lambda](https://docs.aws.amazon.com//powertools/)를 사용하면 이벤트 소스 매핑에서 중간 함수를 재시도할 때 지속성 함수의 중복 간접 호출을 방지합니다.

지속성 함수는 실행 이름이 포함된 페이로드를 수신하고 장기 실행 로직으로 모든 레코드를 처리합니다.

------
#### [ TypeScript ]

```
import { withDurableExecution, DurableContext } from '@aws/durable-execution-sdk-js';

export const handler = withDurableExecution(
  async (payload: any, context: DurableContext) => {
    const sqsEvent = payload.event;
    
    // Process each record with complex, multi-step logic
    const results = await context.map(
      sqsEvent.Records,
      async (ctx, record) => {
        const validated = await ctx.step('validate', async () => {
          return validateOrder(JSON.parse(record.body));
        });
        
        // Wait for external approval (could take hours or days)
        const approval = await ctx.waitForCallback(
          'approval',
          async (callbackId) => {
            await requestApproval(callbackId, validated);
          },
          { timeout: { hours: 48 } }
        );
        
        // Complete processing
        return await ctx.step('complete', async () => {
          return completeOrder(validated, approval);
        });
      }
    );
    
    return { statusCode: 200, processed: results.getResults().length };
  }
);
```

------
#### [ Python ]

```
from aws_durable_execution_sdk_python import durable_execution, DurableContext
from aws_durable_execution_sdk_python.config import Duration, WaitForCallbackConfig
from collections.abc import Sequence
import json

def validate_order(order_data: dict) -> dict:
    """Validate order data - always passes."""
    return order_data

def request_approval(callback_id: str, validated_order: dict) -> None:
    """Request approval for the order - always passes."""
    pass

def complete_order(validated_order: dict, approval_result: str) -> dict:
    """Complete the order processing - always passes."""
    return validated_order

@durable_execution
def lambda_handler(payload, context: DurableContext):
    sqs_event = payload['event']

    def process_record(
        ctx: DurableContext, 
        record: dict, 
        index: int, 
        items: Sequence[dict]
    ) -> dict:
        validated = ctx.step(
            lambda _: validate_order(json.loads(record['body'])),
            name=f'validate-{index}'
        )

        approval = ctx.wait_for_callback(
            submitter=lambda callback_id, wait_ctx: request_approval(callback_id, validated),
            name=f'approval-{index}',
            config=WaitForCallbackConfig(timeout=Duration.from_seconds(172800))
        )

        return ctx.step(
            lambda _: complete_order(validated, approval),
            name=f'complete-{index}'
        )

    results = context.map(
        inputs=sqs_event['Records'],
        func=process_record,
        name='process-records'
    )

    return {
        'statusCode': 200, 
        'started': results.started_count,
        'completed': results.success_count,
        'failed': results.failure_count,
        'total': results.total_count
    }
```

------

### 주요 고려 사항
<a name="durable-esm-intermediary-tradeoffs"></a>

이 패턴은 이벤트 소스 매핑을 지속성 실행에서 분리하여 15분 실행 제한을 없앱니다. 중간 함수는 지속성 실행을 시작한 직후 반환되므로 이벤트 소스 매핑에서 처리가 계속될 수 있습니다. 이후 지속성 함수는 필요한 기간 동안 독립적으로 실행됩니다.

중간 함수는 지속성 실행이 완료될 때가 아니라 지속성 함수를 간접 호출할 때 성공합니다. 지속성 실행이 나중에 실패하면 이미 배치를 성공적으로 처리했기 때문에 이벤트 소스 매핑이 재시도되지 않습니다. 지속성 함수에서 오류 처리를 구현하고 실패한 실행에 대해 Dead Letter Queue(DLQ)를 구성합니다.

멱등성 실행이 시작되도록 실행 이름 파라미터를 사용합니다. 이벤트 소스 매핑이 중간 함수를 재시도하면 실행 이름이 이미 존재하므로 지속성 함수가 중복 실행을 시작하지 않습니다.

## 지원되는 이벤트 소스
<a name="durable-esm-supported-sources"></a>

지속성 함수는 이벤트 소스 매핑을 사용하는 모든 Lambda 이벤트 소스를 지원합니다.
+ Amazon SQS 대기열(표준 및 FIFO)
+ Kinesis 스트림
+ DynamoDB Streams
+ Amazon Managed Streaming for Apache Kafka(Amazon MSK)
+ 자체 관리형 Apache Kafka
+ Amazon MQ(ActiveMQ 및 RabbitMQ)
+ Amazon DocumentDB 변경 스트림

모든 이벤트 소스 유형에는 지속성 함수 간접 호출 시 15분의 지속성 실행 제한이 적용됩니다.

# Lambda 지속성 함수 재시도
<a name="durable-execution-sdk-retries"></a>

지속성 함수는 일시적 실패에 대한 애플리케이션의 복원력을 갖추는 자동 재시도 기능을 제공합니다. SDK는 비즈니스 로직 실패에 대한 단계 재시도와 인프라 실패에 대한 백엔드 재시도라는 2가지 수준에서 재시도를 처리합니다.

## 단계 재시도
<a name="durable-step-retries"></a>

단계 내에 발견되지 않은 예외가 발생하면 SDK는 구성된 재시도 전략에 따라 단계를 자동으로 재시도합니다. 단계 재시도는 SDK가 실행을 일시 중지하고 나중에 진행 상황 손실 없이 재개할 수 있도록 하는 체크포인트가 지정된 작업입니다.

### 단계 재시도 동작
<a name="durable-step-retry-behavior"></a>

다음 표에서는 SDK가 단계 내에서 예외를 처리하는 방법을 설명합니다.


| 시나리오 | 발생한 상황 | 영향 측정 | 
| --- | --- | --- | 
| 단계 내 예외 발생(재시도 횟수가 남아 있음) | SDK는 재시도를 위해 체크포인트를 생성하고 함수를 일시 중지합니다. 다음 간접 호출에서 단계는 백오프 지연이 구성된 상태에서 재시도합니다. | 작업 1개 \$1 오류 페이로드 크기 | 
| 단계 내 예외 발생(재시도 횟수가 남아 있지 않음) | 단계가 실패하고 예외가 발생합니다. 핸들러 코드가 이 예외를 포착하지 못하면 전체 실행이 실패합니다. | 작업 1개 \$1 오류 페이로드 크기 | 

단계에 재시도가 필요한 경우 SDK는 재시도 상태에 체크포인트를 지정하고 실행 중인 다른 작업이 없는 경우 Lambda 간접 호출을 종료합니다. 이를 통해 SDK는 컴퓨팅 리소스를 소비하지 않고 백오프 지연을 구현할 수 있습니다. 백오프 기간이 지나면 함수가 자동으로 재개됩니다.

### 단계 재시도 전략 구성
<a name="durable-step-retry-configuration"></a>

단계에서 실패를 처리하는 방식을 제어하도록 재시도 전략을 구성합니다. 최대 시도 횟수, 백오프 간격 및 재시도 조건을 지정할 수 있습니다.

**다음은 최대 시도 횟수가 포함된 지수 백오프입니다.**

------
#### [ TypeScript ]

```
const result = await context.step('call-api', async () => {
  const response = await fetch('https://api.example.com/data');
  if (!response.ok) throw new Error(`API error: ${response.status}`);
  return await response.json();
}, {
  retryStrategy: (error, attemptCount) => {
    if (attemptCount >= 5) {
      return { shouldRetry: false };
    }
    // Exponential backoff: 2s, 4s, 8s, 16s, 32s (capped at 300s)
    const delay = Math.min(2 * Math.pow(2, attemptCount - 1), 300);
    return { shouldRetry: true, delay: { seconds: delay } };
  }
});
```

------
#### [ Python ]

```
def retry_strategy(error, attempt_count):
    if attempt_count >= 5:
        return {'should_retry': False}
    # Exponential backoff: 2s, 4s, 8s, 16s, 32s (capped at 300s)
    delay = min(2 * (2 ** (attempt_count - 1)), 300)
    return {'should_retry': True, 'delay': delay}

result = context.step(
    lambda _: call_external_api(),
    name='call-api',
    config=StepConfig(retry_strategy=retry_strategy)
)
```

------

**다음은 고정 간격 백오프입니다.**

------
#### [ TypeScript ]

```
const orders = await context.step('query-orders', async () => {
  return await queryDatabase(event.userId);
}, {
  retryStrategy: (error, attemptCount) => {
    if (attemptCount >= 3) {
      return { shouldRetry: false };
    }
    return { shouldRetry: true, delay: { seconds: 5 } };
  }
});
```

------
#### [ Python ]

```
def retry_strategy(error, attempt_count):
    if attempt_count >= 3:
        return {'should_retry': False}
    return {'should_retry': True, 'delay': 5}

orders = context.step(
    lambda _: query_database(event['userId']),
    name='query-orders',
    config=StepConfig(retry_strategy=retry_strategy)
)
```

------

**다음은 조건부 재시도입니다(특정 오류만 재시도).**

------
#### [ TypeScript ]

```
const result = await context.step('call-rate-limited-api', async () => {
  const response = await fetch('https://api.example.com/data');
  
  if (response.status === 429) throw new Error('RATE_LIMIT');
  if (response.status === 504) throw new Error('TIMEOUT');
  if (!response.ok) throw new Error(`API_ERROR_${response.status}`);
  
  return await response.json();
}, {
  retryStrategy: (error, attemptCount) => {
    // Only retry rate limits and timeouts
    const isRetryable = error.message === 'RATE_LIMIT' || error.message === 'TIMEOUT';
    
    if (!isRetryable || attemptCount >= 3) {
      return { shouldRetry: false };
    }
    
    // Exponential backoff: 1s, 2s, 4s (capped at 30s)
    const delay = Math.min(Math.pow(2, attemptCount - 1), 30);
    return { shouldRetry: true, delay: { seconds: delay } };
  }
});
```

------
#### [ Python ]

```
def retry_strategy(error, attempt_count):
    # Only retry rate limits and timeouts
    is_retryable = str(error) in ['RATE_LIMIT', 'TIMEOUT']
    
    if not is_retryable or attempt_count >= 3:
        return {'should_retry': False}
    
    # Exponential backoff: 1s, 2s, 4s (capped at 30s)
    delay = min(2 ** (attempt_count - 1), 30)
    return {'should_retry': True, 'delay': delay}

result = context.step(
    lambda _: call_rate_limited_api(),
    name='call-rate-limited-api',
    config=StepConfig(retry_strategy=retry_strategy)
)
```

------

**재시도를 비활성화합니다.**

------
#### [ TypeScript ]

```
const isDuplicate = await context.step('check-duplicate', async () => {
  return await checkIfOrderExists(event.orderId);
}, {
  retryStrategy: () => ({ shouldRetry: false })
});
```

------
#### [ Python ]

```
is_duplicate = context.step(
    lambda _: check_if_order_exists(event['orderId']),
    name='check-duplicate',
    config=StepConfig(
        retry_strategy=lambda error, attempt: {'should_retry': False}
    )
)
```

------

재시도 전략에서 `shouldRetry: false`가 반환되면 재시도 없이 단계가 즉시 실패하게 됩니다. 멱등성 검사 또는 안전하게 반복할 수 없는 부가 효과가 있는 작업과 같이 재시도해서는 안 되는 작업에 사용합니다.

## 단계 외부의 예외
<a name="durable-handler-exceptions"></a>

핸들러 코드에서 발견되지 않은 예외가 발생하지만 단계에서 벗어난 경우 SDK는 실행을 실패로 표시합니다. 이를 통해 애플리케이션 로직의 오류가 올바르게 파악 및 보고됩니다.


| 시나리오 | 발생한 상황 | 영향 측정 | 
| --- | --- | --- | 
| 임의 단계 외부의 핸들러 코드에서 예외 발생 | SDK는 실행을 FAILED로 표시하고 오류를 반환합니다. 예외는 자동으로 재시도되지 않습니다. | 오류 페이로드 크기 | 

오류가 발생하기 쉬운 코드에 자동 재시도를 활성화하려면 재시도 전략을 통해 단계로 래핑합니다. 단계는 구성 가능한 백오프로 자동 재시도를 제공하는 반면, 단계 외부의 코드는 즉시 실패합니다.

## 백엔드 재시도
<a name="durable-backend-retries"></a>

백엔드 재시도는 Lambda에서 인프라 실패, 런타임 오류가 발생하거나 SDK가 지속성 실행 서비스와 통신할 수 없는 경우에 발생합니다. Lambda는 지속성 함수가 일시적인 인프라 문제로부터 복구될 수 있도록 이러한 실패를 자동으로 재시도합니다.

### 백엔드 재시도 시나리오
<a name="durable-backend-retry-scenarios"></a>

다음 시나리오에서 Lambda는 함수를 자동으로 재시도합니다.
+ **내부 서비스 오류** - Lambda 또는 지속성 실행 서비스가 5xx 오류를 반환하여 일시적인 서비스 문제를 나타내는 경우.
+ **스로틀링** - 동시성 제한 또는 서비스 할당량으로 인해 함수가 스로틀링되는 경우.
+ **제한 시간 초과** - 제한 시간 내에 SDK가 지속성 실행 서비스에 연결할 수 없는 경우.
+ **샌드박스 초기화 실패** - Lambda가 실행 환경을 초기화할 수 없는 경우.
+ **런타임 오류** - Lambda 런타임에서 메모리 부족 오류 또는 프로세스 충돌과 같은 함수 코드 외부의 오류가 발생하는 경우.
+ **유효하지 않은 체크포인트 토큰 오류** - 일반적으로 서비스 측 상태 변경으로 인해 체크포인트 토큰이 더 이상 유효하지 않은 경우.

다음 표는 SDK에서 이러한 시나리오에 어떻게 대처하는지 설명합니다.


| 시나리오 | 발생한 상황 | 영향 측정 | 
| --- | --- | --- | 
| 지속성 핸들러 외부의 런타임 오류(OOM, 제한 시간 초과, 충돌) | Lambda는 호출을 자동으로 재시도합니다. SDK는 마지막 체크포인트에서 재생되고, 완료된 단계를 건너뜁니다. | 오류 페이로드 크기 \$1 재시도당 작업 1개 | 
| CheckpointDurableExecution/GetDurableExecutionState API를 호출할 때 서비스 오류(5xx) 또는 제한 시간 초과 | Lambda는 호출을 자동으로 재시도합니다. SDK는 마지막 체크포인트에서 재생됩니다. | 오류 페이로드 크기 \$1 재시도당 작업 1개 | 
| CheckpointDurableExecution/GetDurableExecutionState API를 호출할 때 스로틀링(429) 또는 잘못된 체크포인트 토큰 | Lambda는 지수 백오프를 포함한 간접 호출을 자동으로 재시도합니다. SDK는 마지막 체크포인트에서 재생됩니다. | 오류 페이로드 크기 \$1 재시도당 작업 1개 | 
| CheckpointDurableExecution/GetDurableExecutionState API에서 클라이언트 오류(4xx, 429 및 잘못된 토큰 제외) | SDK는 실행을 FAILED로 표시합니다. 오류가 영구적인 문제를 나타내므로 자동 재시도는 발생하지 않습니다. | 오류 페이로드 크기 | 

백엔드 재시도는 지수 백오프를 사용하고 함수가 성공하거나 실행 제한 시간에 도달할 때까지 계속합니다. 재생 중에 SDK는 함수가 완료된 작업을 다시 실행하지 않도록 완료된 체크포인트를 건너뛰고 마지막으로 성공한 작업에서 계속 실행합니다.

## 재시도 모범 사례
<a name="durable-retry-best-practices"></a>

재시도 전략을 구성할 때 다음 모범 사례를 따릅니다.
+ **명시적인 재시도 전략 구성** - 프로덕션 환경에서 기본 재시도 동작에 의존하지 않습니다. 사용 사례에 적합한 최대 시도 횟수와 백오프 간격으로 명시적 재시도 전략을 구성합니다.
+ **조건부 재시도 사용** - 일시적 오류(속도 제한, 제한 시간 초과)만 재시도하고 영구적 오류(검증 실패, 찾을 수 없음) 시에는 빠르게 실패하는 `shouldRetry` 로직을 구현합니다.
+ **적절한 최대 시도 횟수 설정** - 복원력과 실행 시간 사이의 균형을 맞춥니다. 재시도 횟수가 너무 많으면 실패 감지가 지연되는 반면, 재시도 횟수가 너무 적으면 불필요한 실패가 발생할 수 있습니다.
+ **지수 백오프 사용** - 지수 백오프는 다운스트림 서비스의 부하를 줄이고 일시적 실패로부터 복구할 가능성을 높입니다.
+ **단계에서 오류가 발생하기 쉬운 코드 래핑** - 단계 외부의 코드는 자동으로 재시도할 수 없습니다. 재시도 전략을 사용하여 외부 API 호출, 데이터베이스 쿼리 및 기타 오류가 발생하기 쉬운 작업을 단계별로 래핑합니다.
+ **재시도 지표 모니터링** - Amazon CloudWatch에서 단계 재시도 작업 및 실행 실패를 추적하여 패턴을 식별하고 재시도 전략을 최적화합니다.

# 멱등성
<a name="durable-execution-idempotency"></a>

지속성 함수는 실행 이름을 통해 시작되는 실행에 대해 기본 멱등성을 제공합니다. 실행 이름을 제공하면 Lambda는 이를 사용하여 중복 실행을 방지하고 간접 호출 요청의 안전한 재시도를 활성화합니다. 단계에는 기본적으로 최소 1회 실행 시맨틱이 있습니다. 재생 도중 SDK는 완료된 단계를 다시 실행하지 않고 체크포인트 결과를 반환하지만 완료 전에 잠재적 재시도를 처리하려면 비즈니스 로직이 멱등성이어야 합니다.

**참고**  
Lambda 이벤트 소스 매핑(ESM)은 시작 시 멱등성을 지원하지 않습니다. 따라서 각 간접 호출(재시도 포함)은 새로운 지속성 실행을 시작합니다. 이벤트 소스 매핑을 사용하는 멱등성 실행을 보장하려면 [Powertools for AWS Lambda](https://docs.aws.amazon.com//powertools/)와 같은 함수 코드에서 멱등성 로직을 구현하거나 일반 Lambda 함수를 프록시(디스패처)로 사용하여 멱등성 키(실행 이름 파라미터)로 지속성 함수를 간접 호출합니다.

## 실행 이름
<a name="durable-idempotency-execution-names"></a>

지속성 함수를 간접 호출할 때 실행 이름을 제공할 수 있습니다. 실행 이름은 멱등성 키 역할을 하므로 중복 실행을 생성하지 않고도 간접 호출 요청을 안전하게 재시도할 수 있습니다. 이름을 제공하지 않으면 Lambda는 고유한 실행 ID를 자동으로 생성합니다.

실행 이름은 계정과 리전 내에서 고유해야 합니다. 실행 이름이 이미 존재하는 함수를 간접 호출하는 경우 Lambda의 동작은 기존 실행의 상태와 페이로드가 일치하는지 여부에 따라 달라집니다.

## 멱등성 동작
<a name="durable-idempotency-behavior"></a>

다음 표에서는 실행 이름, 기존 실행 상태 및 페이로드의 일치 여부에 따라 Lambda가 간접 호출 요청을 처리하는 방법을 설명합니다.


| 시나리오 | 이름 제공 여부 | 기존 실행 상태 | 페이로드 동일 여부 | 동작 | 
| --- | --- | --- | --- | --- | 
| 1 | 아니요 | 해당 사항 없음 | 해당 사항 없음 | 새 실행 시작: Lambda가 고유한 실행 ID를 생성하고 새 실행 시작 | 
| 2 | 예 | 존재하지 않거나 보존이 만료됨 | 해당 사항 없음 | 새 실행 시작: Lambda가 제공된 이름의 새 실행 시작 | 
| 3 | 예 | 실행 | 예 | 멱등성 시작: Lambda가 중복을 시작하지 않고 기존 실행 정보 반환, 동기식 간접 호출의 경우 실행 중인 실행에 다시 연결하는 역할 수행 | 
| 4 | 예 | 실행 | 아니요 | 오류: 이 이름으로 된 실행이 이미 다른 페이로드로 실행 중이므로 Lambda에서 DurableExecutionAlreadyExists 오류 반환 | 
| 5 | 예 | 종료(성공, 실패, 중지 또는 제한 시간 초과) | 예 | 멱등성 시작: Lambda가 새 실행을 시작하지 않고 기존 실행 정보 반환, 종료된 실행 결과가 반환됨 | 
| 6 | 예 | 종료(성공, 실패, 중지 또는 제한 시간 초과) | 아니요 | 오류: 이 이름으로 된 실행이 이미 다른 페이로드로 완료되었으므로 Lambda에서 DurableExecutionAlreadyExists 오류 반환 | 

**Note**  
시나리오 3과 5는 Lambda가 중복을 생성하는 대신 기존 실행 정보를 반환하여 중복 간접 호출 요청을 안전하게 처리하는 멱등성을 보여줍니다.

## 단계 멱등성
<a name="durable-idempotency-steps"></a>

단계에는 기본적으로 최소 1회 실행 시맨틱이 있습니다. 대기, 콜백 또는 실패 후 함수가 재생되면 SDK는 체크포인트 로그를 기준으로 각 단계를 확인합니다. 이미 완료된 단계의 경우 SDK는 단계 로직을 다시 실행하지 않고 체크포인트가 지정된 결과를 반환합니다. 하지만 단계가 실패하거나 단계가 완료되기 전에 함수가 중단되면 단계가 여러 번 실행될 수 있습니다.

잠재적 재시도를 처리하려면 단계로 래핑된 비즈니스 로직이 멱등성이어야 합니다. 멱등성 키를 사용하면 단계가 재시도되더라도 결제 또는 데이터베이스 쓰기와 같은 작업이 한 번만 실행됩니다.

**예제: 단계에서 멱등성 키 사용**

------
#### [ TypeScript ]

```
import { withDurableExecution, DurableContext } from '@aws/durable-execution-sdk-js';
import { randomUUID } from 'crypto';

export const handler = withDurableExecution(
  async (event: any, context: DurableContext) => {
    // Generate idempotency key once
    const idempotencyKey = await context.step('generate-key', async () => {
      return randomUUID();
    });
    
    // Use idempotency key in payment API to prevent duplicate charges
    const payment = await context.step('process-payment', async () => {
      return paymentAPI.charge({
        amount: event.amount,
        idempotencyKey: idempotencyKey
      });
    });
    
    return { statusCode: 200, payment };
  }
);
```

------
#### [ Python ]

```
from aws_durable_execution_sdk_python import durable_execution, DurableContext
import uuid

@durable_execution
def handler(event, context: DurableContext):
    # Generate idempotency key once
    idempotency_key = context.step(
        lambda _: str(uuid.uuid4()),
        name='generate-key'
    )
    
    # Use idempotency key in payment API to prevent duplicate charges
    payment = context.step(
        lambda _: payment_api.charge(
            amount=event['amount'],
            idempotency_key=idempotency_key
        ),
        name='process-payment'
    )
    
    return {'statusCode': 200, 'payment': payment}
```

------

실행 모드를 `AT_MOST_ONCE_PER_RETRY`로 설정하여 최대 1회 실행 시맨틱을 사용하도록 단계를 구성할 수 있습니다. 이를 통해 재시도당 최대 1회 단계가 실행되지만 단계가 완료되기 전에 함수가 중단되면 전혀 실행되지 않을 수 있습니다.

SDK는 재생 중에 해당 단계 이름과 순서가 체크포인트 로그와 일치하는지 확인하여 결정론적 재생을 적용합니다. 코드가 다른 순서로 또는 이름이 다른 단계를 실행하려고 하면 SDK에서 `NonDeterministicExecutionError`가 발생합니다.

**다음은 완료된 단계에서 재생이 작동하는 방식입니다.**

1. 첫 번째 간접 호출: 함수가 A 단계를 실행하고, 체크포인트를 생성한 다음 대기합니다.

1. 두 번째 간접 호출(대기 후): 함수가 처음부터 다시 재생되고, A 단계는 다시 실행하지 않고 체크포인트가 지정된 결과를 즉시 반환한 다음 B 단계를 계속합니다.

1. 세 번째 간접 호출(추가 대기 후): 함수가 처음부터 다시 재생되고, A 단계와 B 단계가 체크포인트가 지정된 결과를 즉시 반환한 다음 C 단계를 계속합니다.

이 재생 메커니즘을 사용하면 완료된 단계가 다시 실행되지 않지만 완료 전에 재시도를 처리하려면 비즈니스 로직이 여전히 멱등성이어야 합니다.

# Lambda 지속성 함수 테스트
<a name="durable-testing"></a>

AWS는 로컬 및 클라우드 모두에서 실행을 실행하고 검사할 수 있는 지속성 함수에 대한 전용 테스트 SDK를 제공합니다. 해당 언어에 맞는 테스트 SDK를 설치합니다.

------
#### [ TypeScript ]

```
npm install --save-dev @aws/aws-durable-execution-sdk-js-testing
```

전체 설명서 및 예제는 GitHub의 [TypeScript testing SDK](https://github.com/aws/aws-durable-execution-sdk-js/tree/development/packages/aws-durable-execution-sdk-js-testing)를 참조하세요.

------
#### [ Python ]

```
pip install aws-durable-execution-sdk-python-testing
```

전체 설명서 및 예제는 GitHub의 [Python testing SDK](https://github.com/aws/aws-durable-execution-sdk-python-testing)를 참조하세요.

------

테스트 SDK는 빠른 유닛 테스트를 위한 로컬 테스트와 배포된 함수에 대한 통합 테스트를 위한 클라우드 테스트라는 2가지 테스트 모드를 제공합니다.

## 로컬 테스트
<a name="durable-local-testing"></a>

로컬 테스트는 배포된 리소스 없이 개발 환경에서 지속성 함수를 실행합니다. 테스트 실행기는 함수 코드를 직접 실행하고 검사를 위해 모든 작업을 캡처합니다.

유닛 테스트, 테스트 기반 개발 및 CI/CD 파이프라인에 로컬 테스트를 사용합니다. 테스트는 네트워크 지연 시간이나 추가 비용 없이 로컬에서 실행됩니다.

**다음은 테스트 예시입니다.**

------
#### [ TypeScript ]

```
import { withDurableExecution } from '@aws/aws-durable-execution-sdk-js';
import { DurableFunctionTestRunner } from '@aws/aws-durable-execution-sdk-js-testing';

const handler = withDurableExecution(async (event, context) => {
  const result = await context.step('calculate', async () => {
    return event.a + event.b;
  });
  return result;
});

test('addition works correctly', async () => {
  const runner = new DurableFunctionTestRunner({ handler });
  const result = await runner.run({ a: 5, b: 3 });
  
  expect(result.status).toBe('SUCCEEDED');
  expect(result.result).toBe(8);
  
  const step = result.getStep('calculate');
  expect(step.result).toBe(8);
});
```

------
#### [ Python ]

```
from aws_durable_execution_sdk_python import durable_execution, DurableContext
from aws_durable_execution_sdk_python_testing import DurableFunctionTestRunner
from aws_durable_execution_sdk_python.execution import InvocationStatus

@durable_execution
def handler(event: dict, context: DurableContext) -> int:
    result = context.step(lambda _: event["a"] + event["b"], name="calculate")
    return result

def test_addition():
    runner = DurableFunctionTestRunner(handler=handler)
    with runner:
        result = runner.run(input={"a": 5, "b": 3}, timeout=10)
    
    assert result.status is InvocationStatus.SUCCEEDED
    assert result.result == 8
    
    step = result.get_step("calculate")
    assert step.result == 8
```

------

테스트 실행기는 최종 결과, 개별 단계 결과, 대기 작업, 콜백 및 오류를 포함한 실행 상태를 캡처합니다. 이름으로 작업을 검사하거나 모든 작업을 반복하여 실행 동작을 확인할 수 있습니다.

### 실행 스토어
<a name="durable-execution-stores"></a>

테스트 SDK는 실행 스토어를 사용하여 테스트 실행 데이터를 유지합니다. 기본적으로 테스트는 빠른 메모리 내 스토어를 사용하므로 정리가 필요하지 않습니다. 실행 내역을 디버깅 또는 분석하려면 실행을 JSON 파일로 저장하는 파일 시스템 스토어를 사용할 수 있습니다.

**메모리 내 스토어(기본값):**

메모리 내 스토어는 테스트 실행 중에 실행 데이터를 메모리에 보관합니다. 테스트가 완료되면 데이터가 손실되기에 테스트 완료 후 실행을 검사할 필요가 없는 표준 유닛 테스트 및 CI/CD 파이프라인에 적합합니다.

**파일 시스템 스토어:**

파일 시스템 스토어는 실행 데이터를 JSON 파일로 디스크에 유지합니다. 각 실행은 별도의 파일에 저장되므로 테스트 완료 후 실행 내역을 쉽게 검사할 수 있습니다. 복잡한 테스트 실패를 디버깅하거나 시간 경과에 따른 실행 패턴을 분석할 때 파일 시스템 스토어를 사용합니다.

환경 변수를 사용하여 스토어를 구성합니다.

```
# Use filesystem store
export AWS_DEX_STORE_TYPE=filesystem
export AWS_DEX_STORE_PATH=./test-executions

# Run tests
pytest tests/
```

실행 파일은 이름이 삭제된 상태로 저장되고 작업, 체크포인트 및 결과를 포함한 전체 실행 상태를 포함합니다. 파일 시스템 스토어는 스토리지 디렉터리가 없는 경우 스토리지 디렉터리를 자동으로 생성합니다.

## 클라우드 테스트
<a name="durable-cloud-testing"></a>

클라우드 테스트는 AWS에서 배포된 지속성 함수를 간접 호출하고 Lambda API를 사용하여 실행 내역을 검색합니다. 클라우드 테스트를 사용하여 실제 AWS 서비스 및 구성을 통해 프로덕션과 유사한 환경에서의 동작을 확인합니다.

클라우드 테스트에는 배포된 함수와 함수를 간접 호출하고 실행 내역을 읽을 수 있는 권한이 있는 AWS 자격 증명이 필요합니다.

```
{
    "Version": "2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "lambda:InvokeFunction",
                "lambda:GetDurableExecution",
                "lambda:GetDurableExecutionHistory"
            ],
            "Resource": "arn:aws:lambda:region:account-id:function:function-name"
        }
    ]
}
```

**다음은 클라우드 테스트 예시입니다.**

------
#### [ TypeScript ]

```
import { DurableFunctionCloudTestRunner } from '@aws/aws-durable-execution-sdk-js-testing';

test('deployed function processes orders', async () => {
  const runner = new DurableFunctionCloudTestRunner({
    functionName: 'order-processor',
    region: 'us-east-1'
  });
  
  const result = await runner.run({ orderId: 'order-123' });
  
  expect(result.status).toBe('SUCCEEDED');
  expect(result.result.status).toBe('completed');
});
```

------
#### [ Python ]

```
from aws_durable_execution_sdk_python_testing import (
    DurableFunctionCloudTestRunner,
    DurableFunctionCloudTestRunnerConfig
)

def test_deployed_function():
    config = DurableFunctionCloudTestRunnerConfig(
        function_name="order-processor",
        region="us-east-1"
    )
    runner = DurableFunctionCloudTestRunner(config=config)
    
    result = runner.run(input={"orderId": "order-123"})
    
    assert result.status is InvocationStatus.SUCCEEDED
    assert result.result["status"] == "completed"
```

------

클라우드 테스트는 실제 배포된 함수를 간접 호출하고 AWS에서 실행 내역을 검색합니다. 이를 통해 다른 AWS 서비스와의 통합을 확인하고, 성능 특성을 검증하고, 프로덕션과 유사한 데이터 및 구성을 활용하여 테스트할 수 있습니다.

## 테스트 대상
<a name="durable-testing-patterns"></a>

실행 결과, 작업 동작 및 오류 처리를 확인하여 지속성 함수를 테스트합니다. 구현 세부 정보가 아닌 비즈니스 로직 정확성에 중점을 둡니다.

**실행 결과 확인:** 함수가 주어진 입력에 대한 예상 값을 반환하는지 확인합니다. 성공적인 실행과 오류 사례를 모두 테스트하여 함수가 잘못된 입력을 적절하게 처리하는지 확인합니다.

**작업 실행 검사:** 단계, 대기 및 콜백이 예상대로 실행되는지 확인합니다. 단계 결과를 확인하여 중간 작업이 올바른 값을 생성하는지 확인합니다. 대기 작업이 적절한 제한 시간으로 구성되고 콜백이 올바른 설정으로 생성되었는지 확인합니다.

**테스트 오류 처리:** 잘못된 입력이 제공되면 함수가 오류를 설명하는 메시지와 함께 올바르게 실패하는지 확인합니다. 일시적인 실패를 시뮬레이션하고 작업을 적절하게 재시도하여 재시도 동작을 테스트합니다. 영구 실패가 불필요한 재시도를 트리거하지 않는지 확인합니다.

**워크플로 검증:** 다단계 워크플로의 경우 작업이 올바른 순서로 실행되는지 확인합니다. 조건부 분기를 테스트하여 다양한 실행 경로가 올바르게 작동하는지 확인합니다. 병렬 작업의 동시 실행 여부를 확인하고 예상 결과를 생성합니다.

SDK 설명서 리포지토리에는 다단계 워크플로, 오류 시나리오, 제한 시간 처리 및 폴링 패턴을 포함한 테스트 패턴의 광범위한 예제가 포함되어 있습니다.

## 테스트 전략
<a name="durable-testing-strategy"></a>

개발 중 유닛 테스트와 CI/CD 파이프라인에는 로컬 테스트를 사용합니다. 로컬 테스트는 빠르게 실행되고, AWS 자격 증명이 필요하지 않으며, 코드 변경과 관련된 즉각적인 피드백이 제공됩니다. 로컬 테스트를 작성하여 비즈니스 로직, 오류 처리 및 운영 동작을 확인합니다.

프로덕션에 배포하기 전에 통합 테스트에 클라우드 테스트를 사용합니다. 클라우드 테스트는 실제 AWS 서비스 및 구성으로 동작을 확인하고, 성능 특성을 검증하고, 전체적인 워크플로를 테스트합니다. 스테이징 환경에서 클라우드 테스트를 실행하여 프로덕션에 도달하기 전에 통합 문제를 파악합니다.

로컬 테스트에서 외부 종속성을 모방하여 함수 로직을 격리하고 테스트를 빠르게 유지합니다. 클라우드 테스트를 사용하여 데이터베이스, API 및 기타 AWS 서비스와 같은 외부 서비스와의 실제 통합을 확인합니다.

하나의 특정 동작을 확인하는 집중 테스트를 작성합니다. 테스트 대상을 설명하는 테스트 이름을 사용합니다. 관련 테스트를 그룹화하고 공통 설정 코드에 테스트 픽스처를 사용합니다. 테스트를 단순하게 유지하고 이해하기 어려운 복잡한 테스트 로직을 지양합니다.

## 실패 디버깅
<a name="durable-testing-debugging"></a>

테스트 실패 시 실행 결과를 검사하여 무엇이 잘못되었는지 파악합니다. 실행 상태를 확인하여 함수가 성공, 실패 또는 제한 시간 초과되었는지 여부를 확인합니다. 오류 메시지를 읽고 실패 원인을 파악합니다.

개별 작업 결과를 검사하여 동작이 예상과 다른 위치를 찾아냅니다. 단계 결과를 확인하여 생성된 값을 파악합니다. 작업 순서를 확인하여 예상 순서로 실행된 작업을 확인합니다. 작업 수를 계산하여 적절한 수의 단계, 대기 및 콜백이 생성되었는지 확인합니다.

공통의 문제로는 재생 시 다양한 결과를 생성하는 비결정론적 코드, 재생 도중 중단되는 전역 변수를 통한 공유 상태, 조건부 로직 오류로 인한 작업 누락 등이 있습니다. 표준 디버거 및 로깅을 사용하여 함수 코드를 단계별로 파악하고 실행 흐름을 추적합니다.

클라우드 테스트의 경우 CloudWatch Logs의 실행 내역을 검사하여 상세 작업 로그를 확인합니다. 트레이스를 사용하여 서비스 전반의 실행 흐름을 추적하고 병목 현상을 식별합니다.

# 지속성 함수 모니터링
<a name="durable-monitoring"></a>

CloudWatch 지표, CloudWatch Logs 및 추적을 사용하여 지속성 함수를 모니터링할 수 있습니다. 지속성 함수는 장기간 실행되고 여러 개의 함수 간접 호출에 걸칠 수 있으므로 이를 모니터링하려면 체크포인트, 상태 전환 및 재생 동작을 비롯한 고유한 실행 패턴을 이해해야 합니다.

## CloudWatch 지표
<a name="durable-monitoring-metrics"></a>

Lambda는 추가 비용 없이 CloudWatch에 지표를 자동으로 게시합니다. 지속성 함수는 표준 Lambda 지표 이외의 추가 지표를 제공하여 장기 실행 워크플로, 상태 관리 및 리소스 사용률을 모니터링하는 데 도움이 됩니다.

### 지속성 실행 지표
<a name="durable-monitoring-execution-metrics"></a>

Lambda는 지속성 실행에 대해 다음 지표를 내보냅니다.


| 지표 | 설명 | 
| --- | --- | 
| ApproximateRunningDurableExecutions | RUNNING 상태의 지속성 실행 수 | 
| ApproximateRunningDurableExecutionsUtilization | 현재 사용 중인 계정의 실행 중인 지속성 실행 할당량의 최대 비율 | 
| DurableExecutionDuration | 지속성 실행이 RUNNING 상태로 유지되는 밀리초 단위의 시계 | 
| DurableExecutionStarted | 시작된 지속성 실행의 수 | 
| DurableExecutionStopped | StopDurableExecution API를 사용하여 중지된 지속성 실행의 수 | 
| DurableExecutionSucceeded | 성공적으로 완료된 지속성 실행의 수 | 
| DurableExecutionFailed | 완료되었지만 실패가 발생한 지속성 실행의 수 | 
| DurableExecutionTimedOut | 구성된 실행 제한 시간을 초과한 지속성 실행의 수 | 
| DurableExecutionOperations | 지속성 있는 실행 내에서 수행한 누적 작업 수(최대: 3,000) | 
| DurableExecutionStorageWrittenBytes | 지속성 실행으로 인한 지속 누적 데이터의 양(최대: 100MB) | 

### CloudWatch 지표
<a name="durable-monitoring-standard-metrics"></a>

Lambda는 지속성 함수에 대한 표준 간접 호출, 성능 및 동시성 지표를 내보냅니다. 지속성 실행은 체크포인트 및 재생을 따라 진행되는 가운데 여러 개의 함수 간접 호출에 걸칠 수 있으므로 이러한 지표는 표준 함수와 작동 방식이 다릅니다.
+ **간접 호출:** 재생을 포함하여 각 함수 간접 호출을 계산합니다. 지속성 단일 실행으로 여러 개의 간접 호출 데이터 포인트가 생성될 수 있습니다.
+ **기간:** 각 함수 간접 호출을 개별적으로 측정합니다. 단일 지속성 실행에 걸리는 총 시간에 `DurableExecutionDuration`을 사용합니다.
+ **오류:** 함수 간접 호출 실패를 추적합니다. 실행 수준 실패에 `DurableExecutionFailed`를 사용합니다.

표준 Lambda 지표의 전체 목록은 [Lambda 함수에 대한 지표 유형](https://docs.aws.amazon.com//lambda/latest/dg/monitoring-metrics-types.html)을 참조하세요.

### CloudWatch 경보 생성
<a name="durable-monitoring-alarms"></a>

지표가 임계값을 초과할 때 알림을 보내는 CloudWatch 경보를 생성합니다. 다음은 일반적인 경보입니다.
+ `ApproximateRunningDurableExecutionsUtilization` - 할당량의 80% 초과
+ `DurableExecutionFailed` - 임계값 이상으로 증가
+ `DurableExecutionTimedOut` - 실행 제한 시간 초과
+ `DurableExecutionStorageWrittenBytes` - 스토리지 제한에 근접

자세한 내용은 [CloudWatch 경보 사용](https://docs.aws.amazon.com//AmazonCloudWatch/latest/monitoring/AlarmThatSendsEmail.html)을 참조하세요.

## EventBridge 이벤트
<a name="durable-monitoring-eventbridge"></a>

Lambda는 EventBridge에 지속성 실행 상태 변경 이벤트를 게시합니다. 이러한 이벤트를 사용하여 지속성 함수에서 워크플로를 트리거하거나, 알림을 보내거나, 실행 수명 주기 변경을 추적할 수 있습니다.

### 지속성 실행 상태 변경 이벤트
<a name="durable-eventbridge-status-changes"></a>

Lambda는 지속성 실행 상태가 변경될 때마다 EventBridge에 이벤트를 내보냅니다. 이러한 로그 파일의 특성은 다음과 같습니다.
+ **소스:** `aws.lambda`
+ **세부 정보 유형:** `Durable Execution Status Change`

상태 변경 이벤트는 다음 실행 상태에 대해 게시됩니다.
+ `RUNNING` - 실행 시작
+ `SUCCEEDED` - 성공적인 실행 완료
+ `STOPPED` - StopDurableExecution API를 사용하여 실행 중지
+ `FAILED` - 실행이 실패하고 오류 발생
+ `TIMED_OUT` - 실행이 구성된 제한 시간 초과

다음 예제에서는 지속성 실행 상태 변경 이벤트를 보여줍니다.

```
{
  "version": "0",
  "id": "d019b03c-a8a3-9d58-85de-241e96206538",
  "detail-type": "Durable Execution Status Change",
  "source": "aws.lambda",
  "account": "123456789012",
  "time": "2025-11-20T13:08:22Z",
  "region": "us-east-1",
  "resources": [],
  "detail": {
    "durableExecutionArn": "arn:aws:lambda:us-east-1:123456789012:function:my-function:$LATEST/durable-execution/090c4189-b18b-4296-9d0c-cfd01dc3a122/9f7d84c9-ea3d-3ffc-b3e5-5ec51c34ffc9",
    "durableExecutionName": "order-123",
    "functionArn": "arn:aws:lambda:us-east-1:123456789012:function:my-function:2",
    "status": "RUNNING",
    "startTimestamp": "2025-11-20T13:08:22.345Z"
  }
}
```

터미널 상태(`SUCCEEDED`, `STOPPED`, `FAILED`, `TIMED_OUT`)의 경우 이벤트에는 실행이 완료된 시간을 나타내는 `endTimestamp` 필드가 포함됩니다.

### EventBridge 규칙 생성
<a name="durable-eventbridge-rules"></a>

지속성 실행 상태 변경 이벤트를 Amazon Simple Notification Service, Amazon Simple Queue Service 또는 기타 Lambda 함수와 같은 대상으로 라우팅하는 규칙을 생성합니다.

다음 예제에서는 모든 지속성 실행 상태 변경과 일치하는 규칙을 생성합니다.

```
{
  "source": ["aws.lambda"],
  "detail-type": ["Durable Execution Status Change"]
}
```

다음 예제에서는 실패한 실행과만 일치하는 규칙을 생성합니다.

```
{
  "source": ["aws.lambda"],
  "detail-type": ["Durable Execution Status Change"],
  "detail": {
    "status": ["FAILED"]
  }
}
```

다음 예제에서는 특정 함수의 상태 변경과 일치하는 규칙을 생성합니다.

```
{
  "source": ["aws.lambda"],
  "detail-type": ["Durable Execution Status Change"],
  "detail": {
    "functionArn": [{
      "prefix": "arn:aws:lambda:us-east-1:123456789012:function:my-function"
    }]
  }
}
```

규칙 생성에 대한 자세한 내용은 EventBridge 사용 설명서의 [Amazon EventBridge 자습서](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-tutorial.html)를 참조하세요.

## AWS X-Ray 추적
<a name="durable-monitoring-xray"></a>

지속성 함수에서 X-Ray 추적을 활성화할 수 있습니다. Lambda는 X-Ray 트레이스 헤더를 지속성 실행에 전달하고, 이를 통해 워크플로 전체에서 요청을 추적할 수 있습니다.

Lambda 콘솔을 사용하는 추적인 X-Ray를 활성화하려면 함수를 선택하고, 구성, 모니터링 및 작업 도구를 선택하고, X-Ray에서 활성 추적을 켭니다.

AWS CLI를 사용하여 X-Ray 추적을 활성화합니다.

```
aws lambda update-function-configuration \
    --function-name my-durable-function \
    --tracing-config Mode=Active
```

AWS SAM을 사용하여 AWS X-Ray 추적을 활성화합니다.

```
Resources:
  MyDurableFunction:
    Type: AWS::Serverless::Function
    Properties:
      Tracing: Active
      DurableConfig:
        ExecutionTimeout: 3600
```

X-Ray에 대한 자세한 내용은 [AWS X-Ray 개발자 안내서를 참조하세요.](https://docs.aws.amazon.com//xray/latest/devguide/aws-xray.html)

# Lambda 지속성 함수 모범 사례
<a name="durable-best-practices"></a>

지속성 함수는 기존 Lambda 함수와 다른 패턴이 필요한 재생 기반 실행 모델을 사용합니다. 다음 모범 사례에 따라 안정적이면서 비용 효율적인 워크플로를 빌드합니다.

## 결정론적 코드 작성
<a name="durable-determinism"></a>

재생 도중에 함수는 처음부터 실행되며 원래 실행과 동일한 실행 경로를 따라야 합니다. 지속성 작업 외부의 코드는 결정론적이어야 하며, 동일한 입력이 주어지면 동일한 결과를 생성해야 합니다.

**비결정론적 작업을 단계로 래핑합니다.**
+ 난수 생성 및 UUID
+ 현재 시간 또는 타임스탬프
+ 외부 API 직접 호출 및 데이터베이스 쿼리
+ 파일 시스템 작업

------
#### [ TypeScript ]

```
import { withDurableExecution, DurableContext } from '@aws/durable-execution-sdk-js';
import { randomUUID } from 'crypto';

export const handler = withDurableExecution(
  async (event: any, context: DurableContext) => {
    // Generate transaction ID inside a step
    const transactionId = await context.step('generate-transaction-id', async () => {
      return randomUUID();
    });
    
    // Use the same ID throughout execution, even during replay
    const payment = await context.step('process-payment', async () => {
      return processPayment(event.amount, transactionId);
    });
    
    return { statusCode: 200, transactionId, payment };
  }
);
```

------
#### [ Python ]

```
from aws_durable_execution_sdk_python import durable_execution, DurableContext
import uuid

@durable_execution
def handler(event, context: DurableContext):
    # Generate transaction ID inside a step
    transaction_id = context.step(
        lambda _: str(uuid.uuid4()),
        name='generate-transaction-id'
    )
    
    # Use the same ID throughout execution, even during replay
    payment = context.step(
        lambda _: process_payment(event['amount'], transaction_id),
        name='process-payment'
    )
    
    return {'statusCode': 200, 'transactionId': transaction_id, 'payment': payment}
```

------

**중요**  
전역 변수 또는 클로저를 사용하여 단계 간에 상태를 공유하지 말고, 반환 값을 통해 데이터를 전달해야 합니다. 단계가 캐시된 결과를 반환하지만 전역 변수가 재설정되므로 재생 도중 전역 상태가 중단됩니다.

**클로저 변형 지양:** 클로저에 캡처된 변수는 재생 도중 변형(mutation)이 손실될 수 있습니다. 단계는 캐시된 결과를 반환하지만 단계 외부의 변수 업데이트는 재생되지 않습니다.

------
#### [ TypeScript ]

```
// ❌ WRONG: Mutations lost on replay
export const handler = withDurableExecution(async (event, context) => {
  let total = 0;
  
  for (const item of items) {
    await context.step(async () => {
      total += item.price; // ⚠️ Mutation lost on replay!
      return saveItem(item);
    });
  }
  
  return { total }; // Inconsistent value!
});

// ✅ CORRECT: Accumulate with return values
export const handler = withDurableExecution(async (event, context) => {
  let total = 0;
  
  for (const item of items) {
    total = await context.step(async () => {
      const newTotal = total + item.price;
      await saveItem(item);
      return newTotal; // Return updated value
    });
  }
  
  return { total }; // Consistent!
});

// ✅ EVEN BETTER: Use map for parallel processing
export const handler = withDurableExecution(async (event, context) => {
  const results = await context.map(
    items,
    async (ctx, item) => {
      await ctx.step(async () => saveItem(item));
      return item.price;
    }
  );
  
  const total = results.getResults().reduce((sum, price) => sum + price, 0);
  return { total };
});
```

------
#### [ Python ]

```
# ❌ WRONG: Mutations lost on replay
@durable_execution
def handler(event, context: DurableContext):
    total = 0
    
    for item in items:
        context.step(
            lambda _: save_item_and_mutate(item, total),  # ⚠️ Mutation lost on replay!
            name=f'save-item-{item["id"]}'
        )
    
    return {'total': total}  # Inconsistent value!

# ✅ CORRECT: Accumulate with return values
@durable_execution
def handler(event, context: DurableContext):
    total = 0
    
    for item in items:
        total = context.step(
            lambda _: save_item_and_return_total(item, total),
            name=f'save-item-{item["id"]}'
        )
    
    return {'total': total}  # Consistent!

# ✅ EVEN BETTER: Use map for parallel processing
@durable_execution
def handler(event, context: DurableContext):
    def process_item(ctx, item):
        ctx.step(lambda _: save_item(item))
        return item['price']
    
    results = context.map(items, process_item)
    total = sum(results.get_results())
    
    return {'total': total}
```

------

## 멱등성을 고려한 설계
<a name="durable-idempotency"></a>

재시도 또는 재생으로 인해 작업이 여러 번 실행될 수 있습니다. 비멱등성 작업으로 인해 고객에게 두 번 요금을 부과하거나 여러 번 이메일을 보내는 등의 중복과 관련된 부작용이 발생합니다.

**멱등성 토큰 사용:** 단계 내에 토큰을 생성하고 외부 API 직접 호출에 포함하여 중복 작업을 방지합니다.

------
#### [ TypeScript ]

```
import { withDurableExecution, DurableContext } from '@aws/durable-execution-sdk-js';

export const handler = withDurableExecution(
  async (event: any, context: DurableContext) => {
    // Generate idempotency token once
    const idempotencyToken = await context.step('generate-idempotency-token', async () => {
      return crypto.randomUUID();
    });
    
    // Use token to prevent duplicate charges
    const charge = await context.step('charge-payment', async () => {
      return paymentService.charge({
        amount: event.amount,
        cardToken: event.cardToken,
        idempotencyKey: idempotencyToken
      });
    });
    
    return { statusCode: 200, charge };
  }
);
```

------
#### [ Python ]

```
from aws_durable_execution_sdk_python import durable_execution, DurableContext
import uuid

@durable_execution
def handler(event, context: DurableContext):
    # Generate idempotency token once
    idempotency_token = context.step(
        lambda _: str(uuid.uuid4()),
        name='generate-idempotency-token'
    )
    
    # Use token to prevent duplicate charges
    def charge_payment(_):
        return payment_service.charge(
            amount=event['amount'],
            card_token=event['cardToken'],
            idempotency_key=idempotency_token
        )
    
    charge = context.step(charge_payment, name='charge-payment')
    
    return {'statusCode': 200, 'charge': charge}
```

------

**최대 1회 시맨틱 사용:** 복제해서는 안 되는 중요 작업(재무 거래, 재고 차감)의 경우 최대 1회 실행 모드를 구성합니다.

------
#### [ TypeScript ]

```
// Critical operation that must not duplicate
await context.step('deduct-inventory', async () => {
  return inventoryService.deduct(event.productId, event.quantity);
}, {
  executionMode: 'AT_MOST_ONCE_PER_RETRY'
});
```

------
#### [ Python ]

```
# Critical operation that must not duplicate
context.step(
    lambda _: inventory_service.deduct(event['productId'], event['quantity']),
    name='deduct-inventory',
    config=StepConfig(execution_mode='AT_MOST_ONCE_PER_RETRY')
)
```

------

**데이터베이스 멱등성:** check-before-write 패턴, 조건부 업데이트 또는 업서트 작업을 사용하여 레코드 중복을 방지합니다.

## 효율적으로 상태 관리
<a name="durable-state-management"></a>

모든 체크포인트는 영구 스토리지에 상태를 저장합니다. 상태 객체의 크기가 크면 비용이 증가하고 체크포인트 속도가 느려지며 성능에 영향을 미칩니다. 필수 워크플로 조정 데이터만 저장해야 합니다.

**상태를 최소한으로 유지합니다.**
+ 전체 객체가 아닌 ID와 참조 저장
+ 필요에 따라 단계 내에서 세부 데이터 가져오기
+ 대용량 데이터에 Amazon S3 또는 DynamoDB 사용, 상태의 참조 전달
+ 단계 사이에 큰 페이로드 전달 방지

------
#### [ TypeScript ]

```
import { withDurableExecution, DurableContext } from '@aws/durable-execution-sdk-js';

export const handler = withDurableExecution(
  async (event: any, context: DurableContext) => {
    // Store only the order ID, not the full order object
    const orderId = event.orderId;
    
    // Fetch data within each step as needed
    await context.step('validate-order', async () => {
      const order = await orderService.getOrder(orderId);
      return validateOrder(order);
    });
    
    await context.step('process-payment', async () => {
      const order = await orderService.getOrder(orderId);
      return processPayment(order);
    });
    
    return { statusCode: 200, orderId };
  }
);
```

------
#### [ Python ]

```
from aws_durable_execution_sdk_python import durable_execution, DurableContext

@durable_execution
def handler(event, context: DurableContext):
    # Store only the order ID, not the full order object
    order_id = event['orderId']
    
    # Fetch data within each step as needed
    context.step(
        lambda _: validate_order(order_service.get_order(order_id)),
        name='validate-order'
    )
    
    context.step(
        lambda _: process_payment(order_service.get_order(order_id)),
        name='process-payment'
    )
    
    return {'statusCode': 200, 'orderId': order_id}
```

------

## 효과적인 단계 설계
<a name="durable-step-design"></a>

단계는 지속성 함수의 기본 작업 단위입니다. 잘 설계된 단계를 통해 워크플로를 더 쉽게 이해하고 디버깅하고 유지 관리할 수 있습니다.

**단계 설계 원칙:**
+ **설명이 포함된 이름 사용** - 로그와 오류를 쉽게 이해할 수 있도록 `step1` 대신 `validate-order` 같은 이름을 사용합니다.
+ **이름을 정적 상태로 유지** - 타임스탬프 또는 임의 값이 포함된 동적 이름은 사용하지 않습니다. 재생을 위해 단계 이름은 결정론적이어야 합니다.
+ **세분화 균형 조정** - 복잡한 작업을 보다 집중된 여러 단계로 나누되 체크포인트 오버헤드를 높일 수 있으므로 지나치게 작은 단계는 생성하지 않습니다.
+ **그룹 관련 작업** - 함께 성공 또는 실패해야 하는 작업은 동일한 단계에 속합니다.

## 효율적으로 대기 작업 사용
<a name="durable-wait-operations"></a>

대기 작업은 리소스를 소비하거나 비용을 발생시키지 않고 실행을 일시 중지합니다. Lambda를 계속 실행하는 대신 대기 작업을 사용하세요.

**시간 기반 대기:** `setTimeout` 또는 `sleep` 대신 지연에 `context.wait()`을 사용합니다.

**외부 콜백:** 외부 시스템을 기다릴 때 `context.waitForCallback()`을 사용합니다. 무기한 대기를 방지하려면 항상 제한 시간을 설정합니다.

**폴링:** 지수 백오프와 함께 `context.waitForCondition()`을 사용하여 과부하 없이 외부 서비스를 폴링합니다.

------
#### [ TypeScript ]

```
// Wait 24 hours without cost
await context.wait({ seconds: 86400 });

// Wait for external callback with timeout
const result = await context.waitForCallback(
  'external-job',
  async (callbackId) => {
    await externalService.submitJob({
      data: event.data,
      webhookUrl: `https://api.example.com/callbacks/${callbackId}`
    });
  },
  { timeout: { seconds: 3600 } }
);
```

------
#### [ Python ]

```
# Wait 24 hours without cost
context.wait(86400)

# Wait for external callback with timeout
result = context.wait_for_callback(
    lambda callback_id: external_service.submit_job(
        data=event['data'],
        webhook_url=f'https://api.example.com/callbacks/{callback_id}'
    ),
    name='external-job',
    config=WaitForCallbackConfig(timeout_seconds=3600)
)
```

------

## 추가 고려 사항
<a name="durable-additional-considerations"></a>

**오류 처리:** 네트워크 제한 시간 및 속도 제한과 같은 일시적 실패가 발생하면 재시도합니다. 잘못된 입력 또는 인증 오류와 같은 영구적 실패는 재시도하지 않습니다. 적절한 최대 시도 횟수 및 백오프 속도로 재시도 전략을 구성합니다. 자세한 예제는 [오류 처리 및 재시도](durable-execution-sdk-retries.md)를 참조하세요.

**성능:** 전체 페이로드 대신 참조를 저장하여 체크포인트 크기를 최소화합니다. `context.parallel()` 및 `context.map()`을 사용하여 독립적인 작업을 동시에 실행합니다. 관련 작업을 배치로 처리하여 체크포인트 오버헤드를 줄이세요.

**버전 관리:** 버전 번호 또는 별칭이 있는 함수를 간접 호출하여 실행을 특정 코드 버전에 고정합니다. 새 코드 버전이 이전 버전의 상태를 처리할 수 있어야 합니다. 재생이 중단되는 방식으로 단계의 이름을 바꾸거나 동작을 변경하지 않습니다.

**직렬화:** 작업 입력 및 결과에 JSON 호환 유형을 사용합니다. 지속성 작업에 전달하기 전에 날짜를 ISO 문자열로 변환하고 사용자 지정 객체를 일반 객체로 변환합니다.

**모니터링:** 실행 ID 및 단계 이름을 포함하여 구조화된 로깅을 활성화합니다. 오류 발생률 및 실행 기간에 대한 CloudWatch 경보를 설정합니다. 추적을 사용하여 병목 현상을 식별합니다. 자세한 지침은 [모니터링 및 디버깅](durable-monitoring.md)을 참조하세요.

**테스트:** 해피 패스, 오류 처리 및 재생 동작을 테스트합니다. 콜백 및 대기에 대한 제한 시간 시나리오를 테스트합니다. 로컬 테스트를 사용하여 반복 시간을 줄입니다. 자세한 지침은 [지속성 함수 테스트](durable-testing.md)를 참조하세요.

**피해야 할 일반적인 실수:** `context.step()` 호출을 중첩하지 않고, 대신 하위 컨텍스트를 사용합니다. 비결정론적 작업을 단계로 래핑합니다. 항상 콜백에 대한 제한 시간을 설정합니다. 단계 세분성과 체크포인트 오버헤드 사이의 균형을 맞춥니다. 큰 객체 대신 참조를 상태로 저장합니다.

## 추가 리소스
<a name="durable-additional-resources"></a>
+ [Python SDK 설명서](https://github.com/aws/aws-durable-execution-sdk-python/tree/main/docs) - 전체 API 참조, 테스트 패턴 및 고급 예제
+ [TypeScript SDK 설명서](https://github.com/aws/aws-durable-execution-sdk-js/tree/main/docs) - 전체 API 참조, 테스트 패턴 및 고급 예제