특정 URL을 공유하다가 encode된 링크가 지저분해보여서 직접 짧은 도메인을 만들어놓고 연결시켜주는 프록시 서버를 구성하기로 했습니다.
이 문서는 어떤 의사결정을 하면서 서비스를 구성하였는가에 대한 내용이 기록되어있습니다.
대표적으로 위와 같은 기능이 필요해보였고 나머지는 만든 이후에 고도화 하기로 했습니다.
웹 서버와 데이터베이스만 있으면 API 서버 자체만으로 서비스 할 수 있지만 사용자 편의성이 떨어집니다.
그런 이유로 SPA 형식의 웹 클라이언트를 작성하여 GUI를 제공하고 사용자가 컴포넌트를 조작하여 API를 호출하는 형식으로 구현하기로 했습니다.
여기까지의 내용은 위와 같은 구성을 가집니다.
흔히 알고있는 3-tier 구성으로 꽤 단순합니다.
만들어놓고 누구나 사용할 수 있도록 클라우드 환경에 배포하기로 했는데
요청수가 그리 많지 않을 것 같기도 하고 사용안하는 시간동안 서버가 자원을 낭비하는건 맘에 안들어서
AWS Lambda에 API Gateway를 트리거로 구성하여 웹 서버를 구성하기로 했습니다.
다만 직접 콘솔에 붙어서 작성하고 설정할 필요 없이 코드만 작성하면 배포할 때 Cloud Formation 템플릿을 적용하여 클라우드 환경을 구성해주는 Serverless 프레임워크를 사용하기로 했습니다.
프레임워크에서 Flask, Node 등의 웹 애플리케이션 서버 템플릿을 제공해주지만, 그것들을 사용하지 않고 FastAPI + Mangum 조합을 사용하여 구성하기로 했습니다.
EC2 및 기타 VM 인스턴스
EC2의 경우 만들어서 올리고 요청만 받으면 되기에 제일 무난합니다만 인스턴스 성능을 올린다거나 사용하는만큼 비용이 발생하기 때문에 굳이 사용 안하는시간이 많은데 자원 낭비같아서 후보군에서 제외하였습니다.
Heroku
Heroku의 경우 무료 사용자에게도 컨테이너를 제공하지만 컴퓨팅 파워가 너무 낮다는 단점과 사용하지 않는경우 컨테이너가 종료되기 때문에 요청이 잦지 않은경우 콜드스타트 이슈를 맞이할 수 있었습니다.
물론 그러한 단점을 상쇄하기 위해 Kaffeine으로 컨테이너를 계속 깨워둘 수 있지만 무료사용자의 경우 하루 최대 18시간만 사용할 수 있기 때문에 할당량을 모두 사용하면 요청을 처리할 수 없다는 치명적인 단점이 있습니다.
그래서 이러한 이유로 7불짜리 플랜 하나를 결제하는 것이 컴퓨팅 파워도 어느정도 보장되고 좋습니다만.. 아시아에 위치한 엣지가 없어서 latency가 다소 높다는 단점이 있습니다.
API를 사용자가 직접 호출하여 사용할 수 있지만
사용자 편의성을 위해 유저 인터페이스를 제공할 필요가 있다고 생각했습니다.
Next.js 를 사용하여 페이지를 생성하기로 했습니다.
서버사이드 랜더링 이외에도 Gatsby등의 다른 정적페이지 생성 프레임워크가 해주는 것들을 다 커버하기도하고 이 외에도 제공하는 기능들이 강력하기 때문에 선택하였습니다.
코드도 간결하고 재미있어보이길래.. 이참에 Svelte도 해볼까? 라는 생각도 있었지만 공식문서가 불친절했고, 커뮤니티가 그리 크지 않아서 후보군에서 제외하였습니다.
이렇게되면 배포는 Vercel에 하는게 좋겠다는 생각이 들었습니다.
AWS로 배포하는경우엔 웹서버를 VM에 올려놓고 서버에서 랜더링 해둔 페이지를 Cloudfront로 캐싱해줘야합니다.
캐싱하지않으면 매 접속마다 랜더링 조건이 달라질 때마다 페이지를 다시 그려주어서 성능이슈가 발생할 수도 있고 이 외의 신경쓰지 못한부분에서 이슈가 발생할 가능성이 있는데 이러한 이유로 개발 피로도가 늘어나게됩니다.
당연한 내용인 것 같지만 Vercel은 이러한 부분들을 해소해주는 기능들을 포함하고있기도 하고 이외에 편의기능들이 다양하여 개발자가 오로지 개발에만 신경쓸 수 있다는 것이 장점으로 와닿는 것 같습니다.
심지어 개인사용 목적의 취미용 프로젝트엔 비용이 부과되지 않기 때문에 호스팅 비용걱정을 안해도 되는점이 좋았습니다.
Next.js를 만든 조직에서 운영하는 배포 플랫폼? 사용안할 이유가 없어보입니다.
위와 같은 이유로 Netlify, Github Pages 및 기타 다른 배포 플랫폼은 후보군에서 밀렸습니다.
사실 이부분에서 조금 갈등된 것 같습니다.
MySQL, PostgreSQL 같은 관계형 데이터베이스
vs
MongoDB, Redis 같은 NoSQL 데이터베이스
유저에게 URL을 받아서 생성한 Unique ID와 매핑하고 Unique ID와 조합한 리디레션 링크를 내어준다라고 할 때, 어떤 데이터 구조를 사용해야할까?
라는 생각이 들었습니다.
관계형 데이터베이스에서는 아래와 같이 스키마를 생성할 것 같습니다.
CREATE TABLE url_map (
id VARCHAR(10) PRIMARY KEY,
url VARCHAR(255) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
그리고.. 테이블 하나밖에 안쓸건데 굳이 DBMS를 써야하나? 라는 생각도 듭니다.
이럴거면 파일이나 메모리에 저장하는편이 괜찮겠다는 생각도 듭니다.
파일의 경우엔 서비스중에 생성되었다가 인스턴스가 다시 부팅되면서 사라지기 때문에 스토리지에 백업하는 기능을 같이 구현해야하는 번거로움이 있습니다. 그래서 서버리스 아키텍처에서는 더욱이 좋은 선택은 아닌 것 같습니다.
위와같은 이유로 Redis를 연결하여 사용하려고합니다만..
URL과 생성된 Unique ID 사이의 관계를 형성하기위해 아래와 같이 두개의 map을 만들어 사용할 수 있을 것 같아보입니다.
{
"aX": "https://my.z1p.link",
}
{
"https://my.z1p.link": "aX",
}
Value를 전달하여 Key를 찾을 수 있는 기능이 있다면 굳이 두개씩 만들어놓지 않을텐데, 그럴 수 없어서 이렇게 하는게 좋겠다고 생각했습니다.
그래도 메모리기반이다보니 성능은 보장될 것 같아서 이정도면 나쁘지 않겠다고 타협하였습니다.
메모리기반 데이터베이스니까 서버에서 장애가 생겨서 다시 부팅되거나 했을 때 데이터가 다 날아가지 않는가? 라는 생각도 할 수 있지만 AOF 기능을 사용하면 이런 상황을 방지할 수 있습니다.
AOF: Append Only File, 레디스 서버에서 파일형식으로 백업해두는 기능
그래서 Redis에서 운영하는 공식 클라우드를 사용하기로 했습니다.
무료사용자는 30MB의 메모리를 사용할 수 있고, 버전도 그리 낮은편이 아니어서 쓸만해보입니다.
원래 이건 계획에 없었는데 만들어보면 좋겠다는 생각이 문득 들어서 해봤습니다.
Chrome API를 사용하지 않았다는 조건하에 어려운 작업은 아니었습니다.
{
"manifest_version": 3,
"name": "Z1pLink",
"version": "1.0.0",
"description": "It makes your link shorter",
"action": {
"default_title": "Z1pLink",
"default_popup": "index.html"
}
}
Next.js로 빌드한 정적 페이지에 manifest.json파일을 추가하고 크롬 웹스토어에 배포하면 됩니다.
manifest는 배포하려는 Chrome Extension App의 정보를 알려주기위한 파일입니다.
처음 올리는거라 그런지 등록비용 $5 결제하였고, 검수 통과까진 하루 반정도 걸렸습니다.
고도화하게된다면 우클릭 했을때 나타나는 context menu를 활용할 수 있는 방법도 적용할 수 있을 듯 합니다.
고민이 담겨있어서 좋네요 잘 읽어봤어요
chrome extension과 GitHub 은
Internal Server Error
가 발생하는 상황이네요?