이 글은 AWS Lambda를 이용해서 HTTP API 만들기 #1에서 이어진 글이다.
API Gateway
Amazon API Gateway는 API 서비스의 입구를 담당해 주는 서비스로 보통 웹 애플리케이션을 만들 때 컨트롤러 혹은 라우팅 부분에서 URL과 Method를 정의하고 각 파라미터 등을 정의한 뒤 어디에 연결할지를 지정할 수 있는 서비스이다. API Gateway를 다양하게 사용할 수 있겠지만(많이 써보진 안 써봤다.) 여기서는 Lambda function에 연결하는 목적으로만 사용하겠다.
HTTP POST API
Lambda를 만들기 위해 새로운 API를 만들어 보자.
새로 API를 만들었으므로 루트 경로 /
만 존재한다. 이제 API를 구성하기 위해 리소스(Resource)와 메소드(Method)를 만들 수 있다. 보통 Restful API를 만들 때 /teams
, /teams/:team-name
, /players
처럼 URL을 구성할 리소스 기반으로 정의하게 되는데 그때의 그 리소스이다. 사실 별로 의식하고 있지 않아서 그렇지 URL, URI도 각각 Uniform Resource Locator, Uniform Resource Identifiers이다. Method는 리소스에 액션을 정의하기 위해서 사용하는 GET, POST, PUT, DELETE를 의미한다.
간단한 테스트이므로 리소스를 따로 만들지 않고 루트에 POST 메서드를 만들고 앞에서 만든 HelloWorld
Lambda Function과 연결했다.
방금 만든 POST API가 간단히 도식화해서 나타난다. 화살표대로 따라가면 Client가 요청을 보내면 Method에서 요청을 받아서 Lambda에 연결한 뒤 Lambda Function이 응답을 그대로 클라이언트에 보내준다. 응답은 모델을 만들어서 연결하고나 형식을 정의할 수 있다.
Client에 있는 "Test" 버튼을 누르면 API를 바로 테스트해볼 수 있다. POST 요청의 바디에 JSON을 넣어서 테스트하면 Lambda Function이 실행되어 아까와 같은 결과가 반환되는 것을 볼 수 있다. 이제 API 동작을 확인했으므로 실제 사용할 수 있게 해야 하는데 그전에 먼저 인증을 걸어야 한다. 앞의 스크린샷에서 눈치챘는지 모르지만, 방금 만든 POST 메서드는 Auth설정이 NONE으로 되어 있다. 누구나 사용해도 된다면 상관없지만 보통 Lambda 사용량에 따라 돈을 내야 하므로 인증을 걸어서 원하는 곳에서만 사용 가능하게 해야 한다.
위의 메서드 도식화면에서 Method Request 부분 클릭하면 요청에 대해 설정을 할 수 있다. 여기서 원하는 쿼리스트링이나 헤더도 설정할 수 있지만, 지금은 인증만 설정했다. 여기서 AWS의 IAM을 사용하려면 Authorization를 설정하면 되는데 HTTP API 목적으로 만들 예정이므로 API 키를 true
로 설정했다.
이제 다 만들었으므로 API를 배포하면 된다. 지금까지처럼 메서드를 추가하고 구성한다고 바로 적용되는 것이 아니라 직접 API 서버를 구축했을 때처럼 배포해야 적용이 된다.
배포를 하려면 스테이지를 만들어서 배포해야 하는데 기존에 만들어 놓은 스테이지가 없다면 새로 만들어서 배포해야 한다. 여기서 스테이지는 서비스를 만들 때 소스를 개발 서버, 스테이지 서버, 리얼 혹은 프로덕션 서버 순으로 배포하듯이 원하는 스테이지를 만들어 놓고 똑같이 사용할 수 있다. 여기서는 test라는 스테이지를 만들었다.
새로운 스테이지를 만들고 API를 배포했으므로 URL이 할당되었다. 여기서 만들어진 URL이 이 API의 URL이고(이 글이 나갈 때쯤에 삭제된 API이므로 시도해 보지 않아도 된다.) 캐시나 로깅, 스로틀링 등을 설정할 수 있다. 해보진 않았지만, iOS나 Android의 SDK도 만들어 준다.
앞에서 API 키를 연결했지만 API 키는 아직 안 만들었다. 상단 메뉴에서 API Keys에 가면 API 키를 생성할 수 있다.
API를 생성하고 활성화한 뒤에 앞에서 만든 API와 스테이지에 연결설정을 하면 해당 API에서 API 키를 사용할 수 있다.
$ curl -X POST https://edzgyy0dx8.execute-api.ap-northeast-1.amazonaws.com/test \
-d "{"myname":"Outsider"}"
{"message": "Forbidden"}
$ curl -X POST https://edzgyy0dx8.execute-api.ap-northeast-1.amazonaws.com/test \
-d '{"myname":"Outsider"}' \
-H "x-api-key: XXXXXXXXXXXXXXXXXXXXXXXXXXXX"
"Hello World, Outsider"
curl로 API를 테스트해보면 정상 동작을 한다. API 키가 없으면 403으로 막히고 API 키를 헤더로 보내면 Lambda가 연동된 결과가 잘 반환된다. API 키는 x-api-key
헤더에 담아서 보내야 한다.
HTTP GET API
GET 메서드보다 POST 메서드를 먼저 만든 이유는 Lambda에서 JSON을 받게 되어 있으므로 POST에서는 JSON을 body에 바로 전달하면 설정이 훨씬 간단하기 때문이다. GET 요청의 경우 쿼리스트링으로 데이터를 보내므로 이 값이 Lambda에 전환되지 않는다. 이제 POST와 같게 GET 메서드를 만들어 보자.
GET 메서드를 추가한 뒤 Method Request에 들어가면 URL Query String Parameters을 설정할 수 있다. 값을 자동으로 받지 않으므로 필요한 파라미터를 여기서 설정해야 한다.(API 키는 POST처럼 true
로 설정한다.) name
이라는 쿼리스트링을 추가했다. Lambda에서는 myname
이라는 값을 쓰지만 쿼리스트링에서는 보통 간단한 이름을 사용하기도 하고 헷갈리지 않게 다른 이름을 사용했다. 이 쿼리스트링은 Lambda function에 전달할 JSON으로 변환하기 위한 설정을 해야 한다.
이 설정은 API 메서드를 Lambda와 연결하는 Integration Request에서 설정할 수 있다. Body Mapping Templates에서 application/json
의 Content-Type
를 가진 매핑템플릿을 추가한다. 기본은 Input Passthrough로 하는 경우 그냥 전달되지만 여기서는 변환을 해야 하므로 Mapping Template를 선택하고 템플릿 내용을 다음 코드로 설정하면 된다.
{ "myname": "$input.params('name')" }
이는 Lamdba function에 보내는 {"myname":""}
의 형태를 만든 것인데 myname
의 값을 입력으로 받은 $input.params('name')
로 설정한 것이다. 이 값에는 GET 요청의 name
쿼리스트링으로 전달받은 값이 들어간다.
설정이 완료하고 테스트를 해보면 잘 동작하는 것을 알 수 있다. 이제 다시 배포만 하면 된다. 앞에서 만든 test 스테이지에 배포를 하고 HTTP 요청을 보내면 확인해 볼 수 있다.
$ curl https://edzgyy0dx8.execute-api.ap-northeast-1.amazonaws.com/test?name=Outsider \
-H "x-api-key: XXXXXXXXXXXXXXXXXXXXXXXXXXXX"
"Hello World, Outsider"
아주 간단한 API 두 개를 만드는데 아주 긴 설명을 했지만, 서버를 하나도 띄우지 않고 HTTP API를 만들었다. 설명은 아주 길게 했지만 실제로 하면 간단한 설정일 뿐이다.(초기에는 삽질이 좀 필요하겠지만 ) 예제는 아주 간단한 API였지만 실제로는 꽤 수준 높은 부분까지도 서버 구성을 하나도 하지 않고 이 조합으로 만들 수 있을 거라고 생각한다. 그래서 요즘 이 serverless 아키텍처에 관심이 있다.
실례가 안된다면 질문 좀 드려도 될까요?
예전에 글쓰셨던 것을 보고 저도 블로그를 해보려고하는데
아웃사이더 님 처럼 개인블로그를 따로 만드는게 나은가요?
Tistory naver 는 제한되는 부분이 많다고해서요
혹시 추천해주실만한 방법이 있으신가요!?
저는 좀 자유롭게 할 수 있다는 부분에서 설치형을 추천하는 편입니다.(일단 제가 그렇게 사용하고 있고 설치형이라서 마음대로 필요에 따라 수정할 수 있는 장점이 있어서요.) 하지만 보통은 블로그 형식보다는 글을 쓰는게 더 중요하기 때문에 블로그에 타입보다 글쓰는데 집중하기를 권합니다. 글을 쓰다가 습관화(?)되기 전에 그만 두는 경우가 더 많아서요. 설치형은 돈이나 노력도 드는데 처음 하시는거라면 서비스형을 쓰시면서 글쓰는 습관을 보시기를 권해드리지만 네이버 블로그 보다는 워드프레스나 미디엄 등의 서비스형이 더 쓰시기 좋다고 생각합니다. 티스토리는 자유도가 얼마나 되는지 잘 모르겠네요.
감사합니다. 덕분에 aws lambda와 api gateway에 대해서 감잡고 갑니다.
네 저도 올해 계속 좀 보려고 하는데 시간이 많이 안나네요.
감사합니다. 잘 읽었습니다 ^^ 궁금한것이 있는데 DB랑 연동하는 것을 참조할만한 글이 있을까요?
Lambda내에서 디비랑 연동하는 건 그냥 Node.js나 Python에서 사용하는것과 크게 다를게 없습니다. RDS나 디비를 두시고 드라이버로 접근하시면 될겁니다.
후우.. 계속 POST API를 curl로 테스트 하는 단계에서
```
curl -X POST https://myapi_id.execute-api.us-west-2.amazonaws.com/prod/HelloWorld -d "{"key1": "This is the value from key1"}" -H 'x-api-key: my_api_key'
```
이런 식으로 curl을 계속 날려보는데,
```
{"message": "Could not parse request body into json: Unexpected character (\'\\\' (code 92)): was expecting double-quote to start field name\n at [Source: [B@537f69bd; line: 1, column: 3]"}
```
와 같은 에러가 발생해서 이곳저곳 찾아보니,
```
(A) : "{"key1": "This is the value from key1"}"
(B) : "{\"key1\": \"This is the value from key1\"}"
```
request body를 (A)처럼 구성하면 안 되고 (B)처럼 작성해놨어야 하네요.
lambda function 작성 언어는 python3.x이고, HelloWorld Blueprint를 사용했습니다.
request body를 json 형태로 편히 보내려면 function에서부터 애초에 따옴표를 '\ + 따옴표'로 해석하도록 하는코드를 넣어야 하는 걸까요. 아직 초보라 이 부분에 시간을 좀 썼네요ㅜ
음 이부분은 curl에 따른 것이라서 응답받는 쪽에서 하실 필요는 없어보입니다. 정확히 하시는 부분은 테스트해봐야겠지만 JSON은 무조건 쌍따옴표를 키와 값에 써야합니다. 여기서는
"{"key1": "This is the value from key1"}"
처럼 하셨기 때문에 요청데이터가 "{"에서 끝나고 그다음에 key라는 문자열이 온것으로 curl이 인식하고 아마 서버에는"{"만 전달되지 않았을가 합니다. 당연히 JSON 파싱은 실패하고요. 말씀하신대로 이스케잎을 하면 잘 동작하는데 이는 받는 쪽이 아니라 요청에서의 문제이므로 작성하신 코드를 수정하지 않으셔도 됩니다.
이는 쌍따옴표를 열고 닫는 문제라 다음과 같이 하셔도 됩니다.
'{"key1": "This is the value from key1"}'