Strapi Docker 설치 가이드: 헤드리스 CMS




개요

Strapi는 오픈소스 헤드리스 CMS입니다. 100% JavaScript/TypeScript 기반으로 완전히 커스터마이징 가능하며, 개발자 친화적입니다. 콘텐츠 타입을 정의하면 REST/GraphQL API가 자동 생성되어 React, Next.js, Vue 등 어떤 프론트엔드와도 연동할 수 있습니다.

항목내용
GitHubhttps://github.com/strapi/strapi
공식 사이트https://strapi.io
문서https://docs.strapi.io
라이선스MIT (Community Edition)
GitHub Stars65K+

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 BuilderGUI로 콘텐츠 타입 생성
Collection Types여러 항목 (글, 상품)
Single Types단일 항목 (홈페이지, 설정)
Components재사용 가능한 필드 그룹
Dynamic Zones유연한 레이아웃

🔌 API

기능설명
REST API자동 생성 RESTful API
GraphQL플러그인으로 지원
필터/정렬/페이지네이션내장
Webhooks콘텐츠 변경 시 알림

🔐 인증/권한

기능설명
RBAC역할 기반 접근 제어
JWT 인증API 인증
Users & Permissions사용자 관리 플러그인
SSOEnterprise 버전

🌍 국제화

기능설명
i18n 플러그인다국어 콘텐츠
로케일 관리여러 언어 버전

📁 미디어

기능설명
Media Library이미지/파일 관리
이미지 최적화자동 리사이즈
외부 스토리지AWS S3, Cloudinary

🗄️ 데이터베이스 지원

DB지원
PostgreSQL
MySQL
MariaDB
SQLite✅ (개발용)

시스템 요구 사항

항목최소권장
CPU1 코어2+ 코어
RAM2GB4GB+
저장소10GB20GB+
Node.js18, 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, sqlite
DATABASE_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. 콘텐츠 타입 생성

  1. Content-Type Builder 메뉴
  2. “Create new collection type” 클릭
  3. 이름 입력 (예: Article)
  4. 필드 추가:
    • title (Text)
    • content (Rich Text)
    • cover (Media)
    • published_at (Date)
  5. 저장 (Strapi 재시작됨)

4. 권한 설정

  1. Settings → Users & Permissions → Roles
  2. “Public” 역할 선택
  3. 해당 콘텐츠 타입의 find, findOne 활성화
  4. 저장

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 호환

대안 비교

기능StrapiDirectusSanityContentful
오픈소스
셀프호스팅
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



댓글 남기기