개요
Directus는 모든 SQL 데이터베이스를 즉시 API와 관리자 패널로 변환하는 오픈소스 헤드리스 CMS입니다. 기존 데이터베이스에 연결하면 자동으로 REST/GraphQL API와 관리 UI가 생성됩니다. 데이터 마이그레이션 없이 바로 사용할 수 있습니다.
| 항목 | 내용 |
|---|
| GitHub | https://github.com/directus/directus |
| 공식 사이트 | https://directus.io |
| 문서 | https://docs.directus.io |
| 라이선스 | BSL 1.1 (소규모 무료) |
| GitHub Stars | 28K+ |
Directus란?
왜 Directus인가?
Strapi
→ 새 프로젝트에서 콘텐츠 타입 정의
→ Strapi 전용 스키마
Directus
→ 기존 DB에 연결하면 끝
→ 표준 SQL 테이블 그대로 사용
→ Directus 제거해도 데이터 그대로
핵심 개념
"Database-First"
1. PostgreSQL/MySQL 등 기존 DB 연결
2. Directus가 스키마 자동 감지
3. REST/GraphQL API 자동 생성
4. 관리자 UI 자동 생성
5. DB 직접 수정해도 동기화
아키텍처
┌─────────────────────────────────────────┐
│ Frontend (React/Next.js/Vue) │
└─────────────────┬───────────────────────┘
│ REST / GraphQL / WebSocket
▼
┌─────────────────────────────────────────┐
│ Directus │
│ ┌─────────────────────────────────┐ │
│ │ Admin App (관리자 UI) │ │
│ ├─────────────────────────────────┤ │
│ │ API Layer (REST/GraphQL) │ │
│ ├─────────────────────────────────┤ │
│ │ Flows (자동화 워크플로우) │ │
│ ├─────────────────────────────────┤ │
│ │ File Storage (미디어 관리) │ │
│ └─────────────────────────────────┘ │
└─────────────────┬───────────────────────┘
│ 스키마 미러링
▼
┌─────────────────────────────────────────┐
│ Your SQL Database (표준 테이블) │
│ PostgreSQL / MySQL / SQLite / etc. │
└─────────────────────────────────────────┘
주요 기능
🗄️ 데이터베이스 지원
| DB | 지원 |
|---|
| PostgreSQL | ✅ |
| MySQL | ✅ |
| MariaDB | ✅ |
| SQLite | ✅ |
| MS SQL Server | ✅ |
| OracleDB | ✅ |
| CockroachDB | ✅ |
🔌 API
| 기능 | 설명 |
|---|
| REST API | 자동 CRUD |
| GraphQL | 자동 생성 |
| WebSocket | 실시간 업데이트 |
| 필터/정렬/페이지네이션 | 강력한 쿼리 |
🎨 Admin App
| 기능 | 설명 |
|---|
| 자동 UI 생성 | 테이블 → 폼/리스트 |
| 커스텀 대시보드 | 드래그앤드롭 |
| 다크 모드 | 내장 |
| 64+ 언어 | 다국어 지원 |
📁 파일 관리
| 기능 | 설명 |
|---|
| Digital Asset Management | 이미지/파일 관리 |
| 이미지 변환 | 실시간 리사이즈/크롭 |
| 외부 스토리지 | S3, GCS, Azure |
⚡ Flows (자동화)
| 기능 | 설명 |
|---|
| Trigger | 데이터 변경, 스케줄, Webhook |
| Operations | 이메일, HTTP 요청, 스크립트 |
| 조건부 실행 | 필터 조건 |
🔐 권한
| 기능 | 설명 |
|---|
| RBAC | 역할 기반 접근 제어 |
| 필드 레벨 권한 | 세밀한 제어 |
| SSO | OAuth, LDAP, SAML |
시스템 요구 사항
| 항목 | 최소 | 권장 |
|---|
| CPU | 1 코어 | 2+ 코어 |
| RAM | 512MB | 2GB+ |
| 저장소 | 10GB | 20GB+ |
| 포트 | 8055 | – |
Docker 설치
방법 1: 빠른 시작 (SQLite)
docker run -d \
--name directus \
-p 8055:8055 \
-e SECRET="your-random-secret-key" \
-e ADMIN_EMAIL="admin@example.com" \
-e ADMIN_PASSWORD="admin123" \
-e DB_CLIENT="sqlite3" \
-e DB_FILENAME="/directus/database/data.db" \
-v directus-data:/directus/database \
-v directus-uploads:/directus/uploads \
directus/directus:11
접속: http://localhost:8055
방법 2: Docker Compose + PostgreSQL
# docker-compose.yml
services:
directus:
image: directus/directus:11
container_name: directus
restart: unless-stopped
ports:
- "8055:8055"
environment:
SECRET: "your-random-secret-key"
ADMIN_EMAIL: "admin@example.com"
ADMIN_PASSWORD: "admin123"
DB_CLIENT: "pg"
DB_HOST: "directus-db"
DB_PORT: "5432"
DB_DATABASE: "directus"
DB_USER: "directus"
DB_PASSWORD: "directus_password"
WEBSOCKETS_ENABLED: "true"
volumes:
- ./uploads:/directus/uploads
- ./extensions:/directus/extensions
depends_on:
directus-db:
condition: service_healthy
directus-db:
image: postgres:16-alpine
container_name: directus-db
restart: unless-stopped
environment:
POSTGRES_DB: directus
POSTGRES_USER: directus
POSTGRES_PASSWORD: directus_password
volumes:
- ./data/postgres:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U directus"]
interval: 5s
timeout: 5s
retries: 5
방법 3: MySQL 사용
# docker-compose.yml
services:
directus:
image: directus/directus:11
container_name: directus
restart: unless-stopped
ports:
- "8055:8055"
environment:
SECRET: "your-random-secret-key"
ADMIN_EMAIL: "admin@example.com"
ADMIN_PASSWORD: "admin123"
DB_CLIENT: "mysql"
DB_HOST: "directus-mysql"
DB_PORT: "3306"
DB_DATABASE: "directus"
DB_USER: "directus"
DB_PASSWORD: "directus_password"
volumes:
- ./uploads:/directus/uploads
depends_on:
- directus-mysql
directus-mysql:
image: mysql:8
container_name: directus-mysql
restart: unless-stopped
command: --default-authentication-plugin=mysql_native_password
environment:
MYSQL_DATABASE: directus
MYSQL_USER: directus
MYSQL_PASSWORD: directus_password
MYSQL_ROOT_PASSWORD: root_password
volumes:
- ./data/mysql:/var/lib/mysql
방법 4: Redis 캐시 추가
# docker-compose.yml
services:
directus:
image: directus/directus:11
container_name: directus
restart: unless-stopped
ports:
- "8055:8055"
environment:
SECRET: "your-random-secret-key"
ADMIN_EMAIL: "admin@example.com"
ADMIN_PASSWORD: "admin123"
DB_CLIENT: "pg"
DB_HOST: "directus-db"
DB_PORT: "5432"
DB_DATABASE: "directus"
DB_USER: "directus"
DB_PASSWORD: "directus_password"
# Redis 캐시
CACHE_ENABLED: "true"
CACHE_STORE: "redis"
REDIS_HOST: "directus-redis"
REDIS_PORT: "6379"
CACHE_AUTO_PURGE: "true"
CACHE_TTL: "5m"
# Rate Limiting
RATE_LIMITER_ENABLED: "true"
RATE_LIMITER_STORE: "redis"
volumes:
- ./uploads:/directus/uploads
depends_on:
- directus-db
- directus-redis
directus-db:
image: postgres:16-alpine
container_name: directus-db
restart: unless-stopped
environment:
POSTGRES_DB: directus
POSTGRES_USER: directus
POSTGRES_PASSWORD: directus_password
volumes:
- ./data/postgres:/var/lib/postgresql/data
directus-redis:
image: redis:alpine
container_name: directus-redis
restart: unless-stopped
방법 5: S3 스토리지 연동
# docker-compose.yml
services:
directus:
image: directus/directus:11
container_name: directus
restart: unless-stopped
ports:
- "8055:8055"
environment:
SECRET: "your-random-secret-key"
ADMIN_EMAIL: "admin@example.com"
ADMIN_PASSWORD: "admin123"
DB_CLIENT: "pg"
DB_HOST: "directus-db"
DB_PORT: "5432"
DB_DATABASE: "directus"
DB_USER: "directus"
DB_PASSWORD: "directus_password"
# S3 스토리지
STORAGE_LOCATIONS: "s3"
STORAGE_S3_DRIVER: "s3"
STORAGE_S3_KEY: "${AWS_ACCESS_KEY_ID}"
STORAGE_S3_SECRET: "${AWS_SECRET_ACCESS_KEY}"
STORAGE_S3_BUCKET: "your-bucket"
STORAGE_S3_REGION: "ap-northeast-2"
depends_on:
- directus-db
directus-db:
image: postgres:16-alpine
container_name: directus-db
restart: unless-stopped
environment:
POSTGRES_DB: directus
POSTGRES_USER: directus
POSTGRES_PASSWORD: directus_password
volumes:
- ./data/postgres:/var/lib/postgresql/data
방법 6: MinIO (S3 호환) 연동
# docker-compose.yml
services:
directus:
image: directus/directus:11
container_name: directus
restart: unless-stopped
ports:
- "8055:8055"
environment:
SECRET: "your-random-secret-key"
ADMIN_EMAIL: "admin@example.com"
ADMIN_PASSWORD: "admin123"
DB_CLIENT: "pg"
DB_HOST: "directus-db"
DB_PORT: "5432"
DB_DATABASE: "directus"
DB_USER: "directus"
DB_PASSWORD: "directus_password"
# MinIO 스토리지
STORAGE_LOCATIONS: "s3"
STORAGE_S3_DRIVER: "s3"
STORAGE_S3_KEY: "minioadmin"
STORAGE_S3_SECRET: "minioadmin"
STORAGE_S3_BUCKET: "directus"
STORAGE_S3_ENDPOINT: "http://minio:9000"
STORAGE_S3_FORCE_PATH_STYLE: "true"
depends_on:
- directus-db
- minio
directus-db:
image: postgres:16-alpine
container_name: directus-db
restart: unless-stopped
environment:
POSTGRES_DB: directus
POSTGRES_USER: directus
POSTGRES_PASSWORD: directus_password
volumes:
- ./data/postgres:/var/lib/postgresql/data
minio:
image: minio/minio
container_name: directus-minio
restart: unless-stopped
ports:
- "9000:9000"
- "9001:9001"
environment:
MINIO_ROOT_USER: minioadmin
MINIO_ROOT_PASSWORD: minioadmin
volumes:
- ./data/minio:/data
command: server /data --console-address ":9001"
방법 7: 기존 DB 연결 (마이그레이션 없음)
# docker-compose.yml
services:
directus:
image: directus/directus:11
container_name: directus
restart: unless-stopped
ports:
- "8055:8055"
environment:
SECRET: "your-random-secret-key"
ADMIN_EMAIL: "admin@example.com"
ADMIN_PASSWORD: "admin123"
# 기존 PostgreSQL DB 연결
DB_CLIENT: "pg"
DB_HOST: "your-existing-db-host"
DB_PORT: "5432"
DB_DATABASE: "your_existing_database"
DB_USER: "your_db_user"
DB_PASSWORD: "your_db_password"
volumes:
- ./uploads:/directus/uploads
방법 8: Traefik + HTTPS
# docker-compose.yml
services:
directus:
image: directus/directus:11
container_name: directus
restart: unless-stopped
environment:
SECRET: "your-random-secret-key"
ADMIN_EMAIL: "admin@example.com"
ADMIN_PASSWORD: "admin123"
PUBLIC_URL: "https://cms.yourdomain.com"
DB_CLIENT: "pg"
DB_HOST: "directus-db"
DB_PORT: "5432"
DB_DATABASE: "directus"
DB_USER: "directus"
DB_PASSWORD: "directus_password"
volumes:
- ./uploads:/directus/uploads
labels:
- "traefik.enable=true"
- "traefik.http.routers.directus.rule=Host(`cms.yourdomain.com`)"
- "traefik.http.routers.directus.entrypoints=websecure"
- "traefik.http.routers.directus.tls.certresolver=letsencrypt"
- "traefik.http.services.directus.loadbalancer.server.port=8055"
depends_on:
- directus-db
networks:
- traefik-net
- directus-net
directus-db:
image: postgres:16-alpine
container_name: directus-db
restart: unless-stopped
environment:
POSTGRES_DB: directus
POSTGRES_USER: directus
POSTGRES_PASSWORD: directus_password
volumes:
- ./data/postgres:/var/lib/postgresql/data
networks:
- directus-net
networks:
traefik-net:
external: true
directus-net:
driver: bridge
환경 변수
필수
| 변수 | 설명 |
|---|
SECRET | 토큰 서명 키 (필수!) |
ADMIN_EMAIL | 첫 관리자 이메일 |
ADMIN_PASSWORD | 첫 관리자 비밀번호 |
데이터베이스
| 변수 | 설명 |
|---|
DB_CLIENT | pg, mysql, sqlite3, mssql, oracledb |
DB_HOST | DB 호스트 |
DB_PORT | DB 포트 |
DB_DATABASE | DB 이름 |
DB_USER | DB 사용자 |
DB_PASSWORD | DB 비밀번호 |
캐시
| 변수 | 설명 |
|---|
CACHE_ENABLED | true/false |
CACHE_STORE | memory, redis |
REDIS_HOST | Redis 호스트 |
REDIS_PORT | Redis 포트 |
CACHE_TTL | 캐시 만료 시간 |
스토리지
| 변수 | 설명 |
|---|
STORAGE_LOCATIONS | local, s3 |
STORAGE_S3_KEY | S3 액세스 키 |
STORAGE_S3_SECRET | S3 시크릿 키 |
STORAGE_S3_BUCKET | S3 버킷 |
STORAGE_S3_REGION | S3 리전 |
기타
| 변수 | 설명 |
|---|
PUBLIC_URL | 공개 URL |
WEBSOCKETS_ENABLED | 실시간 기능 |
RATE_LIMITER_ENABLED | 요청 제한 |
초기 설정
1. 시작
docker compose up -d
2. 로그인
http://localhost:8055 접속
- ADMIN_EMAIL, ADMIN_PASSWORD로 로그인
3. 첫 설정 후 환경 변수 제거
프로덕션에서는 첫 실행 후 ADMIN 변수 제거:
# ADMIN_EMAIL: "admin@example.com" # 제거
# ADMIN_PASSWORD: "admin123" # 제거
4. Collection 생성
- Settings → Data Model
- “Create Collection” 클릭
- 이름 입력 (예:
articles)
- 필드 추가:
title (String)
content (WYSIWYG)
cover (Image)
status (Status)
5. 권한 설정
- Settings → Access Control → Roles
- “Public” 역할 선택
- 해당 Collection의 읽기 권한 부여
6. API 테스트
# 모든 항목 조회
curl http://localhost:8055/items/articles
# 인증이 필요한 경우
curl -H "Authorization: Bearer YOUR_TOKEN" \
http://localhost:8055/items/articles
# 필터링
curl "http://localhost:8055/items/articles?filter[status][_eq]=published"
# GraphQL
curl -X POST http://localhost:8055/graphql \
-H "Content-Type: application/json" \
-d '{"query": "{ articles { id title } }"}'
Flows (자동화)
이메일 알림 Flow
- Settings → Flows → Create Flow
- Trigger: “Event Hook” →
items.create
- Operation: “Send Email”
- 조건 설정 및 저장
Webhook Flow
- Trigger: “Webhook”
- Operation: “Run Script” 또는 “Send Request”
- 외부 서비스 연동
백업 및 복원
데이터 구조
./
├── uploads/ # 업로드 파일
├── extensions/ # 커스텀 확장
└── data/
└── postgres/ # PostgreSQL 데이터
백업
# 컨테이너 정지
docker compose down
# 전체 백업
tar -czvf directus-backup-$(date +%Y%m%d).tar.gz uploads/ data/
# DB만 백업
docker exec directus-db pg_dump -U directus directus > backup.sql
# 스키마 스냅샷 (Directus)
npx directus schema snapshot ./snapshot.yaml
# 재시작
docker compose up -d
복원
tar -xzvf directus-backup.tar.gz
docker compose up -d
업데이트
# 최신 이미지
docker compose pull
# 재시작
docker compose down
docker compose up -d
트러블슈팅
SECRET 오류
SECRET 환경 변수가 필수입니다.
→ 랜덤 문자열 생성: openssl rand -base64 32
DB 연결 오류
- DB 컨테이너가 healthy 상태인지 확인
depends_on에 condition: service_healthy 추가
권한 오류
sudo chown -R 1000:1000 ./uploads ./extensions
대안 비교
| 기능 | Directus | Strapi | Hasura |
|---|
| 기존 DB 연결 | ✅ 즉시 | ❌ | ✅ |
| 스키마 자동 감지 | ✅ | ❌ | ✅ |
| REST API | ✅ | ✅ | ✅ |
| GraphQL | ✅ | ✅ (플러그인) | ✅ |
| WebSocket | ✅ | ❌ | ✅ |
| 관리자 UI | ✅ 풍부 | ✅ | ❌ (별도) |
| Flows/자동화 | ✅ 내장 | ❌ | ❌ |
| 라이선스 | BSL 1.1 | MIT | Apache 2.0 |
선택 가이드
| 용도 | 추천 |
|---|
| 기존 DB 연결 | Directus |
| 새 프로젝트 | Strapi |
| GraphQL 중심 | Hasura |
| 비개발자 관리 | Directus |
활용 사례
1. 레거시 DB 현대화
구성:
- 기존 PostgreSQL/MySQL 연결
- 마이그레이션 없이 즉시 API 생성
- 레거시 시스템과 병행 운영
2. 백오피스/관리자 패널
구성:
- 내부 데이터 관리 UI
- 역할별 권한 설정
- 커스텀 대시보드
3. 헤드리스 CMS
구성:
- 콘텐츠 관리
- Next.js/Nuxt 프론트엔드
- 실시간 WebSocket 업데이트
4. 데이터 플랫폼
구성:
- 다양한 DB 통합
- Flows로 데이터 파이프라인
- API 게이트웨이 역할
마무리
Directus는 “Database-First” 철학의 헤드리스 CMS입니다. 기존 데이터베이스에 연결하면 즉시 API와 관리자 UI가 생성되어, 마이그레이션 없이 바로 사용할 수 있습니다.
핵심 장점
| 장점 | 설명 |
|---|
| 기존 DB 연결 | 마이그레이션 불필요 |
| 자동 API 생성 | REST + GraphQL + WebSocket |
| 풍부한 관리자 UI | 비개발자도 사용 |
| Flows | 내장 자동화 |
| 벤더 종속 없음 | 표준 SQL 테이블 |
| 다양한 DB 지원 | 7개+ 데이터베이스 |