Recent Posts
Recent Comments
반응형
«   2026/08   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31
Archives
Today
Total
관리 메뉴

오늘도 공부

Appwrite 뜯어보기: 오픈소스 BaaS는 내부에서 어떻게 동작할까? 본문

AI/추천 오픈소스

Appwrite 뜯어보기: 오픈소스 BaaS는 내부에서 어떻게 동작할까?

행복한 수지아빠 2026. 8. 12. 12:25
반응형

웹이나 모바일 앱 하나를 만들 때 실제로 필요한 백엔드 기능은 생각보다 많다.

회원가입과 로그인, 데이터베이스, 파일 업로드, 권한 관리, 서버 함수, 실시간 통신, 이메일과 푸시 알림, 웹훅, 배포 환경까지 준비해야 한다.

개별 서비스를 조합해 직접 만들 수도 있지만, Appwrite는 이 영역을 하나의 개발 플랫폼으로 묶으려는 오픈소스 프로젝트다.

Appwrite는 공식 README에서 자신을 웹·모바일·AI 애플리케이션을 위한 오픈소스 개발 플랫폼으로 설명한다. Auth, Databases, Storage, Functions, Messaging, Sites, Realtime 등을 하나의 플랫폼에서 제공하며, Appwrite Cloud를 이용하거나 직접 서버에 Self-hosting할 수 있다.

하지만 Appwrite에서 더 흥미로운 부분은 기능 목록이 아니다.

이 많은 기능을 하나의 백엔드 플랫폼 안에서 어떻게 구조화했는가?

소스 코드를 살펴보면 Appwrite는 꽤 흥미로운 답을 보여준다.


1. Appwrite는 단순한 API 서버가 아니다

Appwrite를 가장 단순하게 보면 다음과 같은 구조다.

Web / Mobile / Server Application
                │
                ▼
             Appwrite
                │
        ┌───────┼────────┐
        │       │        │
       Auth  Database  Storage
        │       │        │
        ├──── Functions ─┤
        │                │
     Realtime         Messaging
        │                │
        └───── Sites ────┘

개발자는 이런 기능을 REST API나 SDK를 통해 사용할 수 있다.

공식 README 기준 주요 제품은 다음과 같다.

  • Auth — 로그인, 세션, OAuth, MFA, 사용자 인증
  • Databases — 데이터 저장, Query, Index, Relationship
  • Storage — 파일 업로드·다운로드와 이미지 처리
  • Functions — 이벤트 또는 스케줄 기반 서버 코드 실행
  • Messaging — Email, SMS, Push
  • Sites — 웹 애플리케이션 호스팅
  • Realtime — 실시간 데이터 전달

Appwrite는 REST뿐 아니라 WebSocket과 GraphQL도 지원한다고 아키텍처 문서에서 설명한다.

즉 Appwrite를 단순히

"Firebase를 오픈소스로 다시 만든 프로젝트"

정도로 이해하면 내부 구조에서 배울 수 있는 많은 부분을 놓치게 된다.

Appwrite는 오히려 여러 백엔드 기능을 하나의 플랫폼으로 구성하는 방법을 보여주는 대규모 백엔드 시스템 사례로 보는 편이 더 흥미롭다.


2. 핵심은 '모놀리스 + 마이크로서비스' 혼합 구조

Appwrite의 아키텍처에서 가장 먼저 눈에 들어오는 부분이다.

공식 CONTRIBUTING 문서에는 현재 Appwrite 구조가 Monolithic Architecture와 Microservice Architecture의 조합이라고 명시되어 있다.

핵심 API는 하나의 애플리케이션으로 구성한다.

              ┌─────────────────────┐
Client ─────▶ │    Appwrite API     │
              │                     │
              │ Auth                │
              │ Database            │
              │ Storage             │
              │ Functions           │
              │ Teams               │
              │ Users               │
              │ Sites               │
              └─────────────────────┘

반면 시간이 많이 걸리거나 독립적으로 처리할 수 있는 작업은 Worker로 분리한다.

실제 Docker Compose에는 다음과 같은 Worker들이 별도 컨테이너로 존재한다.

appwrite-worker-webhooks
appwrite-worker-deletes
appwrite-worker-databases
appwrite-worker-builds
appwrite-worker-jobs
appwrite-worker-screenshots
appwrite-worker-certificates
appwrite-worker-functions
appwrite-worker-mails
appwrite-worker-notifications
appwrite-worker-messaging
appwrite-worker-migrations

그래서 전체 구조를 단순화하면 다음에 가깝다.

                         ┌──────────────┐
Internet ───────────────▶│   Traefik    │
                         └──────┬───────┘
                                │
                    ┌───────────┴──────────┐
                    ▼                      ▼
             Appwrite API           Appwrite Realtime
                    │
                    ▼
             Application Logic
                    │
          ┌─────────┼─────────┐
          ▼         ▼         ▼
      Database    Redis      Storage
                    │
                    ▼
                 Queue
                    │
       ┌────────────┼──────────────┐
       ▼            ▼              ▼
   Functions     Webhooks       Messaging
     Worker        Worker          Worker
       │
       ▼
 OpenRuntimes
 Executor

README도 API Layer에서는 캐시를 활용하고, 무거운 작업은 Background Worker로 넘긴다고 설명한다.

여기서 Appwrite의 설계 방향을 명확하게 볼 수 있다.

모든 기능을 처음부터 마이크로서비스로 분리하지 않았다.

CONTRIBUTING 문서에 따르면 핵심 API를 모놀리스로 유지한 이유 중 하나는 작은 팀이 더 빠르게 개발하기 위해서였다. 대신 Worker와 내부 서비스는 별도 컨테이너로 나누어 독립적으로 확장할 수 있도록 구성했다.


3. PHP인데 일반적인 PHP 서버와 조금 다르다

Appwrite 서버의 중심 언어는 PHP다.

하지만 일반적인

Nginx
  ↓
PHP-FPM
  ↓
Laravel

형태와는 다르다.

현재 main의 composer.json은 PHP >= 8.5.0과 Swoole 6 확장을 요구하고 있다. 또한 상당수의 핵심 구성요소가 utopia-php 패키지로 나뉘어 있다.

주요 의존성을 보면 다음과 같다.

PHP >= 8.5
Swoole 6
Utopia Platform
Utopia Database
Utopia Queue
Utopia Cache
Utopia Storage
Utopia Messaging
Utopia WebSocket
GraphQL PHP
Redis Extension

Appwrite 내부 개발 가이드 역시 Swoole 기반 비동기 런타임과 Utopia PHP Framework를 핵심 Stack으로 설명한다.

Docker 컨테이너의 기본 실행 명령도 흥미롭다.

CMD [ "php", "app/http.php" ]

별도 PHP-FPM이 아니라 Appwrite 애플리케이션 자체가 HTTP 서버 프로세스로 동작하는 구조다.


4. 거대한 Controller 대신 'Module + Action'

대규모 백엔드에서는 기능이 증가하면서 Controller가 비대해지는 문제가 자주 발생한다.

Appwrite 역시 과거에는

app/controllers/api/[service].php

형태의 큰 Controller 파일을 사용했지만, 유지보수 문제가 생기면서 신규 개발을 HTTP Module 구조로 이전하고 있다고 설명한다.

현재 구조는 다음과 같다.

src/Appwrite/Platform/Modules/

그 아래에 기능별 Module이 존재한다.

Account
Avatars
Databases
Functions
Health
Notifications
Projects
Sites
Storage
Teams
Tokens
Users
VCS
Webhooks
...

실제 Appwrite Platform 클래스가 이 모듈들을 하나씩 등록한다.

각 Module 내부는 대략 다음 패턴을 따른다.

Modules/
  Teams/
    Module.php

    Services/
      Http.php
      Workers.php
      Tasks.php

    Http/
      Teams/
        Create.php
        Get.php
        Update.php
        Delete.php
        XList.php

내부 개발 가이드에서는 HTTP Endpoint의 경로까지 파일 구조에 반영하도록 규칙을 정의하고 있다.

예를 들어

POST /v1/teams

API는

Modules/Teams/Http/Teams/Create.php

에서 구현된다.

실제 코드를 보면 하나의 Action에서 HTTP Method, Path, Scope, Event, Audit, SDK Metadata, Parameter, Dependency Injection까지 정의한다.

개념적으로 단순화하면 다음과 같다.

class Create extends Action
{
    public function __construct()
    {
        $this
            ->setHttpMethod('POST')
            ->setHttpPath('/v1/teams')
            ->label('event', 'teams.[teamId].create')
            ->label('scope', 'teams.write')
            ->inject('dbForProject')
            ->inject('queueForEvents')
            ->callback($this->action(...));
    }
}

여기서 눈여겨볼 점은 하나의 Endpoint 정의가 단순 Controller 함수가 아니라는 것이다.

HTTP Route
+
Validation
+
Authorization Scope
+
Audit
+
Event
+
SDK Metadata
+
Dependency Injection

이 모두가 하나의 Action Contract에 들어간다.

API 구현과 SDK 생성 규칙을 최대한 동일한 구조에서 관리하려는 방식이다.


5. 무거운 작업은 Queue 뒤로 넘긴다

Appwrite 내부에서 중요한 구성요소 중 하나가 Event Queue다.

실제 Event.php에는 다양한 Queue 이름이 정의되어 있다.

v1-database
v1-deletes
v1-audits
v1-mails
v1-notifications
v1-functions
v1-webhooks
v1-certificates
v1-builds
v1-jobs
v1-screenshots
v1-messaging
v1-executions
v1-migrations

Event가 발생하면 Publisher를 이용해 Queue에 Payload를 넣는다.

return $this->publisher->enqueue(
    $queue,
    $payload
);

실제 Event 구현에서도 Queue 생성 → Payload 구성 → Publisher enqueue 과정이 확인된다.

이를 구조적으로 보면 다음과 같다.

API Request
    │
    ▼
Business Logic
    │
    ├────────────▶ Database
    │
    ▼
Generate Event
    │
    ▼
Queue
    │
    ├────▶ Webhook Worker
    ├────▶ Function Worker
    ├────▶ Messaging Worker
    ├────▶ Build Worker
    └────▶ Delete Worker

실제로 Team 생성 API에서도 데이터베이스 작업 후 queueForEvents에 이벤트 관련 Parameter를 설정한다.

이 패턴은 대규모 백엔드에서 매우 자주 등장한다.

사용자가 기다릴 필요가 없는 작업을 동기 HTTP Request에서 분리하면 API와 Background Processing을 별도로 운영할 수 있기 때문이다.


6. Redis는 단순 캐시 이상으로 중요하다

Appwrite Compose에는 Redis가 별도 서비스로 포함되어 있다.

현재 설정에서는 Redis 7.4 Alpine 이미지를 사용하며 최대 메모리와 LRU 정책도 설정되어 있다.

Appwrite 개발 가이드에서는 Redis의 역할을 다음처럼 정리한다.

Cache
Queue
Pub/Sub

그래서 전체 Appwrite 구조를 이해하려면 Redis를 단순한 캐시 서버라고 생각하기보다는

서비스와 Worker 사이를 연결하는 인메모리 인프라

라는 관점에서 보는 것이 더 적절하다.


7. 데이터베이스도 하나만 사용하는 구조가 아니다

2026년 8월 현재 main의 개발 환경 설정은 특히 흥미롭다.

.env에는 기본 Platform Database와 DocumentsDB, VectorsDB가 서로 다른 Adapter를 사용할 수 있도록 구성되어 있다.

현재 기본 설정은 다음과 같다.

Main DB
PostgreSQL

DocumentsDB
MongoDB

VectorsDB
PostgreSQL

그리고 Redis는 별도로 사용한다.

Docker Compose에도 MariaDB, MongoDB, PostgreSQL이 각각 정의되어 있다.

특히 현재 main에는

DocumentsDB
TablesDB
VectorsDB
Embeddings

처럼 데이터베이스 기능이 세분화되고 있다. VectorsDB에는 Collection과 Transaction을 포함한 별도 HTTP API 구조도 존재한다.

Compose에는 별도의 Embedding 서비스도 포함되어 있다.

appwrite-embedding:
  image: appwrite/embedding:0.1.0

기본 Embedding Model 설정에는 nomic-embed-text가 지정되어 있다.

즉 현재 개발 브랜치에서는 기존 BaaS 기능뿐 아니라 Vector Database와 Embedding을 위한 AI 애플리케이션 인프라까지 코드베이스에 포함되고 있는 것을 확인할 수 있다.

단, main 브랜치의 기능은 최신 정식 릴리스와 반드시 동일하다고 볼 수 없으므로 버전별 기능 확인은 별도로 필요하다.


8. Serverless Function은 OpenRuntimes가 담당한다

Appwrite Functions도 Appwrite API 프로세스 안에서 직접 실행하지 않는다.

Docker Compose에는 별도의

openruntimes-executor

서비스가 존재한다.

현재 main에서는 openruntimes/executor:0.25.4 이미지를 사용하며 Functions와 Sites의 Runtime을 실행할 수 있도록 구성되어 있다.

또한 Build·Job 처리를 위한

orchestrator-jobs

서비스도 존재한다.

전체 구조를 단순화하면 다음과 같다.

Developer
    │
    ▼
Upload Function
    │
    ▼
Appwrite API
    │
    ▼
Build Worker
    │
    ▼
Jobs / Build Infrastructure
    │
    ▼
OpenRuntimes Executor
    │
    ▼
Isolated Runtime
    │
    ├─ Node
    ├─ PHP
    ├─ Python
    └─ ...

README에서는 현재 Functions 제품이 여러 Runtime을 지원한다고 안내하고 있다.

Appwrite가 단순 API 서버를 넘어 플랫폼으로 복잡해지는 이유 중 하나가 바로 이 Compute Layer다.


9. Realtime도 API 서버와 분리되어 있다

Realtime 역시 별도의 컨테이너다.

appwrite-realtime:
  entrypoint: realtime

Traefik은

/v1/realtime

요청을 Appwrite Realtime 서비스로 라우팅한다.

따라서 HTTP API와 WebSocket 연결을 같은 프로세스에 모두 몰아넣는 구조가 아니다.

               Traefik
                  │
         ┌────────┴─────────┐
         │                  │
     REST / GraphQL      WebSocket
         │                  │
   Appwrite API      Appwrite Realtime

Realtime 연결과 일반 API 요청의 부하 특성이 다르다는 점을 고려한 서비스 분리가 소스 구조에서도 확인된다.


10. 앞단에는 Traefik이 있다

Self-hosted Compose 환경에서 외부 트래픽의 진입점은 Traefik이다.

현재 Compose에서는 Traefik 3.6이 사용되고 있으며 HTTP 80과 HTTPS 443 Entry Point를 구성한다.

Traefik이 요청을 구분해

API
Console
Realtime

등으로 라우팅한다.

전체 구조를 한 번 더 정리하면 다음과 같다.

                    Internet
                       │
                       ▼
                   Traefik
                       │
          ┌────────────┼────────────┐
          ▼            ▼            ▼
      Appwrite      Realtime      Console
        API
          │
          ▼
       Modules
          │
     ┌────┴────┐
     ▼         ▼
 Database     Redis
               │
               ▼
             Queue
               │
      ┌────────┼─────────┐
      ▼        ▼         ▼
 Functions  Webhooks  Messaging
 Worker      Worker     Worker
      │
      ▼
 OpenRuntimes

Appwrite를 이해하는 데 가장 중요한 그림도 사실 이것이다.


11. Self-hosting이 가능한 이유

Appwrite는 Docker 기반 배포를 핵심 설치 방식으로 제공한다.

README에서는 Docker Compose뿐 아니라 Kubernetes, Docker Swarm, Rancher 같은 Container Orchestration 환경에서도 운영할 수 있다고 안내한다.

Dockerfile에는 Storage 관련 디렉터리도 명시적으로 구성되어 있다.

/storage/uploads
/storage/imports
/storage/cache
/storage/config
/storage/certificates
/storage/functions
/storage/debug

Compose에서는 추가로 Functions, Sites, Builds 등을 Volume으로 관리한다.

따라서 Self-hosted Appwrite를 운영한다면 Appwrite API 컨테이너 하나만 관리한다고 생각하기보다는

Reverse Proxy
Database
Redis
Realtime
Workers
Executor
Build infrastructure
Persistent Storage
Certificate

를 포함한 하나의 백엔드 플랫폼 Stack을 운영한다고 이해하는 편이 정확하다.


12. 오픈소스 라이선스는 BSD 3-Clause

Appwrite Server Repository는 BSD 3-Clause License를 사용한다.

라이선스 전문에는 소스 및 바이너리 형태의 재배포와 수정 사용을 허용하면서 Copyright Notice와 Disclaimer 유지 등의 조건을 명시하고 있다.

기업에서 직접 Self-hosting하거나 소스를 분석하려는 경우에도 비교적 접근하기 쉬운 오픈소스 라이선스다.

실제 상용 제품에 수정·재배포하려는 경우에는 Appwrite Server뿐 아니라 함께 사용하는 별도 이미지와 외부 프로젝트들의 라이선스도 각각 확인할 필요가 있다.


13. Appwrite 코드에서 배울 수 있는 다섯 가지

Appwrite를 직접 사용할 계획이 없더라도 백엔드 개발자 입장에서 참고할 만한 설계 요소가 많다.

① 처음부터 모든 것을 Microservice로 만들 필요는 없다

Appwrite는 핵심 API를 Monolith로 유지하면서 Worker를 독립 서비스로 분리한다.

이는

Core Business Logic
        │
        ▼
     Monolith

Heavy / Async Job
        │
        ▼
   Independent Worker

라는 현실적인 분리 방법을 보여준다.


② Side Effect는 Queue 뒤로 분리할 수 있다

메일, Function 실행, Webhook, Messaging, Delete 같은 작업은 별도 Queue와 Worker로 구성되어 있다.

API 서버와 Background Processing의 책임을 분리하는 실제 대규모 사례로 살펴볼 만하다.


③ 기능 단위 Module 구조가 중요하다

Appwrite는 Account, Databases, Functions, Storage, Sites, Teams처럼 도메인별 Module을 구성한다.

프로젝트가 커질수록 단순한

controllers/
services/
repositories/

구조보다

Modules/
  Teams/
  Storage/
  Functions/
  Databases/

처럼 기능 자체를 상위 경계로 두는 방식이 코드 탐색에 유리한 경우가 있다.


④ API 규칙을 코드 구조까지 연결한다

Appwrite의 신규 HTTP Action 파일은 기본적으로

Create
Get
Update
Delete
XList

형태로 제한하는 규칙을 사용한다.

URL, Directory, Action Class, SDK Operation이 최대한 동일한 Resource Model을 표현하도록 만드는 것이다.

대규모 API를 오랫동안 유지할 때 Naming Convention과 Directory Convention 자체가 Architecture의 일부가 될 수 있다는 좋은 사례다.


⑤ BaaS도 결국 분산 시스템이다

사용자는 Appwrite에서 간단하게

account.create(...)
databases.create(...)
storage.createFile(...)

같은 API를 호출하지만 그 뒤에는

Reverse Proxy
API
Authorization
Database
Cache
Queue
Worker
Runtime
Storage
Realtime

가 연결되어 있다.

BaaS 제품이 사용자에게 복잡성을 감춰준다고 해서 시스템 자체가 단순한 것은 아니다.

Appwrite Repository는 그 복잡성을 실제 코드 수준에서 관찰할 수 있다는 점에서 공부할 가치가 있다.


14. 그렇다면 Appwrite는 어떤 프로젝트에 어울릴까?

기능 구성상 Appwrite는 다음과 같은 요구사항을 가진 애플리케이션에서 검토할 수 있다.

사용자 인증이 필요하다
+
Database가 필요하다
+
파일 Storage가 필요하다
+
Realtime 기능이 필요하다
+
Backend Function이 필요하다
+
Messaging이 필요하다

특히 Cloud 서비스뿐 아니라 Self-hosted 선택지도 필요하다면 Appwrite의 구조를 살펴볼 이유가 충분하다. Cloud와 Self-hosted 두 방식을 모두 제공한다는 점은 공식 README에서도 명시되어 있다.

반대로 Self-hosted를 선택한다면 Docker Compose에서 확인할 수 있듯 Appwrite API뿐 아니라 Redis, 데이터베이스, Worker, Executor 등 여러 인프라 컴포넌트를 함께 운영하게 된다.


15. Appwrite Repository를 공부한다면 이 순서가 좋다

처음부터 수많은 소스 파일을 읽는 것은 비효율적이다.

다음 순서가 구조를 이해하기 쉽다.

1. README.md
        ↓
2. docker-compose.yml
        ↓
3. composer.json
        ↓
4. src/Appwrite/Platform/Appwrite.php
        ↓
5. src/Appwrite/Platform/Modules
        ↓
6. 특정 HTTP Action 하나
        ↓
7. Event / Queue
        ↓
8. Worker
        ↓
9. OpenRuntimes Executor

예를 들어 Team 생성 하나만 따라가도 많은 구조를 확인할 수 있다.

POST /v1/teams
       │
       ▼
Teams/Create.php
       │
       ├── Validation
       ├── Authorization
       ├── Database
       ├── Audit Metadata
       └── Event
                │
                ▼
              Queue
                │
                ▼
              Worker

이 방식으로 하나의 요청을 끝까지 추적하면 거대한 Repository를 훨씬 빠르게 이해할 수 있다.


마무리

Appwrite를 겉에서 보면 Auth, Database, Storage를 제공하는 편리한 Backend-as-a-Service다.

하지만 내부를 살펴보면 훨씬 더 흥미롭다.

PHP + Swoole
        │
        ▼
Modular Monolith API
        │
        ▼
Redis / Queue
        │
        ▼
Independent Workers
        │
        ├── Messaging
        ├── Webhook
        ├── Functions
        ├── Build
        └── Migration
        │
        ▼
OpenRuntimes

그리고 이 모든 구성요소를 Docker 환경에서 하나의 개발 플랫폼으로 묶는다.

Appwrite에서 가장 인상적인 부분은 "기능이 많다"는 것이 아니다.

복잡한 백엔드 플랫폼을 하나의 거대한 마이크로서비스 집합으로 시작하지 않고, 핵심 API의 응집도는 유지하면서 비동기 작업과 실행 인프라를 점진적으로 분리했다는 점이다. 이 설계 배경은 Appwrite의 공식 아키텍처 설명과 현재 컨테이너 구조에서 직접 확인할 수 있다.

대규모 백엔드, BaaS, Event-driven Architecture, Worker Architecture, Serverless Runtime에 관심이 있다면 Appwrite는 단순히 사용해 볼 오픈소스를 넘어 실제 프로덕션급 백엔드 플랫폼의 구조를 공부할 수 있는 Repository로 살펴볼 만하다.


분석 정보

  • Repository: appwrite/appwrite
  • 분석 기준: 2026-08-12
  • 분석 브랜치: main
  • 분석 시점 최신 main Commit: ac639005607c71d8a05253b38f3d7398bd298075
  • 확인 시점 최신 정식 Release: 1.9.6
  • License: BSD 3-Clause
  • Repository: https://github.com/appwrite/appwrite

주요 분석 파일

README.md
CONTRIBUTING.md
AGENTS.md
composer.json
Dockerfile
docker-compose.yml
.env

src/Appwrite/Platform/Appwrite.php
src/Appwrite/Platform/Modules/
src/Appwrite/Event/Event.php
src/Appwrite/Platform/Modules/Teams/Http/Teams/Create.php

※ main 브랜치는 개발이 계속 진행되는 브랜치이므로 향후 디렉터리 구조, Runtime, Database 구성 및 서비스 버전은 변경될 수 있다.

 

GitHub - appwrite/appwrite: Appwrite® - complete cloud infrastructure for your web, mobile and AI apps. Including Auth, Databas

Appwrite® - complete cloud infrastructure for your web, mobile and AI apps. Including Auth, Databases, Storage, Functions, Messaging, Hosting, Realtime and more - appwrite/appwrite

github.com

 

반응형