개요
Strapi는 오픈소스 헤드리스 CMS 입니다. 100% JavaScript/TypeScript 기반으로 완전히 커스터마이징 가능하며, 개발자 친화적입니다. 콘텐츠 타입을 정의하면 REST/GraphQL API가 자동 생성되어 React, Next.js, Vue 등 어떤 프론트엔드와도 연동할 수 있습니다.
항목 내용 GitHub https://github.com/strapi/strapi 공식 사이트 https://strapi.io 문서 https://docs.strapi.io 라이선스 MIT (Community Edition) GitHub Stars 65K+
Strapi란?
왜 Strapi인가?
전통적 CMS (WordPress)
→ 화면 + 백엔드 결합
→ 프론트엔드 자유도 낮음
헤드리스 CMS (Strapi)
→ API만 제공 (화면 없음)
→ 어떤 프론트엔드든 자유롭게 연결
→ React, Next.js, Vue, 모바일 앱
핵심 개념
"콘텐츠 타입 정의 → API 자동 생성"
1. 관리자 패널에서 콘텐츠 타입 생성 (예: Article, Product)
2. REST/GraphQL API 자동 생성
3. 프론트엔드에서 API 호출하여 데이터 사용
아키텍처
┌─────────────────────────────────────────┐
│ Frontend (React/Next.js/Vue) │
└─────────────────┬───────────────────────┘
│ REST / GraphQL
▼
┌─────────────────────────────────────────┐
│ Strapi │
│ ┌─────────────────────────────────┐ │
│ │ Admin Panel (관리자) │ │
│ ├─────────────────────────────────┤ │
│ │ Content API (REST/GraphQL) │ │
│ ├─────────────────────────────────┤ │
│ │ Content Type Builder │ │
│ └─────────────────────────────────┘ │
└─────────────────┬───────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ Database (PostgreSQL/MySQL/SQLite) │
└─────────────────────────────────────────┘
주요 기능
🎨 콘텐츠 관리
기능 설명 Content Type Builder GUI로 콘텐츠 타입 생성 Collection Types 여러 항목 (글, 상품) Single Types 단일 항목 (홈페이지, 설정) Components 재사용 가능한 필드 그룹 Dynamic Zones 유연한 레이아웃
🔌 API
기능 설명 REST API 자동 생성 RESTful API GraphQL 플러그인으로 지원 필터/정렬/페이지네이션 내장 Webhooks 콘텐츠 변경 시 알림
🔐 인증/권한
기능 설명 RBAC 역할 기반 접근 제어 JWT 인증 API 인증 Users & Permissions 사용자 관리 플러그인 SSO Enterprise 버전
🌍 국제화
기능 설명 i18n 플러그인 다국어 콘텐츠 로케일 관리 여러 언어 버전
📁 미디어
기능 설명 Media Library 이미지/파일 관리 이미지 최적화 자동 리사이즈 외부 스토리지 AWS S3, Cloudinary
🗄️ 데이터베이스 지원
DB 지원 PostgreSQL ✅ MySQL ✅ MariaDB ✅ SQLite ✅ (개발용)
시스템 요구 사항
항목 최소 권장 CPU 1 코어 2+ 코어 RAM 2GB 4GB+ 저장소 10GB 20GB+ Node.js 18, 20, 22 LTS 포트 1337 –
Docker 설치
방법 1: Docker Compose 기본 (PostgreSQL)
# docker-compose.yml
services:
strapi:
image: strapi/strapi
container_name: strapi
restart: unless-stopped
ports:
- "1337:1337"
environment:
DATABASE_CLIENT: postgres
DATABASE_HOST: strapi-db
DATABASE_PORT: 5432
DATABASE_NAME: strapi
DATABASE_USERNAME: strapi
DATABASE_PASSWORD: strapi_password
volumes:
- ./app:/srv/app
depends_on:
- strapi-db
strapi-db:
image: postgres:16-alpine
container_name: strapi-db
restart: unless-stopped
environment:
POSTGRES_DB: strapi
POSTGRES_USER: strapi
POSTGRES_PASSWORD: strapi_password
volumes:
- ./data/postgres:/var/lib/postgresql/data
방법 2: Strapi v5 + PostgreSQL (공식)
# docker-compose.yml
services:
strapi:
container_name: strapi
build: .
image: strapi:latest
restart: unless-stopped
env_file: .env
environment:
DATABASE_CLIENT: ${DATABASE_CLIENT}
DATABASE_HOST: strapiDB
DATABASE_PORT: ${DATABASE_PORT}
DATABASE_NAME: ${DATABASE_NAME}
DATABASE_USERNAME: ${DATABASE_USERNAME}
DATABASE_PASSWORD: ${DATABASE_PASSWORD}
JWT_SECRET: ${JWT_SECRET}
ADMIN_JWT_SECRET: ${ADMIN_JWT_SECRET}
APP_KEYS: ${APP_KEYS}
NODE_ENV: ${NODE_ENV}
volumes:
- ./config:/opt/app/config
- ./src:/opt/app/src
- ./package.json:/opt/package.json
- ./.env:/opt/app/.env
- ./public/uploads:/opt/app/public/uploads
ports:
- "1337:1337"
networks:
- strapi
depends_on:
- strapiDB
strapiDB:
container_name: strapiDB
platform: linux/amd64
restart: unless-stopped
env_file: .env
image: postgres:16-alpine
environment:
POSTGRES_USER: ${DATABASE_USERNAME}
POSTGRES_PASSWORD: ${DATABASE_PASSWORD}
POSTGRES_DB: ${DATABASE_NAME}
volumes:
- strapi-data:/var/lib/postgresql/data
networks:
- strapi
volumes:
strapi-data:
networks:
strapi:
driver: bridge
.env 파일:
# Database
DATABASE_CLIENT=postgres
DATABASE_PORT=5432
DATABASE_NAME=strapi
DATABASE_USERNAME=strapi
DATABASE_PASSWORD=your_secure_password
# Secrets (generate with: openssl rand -base64 32)
JWT_SECRET=your_jwt_secret
ADMIN_JWT_SECRET=your_admin_jwt_secret
APP_KEYS=key1,key2,key3,key4
# Environment
NODE_ENV=production
Dockerfile:
FROM node:20-alpine
RUN apk add --no-cache build-base gcc autoconf automake zlib-dev libpng-dev vips-dev
WORKDIR /opt/
COPY package.json ./
RUN npm install
WORKDIR /opt/app
COPY . .
RUN npm run build
EXPOSE 1337
CMD ["npm", "run", "start"]
방법 3: MySQL 사용
# docker-compose.yml
services:
strapi:
image: strapi/strapi
container_name: strapi
restart: unless-stopped
ports:
- "1337:1337"
environment:
DATABASE_CLIENT: mysql
DATABASE_HOST: strapi-mysql
DATABASE_PORT: 3306
DATABASE_NAME: strapi
DATABASE_USERNAME: strapi
DATABASE_PASSWORD: strapi_password
volumes:
- ./app:/srv/app
depends_on:
- strapi-mysql
strapi-mysql:
image: mysql:8
container_name: strapi-mysql
restart: unless-stopped
command: --default-authentication-plugin=mysql_native_password
environment:
MYSQL_DATABASE: strapi
MYSQL_USER: strapi
MYSQL_PASSWORD: strapi_password
MYSQL_ROOT_PASSWORD: root_password
volumes:
- ./data/mysql:/var/lib/mysql
방법 4: SQLite (개발용)
# docker-compose.yml
services:
strapi:
image: strapi/strapi
container_name: strapi
restart: unless-stopped
ports:
- "1337:1337"
environment:
DATABASE_CLIENT: sqlite
DATABASE_FILENAME: /srv/app/.tmp/data.db
volumes:
- ./app:/srv/app
방법 5: Redis 캐시 추가
# docker-compose.yml
services:
strapi:
image: strapi/strapi
container_name: strapi
restart: unless-stopped
ports:
- "1337:1337"
environment:
DATABASE_CLIENT: postgres
DATABASE_HOST: strapi-db
DATABASE_PORT: 5432
DATABASE_NAME: strapi
DATABASE_USERNAME: strapi
DATABASE_PASSWORD: strapi_password
REDIS_HOST: strapi-redis
REDIS_PORT: 6379
volumes:
- ./app:/srv/app
depends_on:
- strapi-db
- strapi-redis
strapi-db:
image: postgres:16-alpine
container_name: strapi-db
restart: unless-stopped
environment:
POSTGRES_DB: strapi
POSTGRES_USER: strapi
POSTGRES_PASSWORD: strapi_password
volumes:
- ./data/postgres:/var/lib/postgresql/data
strapi-redis:
image: redis:alpine
container_name: strapi-redis
restart: unless-stopped
방법 6: S3 스토리지 연동
# docker-compose.yml
services:
strapi:
image: strapi/strapi
container_name: strapi
restart: unless-stopped
ports:
- "1337:1337"
environment:
DATABASE_CLIENT: postgres
DATABASE_HOST: strapi-db
DATABASE_PORT: 5432
DATABASE_NAME: strapi
DATABASE_USERNAME: strapi
DATABASE_PASSWORD: strapi_password
# AWS S3
AWS_ACCESS_KEY_ID: your_access_key
AWS_ACCESS_SECRET: your_secret_key
AWS_REGION: ap-northeast-2
AWS_BUCKET: your-bucket-name
volumes:
- ./app:/srv/app
depends_on:
- strapi-db
strapi-db:
image: postgres:16-alpine
container_name: strapi-db
restart: unless-stopped
environment:
POSTGRES_DB: strapi
POSTGRES_USER: strapi
POSTGRES_PASSWORD: strapi_password
volumes:
- ./data/postgres:/var/lib/postgresql/data
방법 7: Traefik + HTTPS
# docker-compose.yml
services:
strapi:
image: strapi/strapi
container_name: strapi
restart: unless-stopped
environment:
DATABASE_CLIENT: postgres
DATABASE_HOST: strapi-db
DATABASE_PORT: 5432
DATABASE_NAME: strapi
DATABASE_USERNAME: strapi
DATABASE_PASSWORD: strapi_password
PUBLIC_URL: https://cms.yourdomain.com
volumes:
- ./app:/srv/app
labels:
- "traefik.enable=true"
- "traefik.http.routers.strapi.rule=Host(`cms.yourdomain.com`)"
- "traefik.http.routers.strapi.entrypoints=websecure"
- "traefik.http.routers.strapi.tls.certresolver=letsencrypt"
- "traefik.http.services.strapi.loadbalancer.server.port=1337"
depends_on:
- strapi-db
networks:
- traefik-net
- strapi-net
strapi-db:
image: postgres:16-alpine
container_name: strapi-db
restart: unless-stopped
environment:
POSTGRES_DB: strapi
POSTGRES_USER: strapi
POSTGRES_PASSWORD: strapi_password
volumes:
- ./data/postgres:/var/lib/postgresql/data
networks:
- strapi-net
networks:
traefik-net:
external: true
strapi-net:
driver: bridge
환경 변수
데이터베이스
변수 설명 DATABASE_CLIENTpostgres, mysql, sqliteDATABASE_HOSTDB 호스트 DATABASE_PORTDB 포트 DATABASE_NAMEDB 이름 DATABASE_USERNAMEDB 사용자 DATABASE_PASSWORDDB 비밀번호 DATABASE_SSLSSL 사용 여부
보안
변수 설명 JWT_SECRETJWT 서명 키 ADMIN_JWT_SECRET관리자 JWT 키 APP_KEYS앱 키 (쉼표 구분) API_TOKEN_SALTAPI 토큰 솔트
서버
변수 설명 HOST호스트 (기본: 0.0.0.0) PORT포트 (기본: 1337) PUBLIC_URL공개 URL NODE_ENVdevelopment, production
스토리지
변수 설명 AWS_ACCESS_KEY_IDAWS 액세스 키 AWS_ACCESS_SECRETAWS 시크릿 키 AWS_REGIONAWS 리전 AWS_BUCKETS3 버킷
초기 설정
1. 시작
docker compose up -d
2. 관리자 계정 생성
http://localhost:1337/admin 접속
첫 관리자 계정 생성
3. 콘텐츠 타입 생성
Content-Type Builder 메뉴
“Create new collection type” 클릭
이름 입력 (예: Article)
필드 추가:
title (Text)
content (Rich Text)
cover (Media)
published_at (Date)
저장 (Strapi 재시작됨)
4. 권한 설정
Settings → Users & Permissions → Roles
“Public” 역할 선택
해당 콘텐츠 타입의 find, findOne 활성화
저장
5. API 테스트
# 모든 Article 조회
curl http://localhost:1337/api/articles
# 단일 Article 조회
curl http://localhost:1337/api/articles/1
# 필터링
curl "http://localhost:1337/api/articles?filters[title][$contains]=hello"
# 페이지네이션
curl "http://localhost:1337/api/articles?pagination[page]=1&pagination[pageSize]=10"
프론트엔드 연동
Next.js 예시
// lib/strapi.js
const STRAPI_URL = process.env.STRAPI_URL || 'http://localhost:1337';
export async function getArticles() {
const res = await fetch(`${STRAPI_URL}/api/articles?populate=*`);
const data = await res.json();
return data.data;
}
export async function getArticle(id) {
const res = await fetch(`${STRAPI_URL}/api/articles/${id}?populate=*`);
const data = await res.json();
return data.data;
}
// pages/blog/index.js
import { getArticles } from '../../lib/strapi';
export default function Blog({ articles }) {
return (
<div>
{articles.map((article) => (
<article key={article.id}>
<h2>{article.attributes.title}</h2>
<p>{article.attributes.content}</p>
</article>
))}
</div>
);
}
export async function getStaticProps() {
const articles = await getArticles();
return { props: { articles }, revalidate: 60 };
}
백업 및 복원
데이터 구조
./
├── app/ # Strapi 앱 코드
│ ├── config/ # 설정
│ ├── src/ # 소스 코드
│ └── public/uploads/ # 업로드 파일
└── data/
└── postgres/ # PostgreSQL 데이터
백업
# 컨테이너 정지
docker compose down
# 전체 백업
tar -czvf strapi-backup-$(date +%Y%m%d).tar.gz app/ data/
# DB만 백업
docker exec strapi-db pg_dump -U strapi strapi > backup.sql
# 재시작
docker compose up -d
복원
tar -xzvf strapi-backup.tar.gz
docker compose up -d
업데이트
# 최신 이미지 가져오기
docker compose pull
# 재시작
docker compose down
docker compose up -d
트러블슈팅
Strapi 시작 안 됨
# 로그 확인
docker logs strapi
# 권한 문제
sudo chown -R 1000:1000 ./app
DB 연결 오류
DATABASE_HOST가 컨테이너 이름인지 확인
DB 컨테이너가 먼저 시작되었는지 확인
이미지 업로드 안 됨
# 업로드 폴더 권한
chmod -R 755 ./app/public/uploads
빌드 오류 (M1 Mac)
strapiDB:
platform: linux/amd64 # M1 Mac 호환
대안 비교
기능 Strapi Directus Sanity Contentful 오픈소스 ✅ ✅ ❌ ❌ 셀프호스팅 ✅ ✅ ❌ ❌ REST API ✅ ✅ ✅ ✅ GraphQL ✅ ✅ ✅ ✅ TypeScript ✅ ✅ ✅ – 커스텀 플러그인 ✅ ✅ ❌ ❌ 무료 플랜 ✅ 셀프호스팅 ✅ 제한 제한
선택 가이드
용도 추천 완전한 커스터마이징 Strapi 기존 DB 연결 Directus SaaS 선호 Sanity, Contentful 빠른 프로토타입 Strapi
활용 사례
1. 기업 웹사이트
구성:
Strapi로 페이지, 뉴스, 제품 관리
Next.js 프론트엔드
마케팅팀이 콘텐츠 직접 수정
2. 이커머스 백엔드
구성:
상품, 카테고리, 주문 콘텐츠 타입
REST API로 모바일 앱 연동
Webhook으로 재고 알림
3. 블로그/미디어
구성:
Article, Author, Category 콘텐츠 타입
Gatsby/Next.js SSG
SEO 최적화
4. 모바일 앱 백엔드
구성:
iOS/Android 앱 API 서버
사용자 인증
푸시 알림 연동
마무리
Strapi는 가장 인기 있는 오픈소스 헤드리스 CMS입니다. 콘텐츠 타입을 정의하면 REST/GraphQL API가 자동 생성되어 개발 생산성을 크게 높입니다.
핵심 장점
장점 설명 자동 API 생성 콘텐츠 타입 → REST/GraphQL 완전 커스터마이징 플러그인, 코드 수정 가능 개발자 친화적 TypeScript, Node.js 프론트엔드 자유 React, Vue, Next.js 등 셀프호스팅 데이터 완전 소유 활발한 커뮤니티 65K+ Stars