목차
이 글은 서버리스 프레임워크를 쓰는 법,
아마존 람다를 통해 다이나모 디비로부터
데이터 불러오는 법을 다루고 있습니다.
(데이터 저장은 이 글을 참고해주세요 2편 - 아마존 DynamoDB 시작하기. CRUD부터 Scan까지 )
이 글을 끝까지 읽으면 아주 기초적인 서버리스 모바일(플러터) 앱을 만들어 볼 수 있습니다.
1.Serverless framwork
1.1. Serverless framwork 시작하기
람다와 다이나모DB를 연결하기 전에 서버리스 프레임워크를 먼저 알아보도록 할께요.
웹 콘솔에서 aws 람다, api 게이트웨이 설정을 하는 건 불편하죠.
이를 코드로 편하게 배포할 수 있게 하는게 Serverless framwork (링크) 입니다.
1.1.1 Serverless 설치
npm install serverless -g설치가 잘 되었는지 확인하려면 serverless -v 를 쳐보세요. 버전 확인이 되면 성공입니다.
서버리스 프레임워크로 aws를 관리하려면 접근 권한을 줘야해요.
1.1.2 AWS IAM에서 자격증명(Credential) 만들기
- 우선 AWS에 접속해서 IAM 페이지에 들어가 주세요.
유저를 하나 추가합니다.

- 유저를 추가할 때 꼭 Programmatic access를 체크해줘야 됩니다. (아래 그림 참고)

유저 추가를 쭉 하다보면 접근 키(Access key, Secret key)를 받을 수 있는 페이지가 나옵니다.
키를 다운받은 다음에 잘 보관해주세요. 이 키들을 서버리스 프레임워크에서 쓸 겁니다.

키를 받고 나면 iam 페이지에서 나오도록 합시다.
1.1.3. Serverless 접근 권한 주기
아까 받은 키들을 서버리스에서 설정해주세요.
xxxxxxxxxxserverlsss loginserverless config --provider aws --key 액세스키 --secret 시크릿액세스키
1.1.4. serverless 기본 템플릿 만들어보기
서버리스 프레임워크는 템플릿을 써서 기본적인 애플리케이션을 만들 수 있습니다.
xxxxxxxxxxserverless create -t aws-nodejs -p serverless-startcd serverless-start
템플릿을 만들면 파일 2개가 생깁니다. handler.js와 serverless.yml 입니다.
handler.js - 람다 실행 코드
x'use strict';module.exports.hello = async (event, context) => { return { statusCode: 200, body: JSON.stringify({ message: 'Go Serverless v1.0! Your function executed successfully!', input: event, }), }; // Use this code if you don't use the http event with the LAMBDA-PROXY integration // return { message: 'Go Serverless v1.0! Your function executed successfully!', event };};
serverless.yml - 서버리스 프레임워크 설정파일
xxxxxxxxxxservicesls-startprovider nameaws runtimenodejs8.10functions hello handlerhandler.hello // handler.js에 있는 hello 함수를 실행
serverless.yml을 조금 수정하고 배포를 해보겠습니다.
1.1.5. serverless.yml 수정 및 배포
serverless.yml - 서버리스 프레임워크 설정파일
xxxxxxxxxxservicesls-startprovider nameaws runtimenodejs8.10 regionap-northeast-2functions hello handlerhandler.hello events// rest api를 만들거니 events는 http로 하겠습니다.http pathhello methodget
이제 serverless deploy 를 터미널에 입력해봅시다.
성공적으로 배포되면 아래처럼 뜰 거에요.
xxxxxxxxxxendpoints: GET - https://kzf2871d.execute-api.ap-northeast-2.amazonaws.com/dev/hellofunctions: hello: sls-start-dev-helloGET 옆의 url을 브라우저에 붙여넣기해 봅시다.
응답이 나오면 성공이에요!
이제 다이나모 DB와 람다를 연결해보겠습니다.
이제부터 쓸 데이터는 저번글 에서 다이나모 DB에 넣었던 Food 테이블의 데이터입니다.
Food 테이블에 데이터가 들어가 있다고 생각하고 설명할께요.
2 DynamoDB 와 람다 연결
2.1. 설정하기
다이나모DB를 쓰려면 사용 권한(iam role 지정)이 있어야해요.
serverless.yml 을 수정해주도록 합시다.
xxxxxxxxxxservicesls-startprovider nameaws runtimenodejs8.10 stagedev regionap-northeast-2 iamRoleStatementsEffect"Allow" Actiondynamodb:* Resource"*"
배포를 해볼께요.
xxxxxxxxxxsls deploy
배포가 성공적으로 되었나요?
이제 다이나모 디비에 접근할 수 있습니다!
이를 위한 람다함수를 만들어보도록 할께요.
다 완성되면 아래 그림 같은 구조가 됩니다.

2.2 람다함수 추가하기
프레임워크 설정에다 어떤 람다 파일을 쓸지 추가해줄께요.
serverless.yml
xxxxxxxxxxprovider nameaws runtimenodejs8.10 stagedev regionap-northeast-2 iamRoleStatementsEffect"Allow" Actiondynamodb:* Resource"*"functions hello handlerhandler.hello eventshttp pathhello methodget get handlerapi/recipes.get eventshttp pathrecipes methodget list handlerapi/recipes.list eventshttp pathrecipes/type methodget
functions 부분에 get, list 함수를 추가했습니다.
get함수는 쿼리 스트링(queryString)에 따라 값을 불러오는 함수이구요.
list함수는 음식의 종류(type)에 따라 데이터를 불러옵니다.
api 폴더에 recipes.js를 추가하고 배포해 볼께요.
api/recipes.js
xxxxxxxxxx'use strict';const AWS = require('aws-sdk');AWS.config.update({ region: 'ap-northeast-2', endPoint: 'http://dynamodb.ap-northeast-2.amazonaws.com'});const docClient = new AWS.DynamoDB.DocumentClient();const TableName = 'Food';module.exports.get = async (event, callback) => { let queryParam = event.queryStringParameters; let type = queryParam['type']; const name = queryParam['name']; const params = { TableName : TableName, Key : { "type": type, "name": name, } } const result = await docClient.get(params).promise(); const response = { statusCode: 200, body: JSON.stringify(result) } return response;}module.exports.query = async (event, callback) => { const { type } = event.pathParameters; const params = { TableName: TableName, KeyConditionExpression: "#type = :type", ExpressionAttributeNames: { "#type": "type" }, ExpressionAttributeValues: { ":type": type } } const result = await docClient.query(params).promise(); const response = { statusCode: 200, body: JSON.stringify(result['Items']) } console.log(result); return response;}
배포를 해볼께요.
xxxxxxxxxxsls deploy
배포가 끝났나요?
이제 함수가 제대로 동작하는지 테스트 해볼께요.
2.3.1 동작 테스트 - GET 함수
우선 GET 함수는 아래의 주소를 브라우저에 붙여 넣어주세요.
xxxxxxxxxxhttps://kzf2871d.execute-api.ap-northeast-2.amazonaws.com/dev/recipes?type=Western&name=CarbonaraDB에 데이터가 있다면 아래처럼 나올 거에요.
xxxxxxxxxx{ "Item": { "ingredients": { "garlic": "1", "bacon": "50g", "spaghetti": "90g", "egg": "1" }, "price": 13000, "name": "Carbonara", "type": "Western", "orderCount": 50 }}
2.3.2 동작 테스트 - list 함수
list 함수를 테스트 해볼께요.
xxxxxxxxxxhttps://kzf2871d.execute-api.ap-northeast-2.amazonaws.com/dev/recipes/Western
type이 Western인 아이템을 모두 조회하는 걸 알 수 있습니다.
xxxxxxxxxx[ { "ingredients": { "garlic": "1", "bacon": "50g", "spaghetti": "90g", "egg": "1" }, "price": 13000, "name": "Carbonara", "type": "Western", "orderCount": 50 }, { "ingredients": { "honey maid graham cracker crumbs": "1 3/4 cups", "eggs": "3", "butter, melted": "1/3 cup", "sugar, divided": "1 1/4 cups", "philadelphia cream cheese": "3 packages(8 ounce)", "vanilla": "2 teaspoons vanilla" }, "price": 13000, "name": "Cheese cake", "type": "Western", "orderCount": 77 }]
3.플러터 앱에서 보여주기
이번엔 API Gateway와 플러터 앱을 연결해 보도록 하겠습니다.
앱은 최대한 간단히 만들어보도록 할께요.
앱은 3개의 클래스로 구성되어 있습니다.
- UI를 그리는 위젯 클래스(main.dart)
- 데이터 저장 하는 모델 클래스( recipe.dart)
- http로 데이터 요청하는 api 클래스 (api.dart)
플러터 구조

일단 http package를 설정 파일에 추가해주세요.
dart 2.0 부터는 http 패키지를 항상 추가해줘야합니다.
pubspec.yaml
xxxxxxxxxxversion1.0.0+1environment sdk">=2.0.0-dev.68.0 <3.0.0"dependencies flutter sdkflutter cupertino_icons^0.1.2 http^0.12.0dev_dependencies flutter_test sdkflutter
AWS로부터 받은 데이터를 저장하는 클래스를 만들어볼께요.
recipe.dart
xxxxxxxxxxclass Recipe { String name; String type; int orderCount; int price; Map<String, dynamic> ingredients; Recipe({this.price, this.name, this.type, this.orderCount, this.ingredients}); Recipe.fromJson(Map<String, dynamic> json) : // json데이터를 클래스 형식으로 변환 name = json['name'], type = json['type'], price = json['price'], orderCount = json['orderCount'], ingredients = json['ingredients'];}
api.dart
xxxxxxxxxximport 'dart:async';import 'dart:convert';import 'package:http/http.dart' as http;import 'model/recipe.dart';class API { String baseUrl = "https://kzf2871d.execute-api.ap-northeast-2.amazonaws.com/dev"; final client = http.Client(); Future<Recipe> fetchRecipe(String type, String name) async { var path = "/recipes?type=${type}&name=${name}"; var url = baseUrl + path; final response = await client.get(url); // http get 메소드로 리퀘스트를 보냅니다. var data = json.decode(utf8.decode(response.bodyBytes)); // utf8로 디코딩해야 한글이 깨지지 않습니다. return Recipe.fromJson(data['Item']); }}response data를 utf8로 디코딩해야 한글이 깨지지 않습니다.
main.dart
xxxxxxxxxximport 'package:flutter/material.dart';import 'package:flutter_api_gateway/model/recipe.dart';import 'api.dart';void main() => runApp(MyApp());class MyApp extends StatelessWidget { Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: MyHomePage(title: 'Flutter Demo Home Page'), ); }}class MyHomePage extends StatefulWidget { MyHomePage({Key key, this.title}) : super(key: key); final String title; API api = API(); _MyHomePageState createState() => _MyHomePageState();}class _MyHomePageState extends State<MyHomePage> { bool loadRecipe = false; Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.start, children: <Widget>[ loadRecipe ? FutureBuilder<Recipe>( future: widget.api.fetchRecipe("Western", "Carbonara"), builder: (context, snapshot) { if (snapshot.hasData) { Recipe recipe = snapshot.data; print(recipe.price); return recipeView(recipe); } else { return Text("no data!"); } }, ) : Text("data is not loaded!"), ], ), ), floatingActionButton: FloatingActionButton( // 버튼을 누르면 데이터를 불러옵니다. onPressed: () { setState(() { loadRecipe = true; }); }, tooltip: 'Data Load', child: Icon(Icons.add), ), // This trailing comma makes auto-formatting nicer for build methods. ); } Widget recipeView(Recipe recipe) { return Container( padding: const EdgeInsets.symmetric(vertical: 18.0, horizontal: 9.0), child: Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ Text("음식종류 : ${recipe.type}"), Text("음식이름 : ${recipe.name}"), Text("가격 : ${recipe.price}") ], ), decoration: BoxDecoration( color: Colors.grey.withOpacity(0.2), borderRadius: BorderRadius.all( Radius.circular(10.0), ), ), ); }}
버튼을 누르면 데이터가 화면에 표시됩니다.

생각보다 마지막편 업데이트가 늦었네요!
AWS 서버리스 시리즈는 3편으로 마무리하고
추후 새로운 내용이 있으면 연재하도록 하겠습니다.
다음에는 플러터 글로 찾아뵙도록 하겠습니다!
'서버' 카테고리의 다른 글
| 서버리스 앱 개발하기 2 - 아마존 DynamoDB 시작하기. CRUD부터 Scan까지 (2) | 2018.12.11 |
|---|---|
| 서버리스 앱 개발하기 1 - AWS Lambda와 AWS Gateway 연결 (6) | 2018.11.27 |