Cloud/Naver Cloud

[NCLOUD] Cloud Functions과 Slack을 이용한 나만의 비용 관리 봇 생성하기

__Evening 2024. 9. 30. 17:49
반응형

안녕하세요.

 

이번 시간에는 네이버 클라우드 플랫폼 서비스 Cloud Functions와 Slack을 이용한 나만의 비용 관리 봇을 생성하는 실습을 진행하려고 합니다. 굉장히 간단하게 개발 환경에서 비용을 관리할 수 있으니 내용 참고해 보시면 좋을 것 같습니다.

 

1. 비용 관리 봇 아이디어 (Billing Management Bot)

네이버 클라우드에서 기본적으로는 비용 관리에 대한 메트릭을 제공하지 않기 때문에 API 중에서 사용한 비용을 조회할 수 있는 getDemandCostList를 사용하여 사용 비용을 조회할 수 있습니다.

 

애플리케이션에서 비용 관리를 위해 Budget을 설정합니다. 사용 금액과 Budget을 비교하여 Budget을 넘겼을 때 곧 바로 사용하고 있는 서비스를 중지하거나 제거할 수 있지만 사용자가 컨트롤할 수 있도록 Slack에 전달하여 정보 확인과 제어를 합니다.

 

Slack 봇으로 메세지를 전송하여 언제 어디서든 Budget을 넘으면 서비스를 중지 혹은 제거할 수 있도록 설정합니다.

 

2. 공통 코드 객체화하기

네이버 클라우드 API를 사용하려면 인증과 호출 부분은 대부분의 API가 공통적으로 사용하는 부분입니다. 이 코드를 객체화하여 공통적으로 사용할 수 있도록 합니다.

import hashlib
import hmac
import base64
import requests
import time
import json
from datetime import datetime
from ncp_crendential import ncp_credentials

class NcloudApiClient:
    def __init__(self, access_key, secret_key, api_server):
        self.access_key = access_key
        self.secret_key = bytes(secret_key, 'UTF-8')
        self.api_server = api_server

    def make_signature(self, uri, method="POST"):
        timestamp = str(int(time.time() * 1000))

        message = method + " " + uri + "\n" + timestamp + "\n" + self.access_key
        message = bytes(message, 'UTF-8')

        signingKey = base64.b64encode(hmac.new(self.secret_key, message, digestmod=hashlib.sha256).digest())
        api_endpoint = self.api_server + uri
        http_header = {
            'x-ncp-apigw-signature-v2': signingKey,
            'x-ncp-apigw-timestamp': timestamp,
            'x-ncp-iam-access-key': self.access_key
        }

        return api_endpoint, http_header

    def get_request(self, uri):
        api_endpoint, http_header = self.make_signature(uri, method="GET")

        response = requests.get(api_endpoint, headers=http_header)
        return json.loads(response.text)

    def post_request(self, uri):
        api_endpoint, http_header = self.make_signature(uri, method="POST")
        response = requests.post(api_endpoint, headers=http_header)
        return json.loads(response.text)

재사용과 유지보수를 편하게 하기 위하여 API를 호출하는 공통 코드 부분을 객체화하여 작성합니다. 이를 통해 인증과 호출을 간편하게 할 수 있습니다.

 

3. 네이버 클라우드 비용 API로 조회하기

https://api.ncloud-docs.com/docs/platform-costandusage-getdemandcostlist

 

getDemandCostList

 

api.ncloud-docs.com

네이버 클라우드 플랫폼에서는 비용을 조회할 수 있도록 getDemandCostList를 제공합니다. 위 사용 가이드를 참고하여 실습을 진행해 주시면 되겠습니다.

 

def main():
    access_key, secret_key = ncp_credentials()
    billing_api = "https://billingapi.apigw.ntruss.com"

    ncloud_api = NcloudApiClient(access_key, secret_key, billing_api)
    current_month = datetime.now().strftime('%Y%m')
    uri = "/billing/v1/cost/getDemandCostList?startMonth=" + current_month + "&endMonth=" + current_month + "&responseFormatType=json"
    getResponse = ncloud_api.get_request(uri)

    print("Get Response Data:")
    print(getResponse)

    useAmount = getResponse['getDemandCostListResponse']['demandCostList'][0]['useAmount']
    print("총 사용 비용: ")
    print(useAmount)

다음과 같이 getDemandCostList과 datetime을 이용하여 항상 이번 달의 사용량을 조회합니다. 사용 금액은 useAmount이기 때문에 위와 같이 파싱 해서 사용할 수 있습니다.

 

응답 예시
{
    "getDemandCostListResponse": {
        "totalRows": 1,
        "demandCostList": [
            {
                "memberNo": "2760000",
                "demandMonth": "202401",
                "demandNo": "9540000",
                "integrationDemandNo": "",
                "demandAttribute": {
                    "code": "GEN",
                    "codeName": "General"
                },
                "useAmount": 36290,
                "promiseDiscountAmount": 0,
                "promotionDiscountAmount": 0,
                "etcDiscountAmount": 0,
                "customerDiscountAmount": 0,
                "productDiscountAmount": 0,
                "creditDiscountAmount": 0,
                "rounddownDiscountAmount": 90,
                "currencyDiscountAmount": 0,
                "coinUseAmount": 0,
                "defaultAmount": 0,
                "thisMonthDemandAmount": 36200,
                "thisMonthVatRatio": 0.1,
                "thisMonthVatAmount": 3620,
                "thisMonthAmountIncludingVat": 39820,
                "totalDemandAmount": 39820,
                "isPaidUp": true,
                "paidUpDate": "2024-02-01T07:08:30+0900",
                "overduePlusAmount": 0,
                "overdueRatio": 0.0,
                "thisMonthOverdueAmount": 39820,
                "beforeMonthDemandNo": "9180000",
                "totalOverdueAmount": 39820,
                "writeDate": "2024-02-01T06:44:51+0900",
                "memberPriceDiscountAmount": 0,
                "memberPromiseDiscountAddAmount": 0,
                "payCurrency": {
                    "code": "KRW",
                    "codeName": "South Korea Won"
                },
                "thisMonthAppliedExchangeRate": 1
            }
        ],
        "requestId": "0000001d-b737-47ef-9875-0d28ce000000",
        "returnCode": "0",
        "returnMessage": "success"
    }
}

getDemandCostList API는 다음과 같이 요청에 대한 응답을 반환합니다. Slack 봇을 더 고도화 하기 위해서는 다른 응답 값을 응용하는 방법도 있습니다.

 

4. Slack Bot 생성하기

https://api.slack.com/

 

AI 업무 관리 및 생산성 도구

Slack은 팀과 커뮤니케이션할 수 있는 새로운 방법입니다. 이메일보다 빠르고, 더 조직적이며, 훨씬 안전합니다.

slack.com

다음과 같이 Slack API에 접속하여 내 워크스페이스에 새로운 앱을 생성합니다.

 

기본적으로 Slack Bot의 권한을 부여해주어야 하는데 OAuth & Permissions에서 설정 가능합니다. [Add an Oauth Scope]를 누릅니다.

 

OAuth Scope 설명
app_mentions:read
앱이 추가된 대화에서 @Bot이 직접 언급된 메시지를 볼 수 있습니다.
channels:history Bot이 추가된 퍼블릭 채널에서 메시지 및 기타 콘텐츠를 볼 수 있습니다.
channels:read
작업공간의 퍼블릭 채널에 대한 기본 정보를 볼 수 있습니다.
chat:write @Bot으로 메시지를 보낼 수 있습니다.
groups:history Bot이 추가된 프라이빗 채널에서 메시지 및 기타 콘텐츠를 볼 수 있습니다.
groups:read Bot이 추가된 프라이빗 채널에 대한 기본 정보를 볼 수 있습니다.
im:history Bot이 추가된 다이렉트 메시지에서 메시지 및 기타 콘텐츠를 볼 수 있습니다.
im:read Bot이 추가된 다이렉트 메시지에 대한 기본 정보를 볼 수 있습니다.
incoming-webhook 특정 Slack 채널에 메시지를 게시할 수 있습니다.
mpim:history Bot이 추가된 그룹 다이렉트 메시지에서 메시지 및 기타 콘텐츠를 볼 수 있습니다.
mpim:read Bot이 추가된 그룹 다이렉트 메시지에 대한 기본 정보를 볼 수 있습니다.
users:read 작업공간의 사람들을 볼 수 있습니다.

다음은 Slack 봇에 부여할 기본 권한입니다. 설명을 보고 필요한 Scope만 부여해도 괜찮습니다.

 

다음과 같이 Scope를 부여한 후 install to Workspace를 눌러줍니다.

 

워크스페이스에 테스트 채널을 생성한 후 테스트 채널을 선택하여 [허용]합니다.

 

테스트 채널에 /invite를 입력한 후 이 채널에 앱을 추가합니다.

 

다음과 같이 채널에 생성한 앱을 추가해 줍니다.

 

5. Slack 라이브러리 설치하기

pip install slack-sdk

다음과 같이 가상 환경을 만들고 그 위에 slack-sdk를 설치합니다.

 

OAuth & Permissions에서 Bot Token을 복사합니다.

 

테스트 채널 우클릭 - [채널 세부정보 보기] - 채널 ID 복사합니다.

 

import os
from slack_sdk import WebClient
from slack_sdk.errors import SlackApiError

slack_token = 'Bot 토큰 입력'

client = WebClient(token=slack_token)

def main():
	channelId = '채널 ID 입력'
    sendMessage = '네이버 클라우드 금액 : ' + str(useAmount) + '사용'


    if useAmount > 30000:
        billingNotification(channelId, sendMessage)

def billingNotification(channelId, sendMessage):
    try:
        response = client.chat_postMessage(
            channel=channelId,
            text=sendMessage
        )
        print(f"Message sent: {response['message']['text']}")
    except SlackApiError as e:
        print(f"Error sending message: {e.response['error']}")

애플리케이션에 해당 값을 붙여넣고 다음과 같이 Budget 값을 초과했을 때 Slack 채널로 메시지가 전송되게 끔 설정합니다.

 

이쯤에서 애플리케이션을 실행하면 네이버 클라우드에서 사용한 금액을 Slack 메세지로 받아볼 수 있습니다. 저희는 여기서 더 나아가 Cron으로 트리거를 설정하고 사용자가 직접 서비스를 중지 혹은 제거할 수 있도록 커스텀하겠습니다.

 

추가적으로 Cloud Functions을 통해 이벤트 기반으로 동작하게 끔 설정해주어야 합니다.

 

6. 애플리케이션 코드 커스텀하기

다음과 같이 Slack으로 보내기 위해서는 애플리케이션 코드를 수정해주어야 합니다. 

 

https://slack.com/intl/ko-kr/help/articles/202288908-%EB%A9%94%EC%8B%9C%EC%A7%80-%EC%84%9C%EC%8B%9D-%EC%A7%80%EC%A0%95

 

메시지 서식 지정

서식을 지정하면 Slack에서의 메시지에 세부적이고 명확한 정보를 추가할 수 있습니다. 메시지 필드에서 서식 도구 모음을 사용하거나 태그를 사용하...

slack.com

Slack 문자열 포맷은 다음을 참고해주시면 됩니다.

 

https://github.com/wjsgur8530/ncp_billing_manager/blob/main/ncp_billing_manager.py

 

ncp_billing_manager/ncp_billing_manager.py at main · wjsgur8530/ncp_billing_manager

Contribute to wjsgur8530/ncp_billing_manager development by creating an account on GitHub.

github.com

위 Git 레포지토리에 디버깅 전용 애플리케이션 코드를 올려둡니다. 우리는 애플리케이션 실행을 통해 Slack으로 메시지를 전송할 수 있지만 네이버 클라우드의 Cloud Functions에 Cron 트리거를 설정하여 매일 개발 환경에 대해 알림을 받고자 합니다.

 

그리고 디버깅을 위해 다음과 같이 코드를 작성했지만 Cloud Functions는 이벤트 기반으로 동작하기 때문에 코드를 수정해주어야 합니다.

 

7. 이벤트 기반으로 동작하게 끔 Cloud Functions 위에 코드 올리기

Cloud Functions - [Trigger 생성]을 선택합니다.

 

트리거 시간은 업무 시간에 맞춰서 조정하기로 하고 저는 테스트를 위해 1-2분 간격으로 조절하겠습니다.

 

Action을 생성하기 전에 Package를 먼저 생성합니다.

 

Action - [Action 생성]을 선택합니다.

 

다음과 같이 트리거를 선택한 후 [추가]를 선택합니다.

API Gateway와 연동하지 않으므로 [일반 액션]을 선택한 후 소스 코드 런타임은 python3.11 타입은 라이브러리와 함께 올려야 하기 때문에 파일을 선택합니다.

 

requests, slack, slack_sdk, 소스 코드를 함께 압축합니다.

 

사전에 디폴트 파라미터를 통해 이벤트를 받을 수 있도록 Cloud Function에 올릴 때는 코드를 수정해야 합니다.

 

압축 파일을 올린 후 디폴트 파라미터를 설정합니다. 기본적으로 압축 파일 이름은 __main__.py 여야 Cloud Functions에서 인식합니다.

 

다음과 같이 디폴트 파라미터를 json 포맷으로 설정해준 후 KMS를 생성하여 암호화 설정하는 것이 보안상 매우 좋습니다. 디폴트 파라미터에서 간편하게 파라미터 값을 변경하여 적용할 수 있습니다.

 

VPC 연결 정보에서는 무조건 KR-2 Private만 가능하기 때문에 설정한 후 [생성]합니다.

 

Slack에 메세지를 던질 때 외부 통신이 필요하기 때문에 기본적으로 해당 Private Subnet에 NAT는 설정되어 있어야 합니다.

 

cron이 설정되어 정상적으로 1-2분마다 애플리케이션이 실행되는 모습입니다.

 

Slack에서 Cloud Functions 이벤트가 발생하는 모습을 확인할 수 있습니다. Cloud Functions를 이용해서 비용 효율적으로 비용 관리를 할 수 있습니다.

 

이렇게 확인이 끝났다면 Trigger에서 값을 업무 시작 시간으로 변경해 주면 매일 출근 시간에 계정에서 비용을 얼마나 사용했는지 확인해 볼 수 있습니다.

 

이번 시간에는 네이버 클라우드 플랫폼 서비스 Cloud Functions와 Slack을 이용한 나만의 비용 관리 봇을 생성하는 실습을 진행해 봤습니다.

 

다음 시간에는 나만의 Slack Bot을 Cloud Functions과 테라폼을 이용한 비용 관리 봇으로 변경하는 실습을 진행하겠습니다.

 

감사합니다.

반응형