

# S3 객체 Lambda 액세스 포인트에 대한 Lambda 함수 작성
<a name="olap-writing-lambda"></a>

**참고**  
2025년 11월 7일부터 S3 Object Lambda는 현재 서비스를 사용하고 있는 기존 고객과 AWS 파트너 네트워크(APN) 파트너를 선택하는 고객만 사용할 수 있습니다. S3 Object Lambda와 유사한 기능의 경우 [Amazon S3 Object Lambda 가용성 변경](https://docs.aws.amazon.com/AmazonS3/latest/userguide/amazons3-ol-change.html) 섹션에서 자세히 알아보세요.

이 섹션에서는 Amazon S3 객체 Lambda 액세스 포인트에 사용할 AWS Lambda 함수를 작성하는 방법에 대해 자세히 설명합니다.

일부 S3 객체 Lambda 태스크 절차를 처음부터 끝까지 완전히 알아보려면 다음을 참조하십시오.
+ [자습서: S3 객체 Lambda를 사용하여 애플리케이션의 데이터 변환](tutorial-s3-object-lambda-uppercase.md)
+ [자습서: S3 객체 Lambda 및 Amazon Comprehend를 사용하여 PII 데이터 감지 및 수정](tutorial-s3-object-lambda-redact-pii.md)
+ [Tutorial: Using S3 Object Lambda to dynamically watermark images as they are retrieved(자습서: S3 객체 Lambda를 사용하여 이미지를 검색할 때 동적으로 워터마크 지정](https://aws.amazon.com/getting-started/hands-on/amazon-s3-object-lambda-to-dynamically-watermark-images/?ref=docs_gateway/amazons3/olap-writing-lambda.html)

**Topics**
+ [Lambda에서 `GetObject` 요청 작업](#olap-getobject-response)
+ [Lambda에서 `HeadObject` 요청 작업](#olap-headobject)
+ [Lambda에서 `ListObjects` 요청 작업](#olap-listobjects)
+ [Lambda에서 `ListObjectsV2` 요청 작업](#olap-listobjectsv2)
+ [이벤트 컨텍스트 형식 및 사용법](olap-event-context.md)
+ [Range 및 partNumber 헤더 작업](range-get-olap.md)

## Lambda에서 `GetObject` 요청 작업
<a name="olap-getobject-response"></a>

이 섹션에서는 객체 Lambda 액세스 포인트가 `GetObject`에 대한 Lambda 함수를 호출하도록 구성되어 있다고 가정합니다. S3 객체 Lambda에는 Lambda 함수에서 `GetObject` 호출자에게 사용자 지정된 데이터 및 응답 헤더를 제공할 수 있는 Amazon S3 API 작업인 `WriteGetObjectResponse`가 포함되어 있습니다.

`WriteGetObjectResponse`는 처리 요구 사항에 따라 상태 코드, 응답 헤더 및 응답 본문에 대한 광범위한 제어 기능을 제공합니다. `WriteGetObjectResponse`를 사용하면 변환된 전체 객체, 변환된 객체의 일부 또는 애플리케이션의 컨텍스트에 따라 다른 응답으로 응답할 수 있습니다. 다음 섹션에서는 `WriteGetObjectResponse` API 작업을 사용하는 고유한 예를 보여줍니다.
+ **예시 1:** HTTP 상태 코드 403(금지)으로 응답 
+ **예제 2:** 변환된 이미지로 응답
+ **예제 3:** 압축된 콘텐츠 스트리밍

**예시 1: HTTP 상태 코드 403(금지)으로 응답**

`WriteGetObjectResponse`를 사용하면 객체의 콘텐츠를 기반으로 HTTP 상태 코드 403(금지)으로 응답할 수 있습니다.

------
#### [ Java ]



```
package com.amazon.s3.objectlambda;

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.events.S3ObjectLambdaEvent;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.WriteGetObjectResponseRequest;

import java.io.ByteArrayInputStream;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Example1 {

    public void handleRequest(S3ObjectLambdaEvent event, Context context) throws Exception {
        S3Client s3Client = S3Client.builder().build();

        // Check to see if the request contains all of the necessary information.
        // If it does not, send a 4XX response and a custom error code and message.
        // Otherwise, retrieve the object from S3 and stream it
        // to the client unchanged.
        var tokenIsNotPresent = !event.getUserRequest().getHeaders().containsKey("requiredToken");
        if (tokenIsNotPresent) {
            s3Client.writeGetObjectResponse(WriteGetObjectResponseRequest.builder()
                    .requestRoute(event.outputRoute())
                    .requestToken(event.outputToken())
                    .statusCode(403)
                    .contentLength(0L)
                    .errorCode("MissingRequiredToken")
                    .errorMessage("The required token was not present in the request.")
                    .build(),
                    RequestBody.fromInputStream(new ByteArrayInputStream(new byte[0]), 0L));
            return;
        }

        // Prepare the presigned URL for use and make the request to S3.
        HttpClient httpClient = HttpClient.newBuilder().build();
        var presignedResponse = httpClient.send(
                HttpRequest.newBuilder(new URI(event.inputS3Url())).GET().build(),
                HttpResponse.BodyHandlers.ofInputStream());

        // Stream the original bytes back to the caller.
        s3Client.writeGetObjectResponse(WriteGetObjectResponseRequest.builder()
                .requestRoute(event.outputRoute())
                .requestToken(event.outputToken())
                .build(),
                RequestBody.fromInputStream(presignedResponse.body(),
                    presignedResponse.headers().firstValueAsLong("content-length").orElse(-1L)));
    }
}
```

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



```
import boto3
import requests 

def handler(event, context):
    s3 = boto3.client('s3')

    """
    Retrieve the operation context object from the event. This object indicates where the WriteGetObjectResponse request
    should be delivered and contains a presigned URL in 'inputS3Url' where we can download the requested object from.
    The 'userRequest' object has information related to the user who made this 'GetObject' request to 
    S3 Object Lambda.
    """
    get_context = event["getObjectContext"]
    user_request_headers = event["userRequest"]["headers"]

    route = get_context["outputRoute"]
    token = get_context["outputToken"]
    s3_url = get_context["inputS3Url"]

    # Check for the presence of a 'CustomHeader' header and deny or allow based on that header.
    is_token_present = "SuperSecretToken" in user_request_headers

    if is_token_present:
        # If the user presented our custom 'SuperSecretToken' header, we send the requested object back to the user.
        response = requests.get(s3_url)
        s3.write_get_object_response(RequestRoute=route, RequestToken=token, Body=response.content)
    else:
        # If the token is not present, we send an error back to the user. 
        s3.write_get_object_response(RequestRoute=route, RequestToken=token, StatusCode=403,
        ErrorCode="NoSuperSecretTokenFound", ErrorMessage="The request was not secret enough.")

    # Gracefully exit the Lambda function.
    return { 'status_code': 200 }
```

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



```
const { S3 } = require('aws-sdk');
const axios = require('axios').default;

exports.handler = async (event) => {
    const s3 = new S3();

    // Retrieve the operation context object from the event. This object indicates where the WriteGetObjectResponse request
    // should be delivered and contains a presigned URL in 'inputS3Url' where we can download the requested object from.
    // The 'userRequest' object has information related to the user who made this 'GetObject' request to S3 Object Lambda.
    const { userRequest, getObjectContext } = event;
    const { outputRoute, outputToken, inputS3Url } = getObjectContext;

    // Check for the presence of a 'CustomHeader' header and deny or allow based on that header.
    const isTokenPresent = Object
        .keys(userRequest.headers)
        .includes("SuperSecretToken");

    if (!isTokenPresent) {
        // If the token is not present, we send an error back to the user. The 'await' in front of the request
        // indicates that we want to wait for this request to finish sending before moving on. 
        await s3.writeGetObjectResponse({
            RequestRoute: outputRoute,
            RequestToken: outputToken,
            StatusCode: 403,
            ErrorCode: "NoSuperSecretTokenFound",
            ErrorMessage: "The request was not secret enough.",
        }).promise();
    } else {
        // If the user presented our custom 'SuperSecretToken' header, we send the requested object back to the user.
        // Again, note the presence of 'await'.
        const presignedResponse = await axios.get(inputS3Url);
        await s3.writeGetObjectResponse({
            RequestRoute: outputRoute,
            RequestToken: outputToken,
            Body: presignedResponse.data,
        }).promise();
    }

    // Gracefully exit the Lambda function.
    return { statusCode: 200 };
}
```

------

**예제 2: 변환된 이미지로 응답**

이미지 변환을 수행할 때는 소스 객체의 전체 바이트가 준비되어야 처리를 시작할 수 있습니다. 이 경우 `WriteGetObjectResponse`는 요청한 애플리케이션에 전체 객체를 한 번의 호출로 반환합니다.

------
#### [ Java ]



```
package com.amazon.s3.objectlambda;

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.events.S3ObjectLambdaEvent;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.WriteGetObjectResponseRequest;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.awt.Image;
import java.io.ByteArrayOutputStream;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Example2V2 {

    private static final int HEIGHT = 250;
    private static final int WIDTH = 250;

    public void handleRequest(S3ObjectLambdaEvent event, Context context) throws Exception {
        S3Client s3Client = S3Client.builder().build();
        HttpClient httpClient = HttpClient.newBuilder().build();

        // Prepare the presigned URL for use and make the request to S3.
        var presignedResponse = httpClient.send(
                HttpRequest.newBuilder(new URI(event.inputS3Url())).GET().build(),
                HttpResponse.BodyHandlers.ofInputStream());

        // The entire image is loaded into memory here so that we can resize it.
        // Once the resizing is completed, we write the bytes into the body
        // of the WriteGetObjectResponse request.
        var originalImage = ImageIO.read(presignedResponse.body());
        var resizingImage = originalImage.getScaledInstance(WIDTH, HEIGHT, Image.SCALE_DEFAULT);
        var resizedImage = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
        resizedImage.createGraphics().drawImage(resizingImage, 0, 0, WIDTH, HEIGHT, null);

        var baos = new ByteArrayOutputStream();
        ImageIO.write(resizedImage, "png", baos);

        // Stream the bytes back to the caller.
        s3Client.writeGetObjectResponse(WriteGetObjectResponseRequest.builder()
                .requestRoute(event.outputRoute())
                .requestToken(event.outputToken())
                .build(), RequestBody.fromBytes(baos.toByteArray()));
    }
}
```

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



```
import boto3
import requests 
import io
from PIL import Image

def handler(event, context):
    """
    Retrieve the operation context object from the event. This object indicates where the WriteGetObjectResponse request
    should be delivered and has a presigned URL in 'inputS3Url' where we can download the requested object from.
    The 'userRequest' object has information related to the user who made this 'GetObject' request to 
    S3 Object Lambda.
    """
    get_context = event["getObjectContext"]
    route = get_context["outputRoute"]
    token = get_context["outputToken"]
    s3_url = get_context["inputS3Url"]

    """
    In this case, we're resizing .png images that are stored in S3 and are accessible through the presigned URL
    'inputS3Url'.
    """
    image_request = requests.get(s3_url)
    image = Image.open(io.BytesIO(image_request.content))
    image.thumbnail((256,256), Image.ANTIALIAS)

    transformed = io.BytesIO()
    image.save(transformed, "png")

    # Send the resized image back to the client.
    s3 = boto3.client('s3')
    s3.write_get_object_response(Body=transformed.getvalue(), RequestRoute=route, RequestToken=token)

    # Gracefully exit the Lambda function.
    return { 'status_code': 200 }
```

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



```
const { S3 } = require('aws-sdk');
const axios = require('axios').default;
const sharp = require('sharp');

exports.handler = async (event) => {
    const s3 = new S3();

    // Retrieve the operation context object from the event. This object indicates where the WriteGetObjectResponse request
    // should be delivered and has a presigned URL in 'inputS3Url' where we can download the requested object from.
    const { getObjectContext } = event;
    const { outputRoute, outputToken, inputS3Url } = getObjectContext;

    // In this case, we're resizing .png images that are stored in S3 and are accessible through the presigned URL
    // 'inputS3Url'.
    const { data } = await axios.get(inputS3Url, { responseType: 'arraybuffer' });

    // Resize the image.
    const resized = await sharp(data)
        .resize({ width: 256, height: 256 })
        .toBuffer();

    // Send the resized image back to the client.
    await s3.writeGetObjectResponse({
        RequestRoute: outputRoute,
        RequestToken: outputToken,
        Body: resized,
    }).promise();

    // Gracefully exit the Lambda function.
    return { statusCode: 200 };
}
```

------

**예제 3: 압축된 콘텐츠 스트리밍**

객체를 압축할 때 압축된 데이터는 점진적으로 생성됩니다. 따라서 `WriteGetObjectResponse` 요청을 사용하여 압축된 데이터가 준비되는 즉시 압축된 데이터를 반환할 수 있습니다. 이 예제에서 볼 수 있듯이, 완료된 변환의 길이를 알아야 할 필요가 없습니다.

------
#### [ Java ]



```
package com.amazon.s3.objectlambda;

import com.amazonaws.services.lambda.runtime.events.S3ObjectLambdaEvent;
import com.amazonaws.services.lambda.runtime.Context;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.WriteGetObjectResponseRequest;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Example3 {

    public void handleRequest(S3ObjectLambdaEvent event, Context context) throws Exception {
        S3Client s3Client = S3Client.builder().build();
        HttpClient httpClient = HttpClient.newBuilder().build();

        // Request the original object from S3.
        var presignedResponse = httpClient.send(
                HttpRequest.newBuilder(new URI(event.inputS3Url())).GET().build(),
                HttpResponse.BodyHandlers.ofInputStream());

        // Consume the incoming response body from the presigned request,
        // apply our transformation on that data, and emit the transformed bytes
        // into the body of the WriteGetObjectResponse request as soon as they're ready.
        // This example compresses the data from S3, but any processing pertinent
        // to your application can be performed here.
        var bodyStream = new GZIPCompressingInputStream(presignedResponse.body());

        // Stream the bytes back to the caller.
        s3Client.writeGetObjectResponse(WriteGetObjectResponseRequest.builder()
                .requestRoute(event.outputRoute())
                .requestToken(event.outputToken())
                .build(),
                RequestBody.fromInputStream(bodyStream,
                    presignedResponse.headers().firstValueAsLong("content-length").orElse(-1L)));
    }
}
```

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



```
import boto3
import requests
import zlib
from botocore.config import Config


"""
A helper class to work with content iterators. Takes an interator and compresses the bytes that come from it. It
implements 'read' and '__iter__' so that the SDK can stream the response. 
"""
class Compress:
    def __init__(self, content_iter):
        self.content = content_iter
        self.compressed_obj = zlib.compressobj()

    def read(self, _size):
        for data in self.__iter__()
            return data

    def __iter__(self):
        while True:
            data = next(self.content)
            chunk = self.compressed_obj.compress(data)
            if not chunk:
                break

            yield chunk

        yield self.compressed_obj.flush()


def handler(event, context):
    """
    Setting the 'payload_signing_enabled' property to False allows us to send a streamed response back to the client.
    in this scenario, a streamed response means that the bytes are not buffered into memory as we're compressing them,
    but instead are sent straight to the user.
    """
    my_config = Config(
        region_name='eu-west-1',
        signature_version='s3v4',
        s3={
            "payload_signing_enabled": False
        }
    )
    s3 = boto3.client('s3', config=my_config)

    """
    Retrieve the operation context object from the event. This object indicates where the WriteGetObjectResponse request
    should be delivered and has a presigned URL in 'inputS3Url' where we can download the requested object from.
    The 'userRequest' object has information related to the user who made this 'GetObject' request to S3 Object Lambda.
    """
    get_context = event["getObjectContext"]
    route = get_context["outputRoute"]
    token = get_context["outputToken"]
    s3_url = get_context["inputS3Url"]

    # Compress the 'get' request stream.
    with requests.get(s3_url, stream=True) as r:
        compressed = Compress(r.iter_content())

        # Send the stream back to the client.
        s3.write_get_object_response(Body=compressed, RequestRoute=route, RequestToken=token, ContentType="text/plain",
                                     ContentEncoding="gzip")

    # Gracefully exit the Lambda function.
    return {'status_code': 200}
```

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



```
const { S3 } = require('aws-sdk');
const axios = require('axios').default;
const zlib = require('zlib');

exports.handler = async (event) => {
    const s3 = new S3();

    // Retrieve the operation context object from the event. This object indicates where the WriteGetObjectResponse request
    // should be delivered and has a presigned URL in 'inputS3Url' where we can download the requested object from.
    const { getObjectContext } = event;
    const { outputRoute, outputToken, inputS3Url } = getObjectContext;

    // Download the object from S3 and process it as a stream, because it might be a huge object and we don't want to
    // buffer it in memory. Note the use of 'await' because we want to wait for 'writeGetObjectResponse' to finish 
    // before we can exit the Lambda function. 
    await axios({
        method: 'GET',
        url: inputS3Url,
        responseType: 'stream',
    }).then(
        // Gzip the stream.
        response => response.data.pipe(zlib.createGzip())
    ).then(
        // Finally send the gzip-ed stream back to the client.
        stream => s3.writeGetObjectResponse({
            RequestRoute: outputRoute,
            RequestToken: outputToken,
            Body: stream,
            ContentType: "text/plain",
            ContentEncoding: "gzip",
        }).promise()
    );

    // Gracefully exit the Lambda function.
    return { statusCode: 200 };
}
```

------

**참고**  
S3 객체 Lambda를 사용할 때는 `WriteGetObjectResponse` 요청을 통해 최대 60초간 호출자에게 전체 응답을 전송할 수 있지만 실제 사용 가능한 시간은 더 적을 수 있습니다. 예를 들어 Lambda 함수 제한 시간이 60초 미만일 수 있습니다. 호출자의 제한 시간이 더 엄격한 경우도 있을 수 있습니다.

원래 호출자가 HTTP 상태 코드500(내부 서버 오류) 외의 응답을 수신하려면 `WriteGetObjectResponse` 호출이 완료되어야 합니다. 예외나 다른 이유로 `WriteGetObjectResponse` API 작업이 호출되기 전에 Lambda 함수가 반환되면 원래 호출자가 500(내부 서버 오류) 응답을 수신합니다. 응답을 완료하는 데 걸리는 시간 동안 예외가 발생하면 호출자가 잘린 응답을 수신하게 됩니다. Lambda 함수가 `WriteGetObjectResponse` API 호출에서 HTTP 상태 코드 200(OK) 응답을 수신하는 경우 원래 호출자의 전체 요청이 전송된 것입니다. Lambda 함수의 응답은 예외가 발생했는지 여부에 관계없이 S3 객체 Lambda에 의해 무시됩니다.

`WriteGetObjectResponse` API 작업을 호출할 때 Amazon S3에는 이벤트 컨텍스트의 라우팅 및 요청 토큰이 필요합니다. 자세한 내용은 [이벤트 컨텍스트 형식 및 사용법](olap-event-context.md) 섹션을 참조하세요.

라우팅 및 요청 토큰 파라미터는 원래 호출자와 `WriteGetObjectResult` 응답을 연결하는 데 필요합니다. 500(내부 서버 오류) 응답이 수신되는 경우 다시 시도하는 것이 항상 적절하지만 요청 토큰은 1회 사용 토큰이기 때문에 이후 사용을 시도할 경우 HTTP 상태 코드 400(잘못된 요청) 응답이 발생할 수 있습니다. 라우팅 및 요청 토큰을 사용한 `WriteGetObjectResponse` 호출을 호출된 Lambda 함수에서 수행할 필요는 없지만 동일한 계정의 자격 증명으로 수행되어야 합니다. 또한 Lambda 함수가 실행을 완료하기 전에 호출이 완료되어야 합니다.

## Lambda에서 `HeadObject` 요청 작업
<a name="olap-headobject"></a>

이 섹션에서는 객체 Lambda 액세스 포인트가 `HeadObject`에 대한 Lambda 함수를 호출하도록 구성되어 있다고 가정합니다. Lambda는 `headObjectContext`라는 키가 포함된 JSON 페이로드를 수신합니다. 컨텍스트 내에는 `HeadObject`의 지원 액세스 지점에 대해 미리 서명된 URL인 `inputS3Url`이라는 하나의 속성이 있습니다.

미리 서명된 URL에는 지정된 경우 다음과 같은 속성이 포함됩니다.
+ `versionId`(쿼리 파라미터에 포함됨)
+ `requestPayer`(`x-amz-request-payer` 헤더에 포함됨)
+ `expectedBucketOwner`(`x-amz-expected-bucket-owner` 헤더에 포함됨)

다른 속성은 미리 서명되지 않으므로 포함되지 않습니다. `userRequest` 헤더에 없는 미리 서명된 URL을 호출할 때 헤더로 전송된 서명되지 않은 옵션을 요청에 수동으로 추가할 수 있습니다. 서버 측 암호화 옵션은 `HeadObject`에 지원되지 않습니다.

요청 구문 URI 파라미터는 Amazon Simple Storage Service API 참조의** [https://docs.aws.amazon.com/AmazonS3/latest/API/API_HeadObject.html](https://docs.aws.amazon.com/AmazonS3/latest/API/API_HeadObject.html) 섹션을 참조하세요.

다음 예는 `HeadObject`에 대한 Lambda JSON 입력 페이로드를 보여줍니다.

```
{
  "xAmzRequestId": "requestId",
  "**headObjectContext**": {
    "**inputS3Url**": "https://my-s3-ap-111122223333.s3-accesspoint.us-east-1.amazonaws.com/example?X-Amz-Security-Token=<snip>"
  },
  "configuration": {
       "accessPointArn": "arn:aws:s3-object-lambda:us-east-1:111122223333:accesspoint/example-object-lambda-ap",
       "supportingAccessPointArn": "arn:aws:s3:us-east-1:111122223333:accesspoint/example-ap",
       "payload": "{}"
  },
  "userRequest": {
       "url": "https://object-lambda-111122223333.s3-object-lambda.us-east-1.amazonaws.com/example",
       "headers": {
           "Host": "object-lambda-111122223333.s3-object-lambda.us-east-1.amazonaws.com",
           "Accept-Encoding": "identity",
           "X-Amz-Content-SHA256": "e3b0c44298fc1example"
       }
   },
   "userIdentity": {
       "type": "AssumedRole",
       "principalId": "principalId",
       "arn": "arn:aws:sts::111122223333:assumed-role/Admin/example",       
       "accountId": "111122223333",
       "accessKeyId": "accessKeyId",
       "sessionContext": {
            "attributes": {
            "mfaAuthenticated": "false",
            "creationDate": "Wed Mar 10 23:41:52 UTC 2021"
       },
       "sessionIssuer": {
            "type": "Role",
            "principalId": "principalId",
            "arn": "arn:aws:iam::111122223333:role/Admin",
            "accountId": "111122223333",
            "userName": "Admin"
            }
       }
    },
  "protocolVersion": "1.00"
}
```

Lambda 함수는 `HeadObject` 호출에 대해 반환될 헤더와 값이 포함된 JSON 객체를 반환해야 합니다.

다음 예는 `HeadObject`에 대한 Lambda 응답 JSON의 구조를 보여줍니다.

```
{
    "statusCode": <number>; // Required
    "errorCode": <string>;
    "errorMessage": <string>;
    "headers": {
        "Accept-Ranges": <string>,
        "x-amz-archive-status": <string>,
        "x-amz-server-side-encryption-bucket-key-enabled": <boolean>,
        "Cache-Control": <string>,
        "Content-Disposition": <string>,
        "Content-Encoding": <string>,
        "Content-Language": <string>,
        "Content-Length": <number>, // Required
        "Content-Type": <string>,
        "x-amz-delete-marker": <boolean>,
        "ETag": <string>,
        "Expires": <string>,
        "x-amz-expiration": <string>,
        "Last-Modified": <string>,
        "x-amz-missing-meta": <number>,
        "x-amz-object-lock-mode": <string>,
        "x-amz-object-lock-legal-hold": <string>,
        "x-amz-object-lock-retain-until-date": <string>,
        "x-amz-mp-parts-count": <number>,
        "x-amz-replication-status": <string>,
        "x-amz-request-charged": <string>,
        "x-amz-restore": <string>,
        "x-amz-server-side-encryption": <string>,
        "x-amz-server-side-encryption-customer-algorithm": <string>,
        "x-amz-server-side-encryption-aws-kms-key-id": <string>,
        "x-amz-server-side-encryption-customer-key-MD5": <string>,
        "x-amz-storage-class": <string>,
        "x-amz-tagging-count": <number>,
        "x-amz-version-id": <string>,
        <x-amz-meta-headers>: <string>, // user-defined metadata 
        "x-amz-meta-meta1": <string>, // example of the user-defined metadata header, it will need the x-amz-meta prefix
        "x-amz-meta-meta2": <string>
        ...
    };
}
```

다음 예는 미리 서명된 URL을 사용하여 JSON을 반환하기 전에 필요에 따라 헤더 값을 수정하여 응답을 채우는 방법을 보여줍니다.

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



```
import requests

def lambda_handler(event, context):
    print(event)
    
    # Extract the presigned URL from the input.
    s3_url = event["headObjectContext"]["inputS3Url"]

    # Get the head of the object from S3.     
    response = requests.head(s3_url)
    
    # Return the error to S3 Object Lambda (if applicable).           
    if (response.status_code >= 400):
        return {
            "statusCode": response.status_code,
            "errorCode": "RequestFailure",                         
            "errorMessage": "Request to S3 failed"    
    }
    
    # Store the headers in a dictionary.
    response_headers = dict(response.headers)

    # This obscures Content-Type in a transformation, it is optional to add
    response_headers["Content-Type"] = "" 

    # Return the headers to S3 Object Lambda.     
    return {
        "statusCode": response.status_code,
        "headers": response_headers     
        }
```

------

## Lambda에서 `ListObjects` 요청 작업
<a name="olap-listobjects"></a>

이 섹션에서는 객체 Lambda 액세스 포인트가 `ListObjects`에 대한 Lambda 함수를 호출하도록 구성되어 있다고 가정합니다. Lambda는 `listObjectsContext`라는 새 객체가 포함된 JSON 페이로드를 수신합니다. `listObjectsContext`는 `ListObjects`에 대한 지원 액세스 지점의 미리 서명된 URL인 하나의 속성, `inputS3Url`을 포함합니다.

`GetObject` 및 `HeadObject`와는 달리 미리 서명된 URL에는 지정된 경우 다음과 같은 속성이 포함됩니다.
+ 모든 쿼리 파라미터
+ `requestPayer`(`x-amz-request-payer` 헤더에 포함됨) 
+ `expectedBucketOwner`(`x-amz-expected-bucket-owner` 헤더에 포함됨)

요청 구문 URI 파라미터는 Amazon Simple Storage Service API 참조의** [https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjects.html](https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjects.html) 섹션을 참조하세요.

**중요**  
애플리케이션을 개발할 때 최신 버전인 [ListObjectsV2](https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjectsV2.html)를 사용하는 것이 좋습니다. 이전 버전과의 호환성을 위해 Amazon S3는 `ListObjects`를 계속 지원합니다.

다음 예는 `ListObjects`에 대한 Lambda JSON 입력 페이로드를 보여줍니다.

```
{
    "xAmzRequestId": "requestId",
     "**listObjectsContext**": {
     "**inputS3Url**": "https://my-s3-ap-111122223333.s3-accesspoint.us-east-1.amazonaws.com/?X-Amz-Security-Token=<snip>",
     },
    "configuration": {
        "accessPointArn": "arn:aws:s3-object-lambda:us-east-1:111122223333:accesspoint/example-object-lambda-ap",
        "supportingAccessPointArn": "arn:aws:s3:us-east-1:111122223333:accesspoint/example-ap",
        "payload": "{}"
    },
    "userRequest": {
        "url": "https://object-lambda-111122223333.s3-object-lambda.us-east-1.amazonaws.com/example",
        "headers": {
            "Host": "object-lambda-111122223333.s3-object-lambda.us-east-1.amazonaws.com",
            "Accept-Encoding": "identity",
            "X-Amz-Content-SHA256": "e3b0c44298fc1example"
        }
    },
    "userIdentity": {
        "type": "AssumedRole",
        "principalId": "principalId",
        "arn": "arn:aws:sts::111122223333:assumed-role/Admin/example",
        "accountId": "111122223333",
        "accessKeyId": "accessKeyId",
        "sessionContext": {
            "attributes": {
                "mfaAuthenticated": "false",
                "creationDate": "Wed Mar 10 23:41:52 UTC 2021"
            },
            "sessionIssuer": {
                "type": "Role",
                "principalId": "principalId",
                "arn": "arn:aws:iam::111122223333:role/Admin",
                "accountId": "111122223333",
                "userName": "Admin"
            }
        }
    },
  "protocolVersion": "1.00"
}
```

Lambda 함수는 S3 객체 Lambda에서 반환되는 상태 코드, 목록 XML 결과 또는 오류 정보가 포함된 JSON 객체를 반환해야 합니다.

S3 객체 Lambda는 `listResultXml`을 처리하거나 검증하지 않고, 그 대신 `ListObjects` 호출자에게 전달합니다. `listBucketResult`의 경우, 지정된 유형에 따라 특정 속성이 있을 것으로 S3 객체 Lambda가 예상하여 이를 파싱할 수 없는 경우 예외를 발생시킵니다. `listResultXml` 및 `listBucketResult`는 동시에 제공할 수 없습니다.

다음 예는 미리 서명된 URL을 사용하여 Amazon S3를 호출하고 그 결과를 사용하여 오류 검사를 비롯한 응답을 채우는 방법을 보여줍니다.

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

```
import requests 
import xmltodict

def lambda_handler(event, context):
    # Extract the presigned URL from the input.
    s3_url = event["listObjectsContext"]["inputS3Url"]


    # Get the head of the object from Amazon S3.
    response = requests.get(s3_url)

    # Return the error to S3 Object Lambda (if applicable).
    if (response.status_code >= 400):
        error = xmltodict.parse(response.content)
        return {
            "statusCode": response.status_code,
            "errorCode": error["Error"]["Code"],
            "errorMessage": error["Error"]["Message"]
        }

    # Store the XML result in a dict.
    response_dict = xmltodict.parse(response.content)

    # This obscures StorageClass in a transformation, it is optional to add
    for item in response_dict['ListBucketResult']['Contents']:
        item['StorageClass'] = ""

    # Convert back to XML.
    listResultXml = xmltodict.unparse(response_dict)
    
    # Create response with listResultXml.
    response_with_list_result_xml = {
        'statusCode': 200,
        'listResultXml': listResultXml
    }

    # Create response with listBucketResult.
    response_dict['ListBucketResult'] = sanitize_response_dict(response_dict['ListBucketResult'])
    response_with_list_bucket_result = {
        'statusCode': 200,
        'listBucketResult': response_dict['ListBucketResult']
    }

    # Return the list to S3 Object Lambda.
    # Can return response_with_list_result_xml or response_with_list_bucket_result
    return response_with_list_result_xml

# Converting the response_dict's key to correct casing
def sanitize_response_dict(response_dict: dict):
    new_response_dict = dict()
    for key, value in response_dict.items():
        new_key = key[0].lower() + key[1:] if key != "ID" else 'id'
        if type(value) == list:
            newlist = []
            for element in value:
                if type(element) == type(dict()):
                    element = sanitize_response_dict(element)
                newlist.append(element)
            value = newlist
        elif type(value) == dict:
            value = sanitize_response_dict(value)
        new_response_dict[new_key] = value
    return new_response_dict
```

------

다음 예는 `ListObjects`에 대한 Lambda 응답 JSON의 구조를 보여줍니다.

```
{ 
  "statusCode": <number>; // Required
  "errorCode": <string>;
  "errorMessage": <string>;
  "listResultXml": <string>; // This can also be Error XML string in case S3 returned error response when calling the pre-signed URL

  "listBucketResult": {  // listBucketResult can be provided instead of listResultXml, however they can not both be provided in the JSON response  
        "name": <string>,  // Required for 'listBucketResult'
        "prefix": <string>,  
        "marker": <string>, 
        "nextMarker": <string>, 
        "maxKeys": <int>,   // Required for 'listBucketResult'
        "delimiter": <string>, 
        "encodingType": <string>  
        "isTruncated": <boolean>,  // Required for 'listBucketResult'
        "contents": [  { 
            "key": <string>,  // Required for 'content'
            "lastModified": <string>,  
            "eTag": <string>,  
            "checksumAlgorithm": <string>,   // CRC32,  CRC32C,  SHA1,  SHA256
            "size": <int>,   // Required for 'content'
            "owner": {  
                "displayName": <string>,  // Required for 'owner'
                "id": <string>,  // Required for 'owner'
            },  
            "storageClass": <string>  
            },  
        ...  
        ],  
        "commonPrefixes": [  {  
            "prefix": <string>   // Required for 'commonPrefix'
        },  
        ...  
        ],  
    }
}
```

## Lambda에서 `ListObjectsV2` 요청 작업
<a name="olap-listobjectsv2"></a>

이 섹션에서는 객체 Lambda 액세스 포인트가 `ListObjectsV2`에 대한 Lambda 함수를 호출하도록 구성되어 있다고 가정합니다. Lambda는 `listObjectsV2Context`라는 새 객체가 포함된 JSON 페이로드를 수신합니다. `listObjectsV2Context`는 `ListObjectsV2`에 대한 지원 액세스 지점의 미리 서명된 URL인 하나의 속성, `inputS3Url`을 포함합니다.

`GetObject` 및 `HeadObject`와는 달리 미리 서명된 URL에는 지정된 경우 다음과 같은 속성이 포함됩니다.
+ 모든 쿼리 파라미터
+ `requestPayer`(`x-amz-request-payer` 헤더에 포함됨) 
+ `expectedBucketOwner`(`x-amz-expected-bucket-owner` 헤더에 포함됨)

요청 구문 URI 파라미터는 Amazon Simple Storage Service API 참조의** [https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjectsV2.html](https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjectsV2.html) 섹션을 참조하십시오.

다음 예는 `ListObjectsV2`에 대한 Lambda JSON 입력 페이로드를 보여줍니다.

```
{
    "xAmzRequestId": "requestId",
     "**listObjectsV2Context**": {
     "**inputS3Url**": "https://my-s3-ap-111122223333.s3-accesspoint.us-east-1.amazonaws.com/?list-type=2&X-Amz-Security-Token=<snip>",
     },
    "configuration": {
        "accessPointArn": "arn:aws:s3-object-lambda:us-east-1:111122223333:accesspoint/example-object-lambda-ap",
        "supportingAccessPointArn": "arn:aws:s3:us-east-1:111122223333:accesspoint/example-ap",
        "payload": "{}"
    },
    "userRequest": {
        "url": "https://object-lambda-111122223333.s3-object-lambda.us-east-1.amazonaws.com/example",
        "headers": {
            "Host": "object-lambda-111122223333.s3-object-lambda.us-east-1.amazonaws.com",
            "Accept-Encoding": "identity",
            "X-Amz-Content-SHA256": "e3b0c44298fc1example"
        }
    },
    "userIdentity": {
        "type": "AssumedRole",
        "principalId": "principalId",
        "arn": "arn:aws:sts::111122223333:assumed-role/Admin/example",
        "accountId": "111122223333",
        "accessKeyId": "accessKeyId",
        "sessionContext": {
            "attributes": {
                "mfaAuthenticated": "false",
                "creationDate": "Wed Mar 10 23:41:52 UTC 2021"
            },
            "sessionIssuer": {
                "type": "Role",
                "principalId": "principalId",
                "arn": "arn:aws:iam::111122223333:role/Admin",
                "accountId": "111122223333",
                "userName": "Admin"
            }
        }
    },
  "protocolVersion": "1.00" 
}
```

Lambda 함수는 S3 객체 Lambda에서 반환되는 상태 코드, 목록 XML 결과 또는 오류 정보가 포함된 JSON 객체를 반환해야 합니다.

S3 객체 Lambda는 `listResultXml`을 처리하거나 검증하지 않고, 그 대신 `ListObjectsV2` 호출자에게 전달합니다. `listBucketResult`의 경우, 지정된 유형에 따라 특정 속성이 있을 것으로 S3 객체 Lambda가 예상하여 이를 파싱할 수 없는 경우 예외를 발생시킵니다. `listResultXml` 및 `listBucketResult`는 동시에 제공할 수 없습니다.

다음 예는 미리 서명된 URL을 사용하여 Amazon S3를 호출하고 그 결과를 사용하여 오류 검사를 비롯한 응답을 채우는 방법을 보여줍니다.

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

```
import requests 
import xmltodict

def lambda_handler(event, context):
    # Extract the presigned URL from the input.
    s3_url = event["listObjectsV2Context"]["inputS3Url"]


    # Get the head of the object from Amazon S3.
    response = requests.get(s3_url)

    # Return the error to S3 Object Lambda (if applicable).
    if (response.status_code >= 400):
        error = xmltodict.parse(response.content)
        return {
            "statusCode": response.status_code,
            "errorCode": error["Error"]["Code"],
            "errorMessage": error["Error"]["Message"]
        }

    # Store the XML result in a dict.
    response_dict = xmltodict.parse(response.content)

    # This obscures StorageClass in a transformation, it is optional to add
    for item in response_dict['ListBucketResult']['Contents']:
        item['StorageClass'] = ""

    # Convert back to XML.
    listResultXml = xmltodict.unparse(response_dict)
    
    # Create response with listResultXml.
    response_with_list_result_xml = {
        'statusCode': 200,
        'listResultXml': listResultXml
    }

    # Create response with listBucketResult.
    response_dict['ListBucketResult'] = sanitize_response_dict(response_dict['ListBucketResult'])
    response_with_list_bucket_result = {
        'statusCode': 200,
        'listBucketResult': response_dict['ListBucketResult']
    }

    # Return the list to S3 Object Lambda.
    # Can return response_with_list_result_xml or response_with_list_bucket_result
    return response_with_list_result_xml

# Converting the response_dict's key to correct casing
def sanitize_response_dict(response_dict: dict):
    new_response_dict = dict()
    for key, value in response_dict.items():
        new_key = key[0].lower() + key[1:] if key != "ID" else 'id'
        if type(value) == list:
            newlist = []
            for element in value:
                if type(element) == type(dict()):
                    element = sanitize_response_dict(element)
                newlist.append(element)
            value = newlist
        elif type(value) == dict:
            value = sanitize_response_dict(value)
        new_response_dict[new_key] = value
    return new_response_dict
```

------

다음 예는 `ListObjectsV2`에 대한 Lambda 응답 JSON의 구조를 보여줍니다.

```
{  
    "statusCode": <number>; // Required  
    "errorCode": <string>;  
    "errorMessage": <string>;  
    "listResultXml": <string>; // This can also be Error XML string in case S3 returned error response when calling the pre-signed URL  
  
    "listBucketResult": {  // listBucketResult can be provided instead of listResultXml, however they can not both be provided in the JSON response 
        "name": <string>, // Required for 'listBucketResult'  
        "prefix": <string>,  
        "startAfter": <string>,  
        "continuationToken": <string>,  
        "nextContinuationToken": <string>,
        "keyCount": <int>, // Required for 'listBucketResult'  
        "maxKeys": <int>, // Required for 'listBucketResult'  
        "delimiter": <string>,  
        "encodingType": <string>  
        "isTruncated": <boolean>, // Required for 'listBucketResult'  
        "contents": [ {  
            "key": <string>, // Required for 'content'  
            "lastModified": <string>,  
            "eTag": <string>,  
            "checksumAlgorithm": <string>, // CRC32, CRC32C, SHA1, SHA256  
            "size": <int>, // Required for 'content'  
            "owner": {  
                "displayName": <string>, // Required for 'owner'  
                "id": <string>, // Required for 'owner'  
            },  
            "storageClass": <string>  
            },  
            ...  
        ],  
        "commonPrefixes": [ {  
            "prefix": <string> // Required for 'commonPrefix'  
            },  
        ...  
        ],  
    }  
}
```

# 이벤트 컨텍스트 형식 및 사용법
<a name="olap-event-context"></a>

**참고**  
2025년 11월 7일부터 S3 Object Lambda는 현재 서비스를 사용하고 있는 기존 고객과 AWS 파트너 네트워크(APN) 파트너를 선택하는 고객만 사용할 수 있습니다. S3 Object Lambda와 유사한 기능의 경우 [Amazon S3 Object Lambda 가용성 변경](https://docs.aws.amazon.com/AmazonS3/latest/userguide/amazons3-ol-change.html) 섹션에서 자세히 알아보세요.

Amazon S3 객체 Lambda는 AWS Lambda 함수에 전달된 이벤트에서 수행되는 요청에 대한 컨텍스트를 제공합니다. 다음 그림에서는 요청 예시를 보여줍니다. 필드에 대한 설명은 예시 뒤에 포함되어 있습니다.

```
{
    "xAmzRequestId": "requestId",
    "getObjectContext": {
        "inputS3Url": "https://my-s3-ap-111122223333.s3-accesspoint.us-east-1.amazonaws.com/example?X-Amz-Security-Token=<snip>",
        "outputRoute": "io-use1-001",
        "outputToken": "OutputToken"
    },
    "configuration": {
        "accessPointArn": "arn:aws:s3-object-lambda:us-east-1:111122223333:accesspoint/example-object-lambda-ap",
        "supportingAccessPointArn": "arn:aws:s3:us-east-1:111122223333:accesspoint/example-ap",
        "payload": "{}"
    },
    "userRequest": {
        "url": "https://object-lambda-111122223333.s3-object-lambda.us-east-1.amazonaws.com/example",
        "headers": {
            "Host": "object-lambda-111122223333.s3-object-lambda.us-east-1.amazonaws.com",
            "Accept-Encoding": "identity",
            "X-Amz-Content-SHA256": "e3b0c44298fc1example"
        }
    },
    "userIdentity": {
        "type": "AssumedRole",
        "principalId": "principalId",
        "arn": "arn:aws:sts::111122223333:assumed-role/Admin/example",
        "accountId": "111122223333",
        "accessKeyId": "accessKeyId",
        "sessionContext": {
            "attributes": {
                "mfaAuthenticated": "false",
                "creationDate": "Wed Mar 10 23:41:52 UTC 2021"
            },
            "sessionIssuer": {
                "type": "Role",
                "principalId": "principalId",
                "arn": "arn:aws:iam::111122223333:role/Admin",
                "accountId": "111122223333",
                "userName": "Admin"
            }
        }
    },
    "protocolVersion": "1.00"
}
```

요청에는 다음 필드가 포함됩니다.
+ `xAmzRequestId` – 이 요청에 대한 Amazon S3 요청 ID입니다. 디버깅에 도움이 되도록 이 값을 기록하는 것이 좋습니다.
+ `getObjectContext` – Amazon S3 및 S3 객체 Lambda 연결에 대한 입력 및 출력 세부 정보입니다.
  + `inputS3Url` – Amazon S3에서 원본 객체를 가져오는 데 사용할 수 있는 미리 서명된 URL입니다. URL은 원래 호출자의 ID를 사용하여 서명되며 URL이 사용될 때 해당 사용자의 권한이 적용됩니다. URL에 서명된 헤더가 있는 경우 Lambda 함수가 Amazon S3에 대한 호출에 이러한 헤더를 포함해야 합니다(`Host` 헤더 제외).
  + `outputRoute` - Lambda 함수에서 를 호출할 때 S3 객체 Lambda URL에 추가되는 라우팅 토큰입니다.`WriteGetObjectResponse`
  + `outputToken` – S3 객체 Lambda에서 원래 호출자와 일치하는 `WriteGetObjectResponse` 호출을 찾을 때 사용되는 불투명 토큰입니다.
+ `configuration` – 객체 Lambda 액세스 포인트에 대한 구성 정보입니다.
  + `accessPointArn` – 이 요청을 수신한 객체 Lambda 액세스 포인트의 Amazon 리소스 이름(ARN)입니다.
  + `supportingAccessPointArn` – 객체 Lambda 액세스 포인트 구성에 지정된 지원 액세스 포인트의 ARN입니다.
  + `payload` – 객체 Lambda 액세스 포인트 구성에 적용되는 사용자 지정 데이터입니다. S3 객체 Lambda는 이 데이터를 불투명 한 문자열로 취급하므로 사용하기 전에 데이터를 디코딩해야 할 수도 있습니다.
+ `userRequest` – S3 객체 Lambda에 대한 원래 호출에 대한 정보입니다.
  + `url` – 권한 부여 관련 쿼리 파라미터를 제외하고 S3 객체 Lambda에서 수신한 요청의 디코딩된 URL입니다.
  + `headers` – 권한 부여 관련 헤더를 제외하고 원래 호출의 HTTP 헤더와 해당 값을 포함하는 문자열에 대한 문자열 맵입니다. 동일한 헤더가 여러 번 나타나는 경우 동일한 헤더를 갖는 각 인스턴스의 값은 쉼표로 구분된 목록으로 결합됩니다. 이 맵에는 원래 헤더의 대/소문자가 유지됩니다.
+ `userIdentity` – S3 객체 Lambda를 호출한 ID에 대한 세부 정보입니다. 자세한 내용은 *AWS CloudTrail 사용 설명서*의 [추적을 위해 데이터 이벤트 로깅](https://docs.aws.amazon.com/awscloudtrail/latest/userguide/logging-data-events-with-cloudtrail.html)을 참조하세요.
  + `type` – 자격 증명의 유형입니다.
  + `accountId` – 자격 증명이 속한 AWS 계정 계정입니다.
  + `userName` – 호출을 수행한 자격 증명의 표시 이름입니다.
  + `principalId` – 호출을 수행한 자격 증명에 대한 고유 식별자입니다.
  + `arn` – 호출을 수행한 보안 주체의 ARN입니다. ARN의 마지막 섹션에는 호출을 수행한 사용자 또는 역할이 포함됩니다.
  + `sessionContext` – 임시 보안 자격 증명으로 요청이 이루어진 경우 이 요소는 해당 자격 증명을 위해 생성된 세션에 관한 정보를 제공합니다.
  + `invokedBy` – 요청을 수행한 AWS 서비스의 이름입니다(예: Amazon EC2 Auto Scaling 또는 AWS Elastic Beanstalk).
  + `sessionIssuer` – 임시 보안 자격 증명으로 요청이 이루어진 경우 이 요소는 자격 증명을 획득하는 방법에 관한 정보를 제공합니다.
+ `protocolVersion` – 제공된 컨텍스트의 버전 ID입니다. 이 필드의 형식은 `{Major Version}.{Minor Version}`입니다. 마이너 버전 번호는 항상 2자리 숫자입니다. 필드의 의미 체계를 제거하거나 변경하면 메이저 버전 범프가 필요하며 활성 옵트인이 요구됩니다. Amazon S3는 언제든지 새 필드를 추가할 수 있으며, 이 시점에서 마이너 버전 범프가 발생할 수 있습니다. 소프트웨어 롤아웃의 특성으로 인해 한 번에 여러 개의 마이너 버전이 표시될 수 있습니다.

# Range 및 partNumber 헤더 작업
<a name="range-get-olap"></a>

**참고**  
2025년 11월 7일부터 S3 Object Lambda는 현재 서비스를 사용하고 있는 기존 고객과 AWS 파트너 네트워크(APN) 파트너를 선택하는 고객만 사용할 수 있습니다. S3 Object Lambda와 유사한 기능의 경우 [Amazon S3 Object Lambda 가용성 변경](https://docs.aws.amazon.com/AmazonS3/latest/userguide/amazons3-ol-change.html) 섹션에서 자세히 알아보세요.

Amazon S3 객체 Lambda에서 대용량 객체로 작업하는 경우 `Range` HTTP 헤더를 사용하면 객체에서 지정된 바이트 범위를 다운로드할 수 있습니다. 동일한 객체 내에서 서로 다른 바이트 범위를 가져오려면 Amazon S3에 대한 동시 연결을 사용하면 됩니다. 객체에서 지정된 부분에 대해 범위가 지정된 요청을 수행하는 `partNumber` 파라미터(1\$110,000 사이의 정수)를 지정할 수도 있습니다.

`Range` 또는 `partNumber` 파라미터를 포함하는 요청을 처리할 수 있는 여러 방법이 있기 때문에 S3 객체 Lambda는 이러한 파라미터를 변환된 객체에 적용하지 않습니다. 대신 AWS Lambda 함수가 애플리케이션에 필요한 경우 이 기능을 구현해야 합니다.

`Range` 및 `partNumber` 파라미터를 S3 객체 Lambda와 함께 사용하려면 다음을 수행하십시오.
+ 객체 Lambda 액세스 포인트 구성에서 이러한 파라미터를 활성화합니다.
+ 이러한 파라미터를 포함하는 요청을 처리할 수 있는 Lambda 함수를 작성합니다.

다음 단계에서 이 작업을 수행하는 방법을 설명합니다.

## 1단계: 객체 Lambda 액세스 포인트 구성
<a name="range-get-olap-step-1"></a>

기본적으로 객체 Lambda 액세스 포인트는 헤더 또는 쿼리 파라미터에 `Range` 또는 `partNumber` 파라미터가 포함되는 모든 `GetObject` 또는 `HeadObject` 요청에 HTTP 상태 코드 501(구현되지 않음) 오류로 응답합니다.

객체 Lambda 액세스 포인트가 이러한 요청을 수락하도록 설정하려면 객체 Lambda 액세스 포인트 구성의 `AllowedFeatures` 섹션에 `GetObject-Range`, `GetObject-PartNumber`, `HeadObject-Range` 또는 `HeadObject-PartNumber`를 포함해야 합니다. 객체 Lambda 액세스 포인트 구성 업데이트에 대한 자세한 내용은 [객체 Lambda 액세스 포인트 생성](olap-create.md) 섹션을 참조하십시오.

## 2단계: Lambda 함수에서 `Range` 또는 `partNumber` 처리 구현
<a name="range-get-olap-step-2"></a>

객체 Lambda 액세스 포인트가 범위가 지정된 `GetObject` 또는 `HeadObject` 요청을 사용하여 Lambda 함수를 호출할 경우 `Range` 또는 `partNumber` 파라미터가 이벤트 컨텍스트에 포함됩니다. 다음 테이블에 설명된 대로 이벤트 컨텍스트에서 파라미터의 위치는 객체 Lambda 액세스 포인트에 대한 원래 요청에서 어떤 파라미터가 사용되었는지와 파라미터가 포함된 방식에 따라 달라집니다.


| 파라미터 | 이벤트 컨텍스트 위치 | 
| --- | --- | 
|  `Range`(헤더)  |  `userRequest.headers.Range`  | 
|  `Range`(쿼리 파라미터)  |  `userRequest.url`(쿼리 파라미터 `Range`)  | 
|  `partNumber`  |  `userRequest.url`(쿼리 파라미터 `partNumber`)  | 

**중요**  
객체 Lambda 액세스 포인트에 제공된 미리 서명된 URL에는 원래 요청의 `Range` 또는`partNumber` 파라미터가 포함되어 있지 않습니다. AWS Lambda 함수에서 이러한 파라미터를 처리하는 방법은 다음 옵션을 참조하세요.

`Range` 또는 `partNumber` 값을 추출한 후 애플리케이션의 요구 사항에 따라 다음 방법 중 하나를 사용할 수 있습니다.

1. **요청된 `Range` 또는 `partNumber`를 변환된 객체에 매핑합니다(권장).**

   `Range` 또는 `partNumber` 요청을 처리하는 가장 신뢰할 수 있는 방법은 다음을 수행하는 것입니다.
   + Amazon S3에서 전체 객체를 검색합니다.
   + 객체를 변환합니다.
   + 요청된 `Range` 또는 `partNumber` 파라미터를 변환된 객체에 적용합니다.

   이 작업을 수행하려면 제공된 미리 서명된 URL을 사용하여 Amazon S3에서 전체 객체를 가져온 다음 필요에 따라 객체를 처리합니다. 이 방식으로 `Range` 파라미터를 처리하는 Lambda 함수의 예제에 대해서는 AWS 샘플 GitHub 리포지토리의 [이 샘플](https://github.com/aws-samples/amazon-s3-object-lambda-default-configuration/blob/main/function/nodejs_20_x/src/response/range_mapper.ts)을 참조하십시오.

1. **요청된 `Range`를 미리 서명된 URL에 매핑합니다.**

   경우에 따라 Lambda 함수가 요청된 `Range`를 미리 서명된 URL에 직접 매핑하여 Amazon S3에서 객체의 일부만 검색할 수 있습니다. 이 접근 방식은 변환이 다음 두 기준을 모두 충족하는 경우에만 적합합니다.

   1. 변환 함수를 부분 객체 범위에 적용할 수 있습니다.

   1. 변환 함수 앞 또는 뒤에 `Range` 파라미터를 사용하면 동일한 변환된 객체가 생성됩니다.

   예를 들어 ASCII로 인코딩된 객체의 모든 문자를 대문자로 변환하는 변환 함수는 이전 두 조건을 모두 충족합니다. 변환을 객체의 일부에 적용할 수 있고 `Range` 파라미터를 변환 전에 적용하는 것과 파라미터를 변환 후에 적용하는 것이 동일한 결과를 얻습니다.

   이와 달리 ASCII로 인코딩된 객체의 문자 순서를 반대로 바꾸는 함수는 이러한 조건을 충족하지 못합니다. 이러한 함수는 부분 객체 범위에 적용할 수 있기 때문에 조건 1을 충족합니다. 그러나 `Range` 파라미터를 변환 전에 적용하는 것과 파라미터를 변환 후에 적용하는 것이 서로 다른 결과를 얻기 때문에 조건 2를 충족하지 못합니다.

   콘텐츠가 `abcdefg`인 객체의 처음 세 문자에 함수를 적용하는 요청을 가정합니다. `Range` 파라미터를 변환 전에 적용하면 `abc`만 가져오며 다음에 데이터의 순서를 반대로 바꾸면 `cba`를 반환합니다. 그러나 파라미터를 변환 후에 적용하면 함수가 전체 객체를 가져온 다음 데이터의 순서를 바꾸고 `Range` 파라미터를 적용하여 `gfe`를 반환합니다. 두 결과가 다르기 때문에 이 함수는 Amazon S3에서 객체를 가져올 때 `Range` 파라미터를 적용해서는 안 됩니다. 따라서 이 함수는 전체 객체를 가져와 변환을 수행한 다음 `Range` 파라미터를 적용해야만 합니다.
**주의**  
많은 경우에 `Range` 파라미터를 미리 서명된 URL에 적용하면 Lambda 함수 또는 요청을 하는 클라이언트에서 예기치 않은 동작이 발생합니다. Amazon S3에서 부분 객체만 가져올 때 애플리케이션이 제대로 작동한다는 확신이 없는 경우 앞서 방법 A에서 설명한 대로 전체 객체를 가져오고 변환하는 것이 좋습니다.

   애플리케이션이 B 방법에서 설명한 조건을 충족하는 경우 요청된 객체 범위만 가져온 다음 해당 범위에서 변환을 실행하여 AWS Lambda 함수를 간소화할 수 있습니다.

   다음 Java 코드 예시는 다음 작업을 실행하는 방법을 설명한 것입니다.
   + `GetObject` 요청에서 `Range` 헤더를 검색합니다.
   + `Range` 헤더를 Lambda가 Amazon S3에서 요청된 범위를 가져오는 데 사용할 수 있는 미리 서명된 URL에 추가합니다.

   ```
   private HttpRequest.Builder applyRangeHeader(ObjectLambdaEvent event, HttpRequest.Builder presignedRequest) {
       var header = event.getUserRequest().getHeaders().entrySet().stream()
               .filter(e -> e.getKey().toLowerCase(Locale.ROOT).equals("range"))
               .findFirst();
   
       // Add check in the query string itself.
       header.ifPresent(entry -> presignedRequest.header(entry.getKey(), entry.getValue()));
       return presignedRequest;
   }
   ```