Serverless Database Integration
Serverless 의 개념과 Neon 아키텍처, Prisma ORM 을 활용한 framework-agnostic 데이터베이스 패키지 구성까지.

What is Serverless?
Serverless is a cloud-native development model that allows developers to build and run applications without having to manage servers. The term “serverless” doesn’t mean there are no servers. - Red Hat
Cloud Native 환경이 발전함에 따라 전통적인 구조를 벗어난 새로운 형태의 서비스 또는 어플리케이션들이 많이 등장하고 있다. 마치 서버에 대한 의존도가 사라지는 것 같은 착각을 주는, serverless 도 그 중 하나이다. 도대체 serverless 는 무엇을 말하는 걸까?
Serverless 는 언급했듯, "No Server" 를 의미하는 것이 아니다. Server-maintenance-less 가 의미적으로는 더 정확하다. 서버가 없어지는 게 아니라, 서버에 대한 유지 관리 작업이 없어지는 셈이다. 엄밀하게는, 서버의 유지 관리 작업을 third party vendor 에게 위탁하는 것을 말한다. 기존의 물리 서버, bare metal server 는 24 시간 가동 및 외부 접근 허용이라는 특성 상 보안에 굉장히 취약할 수 밖에 없다. 간단한 NAS (Network-Attached Storage) 를 구축해보려고 해도 각종 보안 설정들 때문에 주저하게 될 정도. 하지만 serverless 는 이런 부분을 떠넘겨버릴 수 있는 것이다(!).
물론 장점만 있는 것은 아니다. 사실상 서버의 통제 권한을 위임하는 것과 다름 없기 때문에, 서버 측의 디버깅이 어려워질 수 있으며, 서버 측 장애 발생 시 즉각적인 대응이 불가능하다는 점, 그리고 직접 서버를 구성하는 것 만큼의 유연성은 확보하기 어렵고, vendor lock-in 이 발생한다. 특히 서비스 운영에 있어서는 서버 장애 대응이 큰 부분을 차지할 수도 있다.
Serverless Computing
24시간 always-on 상태여야 하는 물리 서버와 달리, serverless 는 필요할 때에만 활성화 된다는 on-demand 특징을 갖는다. 또한 Cloud infrastructure 를 기반으로, 그 특징을 살려 auto scaling 과 같은 기능을 함께 제공하는 경우가 많다. 이렇게 server 관리에 더해 cloud 환경 관리까지 겸하는 것을 serverless computing 이라고 하며, 다음과 같이 분류할 수 있다.
- Function-as-a-Service (FAAS): 단일 함수를 서비스로서 제공하는 것으로, 가장 극단적인 형태의 microservice 가 아닐까 싶다.
- Backend-as-a-Service (BAAS): 권한처리, 데이터베이스, 메시징, 스토리지 등 backend 서비스 그 자체를 제공한다.
- Serverless Databases: 인프라 유지 관리를 필요로 하지 않으며 auto scaling 이 가능한 데이터베이스 서비스
- Serverless Containers: Cloud 환경에 대한 관리가 필요 없는 컨테이너 서비스
- Serverless Edge Computing: 컴퓨팅을 사용자 또는 리소스에서 물리적으로 가까운 곳에서 진행하여 네트워크 지연을 낮출 수 있는 서비스
- Serverless GPU: AI/ML 연산을 위한 GPU 자원을 제공하는 서비스
Serverless computing 은 cloud 설정에 대한 복잡성을 신경쓰지 않고도 cloud native 환경을 이용할 수 있다는 점이 다른 무엇보다 매력적이다. 비용적인 측면에서도, 트래픽 변동이 심한 서비스들에 선택적으로 적용하면 큰 이점을 가져다 줄 수 있다. 하지만 Cloud 인프라 장애에 영향을 받는 또다른 어플리케이션이 될 가능성도 무시할 수 없다.
Serverless Database
이 프로젝트의 블로그 시스템은 처음부터 데이터베이스 사용을 염두에 두고 설계됐다. 전까지 사용하고 있었던 file-based dynamic routing 은 인프라를 확정하기 전까지의 임시방편에 불과했다. 사설 서버나 AWS 클라우드 같은 환경에의 서버를 직접 구축할 생각만 하다가, Next.js Dashboard Tutorial 을 진행하면서 Vercel Integrations 에서 제공하는 Neon 데이터베이스를 접하게 됐고, 이를 바탕으로 블로그 시스템을 개편했다. 여기서는 오랜 기간 결정을 내리지 못하고 있던 인프라 문제를 해결해준 Serverless Database, 그 중에서도 Neon 을 중심으로 알아보고자 한다.
Neon
Neon architecture
Serverless database vendor 중 하나인 Neon 은 compute 와 storage 를 분리하고, Neon Control Plane 을 통해 이들의 cloud 자원을 조율하여 Postgres 데이터베이스를 제공한다. Neon Architecture 를 정리하자면 다음과 같다:
- On-Demand Compute(Postgres): Stateless Query Processor. SQL 쿼리들을 실행하거나 WAL records 를 생성하고, 자주 접근된 data page 의 캐싱을 진행하는 등, 요청받은 작업을 수행하여 Safekeeper 에게 전달한다.
- Safekeeper: Durability & Replication Mediator. 트랜잭션의 안정성을 보장하며 Pageserver 의 버퍼 또는 relay point 의 역할을 한다. WAL stream 을 compute 로부터 Pageserver 에게 전달하는 일도 맡고 있다.
- Pageserver: Managed Storage & Caching. multi-tenant storage 의 핵심으로, WAL stream 을 전달 받아 database page 에 적용하고, 데이터 캐시를 최신 상태로 유지하며, Object Storage 로 전달할 데이터를 관리한다. 각 tenant 들의 data isolation 을 담당함과 동시에 git 과 같은 branching 기능도 제공한다.
- Object Storage(S3): Infinite & Immutable & Durable Archive. 데이터베이스의 이력 (historical data)과 스냅샷, 레이어 파일 등을 저장하는 저장소이다. Pageserver 는 이 곳을 참조해 복구를 진행하지만, 실시간 transaction 커밋에는 관여하지 않는다.
실제 연산 작업이 일어나는 compute 와 데이터를 저장하는 storage 를 분리해내고, compute 를 stateless 상태로 구조화하여 scale-to-zero, 비활성화를 가능하게 함으로써 serverless 서비스로 거듭날 수 있었다.
Integration
Vercel Integrations 에서는 통합 가능한 여러 서비스들을 소개하고 있으며, 이 프로젝트에서는 Neon 을 선택했다. 인프라 및 서버 운영을 위임한다는 부분이 제일 컸고, serverless database 라는 낯선 서비스 형태를 도입하는 것이기 때문에, 다른 대안을 알아보거나 하지는 않았다. Neon 에 Vercel 계정을 연동하여 체험 서비스까지 연결해주는 일련의 과정이 잘 짜여져 있어 따라하기만 해도 쉽게 사용 가능했다. Connection Configuration 에서 확인할 수 있는 PGHOST, PGUSER, PGPASSWORD 로 해당 데이터베이스를 DBeaver 에 직접 연결할 수도 있다. Neon 이 인프라와 데이터베이스를 제공해주지만, 그럼 query 는 어떻게 처리할까?
Prisma ORM
여기서는 튜토리얼에 함께 제공된 Prisma ORM 을 사용했다. 데이터베이스 데이터와 프로그래밍 언어의 객체 형태 사이를 중개해주는 매개체인 ORM, Object-Relational Mapping 중 하나인 Prisma ORM 은 TypeScript 및 Node.js 생태계를 위한 type-safe ORM 이다. 기본적인 사용법은 다음과 같다.
prisma.config.ts에 연결할 데이터베이스 connection configuration 을 설정한다. (7.x 기준)- Prisma Schema 를 통해 데이터를 직접 모델링하거나, 설계된 모델링을 데이터베이스로부터 받아온다.
prisma generate를 실행한다. (여기서 prisma 가 객체를 생성해준다.)- Prisma Client 로 생성된 객체를 사용한다.
Framework-Agnostic Database Package
여기에서는 monorepo 구조를 활용해 데이터베이스 패키지를 분리했다. React 나 Next 같은 프레임워크에 대한 dependency 를 완전히 제거하고 데이터베이스 접근 자체를 분리하여 순수한 query 실행의 역할만 할 수 있도록 @juun/db 패키지를 별도로 생성했다.
블로그 포스트를 위한 데이터 모델은 위와 같이 정의했다. post_tag 는 n:n 관계를 처리하기 위해 junction 테이블을 따로 만들어주었다.
데이터베이스의 connection pool 이 불필요하게 많아지는 것을 방지하기 위해서 Singleton 형태로 instance 를 제한한 Prisma Client 를 사용했다.
실제 query 는 entity > method > specific 순서를 따르는 namespace 패턴으로 정리했다. 이 프로젝트에는 GET 외의 method 를 추가할 생각은 없지만, 따로 주석이나 설명을 달지 않고도 의미를 보존하기 위한 게으름의 연장 작업이다.
Closing
Serverless Database 라는 새로운 도메인을 도전적으로 도입해봤다. 이 프로젝트에서는 물리 서버의 유지 관리를 위임할 수 있다는 부분이 가장 크게 작용했고, backend framework 없이도 type-safe 한 데이터베이스 접근이 가능해졌다. 다만 서비스의 특징이 장점이자 동시에 단점일 수 있기 때문에, 실제 도입에서는 고려할 점이 많다.
그 중 하나인 on-demand 의 경우, 인스턴스가 필요에 따라 생성된다는 말은 곧 데이터베이스의 connection 이 없으면 인스턴스가 없어진다는 말과 같다. 즉, connection 이 없는 상태에서는 필요한 모든 인스턴스가 준비될 시간이 추가적으로 필요해지는 셈. 이를 Cold Start 라고 하는데, 이로 인한 지연 시간을 줄이기 위한 가이드도 별도로 존재하니, 성능 면에서 무시할 수 없는 비중을 차지하는 것으로 보인다.
ORM 또한 query 작성에 대한 자유도가 떨어진다는 점에서 복잡한 쿼리가 필요한 서비스에는 적합하지 않을 수 있다. 비용 측면에서도, 이 프로젝트처럼 트래픽이 적은 경우에는 오히려 scale-to-zero 가 비용 절감으로 이어지지만, 꾸준한 트래픽이 발생하는 서비스에서는 직접 운영 비용보다 커질 수도 있다. 도입에는 충분한 고려가 필요하다.
