AWS 시작하기

서버리스 웹 애플리케이션 구축

AWS Lambda, Amazon API Gateway, AWS Amplify, Amazon DynamoDB, Amazon Cognito 사용

모듈 3: 서버리스 서비스 백엔드

AWS Lambda와 Amazon DynamoDB를 사용하여 웹 애플리케이션에 대한 요청을 처리하기 위한 백엔드 프로세스를 만듭니다.

개요

이 모듈에서는 AWS Lambda와 Amazon DynamoDB를 사용하여 웹 애플리케이션에 대한 요청을 처리하기 위한 백엔드 프로세스를 만듭니다. 첫 번째 모듈에서 배포한 브라우저 애플리케이션을 사용하여 사용자는 선택한 위치로 unicorn을 전송하도록 요청할 수 있습니다. 이러한 요청을 이행하려면 브라우저에서 실행 중인 JavaScript가 클라우드에서 실행하는 서비스를 간접적으로 호출해야 합니다.

아키텍처 개요

아키텍처 개요

사용자가 unicorn을 요청할 때마다 간접적으로 호출되는 Lambda 함수를 구현하겠습니다. 이 함수는 플릿에서 유니콘을 선택하고 DynamoDB 테이블에 요청을 기록한 다음, 프런트엔드 애플리케이션에 응답하여 발송되는 유니콘에 대한 세부 정보를 제공합니다.

이 함수는 Amazon API Gateway를 사용하여 브라우저에서 간접적으로 호출됩니다. 다음 모듈에서는 이 연결을 설정합니다. 이 모듈에서는 함수를 따로 테스트만 합니다.

 소요 시간

30분

 사용 서비스

구현

  • Amazon DynamoDB 콘솔을 사용하여 새로운 DynamoDB 테이블을 생성합니다. 

    1. Amazon DynamoDB 콘솔에서 테이블 생성을 선택합니다.
    2. 테이블 이름Rides를 입력합니다. 이 필드는 대/소문자를 구분합니다.
    3. 파티션 키에서 RideId 입력하고 키 형식으로 문자열을 선택합니다. 이 필드는 대/소문자를 구분합니다.
    4. 테이블 설정 섹션에서 기본 설정이 선택되어 있는지 확인하고 테이블 생성을 선택합니다. 
    5. 테이블 페이지에서 테이블 생성이 완료될 때까지 기다립니다. 작업이 완료되면 상태가 활성으로 표시됩니다. 테이블 이름을 선택합니다.
    6. 새 테이블의 개요 탭 > 일반 정보 섹션에서 추가 정보를 선택합니다. ARN을 복사합니다. 이 이름은 다음 섹션에서 사용할 것입니다.
  • 각 Lambda 함수에는 IAM 역할이 연결되어 있습니다. 이 역할은 기능이 상호 작용하도록 허용된 다른 AWS 서비스를 정의합니다. 이 자습서의 목적상 Amazon CloudWatch Logs에 로그를 쓸 수 있는 권한과 DynamoDB 테이블에 항목을 쓸 수 있는 액세스 권한을 Lambda 함수에 부여하는 IAM 역할을 만들어야 합니다.

    1. IAM 콘솔의 왼쪽 탐색 창에서 역할을 선택한 다음 역할 생성을 선택합니다.
    2. 신뢰할 수 있는 엔티티 유형 섹션에서 AWS 서비스를 선택합니다. 사용 사례에서는 Lambda를 선택한 후 다음을 선택합니다. 
      참고: 역할 유형을 선택하면 역할에 대해 신뢰할 수 있는 정책이 자동으로 생성됩니다. 이 정책은 AWS 서비스가 이 역할을 대리할 수 있도록 허용합니다. CLI, AWS CloudFormation 또는 다른 방법을 사용하여 이 역할을 생성한 경우 신뢰할 수 있는 정책을 직접 지정해야 합니다.
    3. 필터 텍스트 상자에 AWSLambdaBasicExecutionRole을 입력하고 Enter 키를 누릅니다. 
    4. AWSLambdaBasicExecutionRole 정책 이름 옆의 확인란을 선택하고 다음을 선택합니다.
    5. 역할 이름에 WildRydesLambda를 입력합니다. 다른 파라미터의 기본 설정을 유지합니다.
    6. 역할 생성을 선택합니다.
    7. 역할 페이지의 필터 상자에 WildRydesLambda를 입력하고 방금 생성한 역할의 이름을 선택합니다.
    8. 권한 탭에서 권한 추가 아래에 인라인 정책 생성을 선택합니다.
    9. 서비스 선택 섹션에서 검색 창에 DynamoDB를 입력하고 DynamoDB가 나타나면 DynamoDB를 선택합니다.
    10. 작업 선택을 선택합니다.
    11. 작업 허용 섹션에서 검색 창에 PutItem을 입력하고 PutItem이 나타나면 PutItem 옆의 확인란을 선택합니다.
    12. 특정 옵션이 선택된 상태에서 리소스 섹션에 있는 ARN 추가 링크를 선택합니다.
    13. 텍스트 탭을 선택합니다. DynamoDB(이전 섹션의 6단계)에서 생성한 테이블의 ARN을 붙여넣고 ARN 추가를 선택합니다.
    14. 다음을 선택합니다.
    15. 정책 이름에 DynamoDBWriteAccess를 입력하고 정책 생성을 선택합니다.
  • AWS Lambda는 HTTP 요청 등과 같은 이벤트에 대응하여 코드를 실행합니다. 이 단계에서는 웹 애플리케이션에서 unicorn을 디스패치하는 API 요청을 처리할 코어 함수를 만듭니다. 다음 모듈에서는 Amazon API Gateway를 사용하여 사용자의 브라우저에서 호출할 수 있는 HTTP 엔드포인트를 표시할 RESTful API를 만들겠습니다. 그런 다음 이 단계에서 생성한 Lambda 함수를 해당 API에 연결하여 웹 애플리케이션에 대해 제대로 기능하는 백엔드를 생성하겠습니다.

    AWS Lambda 콘솔을 사용하여 RequestUnicorn이라는 새로운 Lambda 함수를 생성합니다. 이 함수는 API 요청을 처리하게 됩니다. 함수 코드에 대해 다음 requestUnicorn.js 구현 예제를 사용하세요. 파일에서 복사하여 AWS Lambda 콘솔의 편집기에 붙여 넣습니다.

    함수가 이전 섹션에서 생성한 WildRydesLambda IAM 역할을 사용하도록 해야 합니다.

    1. AWS Lambda 콘솔에서 함수 생성을 선택합니다.
    2. 기본 처음부터 새로 작성 카드가 선택된 채로 둡니다.
    3. 함수 이름 필드에 RequestUnicorn을 입력합니다.
    4. 런타임Node.js 16.x를 선택합니다(이 자습서에서는 Node.js 최신 버전을 사용할 수 없습니다).
    5. 기본 실행 역할 변경 드롭다운에서 기존 역할 사용을 선택합니다.
    6. 기존 역할 드롭다운 메뉴에서 WildRydesLambda를 선택합니다.
    7. 함수 생성을 클릭합니다.
    8. 코드 소스 섹션으로 스크롤다운하여 index.js 코드 편집기의 기존 코드를 requestUnicorn.js의 콘텐츠로 바꿉니다. 다음 코드 블록은 requestUnicorn.js 파일을 표시합니다. 이 코드를 복사하여 코드 편집기의 index.js 탭에 붙여넣습니다.
    const randomBytes = require('crypto').randomBytes;
    const AWS = require('aws-sdk');
    const ddb = new AWS.DynamoDB.DocumentClient();
    
    const fleet = [
        {
            Name: 'Angel',
            Color: 'White',
            Gender: 'Female',
        },
        {
            Name: 'Gil',
            Color: 'White',
            Gender: 'Male',
        },
        {
            Name: 'Rocinante',
            Color: 'Yellow',
            Gender: 'Female',
        },
    ];
    
    exports.handler = (event, context, callback) => {
        if (!event.requestContext.authorizer) {
          errorResponse('Authorization not configured', context.awsRequestId, callback);
          return;
        }
    
        const rideId = toUrlString(randomBytes(16));
        console.log('Received event (', rideId, '): ', event);
    
        // Because we're using a Cognito User Pools authorizer, all of the claims
        // included in the authentication token are provided in the request context.
        // This includes the username as well as other attributes.
        const username = event.requestContext.authorizer.claims['cognito:username'];
    
        // The body field of the event in a proxy integration is a raw string.
        // In order to extract meaningful values, we need to first parse this string
        // into an object. A more robust implementation might inspect the Content-Type
        // header first and use a different parsing strategy based on that value.
        const requestBody = JSON.parse(event.body);
    
        const pickupLocation = requestBody.PickupLocation;
    
        const unicorn = findUnicorn(pickupLocation);
    
        recordRide(rideId, username, unicorn).then(() => {
            // You can use the callback function to provide a return value from your Node.js
            // Lambda functions. The first parameter is used for failed invocations. The
            // second parameter specifies the result data of the invocation.
    
            // Because this Lambda function is called by an API Gateway proxy integration
            // the result object must use the following structure.
            callback(null, {
                statusCode: 201,
                body: JSON.stringify({
                    RideId: rideId,
                    Unicorn: unicorn,
                    Eta: '30 seconds',
                    Rider: username,
                }),
                headers: {
                    'Access-Control-Allow-Origin': '*',
                },
            });
        }).catch((err) => {
            console.error(err);
    
            // If there is an error during processing, catch it and return
            // from the Lambda function successfully. Specify a 500 HTTP status
            // code and provide an error message in the body. This will provide a
            // more meaningful error response to the end client.
            errorResponse(err.message, context.awsRequestId, callback)
        });
    };
    
    // This is where you would implement logic to find the optimal unicorn for
    // this ride (possibly invoking another Lambda function as a microservice.)
    // For simplicity, we'll just pick a unicorn at random.
    function findUnicorn(pickupLocation) {
        console.log('Finding unicorn for ', pickupLocation.Latitude, ', ', pickupLocation.Longitude);
        return fleet[Math.floor(Math.random() * fleet.length)];
    }
    
    function recordRide(rideId, username, unicorn) {
        return ddb.put({
            TableName: 'Rides',
            Item: {
                RideId: rideId,
                User: username,
                Unicorn: unicorn,
                RequestTime: new Date().toISOString(),
            },
        }).promise();
    }
    
    function toUrlString(buffer) {
        return buffer.toString('base64')
            .replace(/\+/g, '-')
            .replace(/\//g, '_')
            .replace(/=/g, '');
    }
    
    function errorResponse(errorMessage, awsRequestId, callback) {
      callback(null, {
        statusCode: 500,
        body: JSON.stringify({
          Error: errorMessage,
          Reference: awsRequestId,
        }),
        headers: {
          'Access-Control-Allow-Origin': '*',
        },
      });
    }

        9. 배포를 선택합니다.

  • 이 모듈에서는 AWS Lambda 콘솔을 사용하여 만든 함수를 테스트하겠습니다. 다음 모듈에서는 첫 번째 모듈에서 배포한 브라우저 기반 애플리케이션으로부터 함수를 간접적으로 호출할 수 있도록 하기 위해 API Gateway를 사용하여 REST API를 추가하겠습니다.

    1. 이전 섹션에서 빌드한 RequestUnicorn 함수의 코드 소스 섹션에서 테스트를 선택하고 드롭다운에서 테스트 이벤트 구성을 선택합니다.
    2. 새 이벤트 생성 기본 선택 항목을 유지합니다.
    3. 이벤트 이름 필드에 TestRequestEvent를 입력합니다.
    4. 다음 테스트 이벤트를 복사해 이벤트 JSON 섹션에 붙여넣습니다.
    {
        "path": "/ride",
        "httpMethod": "POST",
        "headers": {
            "Accept": "*/*",
            "Authorization": "eyJraWQiOiJLTzRVMWZs",
            "content-type": "application/json; charset=UTF-8"
        },
        "queryStringParameters": null,
        "pathParameters": null,
        "requestContext": {
            "authorizer": {
                "claims": {
                    "cognito:username": "the_username"
                }
            }
        },
        "body": "{\"PickupLocation\":{\"Latitude\":47.6174755835663,\"Longitude\":-122.28837066650185}}"
    }

        5. 저장을 선택합니다.

        6. 함수의 코드 소스 섹션에서 테스트를 선택하고 드롭다운에서 TestRequestEvent를 선택합니다.

        7.  테스트 탭에서 테스트를 선택합니다.

        8. 표시되는 함수 실행 중: 성공 메시지에서 세부 정보 드롭다운을 확장합니다.

        9. 함수 결과가 다음과 같은지 확인합니다.

    {
        "statusCode": 201,
        "body": "{\"RideId\":\"SvLnijIAtg6inAFUBRT+Fg==\",\"Unicorn\":{\"Name\":\"Rocinante\",\"Color\":\"Yellow\",\"Gender\":\"Female\"},\"Eta\":\"30 seconds\"}",
        "headers": {
            "Access-Control-Allow-Origin": "*"
        }
    }

이 페이지의 내용이 도움이 되었나요?

RESTful API 배포