๐Ÿ”ฅ TIL - Day 44

๐Ÿ“Œ Flask API ์„œ๋ฒ„ EB์—์„œ Lambda๋กœ ์ด์‚ฌ๊ฐ€๊ธฐ

์ผ๋‹จ ์˜ค๋Š˜์€ ์ธ๋ฑ์Šค ํŽ˜์ด์ง€์—์„œ ์™ธ๋ถ€ API๋ฅผ ํ˜ธ์ถœํ•ด์„œ ๋ฆฌ์ŠคํŒ…ํ•˜๋Š” API๋ฅผ Lambda๋กœ ์˜ฎ๊ธฐ๋Š” ์ž‘์—…์„ ํ–ˆ๋‹ค.

aws sam์„ ์‚ฌ์šฉํ–ˆ๋Š”๋ฐ sam์— ๋Œ€ํ•ด ๊ฐ„๋‹จํ•˜๊ฒŒ ์•Œ์•„๋ณด์ž.

SAM (Serverless Application Model)
sam์€ ์„œ๋ฒ„๋ฆฌ์Šค ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋ณด๋‹ค ์‰ฝ๊ฒŒ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ AWS๊ฐ€ ์ œ๊ณตํ•ด์ฃผ๋Š” ํ”„๋ ˆ์ž„์›Œํฌ์ด๋‹ค. sam์„ ์‚ฌ์šฉํ•˜๊ธฐ ์ „์— ์šฐ๋ฆฌ๋Š” ์ง์ ‘ API ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ณ  ํ•„์š”ํ•œ ๋ชจ๋“ˆ์„ ํ•จ๊ป˜ ์••์ถ•ํ•ด์„œ ๋ฏธ๋ฆฌ ์ƒ์„ฑํ•œ Lambda ํ•จ์ˆ˜์— ์˜ฌ๋ ค์คฌ์—ˆ๋‹ค.
SAM์„ ์‚ฌ์šฉํ•˜๋ฉด ์ฝ”๋“œ ์ˆ˜์ค€์—์„œ ์ด๋Ÿฐ ์ผ๋ จ์˜ ์ž‘์—…์— ๋Œ€ํ•œ ์ž๋™ํ™” ์„ค์ •์„ ํ•  ์ˆ˜ ์žˆ๋‹ค. (IoC, Infrastructure as Code)

์Œ.. ๊ทผ๋ฐ ์†”์งํžˆ ๊ฐ„๋‹จํ•œ API๋Š” ๊ทธ๋ƒฅ ์ง์ ‘ ์˜ฌ๋ฆฌ๋Š” ๊ฒŒ ๋” ํŽธํ•˜์ง€ ์•Š์„๊นŒ? ๋ผ๋Š” ์ƒ๊ฐ์ด ๋“ค๊ธด ํ•œ๋‹ค.. ใ…Ž

ํ•˜์ง€๋งŒ ์ฝ”๋“œ๋กœ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์€ ์„ค์ • ์ž์ฒด๋ฅผ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ด๋‹ˆ ๊ทธ๋ƒฅ ์“ฐ๋Š”๊ฒŒ ๋‚˜์ค‘์„ ์œ„ํ•ด์„œ ๋” ์ข‹์„ ๊ฒƒ ๊ฐ™๋‹ค. ์ž˜ ์จ๋ณด์ž.

sam ์„ ์‚ฌ์šฉํ• ๋ ค๋ฉด aws-cli์™€ sam-cli๊ฐ€ ์„ค์น˜๋˜์–ด์•ผ ํ•œ๋‹ค.

macos ๊ธฐ์ค€

brew install awscli

brew tap aws/tap
brew install aws-sam-cli

๐Ÿ“Œ ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ

์ด์ œ sam ํ”„๋ ˆ์ž„์›Œํฌ ํ”„๋กœ์ ํŠธ๋ฅผ ํ•˜๋‚˜ ์ƒ์„ฑํ•œ๋‹ค.

sam init

์ด๋Ÿฐ ๋””๋ ‰ํ† ๋ฆฌ ๊ตฌ์กฐ๊ฐ€ ๋ณด์ผ ๊ฒƒ์ด๋‹ค.
aws sam์€ ์„ค์ •ํŒŒ์ผ์„ ํ†ตํ•ด ์„œ๋ฒ„๋ฆฌ์Šค ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๊ด€๋ฆฌํ•œ๋‹ค๊ณ  ํ–ˆ๋‹ค.
๊ทธ ์„ค์ •ํŒŒ์ผ์ด template.yaml ์ด๋‹ค.

์ผ๋‹จ ์ž์„ธํ•œ ์„ค์ •์€ ์ฐจ์ฐจ ํ•„์š”ํ•  ๋•Œ ์ฐพ์•„๋ณด๋Š” ๊ฑธ๋กœ ํ•˜๊ณ  ๋ฐฐํฌ์— ๋ฐ˜๋“œ์‹œ ํ•„์š”ํ•œ ๋ถ€๋ถ„๋งŒ ์‚ดํŽด๋ณด์ž. (์ฃผ์„ ์ฃผ์„)

Resources:
  # HelloWorld API
  HelloWorldFunction:
    Type: AWS::Serverless::Function 
    Properties:
      CodeUri: hello_world/ # API๊ฐ€ ์žˆ๋Š” ๋””๋ ‰ํ† ๋ฆฌ๋ช…
      Handler: app.lambda_handler # ํ•ธ๋“ค๋Ÿฌ ํ•จ์ˆ˜๋ช… (API์ ‘๊ทผ์‹œ ์‹คํ–‰๋  ํ•จ์ˆ˜)
      Runtime: python3.8
      Architectures:
        - x86_64
      Events:
        HelloWorld:
          Type: Api 
          # APIGateway์—”๋“œํฌ์ธํŠธ/hello  <-- ์ด๊ฒŒ API์˜ ์—”๋“œํฌ์ธํŠธ๊ฐ€ ๋˜๋Š” ๊ฒƒ
          Properties: # API Gateway์— ๋Œ€ํ•œ ์„ค์ •
            Path: /hello # HTTP ๊ฒฝ๋กœ
            Method: get # HTTP Method

๊ทธ๋Ÿผ ์ด์ œ API์— ๋Œ€ํ•œ ์„ค์ •์„ ํ•ด์ฃผ์ž.
์•„์ง ์—ฐ์Šต๋‹จ๊ณ„๋ผ VPC ์„ค์ •์€ ํ•ด์ฃผ์ง€ ์•Š์•˜๋‹ค.

  TodayLawListFunction:
    Type: AWS::Serverless::Function 
    Properties:
      CodeUri: tl_list/
      Handler: app.lambda_handler
      Runtime: python3.8
      Architectures:
        - x86_64
      Events:
        TodayLawList:
          Type: Api 
          Properties:
            Path: /laws
            Method: get

API ์ฝ”๋“œ
event ๊ฐ์ฒด๋กœ ์ฟผ๋ฆฌ์ŠคํŠธ๋ง์„ ๋ฐ›์„ ๋•Œ null ์ฒดํฌ๊ฐ€ ํž˜๋“ค๋‹ค..
์ง์ ‘ if๋ฌธ์œผ๋กœ null์ฒดํฌ๋ฅผ ํ•˜๋ ค๊ณ  queryStringParameters๋กœ ์ ‘๊ทผํ•œ ์ˆœ๊ฐ„ ๋„ ์—๋Ÿฌ๋ฅผ ๋ฐœ์ƒ์‹œํ‚จ๋‹ค.

๊ทธ๋ž˜์„œ Front ๋‹จ์—์„œ ๋นˆ ๊ฐ’์„ ๋˜์ ธ์ฃผ๋Š” ๊ฑธ๋กœ ์ˆ˜์ •ํ–ˆ๋‹ค.
์ด๋Ÿฐ์‹์œผ๋กœ ใ… ใ… 

$.ajax({
        type: "GET",
        url: `${api_url}/laws?offset=${offset}&query=&proposer=&condition=๋ฒ•์•ˆ๋ช…`,

๋ณ„๋กœ๋‹ค..

def lambda_handler(event, context):
    try:
        query = event['queryStringParameters']['query']
        proposer_name = event['queryStringParameters']['proposer']
        condition = event['queryStringParameters']['condition']
        pIndex = event['queryStringParameters']['offset']

        #== ๊ฒ€์ƒ‰์กฐ๊ฑด์ด ์—†๋Š” ๊ฒฝ์šฐ ==#
        if query is None and condition is None:
            data = requests.get(
                f'https://open.assembly.go.kr/portal/openapi/nzmimeepazxkubdpn?Key={api_key}&Type={type}&AGE={age}&pIndex={pIndex}&pSize=10')
        #== ๋ฒ•์•ˆ๋ช…์œผ๋กœ ๊ฒ€์ƒ‰ ==#
        elif condition == '๋ฒ•์•ˆ๋ช…':
            url = f'https://open.assembly.go.kr/portal/openapi/nzmimeepazxkubdpn?Key={api_key}&Type={type}&AGE={age}&BILL_NAME={query}&pIndex={pIndex}&pSize=10'
            query = encode_querystring(url)

            data = requests.get('https://open.assembly.go.kr/portal/openapi/nzmimeepazxkubdpn?' + query)
        #== ๋ฒ•์•ˆ๋ฐœ์˜ ์ œ์•ˆ์ž๋ช…์œผ๋กœ ๊ฒ€์ƒ‰==#
        elif condition == '์ œ์•ˆ์ž':
            url = f'https://open.assembly.go.kr/portal/openapi/nzmimeepazxkubdpn?Key={api_key}&Type={type}&AGE={age}&PROPOSER={proposer_name}&pIndex={pIndex}&pSize=10'
            query = encode_querystring(url)

            data = requests.get('https://open.assembly.go.kr/portal/openapi/nzmimeepazxkubdpn?' + query)

        data = data.json()
        total_count = data['nzmimeepazxkubdpn'][0]['head'][0]['list_total_count']
        data = data['nzmimeepazxkubdpn'][1]['row']

        response = []
        response.append({'total_count':total_count})
        for d in data:
            names = d['PUBL_PROPOSER']
            names = get_other_proposer(names)
            response.append({
                'id':d['BILL_ID'],
                'title':d['BILL_NAME'],               # ๋ฒ•์•ˆ์ œ๋ชฉ
                'proposer_name':d['RST_PROPOSER'],    # ๋Œ€ํ‘œ์ œ์•ˆ์ž
                'proposer_names':names,               # ๋Œ€ํ‘œ์ œ์•ˆ์ž ์™ธ ์ œ์•ˆ์ž
                'date':d['PROPOSE_DT'],               # ๋ฐœ์˜ ๋‚ ์งœ
                'url':d['DETAIL_LINK'],               # ์ƒ์„ธ๋‚ด์šฉ ํฌ๋กค๋ง link
                'total_count':total_count
            })

        body = json.dumps(response)

        return {
            "statusCode": 200,
            'headers': {
                'Access-Control-Allow-Headers': 'Content-Type',
                'Access-Control-Allow-Origin': '*',
                'Access-Control-Allow-Methods': 'OPTIONS,POST,GET,DELETE'
            },
            "body": body
        }
    except Exception as e:
        print(e)
        return {
            "statusCode": 500,
            # Cross Origin์ฒ˜๋ฆฌ
            'headers': {
                'Access-Control-Allow-Headers': 'Content-Type',
                'Access-Control-Allow-Origin': '*',
                'Access-Control-Allow-Methods': 'GET'
            },
            "body": json.dumps({
                "message": "fail",
            }),
        }

๊ทธ๋Ÿผ ์ค€๋น„๋Š” ๋๋‚ฌ์œผ๋‹ˆ ๋นŒ๋“œ / ๋ฐฐํฌ๋ฅผ ํ•ด์ค€๋‹ค.

sam build 

sam deploy [--guided]

HelloWorld API ๊นŒ์ง€ ์ด 2๊ฐœ Lambda ํ•จ์ˆ˜๊ฐ€ ์ƒ์„ฑ๋œ๋‹ค.

Lambda ํ•จ์ˆ˜์˜ ํŠธ๋ฆฌ๊ฑฐ์— ์„ค์ •๋œ API Gateway๋ฅผ ๋ณด๋ฉด ์šฐ๋ฆฌ๊ฐ€ ์„ค์ •ํ•œ ๋Œ€๋กœ ์—”๋“œํฌ์ธํŠธ๊ฐ€ ์„ค์ •๋œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

๋กœ์ปฌ์—์„œ ๋ฆฌ์ŠคํŒ… API ํ…Œ์ŠคํŠธ!

์ข‹์€ ์›นํŽ˜์ด์ง€ ์ฆ๊ฒจ์ฐพ๊ธฐ