안경잡이개발자

728x90
반응형

  AWS EC2 인스턴스를 생성한 뒤에, 거기에 접속해서 MySQL, Apache, PHP를 설치하여 연동하는 방법에 대해서 소개하겠습니다.

 

AWS EC2 인스턴스 생성

 

1. AWS EC2 관리 페이지로 이동합니다.

 

  EC2 관리 페이지: https://ap-northeast-2.console.aws.amazon.com/ec2/v2/home

 


2. [인스턴스 시작] 버튼을 눌러 [Ubuntu Server 18.04 LTS (HVM), SSD Volume Type]로 인스턴스를 생성합니다.

 


3. 무료 유형인 [t2.micro] 인스턴스 유형을 선택한 뒤 [검토 및 시작] 버튼을 누릅니다. 이후에 인스턴스를 시작할 수 있도록 하면 됩니다.

 


4. 키 페어를 생성해 저장한 뒤에 [인스턴스 시작] 버튼을 누릅니다.

 

 

인스턴스 접속


1. 아까 EC2 접속 목적의 키 페어 파일을 준비합니다.

 


2. 키 페어에 적절한 권한을 줍니다.


  1) 리눅스: 다음의 명령어로 권한을 설정합니다.

chmod 400 {키 페어 파일 이름.pem}


  2) 윈도우: 키 페어를 우클릭 한 뒤에 [속성] - [보안] [고급] - [상속 사용 안 함] - [명시적 사용] - [관리자 제외하고 모든 그룹 삭제]를 진행합니다. 이후에 관리자 권한으로 CMD를 실행합니다.

 

 

3. SSH 명령어로 서버에 접속합니다. 서버 접속 주소는 [Connect] 버튼을 누르면 나옵니다.

ssh -i "{키 페어 파일 이름.pem}" ubuntu@{AWS EC2 서버 URL}

 

 

  저는 다음과 같은 명령어를 확인할 수 있습니다.

 

 

  이제 다음과 같이 SSH로 AWS EC2 서버에 접속해보겠습니다.

 

 

MySQL 서버 설치 및 환경설정


1. AWS EC2 서버에 접속합니다.


2. MySQL를 설치하고 접속합니다.

sudo apt update # MySQL 설치를 위해 업데이트
sudo apt install mysql-server # MySQL 서버 설치
sudo mysql -u root -p # MySQL 접속

 


3. MySQL 접속 이후에 비밀번호를 설정합니다.

use mysql;
CREATE USER '{생성할 사용자 이름}'@'%' IDENTIFIED BY '{비밀번호}'; # 생성할 사용자 비밀번호 설정
GRANT ALL PRIVILEGES ON *.* TO '{사용자 이름}'@'%'; # 외부 접속이 가능하도록 설정
FLUSH PRIVILEGES;
exit;

 


4. MySQL 설정 파일을 수정합니다.

cd /etc/mysql/mysql.conf.d;
sudo vi mysqld.cnf;

 

  (이 때 만약 vi가 설치되어 있지 않다면 다음과 같이 vim을 설치합니다.)

 


5. mysqld.cnf를 수정하여 MySQL의 외부 접속을 허용합니다.

bind-address = 0.0.0.0


6. 수정 이후에 Mysql를 재시작 합니다.

sudo service mysql restart

 

MySQL 외부 접속 테스트


1. AWS EC2 보안 그룹 페이지로 이동합니다.

 

2. 보안 그룹 설정을 진행합니다. [인바운드] - [편집] - [규칙 추가] - [MySQL/Aurora] - 허용 IP로 [0.0.0.0/0]를 설정합니다.

 


3. MySQL Workbench와 같은 MySQL 관리 도구를 이용하여 접속합니다.

Hostname: {AWS EC2의 공인 IP 주소}
Username: {생성한 사용자 이름}
Password: {비밀번호}

 


4. MySQL에 접속하여 간단한 쿼리를 날립니다.

select now();

 

 

Apache 웹 서버 설치 및 환경설정


1. Apache 웹 서버를 설치합니다.

 

sudo apt-get install apache2

 

 

2. AWS EC2 보안 그룹 페이지로 이동합니다.


3. 보안 그룹 설정을 진행합니다. [인바운드] - [편집] - [규칙 추가] - [HTTP] - 허용 IP로 [0.0.0.0/0]를 설정합니다.

 


4. 공인 IP로 접속하여 웹 서버가 정상적으로 구동하는지 확인합니다.

 

 

5. apache2.conf 설정 파일을 엽니다.

sudo vi /etc/apache2/apache2.conf

 

6. 루트 디렉토리를 [/home/project]로 변경합니다.

<Directory /home/project/>
        Options Indexes FollowSymLinks
        AllowOverride None
        Require all granted
</Directory>

 


7. default.conf 설정 파일 열기

sudo vi /etc/apache2/sites-available/000-default.conf

 

8. 루트 디렉토리를 [/home/project]로 변경합니다.

DocumentRoot /home/project

 


9. /home/project 폴더 생성 및 index.html 파일을 생성합니다.

sudo mkdir /home/project
sudo vi /home/project/index.html


10. index.html 파일을 작성합니다.

Hello World!


11. Apache 웹 서버 재시작

sudo apachectl restart


12. 공인 IP로 접속하여 웹 서버가 정상적으로 구동하는지 확인합니다.

 

 

PHP 웹 서버 설치 및 Apache 연동

 

1. 간단히 옛날 버전의 PHP 5.6를 설치해보겠습니다.

sudo add-apt-repository ppa:ondrej/php
sudo apt-get update
sudo apt-get install -y php5.6

 

  (이 때 만약 add-apt-repository가 설치되어 있지 않다면 다음과 같이 설치합니다.)

 


2. 설치된 PHP 버전을 확인합니다.

php -version

 


3. Apache와 PHP 연동 테스트

sudo vi /home/project/index.php


4. PHP 소개 페이지를 작성합니다.

<?php phpinfo();?>


5. 공인 IP의 /index.php로 접속하여 PHP 연동이 제대로 됐는지 확인합니다.

 

  (index.html 파일을 지워야 할 수 있습니다.)

 

 

PHP와 MySQL 연동


1. MySQL 연동 라이브러리를 설치합니다.

sudo apt install php5.6-mysql
sudo service mysql restart
sudo apachectl restart


2. 설치된 패키지를 확인합니다.

dpkg -l | grep php

 

 

3. index.php 소스코드를 작성합니다.

 

<?php
$conn = mysqli_connect(
  '{데이터베이스 IP}',
  '{사용자 이름}',
  '{비밀번호}',
  '{데이터베이스 이름}',
  '3306');
if (mysqli_connect_errno())
{
  echo "Failed to connect to MySQL: " . mysqli_connect_error();
}
$sql = "SELECT VERSION()";
$result = mysqli_query($conn, $sql);
$row = mysqli_fetch_array($result);
print_r($row["VERSION()"]);
?>

 

 

4. 접속 결과를 확인합니다.

 

 

  정상적으로 MySQL, PHP, Apache가 설치되어 연동이 모두 완료되었습니다.

 

728x90
반응형

728x90
반응형

  지금까지 AWS Lambda를 이용하는 방법에 대해서 알아보았습니다. 다만 단순히 AWS Lambda를 이용하여 코딩하고, 서비스를 개발하면 다양한 어려움에 봉착할 수 있습니다. 특히 AWS Lambda를 이용하면, 프로그램의 크기가 커질 때 직접 웹 사이트에서 코딩을 하는 것이 어려워집니다. 뿐만 아니라 API 게이트웨이 등을 주기적으로 설정해주어야 한다는 점에서 손이 많이 가게 됩니다.

 

  이럴 때 Serverless Framework를 이용하게 되면, AWS Lambda를 보다 손쉽게 이용할 수 있습니다. 직접 수작업으로 설정하던 것과 비교했을 때, 복잡한 작업을 간단한 명령어로 대신 처리할 수 있도록 해주기 때문에 매우 유용합니다.

 

  Serverless Framework를 이용하기 위해서는, IAM 사용자 설정을 해주셔야 합니다. 따라서 AWS 콘솔에 로그인을 한 뒤에 [My Security Credentials] 탭으로 이동합니다.

 

 

  이후에 [IAM Users]를 설정할 수 있도록 합니다.

 

 

  Serverless 프레임워크(Framework)의 실습을 위하여 간단히 하나의 사용자를 추가할 수 있습니다. 따라서 [Add user]를 눌러주세요.

 

 

  이후에 다음과 같이 사용자 이름을 설정한 뒤에 [Programmatic access]에 체크합니다. 그러면 프로그래밍 환경에서 AWS 계정 권한에 접근할 수 있게 됩니다.

 

 

  또한 해당 IAM 사용자에게 권한을 설정해 줄 수 있습니다. 다음과 같이 [AdministratorAccess] 권한을 부여하시면 됩니다. 이 권한은 말 그대로 AWS 계정과 동일한 수준의 권한을 의미합니다. 따라서 향후 만들어 질 IAM 사용자의 Secret Key 값을 다른 사람에게 노출하게 되면, 심각한 문제가 발생할 수 있으므로 반드시 유의하여 Secret Key 값을 관리하셔야 합니다.

 

 

  결과적으로 다음과 같이 사용자를 생성할 수 있습니다.

 

 

  리뷰를 완료하고, 사용자를 최종적으로 생성합니다.

 

 

  사용자가 생성되면 Access Key ID와 Secret Access Key 값이 주어집니다. 이 때 Secret Access Key 값은 반드시 유의하여 저장하셔야 합니다. 이 값을 다른 사람에게 노출하게 되면, 악의적인 의도를 가진 해커가 이를 악용하여 우리의 AWS 계정을 마음대로 다룰 수 있습니다. 예를 들면 무작위로 EC2 인스턴스를 생성해서 하루 만에 1억 가까이 과금이 되도록 할 수도 있겠죠? 따라서 매우 매우 조심하셔야 합니다.

 

 

  이제 Serverless Framework를 위한 AWS IAM 사용자가 만들어졌습니다. 이제 Serverless 프레임워크를 이용해 보겠습니다. 가장 먼저 serverless 모듈을 설치하셔야 합니다. 다음과 같은 명령어로 설치할 수 있습니다.

 

npm install -g serverless

 

 

  Serverless 프레임워크(Framework)는 기본적으로 다양한 템플릿을 제공하고 있습니다. 이러한 템플릿을 이용하면, 자신이 원하는 프로그래밍 언어를 선택하여 빠르게 개발을 진행할 수 있습니다. 다음의 명령어를 입력하여 템플릿(Template) 종류를 확인합시다.

 

serverless create --template

 

 

  이 중에서 가장 기본적인 Node.js 프로젝트 형태를 가지고 있는 aws-nodejs라는 템플릿을 이용해보도록 하겠습니다. 다음의 명령어를 입력하면, 현재 폴더에 aws-nodejs 템플릿의 파일들을 생성하게 됩니다.

 

serverless create --template aws-nodejs

 

 

  실제로 현재 폴더를 확인해 보시면, handler.js와 serverless.yaml 파일이 생성되어 있습니다.

 

  - handler.js: 실제로 API 역할을 수행하는 Node.js 소스코드입니다.

  - serverless.yaml: Serverless 프레임워크의 기본적인 설정 파일입니다.

 

 

  이제 Serverless 프레임워크(Framework)를 사용하기 위하여 AWS 계정 정보를 설정합니다. 아래와 같은 명령어로 AWS 계정 정보를 설정하면, 배포 할 때 해당 AWS 계정으로 배포하게 됩니다.

 

serverless config credentials --provider aws --key {Key ID} --secret {Secret Key}

 

 

  이제 실제로 API를 위한 소스코드를 작성해보도록 하겠습니다. 따라서 handler.js 코드를 수정합니다.

 

'use strict';

module.exports.hello = async (event) => {
  return {
    statusCode: 200,
    body: JSON.stringify({
      message: '성공적으로 배포가 완료되었습니다!',
      input: event,
    }, null, 2),
  };
};

 

  이후에 실질적인 배포 설정을 위하여 serverless.yaml 코드를 수정합니다. 서울(Seoul) 지역(Region)에 Node.js 10 버전으로 배포를 진행하는 것입니다. 또한 handler.js에 있는 hello 함수를 api/hello라는 경로의 GET 방식으로 배포하게 됩니다.

 

service: serverless-nodejs-study

provider:
  name: aws
  runtime: nodejs10.x
  region: ap-northeast-2

functions:
  hello:
    handler: handler.hello
    events:
      -  http:
            path: api/hello
            method: get

 

  이후에 배포를 진행합니다. 배포는 다음과 같은 명령어로 수행할 수 있습니다. deploy 명령어를 수행하면, 현재 폴더에 있는 serverless.yaml 파일에 적혀 있는 설정에 의하여 배포가 진행됩니다.

 

serverless deploy

 

 

  또한 배포가 완료되면, 각 HTTP 메소드(Method) 별로 호출할 수 있는 URL 주소가 나옵니다. 실제로 URL 주소로 접속해 보면 다음과 같이 우리가 작성한 API가 실행되는 것을 확인할 수 있습니다.

 

 

  이후에 AWS 콘솔에 접속하여, 실제로 배포가 완료된 AWS Lambda 함수를 확인할 수 있습니다.

 

 

  뿐만 아니라 작성한 API 소스코드의 길이가 짧기 때문에, AWS Lambda 소스코드를 직접 수정할 수도 있군요.

 

 

728x90
반응형

728x90
반응형

  이번 시간에는 AWS Lambda와 MongoDB를 활용하여 하나의 게시물(Board) API를 완전히 개발해보는 시간을 가져보도록 하겠습니다. 따라서 가장 먼저, 다음과 같이 /board 경로에 GET, POST, PUT, DELETE 메소드의 API를 Gateway로 열어줍니다. 그리고 /board/{proxy+} 경로의 모든 메소드를 사용할 수 있도록 합니다.

 

  /board/{proxy+}로 설정하면, 모든 Path Parameter를 받을 수 있게 됩니다.

 

 

  이후에 소스코드를 작성합니다. 우리가 작성할 게시물 Rest API의 기본적인 명세는 다음과 같습니다.

 

[ 게시물 ]

 

- 게시물 번호

- 게시물 작성자

- 게시물 비밀번호

- 게시물 내용

- 게시물 작성일자

 

  소스코드는 다음과 같습니다.

"use strict";
const mongoose = require('mongoose');
const MONGODB_URI = process.env.MONGODB_URI;

/* Board 오브젝트를 정의합니다. */
const boardSchema = mongoose.Schema({
    id: {
        type: Number,
        required: true
    },
    name: {
        type: String,
        required: true
    },
    password: {
        type: String,
        required: true
    },
    content: {
        type: String,
        required: true
    },
    date: {
        type: Date,
        required: true
    }
});

/* 하나의 연결 객체를 반복적으로 사용합니다. */
let connection = null;

const connect = () => {
  if (connection && mongoose.connection.readyState === 1) return Promise.resolve(connection);
  return mongoose.connect(process.env.MONGODB_URI, { useNewUrlParser: true }).then(
    conn => {
      connection = conn;
      return connection;
    }
  );
};

module.exports.handler = (event, context, callback) => {
  let operation = event.httpMethod;
  let Board = mongoose.model('board', boardSchema);
  let proxy, password;
  switch (operation) {
    case 'GET':
      /* 
        경로: /board
        설명: 전체 게시글 정보를 불러옵니다.
      */
      if(event.pathParameters === null) {
        let query = {};
        if(event.queryStringParameters.name) {
          query.name = {$regex:event.queryStringParameters.name, $options: 'i'};
        }
        if(event.queryStringParameters.content) {
          query.content = {$regex:event.queryStringParameters.content, $option: 'i'};
        }
        // name과 content를 이용하여 검색한 결과를 내림차순으로 반환합니다.
        connect().then(() =>
        Board.find(query)
        .select("-password")
        .sort({id: -1})
        .exec(function(error, boards) {
            if(error) {
              context.done(null, { 'statusCode': 500, 'body': JSON.stringify(error) });
            }
            else {
              context.done(null, { 'statusCode': 200, 'body': JSON.stringify(boards) });
            }
        }));
      }
      /* 
        경로: /board/:id
        설명: 특정 게시글 정보를 불러옵니다.
      */
      else {
        proxy = event.pathParameters.proxy;
        connect().then(() =>
        Board.findOne({id:proxy})
        .select("-password")
        .exec(function(err, board) {
            if(err) {
              context.done(null, { 'statusCode': 500, 'body': JSON.stringify(err) });
            }
            else if(!board) {
              context.done(null, { 'statusCode': 500, 'body': JSON.stringify("Board not found.") });
            }
            else {
              context.done(null, { 'statusCode': 200, 'body': JSON.stringify(board) });
            }
        }));
      }
      break;
    case 'POST':
      /* 
        경로: /board
        파라미터: {"name":"작성자","content":"내용","password":"비밀번호"}
        설명: 특정 게시글을 작성합니다.
      */
      let lastId = 0;
      // 가장 최근에 작성된 게시물 번호를 가져옵니다.
      connect().then(() =>
      Board.findOne({})
        .sort({id: -1})
        .exec(function(err, board) {
          if(err) {
            context.done(null, { 'statusCode': 500, 'body': err });
          } else {
            lastId = board ? board.id : 0;
            const { name, content, password } = JSON.parse(event.body);
            const newBoard = new Board({ name, content, password });
            newBoard.date = new Date();
            newBoard.id = lastId + 1;
            // 새로운 글을 등록합니다.
            newBoard.save(function(err, board) {
              if(err) {
                context.done(null, { 'statusCode': 500, 'body': JSON.stringify(err) });
              } else {
                context.done(null, { 'statusCode': 200, 'body': JSON.stringify(lastId + 1) });
              }
            });
          }
        })
      );
      break;
    case 'PUT':
      /* 
        경로: /board/:id
        헤더: password:"현재 비밀번호"
        파라미터: {"name":"작성자","content":"내용","password":"비밀번호"}
        설명: 특정 게시글을 수정합니다.
      */
      proxy = event.pathParameters.proxy;
      password = event.headers.password;
      // 사용자가 입력한 번호의 게시물을 찾습니다.
      connect().then(() =>
      Board.findOne({id:proxy})
      .exec(function(err, board) {
          if(err) {
              context.done(null, { 'statusCode': 500, 'body': JSON.stringify(err) });
          }
          else if(!board) {
              context.done(null, { 'statusCode': 500, 'body': JSON.stringify("Board not found.") });
          }
          else {
              if(board.password != password) {
                context.done(null, { 'statusCode': 500, 'body': JSON.stringify("Password is incorrect.") });
              } else {
                  const { name, content, password } = JSON.parse(event.body);
                  // 사용자가 입력한 name, content, password에 맞게 정보를 변경합니다.
                  Board.findOneAndUpdate({id:proxy}, { name, content, password })
                  .exec(function(err, board) {
                      if(err) {
                        context.done(null, { 'statusCode': 500, 'body': JSON.stringify(err) });
                      }
                      else {
                        context.done(null, { 'statusCode': 200, 'body': JSON.stringify('success') });
                      }
                  });
              }
          }
      }));
      break;
    case 'DELETE':
      /* 
        경로: /board/:id
        헤더: password:"현재 비밀번호"
        설명: 특정 게시글을 삭제합니다.
      */
      proxy = event.pathParameters.proxy;
      password = event.headers.password;
      connect().then(() =>
      Board.findOne({id:proxy})
      .exec(function(err, board) {
          if(err) {
              context.done(null, { 'statusCode': 500, 'body': JSON.stringify(err) });
          }
          else if(!board) {
              context.done(null, { 'statusCode': 500, 'body': JSON.stringify("Board not found.") });
          }
          else {
              if(board.password != password) {
                context.done(null, { 'statusCode': 500, 'body': JSON.stringify("Password is incorrect.") });
              } else {
                // 사용자가 입력한 번호에 해당하는 게시물을 삭제합니다.
                Board.findOneAndRemove({id:proxy})
                .exec(function(err, board) {
                    if(err) {
                      context.done(null, { 'statusCode': 500, 'body': JSON.stringify(err) });
                    }
                    else {
                        context.done(null, { 'statusCode': 200, 'body': JSON.stringify('success') });
                    }
                });
              }
          }
      }));
      break;
    default:
      callback(new Error(`Unrecognized operation "${operation}"`));
  }
};

 

  테스트 결과는 다음과 같습니다.

 

1. 게시글 리스트 보기


경로: /board 
설명: 전체 게시글 정보를 불러옵니다. 

 

 

 

2. 게시물 정보 확인하기


경로: /board/:id 

설명: 특정 게시글 정보를 불러옵니다. 

 

 

3. 게시물 등록하기


경로: /board 
파라미터: {"name":"작성자","content":"내용","password":"비밀번호"} 
설명: 특정 게시글을 작성합니다. 

 

 

4. 게시물 수정하기

 

경로: /board/:id
헤더: password:"현재 비밀번호" 
파라미터: {"name":"작성자","content":"내용","password":"비밀번호"} 
설명: 특정 게시글을 수정합니다. 

 

 

5. 게시물 삭제하기


경로: /board/:id 
헤더: password:"현재 비밀번호" 
설명: 특정 게시글을 삭제합니다. 

 

728x90
반응형

728x90
반응형

  이번 시간에는 간단히 AWS Lambda와 MongoDB를 연동하는 방법에 대해서 알아보도록 하겠습니다. 우리는 추후에 결과적으로 간단한 형태의 게시판 게시글 API를 만들어볼 것입니다. AWS Lambda와 MongoDB 모두 구축이 매우 간단하고, 편리하기 때문에 이 두 서비스를 한꺼번에 이용하는 경우가 많습니다.

 

  따라서 바로  Atlas 회사의 MongoDB를 구축해보도록 하겠습니다. MongoDB는 다음의 포스팅에서 제가 간단히 소개한 바 있습니다. https://ndb796.tistory.com/302

 

Atlas 무료 MongoDB 사용 방법

공부 목적으로 MongoDB를 이용하시는 분들은 Atlas 회사의 MongoDB 서비스를 가장 많이 사용합니다. 이번 포스팅에서는 Atlas 회사의 무료 MongoDB를 사용하는 방법에 대해서 소개하도록 하겠습니다. ▶ Atlas Mon..

ndb796.tistory.com

  위 과정은 매우 금방 따라하실 수 있을 거고, 최종적으로 만들어진 MongoDB의 접속 문자열(String)을 메모장에 기록해 놓은 뒤에 다음의 과정을 따라오시면 됩니다.

 

  더불어 Atlas에서는 공식 AWS Lambda와 Atals MongoDB를 연동하는 예제를 제공하고 있으므로 이를 참고하시면 좋습니다. https://docs.atlas.mongodb.com/best-practices-connecting-to-aws-lambda/

 

Best Practices Connecting from AWS Lambda — MongoDB Atlas

Best Practices Connecting from AWS Lambda MongoDB recommends the following best practices to optimize connection times between AWS Lambda and Atlas: Define the MongoDB client connection to the MongoDB server outside the AWS Lambda handler function. This ma

docs.atlas.mongodb.com

 

  가장 먼저 AWS Lambda의 환경 변수(Environment Variables)로 MONGODB_URI을 설정합니다. 이 때 <password> 부분에 자신의 MongoDB 비밀번호를 넣어주셔야 합니다.

 


이제 Lambda 소스코드를 작성합니다.

 

"use strict";

const mongoose = require('mongoose');
const MONGODB_URI = process.env.MONGODB_URI;

module.exports.handler = (event, context, callback) => {
  console.log('event: ', event);
  
  /* Database 설정 및 변수 초기화 */
  mongoose.Promise = global.Promise;
  mongoose.connect(process.env.MONGODB_URI, { useNewUrlParser: true });
  const db = mongoose.connection;
  db.once('open', function () {
      console.log('Database Connected.');
  });
};

 

  이후에 코드를 [Save]하고 [Test] 버튼을 눌러서 아무렇게나 테스트 설정을 하여 테스트를 해보시면, 아래 쪽에 Cannot find module 'mongoose'라는 메시지가 나오게 됩니다. 이는 Mongoose 라이브러리가 AWS Lambda에 기본적으로 포함되어 있지 않기 때문에 발생하는 문제입니다.

 

 

  따라서 우리가 AWS Lambda에 특정한 라이브러리를 추가하여 사용하고 싶다면, 직접 Node.js 라이브러리를 압축하여 AWS Lambda에 올려야 합니다. 따라서 특정한 폴더에서 npm install mongoose로 MongoDB 라이브러리를 설치합니다.

 


  그리고 nodejs라는 이름의 폴더를 만들고, 그 안에 node_modules 폴더를 둡니다. 그리고 library라는 이름의 폴더를 하나 더 만들어서 nodejs 폴더를 그 안에 둡니다. AWS Lambda에 업로드 하게 되는 압축 파일의 루트 폴더는 nodejs라는 이름을 가진다고 약속되어 있어요. 

 

 

  이후에 library 폴더를 압축합니다. 이후에 그 압축 파일을 AWS Lambda의 Layer에 올립니다. 이 때 Runtime이 자신의 AWS Lambda의 Runtime과 일치하는지 확인할 수 있도록 합니다. (Node.js 8.10 버전인 경우 Layer Runtime도 8.10을 선택합니다.)

 

 

  이제 다시 AWS Lambda로 돌아가서, 우리의 Lambda 함수를 클릭한 뒤에 [Add a layer]를 눌러 레이어를 추가합니다.

 

 

  결과적으로 다음과 같이 업로드한 라이브러리를 선택해서 [Add] 버튼을 누르시면 됩니다.

 

 

  이제 이 상태에서 다시 AWS Lambda를 [Save]하고 [Test]하면 이제 Mongoose를 사용할 수 있게 되는 것을 알 수 있습니다.

 

 

  다만 이 때 위와 같이 정상적으로 DB 접근이 되지 않아서, 'Database Connected.' 라는 로그(Log)가 출력되지 않으면, Atlas에서 DB White List 설정을 확인하실 필요가 있습니다. 혹은 MongoDB 비밀번호가 제대로 입력이 되어 있는지 확인하실 필요가 있습니다.

 

  기본적으로 AWS Lambda의 IP를 Atlas의 White List에 등록해야 사용할 수 있습니다. 하지만 AWS Lambda는 유동 IP 정책을 따르고 있어서, 일일히 모든 IP를 White List에 등록할 수 없습니다. 그래서 흔히 VPC를 이용하여 고정 IP를 할당할 수 있습니다.  다만 이렇게 하면 어렵고, 비용도 많이 발생할 수 있어요. 따라서 그냥 실습 과정에서는 MongoDB에서 모든 IP를 허용할 수 있도록 할 수 있습니다. 따라서 Atlas의 MongoDB에서 [CONNECT]를 눌러 연결 정보를 확인합니다.

 

 

  이후에 다음과 같이 [IP Whitelist tab]을 누릅니다.

 

 

  이후에 다음과 같이 모든 IP 주소에서 접속할 수 있는 Entry를 추가하시면 됩니다.

 

 

  이제 다음과 같이 정상적으로 AWS Lambda를 실행하면, 3초 가량의 시간이 흐른 뒤에 'Database Connected.'라는 메시지가 출력됩니다. 다만 DB 연결과 관련하여 많은 시간이 소요되며, Error Message를 뱉을 수 있습니다.

 

 

  그래서 실제로 코딩을 할 때에는 다음과 같은 방식으로 코드 맨 아래에서 context.done() 함수를 통해 결과를 돌려주며 함수를 마치도록 해야 합니다.

 

"use strict";
const mongoose = require('mongoose');
const MONGODB_URI = process.env.MONGODB_URI; // or Atlas connection string
module.exports.handler = (event, context, callback) => {
  console.log('event: ', event);
  /* Database 설정 및 변수 초기화 */
  mongoose.Promise = global.Promise;
  mongoose.connect(process.env.MONGODB_URI, { useNewUrlParser: true });
  const db = mongoose.connection;
  db.once('open', function () {
      console.log('Database Connected.');
  });
  context.done(null, { 'statusCode': 200 });
};

 

  이제 결과적으로 200번 응답 코드가 출력되는 것을 알 수 있습니다. 다만 'Database Connected.'라는 로그 메시지는 출력되지 않고, 바로 종료되는 것을 알 수 있습니다. 이제 이러한 구조에서 실제로 DB로부터 데이터를 조회하는 등의 작업을 수행하면, 그 때 정상적으로 동작하게 됩니다. 이상으로 이번 시간에는 AWS Lambda와 MongoDB의 연동 방법에 대한 개괄적인 내용을 다루어 보았습니다.

 

 

728x90
반응형

728x90
반응형

  AWS에서 API Gateway와 Lambda를 활용해 REST API를 구축해보는 시간을 가져보도록 하겠습니다. 초기 설정부터 차근차근 개발하게 되므로 Lambda Function부터 만들어야 합니다. [Create Function] 버튼을 누릅니다.

 

 

  가장 기본적인 형태인 Node.js 함수로 만들겠습니다. 또한 사용자 관련 API라는 의미에서 다음과 같이 함수 이름도 지어 보았습니다.

 

 

  실제로 사용자 API를 DB 연동까지 하려면 귀찮은 작업이 될 것입니다. 따라서 다음과 같이 클라이언트의 메소드(Method)의 종류에 따라서 단순히 응답하는 함수를 만들었습니다. GET 방식POST 방식에 대해서만 작업했습니다.

 

exports.handler = (event, context, callback) => {
    const operation = event.httpMethod;
    switch (operation) {
        case 'POST':
            callback(null, {
                'statusCode': 200
            });
            break;
        case 'GET':
            data = {
                'id': 1,
                'name': '나동빈'
            }
            callback(null, {
                'statusCode': 200,
                'headers': {},
                'body': JSON.stringify(data)
            });
            break;
        default:
            callback(new Error(`Unrecognized operation "${operation}"`));
    }
};

 

  다음과 같이 람다 함수를 작성한 뒤에 [Save]를 꼭 해주셔야 적용됩니다.

 

 

  이제 이러한 람다 함수를 실행할 수 있도록 API Gateway 페이지에서 [Create API]로 API를 만들겠습니다.

 

 

  우리가 만들 API는 REST API이므로 [REST]를 선택하고, 이름을 설정하여 만들어 주시면 됩니다.

 

 

  이제 API 프로젝트가 만들어지면, 바로 [Create Resource] 버튼을 눌러서 리소스(Resource)를 만들 수 있습니다.

 

 

  이후에 다음과 같이 user라는 이름의 리소스를 생성하겠습니다. 일반적으로 REST API에서는 실제 데이터의 종류에 따라서 리소스를 만들게 됩니다. 다시 말해 사용자에 대한 처리를 위해 user라는 리소스를 정의하는 식입니다.

 

 

  이제 [Create Method]를 눌러서 실제로 해당 user 리소스에 대한 메소드를 설정합니다.

 

 

  GET 방식의 메소드를 만든 뒤에 Lambda Function을 선택하여 함수 이름을 적습니다. 또한 [Use Lambda Proxy Integration]에 체크합니다.

 

 

  이제 [Deploy API]를 누르면 실제로 API Gateway가 동작합니다.

 

 

  일반적으로 API를 개발할 때는 버전별로 API를 관리합니다. API Gateway는 그러한 기능을 지원하고 있습니다. 따라서 저는 다음과 같이 오늘 날짜인 2019-04-24라는 이름으로 API 배포를 진행하겠습니다.

 

 

  배포 이후에 AWS Lambda 페이지로 이동해 보시면, 실제로 API 게이트웨이가 람다 함수 앞에 붙게 됩니다. 이제 해당 URL로 접속해 보도록 하겠습니다.

 

 

  저는 다음과 같이 Restlet 확장 프로그램을 이용하여 접속해보았습니다. 그 결과 정상적으로 내용이 출력됩니다.

 

 

  만약 메소드(Method)를 PUT 방식으로 설정하는 등, API Gateway에서 지정하지 않은 방식을 이용하려고 하면 오류가 발생합니다.

 

 

  이제 POST 방식의 메소드 또한 추가하겠습니다.

 

 

  마찬가지로 람다 함수를 적용하고, [Use Lambda Proxy Integration]에 체크합니다.

 

 

  이후에 배포까지 진행합니다.

 

 

  POST 메소드 적용 이후에는 다음과 같이 [POST] 방식으로 요청을 보냈더니 정상적으로 응답이 돌아옵니다.

 

 

  그렇다면 자바 스크립트 등의 웹에서 해당 API를 이용하려면 어떻게 하면 될까요? 간단히 다음과 같은 HTML 소스코드를 작성해서 테스트 해보도록 하겠습니다.

 

<!doctype html>
<html>
  <head>
    <meta charset="UTF-8">
  </head>
  <body>
    <input type="button" value="GET" onClick="get()"/>
    <script>
      const API_URL = 'URL';
      function get() {
        fetch(API_URL, {
          headers: {
            'Accept': 'application/json'
          }
        }).then(res => res.json())
          .then(data => console.log(data))
          .catch(err => console.error(err));
      }
    </script>
  </body>
</html>

 

  실행 결과 다음과 같이 [GET] 버튼을 누르면 CORS 관련 오류가 발생합니다.

 

 

  따라서 GET 메소드에서 [Enable CORS]를 눌러서 CORS를 활성화 해주시면 됩니다.

 

 

  기본 설정 그대로 CORS 설정을 진행하시면 됩니다.

 

 

  이후에 다시 [Deploy API]를 눌러서 배포하시면 됩니다.

 

 

  이제 최종적으로 Lambda 함수에서 응답 헤더(Header)에 Access-Control-Allow-Origin을 추가하시면 됩니다.

 

        case 'GET':
            data = {
                'id': 1,
                'name': '나동빈'
            }
            callback(null, {
                'statusCode': 200,
                'headers': {'Access-Control-Allow-Origin': '*'},
                'body': JSON.stringify(data)
            });
            break;

 

  이제 다시 버튼을 누르면 매우 정상적으로 동작합니다.

 

 

728x90
반응형

728x90
반응형

  이번 시간에는 AWS Lambda(Node.js)와 AWS DynamoDB를 연동하는 예제를 다루어 보도록 하겠습니다. DynamoDBNoSQL 데이터베이스로, 종합 관리(Fully Managed)된다는 특징이 있습니다. 또한 데이터는 자동으로 복제되기 때문에 상대적으로 안전합니다. 그리고 배포와 확장이 단순하다는 장점이 있습니다.

 

  DynamoDB는 간단한 모바일 API를 구축하기 위한 목적으로 이용하면 효과적입니다. 간단히 실습을 해봅시다. 바로 AWS Lambda 서비스 화면으로 이동하여 하나의 함수(Function)을 만듭시다.

 

 

  저는 Node.js로 기본 람다 함수를 만들었습니다.

 

 

  이후에 [API Gateway]를 하나 선택해서 달아줍니다. 그러면 자동으로 해당 람다 함수에 접근할 수 있는 API URL이 생성되고, 이를 관리할 수 있게 됩니다.

 

 

  설정은 다음과 같이 [Open with API Key]를 선택해서, 클라이언트가 API Key를 이용해 람다 함수에 접근할 수 있도록 합니다.

 

 

  [Add] 버튼을 누른 이후에는 [Save]를 해주셔야 합니다. 그러면 다음과 같이 API에 접근할 수 있는 URL이 생성됩니다. 또한 우리는 API Key를 이용해 접근하도록 설정하여, API Key 또한 출력됩니다.

 

 

  이제 다음과 같이 REST API 테스트 도구를 이용하여 테스트할 수 있습니다. 저는 Restlet을 이용했습니다. API Key를 보낼 때에는 HTTP Header에 x-api-key라는 이름으로 키 값을 보내주어야 합니다.

 

 

※ DynamoDB와 연동하기 ※

 

  이제 DynamoDB와 연동하기 위해서 DynamoDB에서 하나의 테이블을 생성해보도록 합시다.

 

 

  간단히 사용자 정보가 담기는 테이블을 만들어 보겠습니다. 테이블 이름은 User, 기본키로는 id를 넣어줍니다.

 

 

  이후에 실습을 위해 하나의 아이템(Item)을 넣어주겠습니다. [Create Item] 버튼을 누릅니다.

 

 

  저는 다음과 같이 하나의 사용자 데이터를 넣어보았습니다.

 

 

  이제 AWS Lambda 소스코드를 수정합니다. 기본 테스트 양식은 다음과 같습니다. dynamodb-doc이라는 라이브러리를 이용하면 간단히 DynamoDB와 통신할 수 있습니다. 기본적으로 GET 방식으로 사용자가 접근했을 때, User 테이블에 있는 모든 정보를 그대로 JSON 형식으로 출력하도록 만들었습니다.

 

const doc = require('dynamodb-doc');
const dynamo = new doc.DynamoDB();

exports.handler = (event, context, callback) => {
    const operation = event.httpMethod;
    const payload = {
        TableName: '{테이블 이름}'
    }
    switch (operation) {
        case 'POST':
            dynamo.putItem(payload, callback);
            break;
        case 'PATCH':
            dynamo.updateItem(payload, callback);
            break;
        case 'DELETE':
            dynamo.deleteItem(payload, callback);
            break;
        case 'GET':
            dynamo.scan(payload, (err, data) => {
                callback(null, {
                    'statusCode': 200,
                    'headers': {},
                    'body': JSON.stringify(data)
                });
            });
            break;
        default:
            callback(new Error(`Unrecognized operation "${operation}"`));
    }
};

 

  [Save] 버튼을 눌러 저장한 뒤에, 한 단계만 더 거치면 테스트할 수 있습니다.

 

 

  우리가 거칠 단계는 AWS Lambda의 권한입니다. 기본적으로 AWS Lambda에서는 DynamoDB에 접근할 권한이 없습니다. 그렇기 때문에 [Execution role] 탭에서 권한 설정을 확인해야 합니다.

 

 

  다음과 같이 정책 부분에서 [Edit Policy] 버튼을 눌러서 정책을 수정할 수 있습니다.

 

 

  다음과 같이 [Add additional permissions] 탭을 눌러서 DynamoDB 접근 권한을 추가할 수 있도록 합니다.

 

 

  저는 다음과 같이 DynamoDB에 대한 모든 권한을 허용했습니다.

 

 

  이후에 저장까지 잘 해주시면 됩니다. 이제 아까와 동일한 요청을 다시 하게 되면 다음과 같이 나옵니다.

 

 

728x90
반응형

728x90
반응형

  하나의 람다 함수(Lambda Function)에서 특정한 라이브러리를 이용하는 경우, 라이브러리를 포함하는 압축 파일(.ZIP) 통째로 람다 함수로 업로드를 하면 라이브러리를 손쉽게 이용할 수 있습니다. 하지만 라이브러리를 포함하여 업로드하는 경우 3MB를 넘어가기 때문에 AWS Lambda 사이트에서 직접 소스코드를 수정하는 것은 불가능했습니다. 소스코드를 변경해야 할 일이 생기면 소스코드를 수정한 뒤에 다시 압축해서 Lambda에 업로드해야 했습니다.

 

  더군다나 하지만 여러 개의 람다 함수에서, 공통된 라이브러리를 이용하는 경우에는 매 번 라이브러리를 압축해서 올려야 한다는 단점이 있습니다. 여러 개의 람다 함수가 공통적으로 사용하는 라이브러리를 일괄적으로 관리할 수 있다면 더욱 편리하겠죠.

 

  2018년 12월부터는 Lambda Layers 기능이 지원되기 때문에 공통된 라이브러리를 Lambda Layers에 올리고, Lambda Function에서는 이를 단순히 불러와 사용할 수 있게 되었습니다. (2018년 12월 이전에는 얼마나 불편했을지 상상이 안 가네요.) 말 그대로 의존성을 효과적으로 관리할 수 있게 되었습니다.

 

  실습을 위해 하나의 Lambda Function을 생성합니다. 예제 코드는 Python으로 작성하겠습니다. 예제 함수에서는 간단한 크롤링 프로그램을 구현할 것입니다. 크롤링을 위해서는 b4라는 라이브러리가 필요합니다.

 

 

  일단 API Gateway를 붙여서 API 테스트를 해보겠습니다.

 

 

  다음과 같이 [Open]으로 보안 설정을 해서 누구든지 API에 접근할 수 있도록 합니다.

 

 

  이후에 [Save]를 눌러 적용합니다. 그리고 API Gateway의 URL을 확인하여 접속합니다.

 

 

  접속 결과, Lambda의 기본 코드에 의해서 다음과 같이 출력됩니다.

 

 

  이제 Lambda Function의 코드를 다음과 같이 바꿉니다. bs4라는 라이브러리를 이용해서 크롤링을 수행합니다.

 

import json
import urllib.request
from bs4 import BeautifulSoup

def lambda_handler(event, context):
    url = "https://www.google.com"
    soup = BeautifulSoup(urllib.request.urlopen(url).read(), "html.parser")
    a_tags = soup.find_all("a")
    result_list = []
    for i in a_tags:
        result_list.append(i.get_text())
    return {
        'statusCode': 200,
        'body': json.dumps(result_list)
    }

 

  이제 코드를 [Save]하여 URL에 접속해 봅시다.

 

 

  당연히 라이브러리가 없으니 오류가 발생합니다. 따라서 로컬에서 pip3 명령을 이용해 bs4를 설치합니다. -t 옵션을 이용하면 특정한 경로에 bs4 라이브러리를 다운로드 할 수 있습니다.

 

 

  라이브러리 다운로드가 완료되면 이를 하나의 폴더에 담아주어야 합니다.

 

 

 

  python이라는 이름의 폴더를 만들어, 모든 라이브러리 폴더를 python 폴더 안으로 넣습니다. 폴더 이름이 python이어야 AWS Lambda에서 이를 성공적으로 인식할 수 있습니다.

 

 

  이후에 이 python 폴더를 포함하는 폴더를 압축합니다. 다시 말해, 압축 파일의 상위 폴더가 python이 되어야 합니다. 저는 다음과 같이 bs4.zip라는 이름의 압축 파일로 압축했습니다. 

 

 

  이제 압축된 파일을 확인해 보시면, 상위 폴더 이름이 python인 것을 확인할 수 있습니다.

 

 

  이제 AWS Lambda 서비스의 [Layers] 탭으로 가서 [Create Layer]로 레이어를 생성합니다.

 

 

  다음과 같이 레이어 이름을 설정하여 생성합니다.

 

 

  이제 람다 함수의 Layer로, 방금 생성한 Layer를 넣습니다.

 

 

  다음과 같이 설정합니다.

 

 

  이후에 람다 함수의 [Save]를 진행하면, 이제 API에 접속했을 때 성공적으로 크롤링 된 결과가 출력됩니다.

 

728x90
반응형

728x90
반응형

  AWS SNS의 문자 메시지(SMS) 전송 기능을 이용하다 보면, 때때로 다양한 문제에 직면할 수 있습니다. 그러한 문제에 적절히 대응하기 위해서는 AWS SMS 기능에 대한 로그(Log) 관리가 필요합니다. 로그 관리는 Text messaging (SMS) 탭에서 [Edit] 버튼을 눌러서 수행할 수 있습니다.

 

 

  여기에서는 가장 먼저 Amazon SNS가 CloudWatch Logs에 로그를 작성할 SMS 전송 성공 비율을 지정합니다. 예를 들어, 전송 실패에 대한 로그만 작성하려면 이 값을 0으로 설정합니다. 전송 성공의 10%에 대한 로그를 작성하려면 이 값을 10으로 설정합니다. 비율을 지정하지 않으면 Amazon SNS는 모든 전송 성공에 대한 로그를 작성합니다.

 

  저는 100%으로 설정했으므로 모든 문자 메시지(SMS) 전송에 대해서 로그에 기록합니다. 그리고 [Create new service role] 탭을 클릭하고 [Create new roles] 버튼을 눌러 새롭게 규칙을 설정할 수 있습니다.

 

 

  다음과 같이 SNSSuccessFeedback 규칙 이름 그대로 생성합니다.

 

 

   이제 다음과 같이 IAM 규칙(Role)이 설정이 되어서 이제 CloudWatch의 로그(Log)가 쌓이게 됩니다.

 

 

※ S3 로그 저장 ※

 

  AWS 문자 메시지(SMS) 로그는 S3에 저장할 수도 있습니다. 이를 위해서 AWS S3 서비스에서 [Create Bucket]을 눌러서 하나의 새로운 버킷(Bucket)을 만들 수 있습니다.

 

 

  버킷의 이름을 설정해 생성합니다.

 

 

  이후에 [Edit Text Messaging Preferences] 탭에서 해당 AWS S3 버킷을 Report 전송용으로 설정하면 됩니다. 그러면 매일매일의 SMS 사용량 Report가 S3에 전송된다는 특징이 있습니다.

 

 

  다만 이 경우 AWS SNS가 해당 버킷(Bucket)에 쓰기 권한이 있어야 하기 때문에, 간단히 S3의 [Bucket Policy]를 설정해보도록 하겠습니다. [Policy Generator]를 눌러서 정책을 생성합니다.

 

 

  먼저 자신의 AWS Bucket의 경로(ARN)에 Put Object 액션(Action)을 부여합니다.

 

 

  이후에 자신의 AWS Bucket의 경로(ARN)에 GetBucketLocation 액션(Action)을 부여합니다.

 

 

  그리고 [Generate Policy] 버튼을 눌러서 정책 명령어를 생성할 수 있습니다.

 

 

  이제 다시 AWS S3의 [Permissions] 탭에서 버킷 정책(Bucket Policy)을 바꿀 수 있도록 설정합니다.

 

 

  이제 [Bucket Policy] 탭에 정책 내용을 붙여넣기 하여 적용하면 됩니다.

 

 

728x90
반응형

728x90
반응형

  AWS에서 몇몇 서비스는 의도치 않은 과금을 막기 위해 서비스 한도가 정해져 있습니다. 대표적으로 AWS SNS의 문자 메시지(SMS) 전송 서비스는 기본적으로 한 달에 1달러($) 어치만 사용할 수 있도록 제한되어 있습니다. 그래서 아무 의심 없이 AWS SMS 기능을 구현해 놓았는데, 기본 한도인 1달러를 초과하여 앱 사용자에게 문자 메시지가 가지 않아서 낭패를 보는 경우가 있습니다.

 

  이럴 때는 AWS 지원 센터에 방문해서 특정한 서비스의 한도를 증가하겠다고 요청 문서를 작성하시면 됩니다.

 

▶ AWS 지원 센터(Support Center)https://console.aws.amazon.com/support/home

 

  AWS 지원 센터에서 [Create Case] 버튼을 눌러서 요청 글을 작성할 수 있습니다.

 

 

  이후에 [Service Limit Increase] 탭을 누르고, 원하는 서비스를 선택할 수 있습니다. 저는 AWS [SMS Text Messaging] 서비스의 한도를 높이고자 합니다.

 

 

  이후에 [General Limits]를 선택한 뒤에 한계 값(Limit Value)을 설정할 수 있습니다. 일반적으로 이 값은 늘리고자 하는 수치를 의미합니다. 저는 AWS SMS를 한 달에 400 달러까지 사용할 수 있다고 적은 것입니다.

 

 

  이후에 [Case Description] 탭에서는 어떻게 서비스를 사용할 것인지, 한도를 증가시키는 이유는 무엇인지 등에 대한 정보를 적으면 됩니다.

 

 

  일반적으로 다음과 같은 형태로 영어 문장을 작성해서 보내시면 됩니다. 제가 실제로 사용했던 문장 양식입니다.

 

"We require an SMS limit increase of {금액} USD per month to make sure of the serviceability of our application ({웹 사이트 주소}) The app provides a {제공하는 서비스} service that uses SMS messaging for a one-time password verification. Users are required to submit and verify their mobile numbers upon the sign-up process. Our customers are located in worldwide countries such as Korea, United States, Singapore, India, etc. We’re expecting up to {사용량} SMS to be sent out everyday."

 

  이후에 [Contact Options] 탭에서 회신을 받을 언어 등을 설정해서 [Submit]을 진행하시면 됩니다.

 

 

(+ 추가)

 

  위 문장 양식으로 SMS 한도 증가 요청을 보냈더니 AWS 측에서 [사용하고자 하는 API 지역(Region)], [고객에게 보낼 문자 메시지 템플릿(Template)]을 추가 정보로 요청했습니다. 따라서 다음과 같은 두 가지 정보를 추가적으로 적어서 보내시면 좋을 것 같습니다.

 

1) Which AWS region will the user be calling our API/service from: Tokyo

2) Message templates for this country:

 

"Welcome to {서비스 이름}, your verification code is ({번호})."

 

  일반적으로 국내에서 AWS SMS 기능을 이용하시는 분은 Tokyo를 설정합니다. 그래서 위와 같이 추가적인 내용도 함께 보내시면 좋을 것 같습니다. 보내지 않으시면 AWS 서포트 센터 측에서 추가적인 정보를 요청하고, 이에 대해 응답하는 과정에서 5일 정도 시간이 소요될 수 있으므로 미리 보내시면 좋습니다. (AWS의 서포트 센터는 그렇게 빠르게 응답을 해주지 않습니다. 따라서, 가능하면 한 번에 끝낼 수 있도록 AWS가 필요해 할 만한 모든 정보를 기입하면 좋아요.)

 

  아무튼 AWS SMS 지출 한도 승인을 완료되었을 때는 다음과 같이 AWS에서 연락이 옵니다. (저는 400달러로 한도를 설정했습니다.)

 

 

  이후에 다음과 같이 AWS SMS 탭으로 이동하여 한도를 늘여 주시면 됩니다.

 

 

  또한 설정 이후에 문자 메시지 기능이 즉시 동작하지는 않을 수 있습니다. 저는 한 10분 이상 보내지지 않았습니다. 그 당시에 "No quota left for account"라는 로그 메시지가 출력되었다가, 시간이 지나니까 정상적으로 문자 메시지가 보내지더군요.

728x90
반응형

728x90
반응형

※ AWS S3에 접근하기 ※

 

  실습을 위하여 하나의 AWS Lambda 함수를 생성해 봅시다.

 

 

  AWS Lambda는 기본적으로 Boto3와 같은 라이브러리를 내장하고 있습니다. 따라서 별도로 Boto3를 AWS Lambda에 업로드 하지 않아도 AWS의 다양한 기능을 이용할 수 있다는 장점이 있습니다. 바로 다음과 같이 현재 자신의 AWS 계정에 포함되어 있는 모든 S3 버킷(Bucket) 리스트를 출력하도록 할 수 있습니다.

 

import json
import boto3
import botocore

s3 = boto3.resource('s3')

def lambda_handler(event, context):
    result_list = []
    for bucket in s3.buckets.all():
        result_list.append(bucket.name)
    return {
        'statusCode': 200,
        'body': json.dumps(result_list)
    }

 

  이후에 트리거를 붙여서 실행 결과를 확인할 수 있습니다.

 

 

  트리거를 구성한 이후에 [저장]하면 됩니다.

 

 

  다만, 지금 바로 실행하면 S3에 접근할 수 없어서 오류가 발생할 수 있습니다. AWS Lambda가 S3에 접근할 수 있는 권한이 있는지 확인해야 합니다. 따라서 [실행 역할]에서 IAM 계정 정보를 확인합니다.

 

 

 

  이후에 새로운 정책으로 AmazonS3FullAccess를 추가합니다.

 

 

  권한을 추가하면 됩니다.

 

 

  이후에 API 게이트웨이URL에 접속하여 결과를 확인할 수 있습니다.

 

 

  결과는 다음과 같습니다.

 

 

※ AWS S3 버킷(Bucket)에서 파일 읽기 ※

 

  실습을 위해 퍼블릭(Public)한 상태의 AWS S3 버킷이 필요합니다.

 

참고 사이트:  https://ndb796.tistory.com/280

 

  따라서 하나의 AWS S3 버킷에 파일을 업로드 해봅시다.

 

 

  저는 다음과 같이 텍스트 파일을 하나 업로드 해보았습니다.

 

 

  이제 다음과 같이 소스코드를 작성합니다. 버킷(Bucket) 이름과, 파일 객체(Key) 이름을 각각 이용하면 됩니다. 저는 다음과 같이 get_object() 함수를 이용하여 파일의 내용을 출력하도록 만들었습니다. 다만 read() 함수를 이용해서 파일을 읽으면, 읽은 데이터가 바이트(Byte) 형태로 기록됩니다. 따라서 이를 UTF-8으로 디코드하여 출력하도록 했습니다.

 

import json
import boto3
import botocore

BUCKET_NAME = 's3-temp-dongbin'
KEY = 'test.txt'

s3_client = boto3.client('s3')

def lambda_handler(event, context):
    data = s3_client.get_object(Bucket=BUCKET_NAME, Key=KEY)
    content = data['Body'].read()
    print(content)
    return {
        'statusCode': 200,
        'body': json.dumps(content.decode('UTF-8'))
    }

 

  이제 실행 결과 다음과 같이 정상적으로 파일의 내용인 "Hello World"가 출력되었습니다.

 

 

728x90
반응형