AWS로 Serverless web application 만들기 (2) - Lambda

예전에 django 나 cakephp로 했던 일들을 처리해줄 것이 Lambda 다.
서버 인스턴스 없이 함수로 존재할 Lambda로 포스트 등록, 조회, 수정, 삭제 등의 프로세스를 담당시킬 예정이다.

Lambda 는 node.js 나 java, c# 으로도 만들 수 있지만, 익숙한 python 으로 만들어보기로 했다. 사실 문서나 샘플은 대부분 node.js 위주로 되어 있긴 한데, 그래도 이번엔 python 으로 시도.

Lambda - New Function 선택하고 Select Blueprint 에서 일단 Blank Function 선택.



Configure triggers 에서 API Gateway 와 연결해주고 이전에 만들었던 API Gateway 이름을 골라준다. Deployment Stage 는 일단 prod 로 잡아주고, Security 는 AWS IAM 으로 설정.

Configure function 에서 Lambda 이름 정해주고, Runtime 은 Python 2.7로 선택했다. Code entry type 에서 직접 에디팅할 것인지 zip 파일을 업로드할 것인지 정할 수 있는데, 일단 테스트는 인라인으로 하고, 나중에는 zip 파일 올려서 처리하는 것으로.


by mine | 2017/06/10 15:22 | 트랙백 | 덧글(0)

AWS로 Serverless web application 만들기 (1) - API Gateway

일단 AWS 계정은 있다고 치고, 첫 단계인 API Gateway 부터 세팅해보자.
나는 AWS 에 익숙하지 않으니 당연히 이 포스트는 강좌가 아니고, 잘못된 부분이 있을 수도 있다. 최대한 제대로 했기만을 바랄 뿐. :p

과거에는 조회 요청은 무조건 GET, form 은 POST를 썼지만, 이번에는 GET, POST, PUT, DELETE method 를 최대한 목적에 맞게 써보기로 했다.

/posts 
  • GET: 리스트 조회
  • PUT: 포스트 작성
/posts/{postid}
  • GET: 특정 포스트 조회
  • POST: 특정 포스트 수정
일단 간단하게 이 정도부터 설정 시작.

API Gateway 메뉴에서 APIs - Create API 선택하고 API 이름 입력. JHPosts 라는 조악한 이름으로 하나 생성했다.

생성한 JHPosts 하위에 리소스들을 만들어보자.


JHPosts - Resources - Create Resource 에서 posts 란 이름으로 하나 생성. 일단 설정은 default로 한다.


계획한대로 posts 하위에 각각 GET, PUT method 생성하려고 했으나, 아. 연결할 Lambda 를 지정해야 한다. 일단 Lambda 는 비워놓고, API 구조부터 생성해보자.
posts 하위에 /{postid} 생성하고 각각 GET, POST, DELETE method 생성.


일단 method 는 만들어놨지만 아직 이 method 들을 어딘가에 연결하지는 않았다.
다음에는 API Gateway의 method 들이 연결되어 실행될 Lambda function을 만들어본다.


by mine | 2017/06/10 00:24 | | 트랙백 | 덧글(0)

AWS로 Serverless web application 만들기 (0)

생각해보면 HTML이 뭔지도 잘 모르던 시절부터 웹개발은 항상 개인 홈페이지를 만들면서 배웠던 것 같다.

HTML만 가지고 얼기설기 만들어봤던 첫 사이트를 perl을 배우면서 새로 만들고, php로 뜯어 고치고, 날 php 에서 cake framework 를 적용한 사이트로 바꿨다가 python django로 다시 만들어보고, 그러면서 DOM의 개념이나 CSS, javascript도 같이 익혔던 것 같다.

개인 홈페이지라는게 한물 가고 한동안 나도 개인 홈페이지를 버려놨더니 서버 계정이 만료되면서 데이터도 날아가고... 이걸 어쩌나 하다가 시류에 발맞춰 AWS를 이용하여 서버리스 웹 서비스라는걸 한번 해볼까 하는 생각이 들었다.

육아에 전념하느라 열심히 단시간에 만들기는 힘들겠지만, 짬 날때마다 조금씩 이것저것 찾아보면서 만들어볼까 한다. 이 포스트는 잊지 않기 위해, 조잡한 경험이라도 혹시 누군가에게는 도움이 될까 해서 기록하면서 진행하려는 시도.

가장 최근의 홈페이지는

 apache - mod_wsgi - virtualenv - django / jquery - mysql

를 이용해서 만들어져 있는데,

AWS API Gateway - Lambda - S3 / dojo - DynamoDB

구조로 다시 만들어볼까 한다. 

자세한 시도는 다음부터. : )

by mine | 2017/06/09 23:49 | | 트랙백 | 덧글(0)

서울 N타워 철탑

입사 기념으로 올라가 본 남산 N 타워 철탑

내려다보면 이렇다. 날씨 좋네~

이것은 서울에서 가장 높은 화장실

베를린까지는 8,142.17 km 라고 합니다. 5km/h 로 걸어가면 대략 1600시간이니 두달만 걸으면 되나 했더니 -_-; 헤엄도 쳐야하네.
그냥 비행기 타고 가련다.

by mine | 2008/03/17 22:33 | 트랙백 | 덧글(0)

헨리 제임스 - 나사의 회전

Henry James - The Turn of the Screw (1898)

'나사의 회전' 이라는 제목은 도입부에서 앞으로 펼쳐질 이야기를 소개하면서 등장한다.


"만일 어린아이 하나가 나사를 한 번 더 죄는 효과를 낸다면 어린아이가 둘일 경우 어떻게 되겠어요?"

뭐 -_-; 결국 이 이야기에는 애가 두 명 나오니 한 명 나오는 이야기보다 두배 더 무시무시하다. 이런 어처구니 없는 제목 되시겠다.

원래 글이 이런건지 번역이 엉망인건지 집중하기 무척 어려웠다. 번역하신 최경도씨는 '한국 헨리 제임스 학회' 회장까지 하셨다는 분이니 원 글이 이모냥이라고... 믿어도 될까?

"난 무척이나 동의해요. 지난번 그리핀 씨가 말한 유령 이야기든 무엇이든. 그토록 여린 어린아이에게 맨 먼저 유령이 나타난 게 각별한 기미가 있다는 거 말이오. 하지만 내가 알기로 어린아이와 관련된 감칠 듯한 이야기치고 그게 처음 일어난 건 아닐 거에요."

젠장. 이게 도대체 무슨 소리냔 말이지.

하여간, 시골 목사 딸로 자란 젊은 여성이 가정교사로 들어가서 순진무구해보이는 두 아이들을 맡게 되는데, 사실은 이 아이들이 유령을 보고 있으면서도 아무것도 모르는 척 하고 있었다. 뭐 이런 이야기인데, 원래대로라면 이 가정교사의 불안한 심리와 음울한 분위기, 혼란스러움 같은게 막 잘 표현이 되었어야 할텐데, 도무지... -_-;

사실 arial에게 빌려서 읽기 시작한 지는 두달 쯤 되어가는데, 반쯤 읽다가 난잡함(?)을 견디지 못하고 던져놨다가 마침 며칠 전에 읽은 온다 리쿠 소설에서 헨리 제임스 이야기가 나오기에 다시 한번 마음을 다잡고 끝내버렸다.
역시 원작과 번역본은 다른 작품이라고 봐야한다는 생각을 하게 되는 책.

by mine | 2008/03/15 13:15 | | 트랙백(1) | 덧글(1)

HABTM Pagination on CakePHP 1.2x

게시판 따위를 만들 때마다 항상 귀찮게 하는 pagination 작업을 CakePHP 1.2 에서는 paginator helper 를 통해서 가볍게 지원해준다.

Post 를 페이지별로 보여주고 싶을 때, 간단히 몇줄로 작업 끝이다.

class PostsController extends AppController {
  
    var $paginate = array( 'limit' => 5, 'order' => array( 'Post.created' => 'desc' ) );

    function idex()
    {
        $rs = $this->paginate( 'Post', array( 'Post.status' => '>0' ) );
        debug( $rs ); exit;
    }
}


그런데, 여기에 HABTM association 이 들어가게 되면 문제가 좀 더 복잡해진다.

class Post extends AppModel {
    var $hasAndBelongsToMany = array( 'Tag' => array( 'conditions' => 'PostsTag.status > 0' ) );
}


이렇게 Tag 과 HABTM 관계를 정의해놓고 위의 index 를 그대로 호출하면 아름답게 Post 에 붙은 Tag 들이 끌려 나온다.

Array
(
    [0] => Array
        (
            [Post] => Array
                (
                    [id] => 1
                    [status] => 1
                    [created] => 2007-11-18 23:49:59

                    [text] => hello
                )
            [Tag] => Array
                (
                    [0] => Array
                        (
                            [id] => 1
                            [text] => testTag
                            [PostsTag] => Array
                                (
                                    [post_id] => 1
                                    [tag_id] => 1
                                    [status] => 1
                                )
                        )
                    [1] => Array
                        (
                            [id] => 13
                            [text] => hello
                            [PostsTag] => Array
                                (
                                    [post_id] => 1
                                    [tag_id] => 13
                                    [status] => 1
                                )
                        )
            )
)

뭐 아직까지는 별다른 작업 없이도 원하는 결과를 얻을 수 있다.

참고로 이 때 실제로 수행되는 query 는 아래와 같다

SELECT COUNT(*) AS `count` FROM `posts` AS `Post` WHERE `Post`.`status` > 0        // pagination 을 위한 count 처리
SELECT `Post`.`id`, `Post`.`status`, Post`.`created`, `Post`.`text` FROM `posts` AS `Post` WHERE `Post`.`status` > 0 ORDER BY `Post`.`created` desc LIMIT 5
SELECT `Tag`.`id`, `Tag`.`text`, `PostsTag`.`post_id`, `PostsTag`.`tag_id`, `PostsTag`.`status` FROM `tags` AS `Tag` JOIN `posts_tags` AS `PostsTag` ON (`PostsTag`.`post_id` IN (1, 2) AND `PostsTag`.`tag_id` = `Tag`.`id`) WHERE `PostsTag`.`status` > 0
    // 각 Post 에 해당하는 Tag 검색


자, 여기까지는 무난하다.
그럼 이번에는 Tag 별 검색을 해보도록 하자.
원하는 결과는 아래와 같다

INDEX BY 'testTag'

Post1
    tags : testTag, hello, tag1

Post4
    tags : testTag

Post11
    tags : asdf test testTag


즉, 간단하게 말하면 특정 tag 을 포함하는 게시물의 리스트를 보여주는 것이다.

사실 이것을 처리하는 데에는 여러가지 방법이 있다.

posts_tags 테이블에서 tag_id 로 query 한 결과를
$this->paginate( 'Post', array( 'Post.status' => '>0', 'Post.id' => 'IN (' . implode( ', ', $list ) . ')' ) );
해서 처리해도 되지만, 뭔가 깔끔해보이지 않는다.

paginate HABTM 을 구글해보면 몇가지 글이 검색된다.
참고할만한 것들은 이것
http://cakebaker.42dh.com/2007/10/17/pagination-of-data-from-a-habtm-relationship/
http://www.cricava.com/blogs/index.php?blog=6&title=modelizing_habtm_join_tables_in_cakephp_&more=1&c=1&tb=1&pb=1

두번째 링크에 있는 mariano iglesias 의 방법을 살짝 바꿔서 써보도록 하자.
여기 나온 것은 일단 paginate 를 사용하지 않고 findAll 만 호출했고, 각 게시물별 tag 이 결과에 포함되어 있지 않기도 하고, 하여간 직접 쓰기에는 적당하지 않다.

function tags( $tagid )
{
    $rs = $this->paginate( 'PostsTag', array( 'PostsTag.tag_id' => $tagid ) );
    debug( $rs );
}


아이쿠 $paginate 를 정의해준 데서 사용한 'order' => array( 'Post.created' => 'desc' )  부분이 문제다. 실제 쿼리를 보면

SELECT COUNT(*) AS `count` FROM `posts_tags` AS `PostsTag` WHERE `PostsTag`.`tag_id` = 17
SELECT `PostsTag`.`post_id`, `PostsTag`.`tag_id`, `PostsTag`.`status` FROM `posts_tags` AS `PostsTag` WHERE `PostsTag`.`tag_id` = 17 ORDER BY `Post`.`created` desc LIMIT 5


이렇게 되어있다. PostsTag 과 Post 간의 관계가 정의되어 있지 않아서 생기는 문제다.
살짝
$this->paginate = array( 'limit' => 2 );
라고 끼워넣어보면 결과가 나오기는 하지만, 역시 원하는 결과는 아니다.

자, 그러면 여기서 PostsTag 과 Post 간의 관계를 정의해버리면 되겠다.
$this->Post->PostsTag->bindModel( array( 'belongsTo' => array( 'Post' ) ) );
$rs = $this->paginate( 'PostsTag', array( 'PostsTag.tag_id' => $tagid ) );


어라? 여전히 똑같은 에러가 나온다. 하지만 실제 쿼리는 살짝 다르다.

SELECT COUNT(*) AS `count` FROM `posts_tags` AS `PostsTag` LEFT JOIN `posts` AS `Post` ON (`PostsTag`.`post_id` = `Post`.`id`) WHERE `PostsTag`.`tag_id` = 17
SELECT `PostsTag`.`post_id`, `PostsTag`.`tag_id`, `PostsTag`.`status` FROM `posts_tags` AS `PostsTag` WHERE `PostsTag`.`tag_id` = 17 ORDER BY `Post`.`created` desc LIMIT 5


두번째 쿼리는 동일하지만 첫번째 쿼리에 posts 테이블이 (바라던 대로) left join 되었다.
bindModel(), unbindModel()은 이후에 시행되는 쿼리 한번에 대해서만 유효한데, paginate() 안에서 쿼리를 두번 하기 때문에 두번째 쿼리에는 적용이 되지 않기 때문이다.

원인을 알았으니 다시 해보자.

$this->Post->PostsTag->bindModel( array( 'belongsTo' => array( 'Post' ) ), false );
$rs = $this->paginate( 'PostsTag', array( 'PostsTag.tag_id' => $tagid ) );


bindModel() 에서 두번째 인자로 false 를 주면 한번 쿼리 한 이후에도 bind/ unbind 가 해제되지 않는다.
자, 이제 결과가 나왔다.

Array
(
    [0] => Array
        (
            [PostsTag] => Array
                (
                    [post_id] => 34
                    [tag_id] => 17
                    [status] => 1
                )

            [Post] => Array
                (
                    [id] => 34
                    [status] => 1
                    [created] => 2007-11-18 23:49:59
                    [modified] => 2007-11-20 01:28:41
                    [text] => 그렇다.
                )
        )

    [1] => Array
        (
            [PostsTag] => Array
                (
                    [post_id] => 29
                    [tag_id] => 17
                    [status] => 1
                )

            [Post] => Array
                (
                    [id] => 29
                    [status] => 1
                    [created] => 2007-11-17 20:08:35
                    [text] => 에헤라 얼씨구 절씨구 차차차 얼쑤~
                )
        )
)

자, 원하던대로 TAG 17 을 가지는 POST (29, 34) 가 반환되었다.
아니 그런데 잘 살펴보니, tag 은 어디갔어?

위의 mariano iglesias 의 방법대로
$this->Post->PostsTag->bindModel( array( 'belongsTo' => array( 'Post', 'Tags ) ), false );
해주면 어떨까?

Array
(
    [0] => Array
        (
            [PostsTag] => Array
                (
                    [post_id] => 34
                    [tag_id] => 17
                    [status] => 1
                )

            [Post] => Array
                (
                    [id] => 34
                    [status] => 1
                    [created] => 2007-11-18 23:49:59
                    [text] => 그렇다.
                )

            [Tag] => Array
                (
                    [id] => 17
                    [text] => 테스트
                )
        )

    [1] => Array
        (
            [PostsTag] => Array
                (
                    [post_id] => 29
                    [tag_id] => 17
                    [status] => 1
                )

            [Post] => Array
                (
                    [id] => 29
                    [status] => 1
                    [created] => 2007-11-17 20:08:35
                    [text] => 에헤라 얼씨구 절씨구 차차차 얼쑤~
                )

            [Tag] => Array
                (
                    [id] => 17
                    [text] => 테스트
                )
        )
)

오. tag 정보까지 나왔다.
그런데 잘 살펴보면 이것은 원하는 결과가 아니다. 그 게시물에 포함된 모든 tag 가 반환된 것이 아니라 id=17 인 tag 정보만 포함되어서 나왔다.
자, 그럼 원하는 결과를 얻기 위해서는 어떻게 해야할까? 아래와 같이 가볍게 recursive 만 하나 더해주면 된다.

$this->Post->PostsTag->bindModel( array( 'belongsTo' => array( 'Post' ) ), false );
$this->Post->PostsTag->recursive = 2;
$rs = $this->paginate( 'PostsTag', array( 'PostsTag.tag_id' => $tagid ) );


결과는 아래와 같다

Array( 
[0] => Array (
[PostsTag] => Array (
[post_id] => 34
[tag_id] => 17
[status] => 1
)
[Post] => Array (
[id] => 34
[status] => 1
[created] => 2007-11-18 23:49:59
[text] => 그렇다.
[Tag] => Array (
[0] => Array (
[id] => 2
[text] => asdf
[PostsTag] => Array (
[post_id] => 34
[tag_id] => 2
[status] => 1
)
)
[1] => Array (
[id] => 17
[text] => 테스트     
[PostsTag] => Array (
[post_id] => 34
[tag_id] => 17
[status] => 1
)
)
)
)
)
[1] => Array (
[PostsTag] => Array (
[post_id] => 29
[tag_id] => 17
[status] => 1
)
[Post] => Array (
[id] => 29
[status] => 1
[created] => 2007-11-17 20:08:35
[text] => 에헤라 얼씨구 절씨구 차차차 얼쑤~
[Tag] => Array (
[0] => Array (
[id] => 16
[text] => 타령     
[PostsTag] => Array (
[post_id] => 29
[tag_id] => 16
[status] => 1
)
)
[1] => Array (
[id] => 17
[text] => 테스트     
[PostsTag] => Array (
[post_id] => 29
[tag_id] => 17
[status] => 1
)
)
)
)
)
)

자, 이제는 정말 원하는 결과가 나왔다.
Tag 밑에 필요없는 PostsTag 까지 따라나오긴 했지만, 이건 그냥 무시하면 된다.

실제 query는 아래와 같다.

SELECT COUNT(*) AS `count` FROM `posts_tags` AS `PostsTag` LEFT JOIN `posts` AS `Post` ON (`PostsTag`.`post_id` = `Post`.`id`) WHERE `PostsTag`.`tag_id` = 17
SELECT `PostsTag`.`post_id`, `PostsTag`.`tag_id`, `PostsTag`.`status`, `Post`.`id`, `Post`.`status`, `Post`.`created`,  `Post`.`text` FROM `posts_tags` AS `PostsTag` LEFT JOIN `posts` AS `Post` ON (`PostsTag`.`post_id` = `Post`.`id`) WHERE `PostsTag`.`tag_id` = 17 ORDER BY `Post`.`created` desc LIMIT 5
SELECT `Post`.`id`, `Post`.`status`, `Post`.`created`, `Post`.`text` FROM `posts` AS `Post` WHERE `Post`.`id` = 34
SELECT `Tag`.`id`, `Tag`.`text`, `PostsTag`.`post_id`, `PostsTag`.`tag_id`, `PostsTag`.`status` FROM `tags` AS `Tag` JOIN `posts_tags` AS `PostsTag` ON (`PostsTag`.`post_id` IN (34) AND `PostsTag`.`tag_id` = `Tag`.`id`) WHERE `PostsTag`.`status` > 0
SELECT `Post`.`id`, `Post`.`status`, `Post`.`created`, `Post`.`text` FROM `posts` AS `Post` WHERE `Post`.`id` = 29
SELECT `Tag`.`id`, `Tag`.`text`, `PostsTag`.`post_id`, `PostsTag`.`tag_id`, `PostsTag`.`status` FROM `tags` AS `Tag` JOIN `posts_tags` AS `PostsTag` ON (`PostsTag`.`post_id` IN (29) AND `PostsTag`.`tag_id` = `Tag`.`id`) WHERE `PostsTag`.`status` > 0

recursive 때문에 검색된 Post 갯수 * 2 만큼 쿼리 수가 늘어나는 문제는 있지만, 일단 결과는 무난히 처리되었다.
만약 이렇게 쿼리 수가 늘어나는 것이 싫다면 recursive=1 의 결과를 취합해서 해당 Post 와 연관되는 Tag 들을 모두 검색하여 다시 연관지어주는 작업을 손으로 해줘야 한다.
어느쪽이 좋을지는 각자의 판단대로 : )

by mine | 2007/11/22 11:05 | | 트랙백 | 덧글(0)

◀ 이전 페이지 다음 페이지 ▶