람다로 gist 이미지 반환하기

개요

현재 나는 gist에 이미지를 base64로 올린 후 이를 이미지화 해주는 gist 플러그인을 사용 중이다.
그런데 2025년 3월부터 더 이상 이미지화를 해주는 서버를 제공하지 않겠다는 글이 올라왔다.
이에 따라, 해당 서버를 내가 직접 구축해야 할 것 같다.[1]

내 생각으로는, 람다의 프리티어를 활용해서 이미지 처리 로직을 수행하면 되지 않을까 싶다.
그래서 해당 코드를 간단하게 분석한 후 활용해볼까 한다.

로직 분석

https://itg.singhinder.com?url=https://gist.githubusercontent.com/Zerotay/c025f97c5827f1e135cdf3b791b70b4b/raw/image.png

요청은 이런 식으로 이뤄진다.
gist url을 쿼리스트링에 담아 보내면, 이를 서버에서 요청을 수행하고 처리한다.
image.png
보다시피 get 요청으로 데이터를 받아오고, 해당 값을 디코딩을 해버리는 것이다.

로컬 환경 구현

고 언어에 대한 이해도가 아직 부족한 관계로, 파이썬으로 코드를 옮겼다.
image.png
이렇게 간단하게 짰는데, 쉽게 성공했다.
여기에 기존 로직에 있는 코드도 구현해본다.

람다로 옮기기

image.png
음.. 사실 뭐 대단하게 한 건 없는데 금방 구현돼버렸다.
람다 코드 올리고, api 게이트웨이 설정까지.
requests가 기본 라이브러리에 없다기에 이걸 zip으로 만들어 레이어를 올리는 건 처음 해봐서 조금 헤맸다.
https://itg.singhinder.com
image.png
와 잘 된다!

import json
import requests
from urllib.parse import urlparse

def lambda_handler(event, context):
    query_params = event.get("queryStringParameters", {})
    url = query_params.get("url")
    #url = 'https://gist.githubusercontent.com/Zerotay/c025f97c5827f1e135cdf3b791b70b4b/raw/image.png'
    parsed =  urlparse(url.strip())
    if parsed.hostname not in ["gist.github.com", "gist.githubusercontent.com"]:
        return {
            "status_code": 400,
            "headers": { "Content-Type": "application/json" },
            "body": json.dumps({
                "detail": "invalid_url"
            })
        }
    gist_code = requests.get(url)
    if gist_code.status_code != 200:
        return {
            "status_code": 400,
            "headers": { "Content-Type": "application/json" },
            "body": json.dumps({
                "detail": "gist server error: " + str(gist_code.status_code) + " " + gist_code.text
            })
        }
    image_ext = parsed.path.split('.')[-1]
    image_ext = image_ext if image_ext else "jpeg"
    return {
        "statusCode": 200,
        "headers": {
            "Content-Type": "image/" + image_ext,
            "Cache-Control": "private, max-age=" + str(3600 * 24 * 7)
        },
        "isBase64Encoded": True,
        "body": gist_code.content
    }

로직 자체는 정말 간단하다.
나는 원래 코드에서처럼 한번이라도 인코딩을 해야 하나 했는데, 막상 해보니 그럴 필요 없이 받은 base64 데이터를 그대로 보내주기만 하면 되더라.

추가 고려 사항

image.png
고맙게도 gist 플러그인에서는 현재 이미지를 요청하는 모든 사진 url을 업데이트하는 기능을 제공한다.
그러나 이것은 한 문서에 대해서만 적용되기에, 여태 올린 모든 문서에 대해서 이를 수행하는 스크립트를 작성할 필요가 있다.

gist는 원없이 쓸 수 있는 걸까?
gist에 데이터를 올리는 비용이 어떻게 될지 궁금하다.
image.png
명시가 안 돼 있어 확실하진 않지만, 기스트는 아마도 일반적인 요청에 해당하는 것 같고, 이 요청은 시간당 5000개 쯤 되는 모양이다.[2]
순수 api 접근에 대한 제약이고, 나는 기스트 이외에 api를 사용하지 않으니 이걸 토대로 결론을 내려도 될 것 같다.
업로드와 겟을 다 합쳐 5000개라.. 한 시간에 내 블로그에 500명 정도 바글바글대는 것 아니면 신경쓸 수치가 아니라고 본다.

비용적 고려

정말 기본적으로 세팅을 했다.
람다 앞단에 api gateway를 두고, 이 게이트웨이의 주소를 사용했다.
여기에는 어떠한 캐싱도 들어가지 않는다.
한달 1000의 요청이 발생한다고 봤을 때, 얼마나 비용이 발생할 것인가?

람다

image.png
람다는 항상 월 100만 건까지는 무료이다.
또한 메모리 사용량 40만 Gb/초까지도 무료이다.
내 한번 요청 로그를 보았을 때 425.0 밀리초만치, 기본값이 128메가를 사용했다.
그렇다면 0.128 * 0.425 * 1000으로 한 달에 50Gb/초 정도 사용할 것이기에, 종합적으로 람다에서는 비용이 발생하지 않을 것이다.
월 10만건이 들어와도 비용이 발생하지 않을 것으로 생각된다..

api gateway

image.png
dataprocessed 지표를 봤을 때, 딱 이정도만큼 트래픽이 오가는 듯하다.
일단 요청 건수는 1000개 정도에, 기본 전송 데이터 크기를 재는 기준이 512kb이다.
이쪽은 명확하게 계산이 잘 안되는데, 그래도 0.1달러도 안 나올 것으로 생각한다.

처음에는 캐싱과 비용 절감 방안으로 클라우드 프론트나 캐싱 기능을 더 활용하는 것을 생각했는데, 이러면.. 굳이..?

관련 문서

이름 noteType created

참고


  1. https://github.com/singh-inder/images-to-gist-server ↩︎

  2. https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api?apiVersion=2022-11-28 ↩︎