EmDash는 콘텐츠 관리, 미디어 업로드, 스키마 작업을 위한 REST API를 /_emdash/api/에 노출합니다.
인증
API 요청은 Bearer 토큰을 통한 인증이 필요합니다:
Authorization: Bearer <token>
관리 인터페이스를 통해 또는 프로그래밍 방식으로 토큰을 생성합니다.
응답 형식
모든 응답은 일관된 형식을 따릅니다:
// 성공
{
"success": true,
"data": { ... }
}
// 오류
{
"success": false,
"error": {
"code": "ERROR_CODE",
"message": "사람이 읽을 수 있는 메시지",
"details": { ... }
}
}
콘텐츠 엔드포인트
콘텐츠 목록
GET /_emdash/api/content/:collection
매개변수
| 매개변수 | 타입 | 설명 |
|---|---|---|
collection | string | 컬렉션 슬러그 (경로) |
cursor | string | 페이지네이션 커서 (쿼리) |
limit | number | 페이지당 항목 수 (쿼리, 기본값: 50) |
status | string | 상태별 필터 (쿼리) |
orderBy | string | 정렬 필드 (쿼리) |
order | string | 정렬 방향: asc 또는 desc (쿼리) |
응답
{
"success": true,
"data": {
"items": [
{
"id": "01HXK5MZSN...",
"type": "posts",
"slug": "hello-world",
"data": { "title": "Hello World", ... },
"status": "published",
"createdAt": "2025-01-24T12:00:00Z",
"updatedAt": "2025-01-24T12:00:00Z"
}
],
"nextCursor": "eyJpZCI6..."
}
}
콘텐츠 가져오기
GET /_emdash/api/content/:collection/:id
응답
{
"success": true,
"data": {
"item": {
"id": "01HXK5MZSN...",
"type": "posts",
"slug": "hello-world",
"data": { "title": "Hello World", ... },
"status": "published",
"createdAt": "2025-01-24T12:00:00Z",
"updatedAt": "2025-01-24T12:00:00Z"
}
}
}
콘텐츠 생성
POST /_emdash/api/content/:collection
Content-Type: application/json
요청 본문
{
"data": {
"title": "New Post",
"content": [...]
},
"slug": "new-post",
"status": "draft"
}
응답
{
"success": true,
"data": {
"item": { ... }
}
}
콘텐츠 업데이트
PUT /_emdash/api/content/:collection/:id
Content-Type: application/json
요청 본문
{
"data": {
"title": "Updated Title"
},
"status": "published"
}
콘텐츠 삭제
DELETE /_emdash/api/content/:collection/:id
응답
{
"success": true,
"data": {
"success": true
}
}
미디어 엔드포인트
미디어 목록
GET /_emdash/api/media
매개변수
| 매개변수 | 타입 | 설명 |
|---|---|---|
cursor | string | 페이지네이션 커서 |
limit | number | 페이지당 항목 수 (기본값: 20) |
mimeType | string | MIME 타입 접두사로 필터 |
응답
{
"success": true,
"data": {
"items": [
{
"id": "01HXK5MZSN...",
"filename": "photo.jpg",
"mimeType": "image/jpeg",
"size": 102400,
"width": 1920,
"height": 1080,
"url": "https://cdn.example.com/photo.jpg",
"createdAt": "2025-01-24T12:00:00Z"
}
],
"nextCursor": "eyJpZCI6..."
}
}
미디어 가져오기
GET /_emdash/api/media/:id
미디어 생성
POST /_emdash/api/media
Content-Type: application/json
요청 본문
{
"filename": "photo.jpg",
"mimeType": "image/jpeg",
"size": 102400,
"width": 1920,
"height": 1080,
"storageKey": "uploads/photo.jpg"
}
미디어 업데이트
PUT /_emdash/api/media/:id
Content-Type: application/json
요청 본문
{
"alt": "Photo description",
"caption": "Photo caption"
}
미디어 삭제
DELETE /_emdash/api/media/:id
미디어 파일 가져오기
GET /_emdash/api/media/file/:key
실제 파일 콘텐츠를 제공합니다. 로컬 스토리지에서만 사용 가능합니다.
리비전 엔드포인트
리비전 목록
GET /_emdash/api/content/:collection/:entryId/revisions
매개변수
| 매개변수 | 타입 | 설명 |
|---|---|---|
limit | number | 반환할 최대 리비전 수 (기본값: 50) |
응답
{
"success": true,
"data": {
"items": [
{
"id": "01HXK5MZSN...",
"collection": "posts",
"entryId": "01HXK5MZSN...",
"data": { ... },
"createdAt": "2025-01-24T12:00:00Z"
}
],
"total": 5
}
}
리비전 가져오기
GET /_emdash/api/revisions/:revisionId
리비전 복원
POST /_emdash/api/revisions/:revisionId/restore
이 리비전의 상태로 콘텐츠를 복원하고 새 리비전을 생성합니다.
스키마 엔드포인트
컬렉션 목록
GET /_emdash/api/schema/collections
응답
{
"success": true,
"data": {
"items": [
{
"id": "01HXK5MZSN...",
"slug": "posts",
"label": "Posts",
"labelSingular": "Post",
"supports": ["drafts", "revisions", "preview"]
}
]
}
}
컬렉션 가져오기
GET /_emdash/api/schema/collections/:slug
매개변수
| 매개변수 | 타입 | 설명 |
|---|---|---|
includeFields | boolean | 필드 정의 포함 (쿼리) |
컬렉션 생성
POST /_emdash/api/schema/collections
Content-Type: application/json
요청 본문
{
"slug": "products",
"label": "Products",
"labelSingular": "Product",
"description": "Product catalog",
"supports": ["drafts", "revisions"]
}
컬렉션 업데이트
PATCH /_emdash/api/schema/collections/:slug
Content-Type: application/json
컬렉션 삭제
DELETE /_emdash/api/schema/collections/:slug
매개변수
| 매개변수 | 타입 | 설명 |
|---|---|---|
force | boolean | 콘텐츠가 있어도 삭제 (쿼리) |
필드 목록
GET /_emdash/api/schema/collections/:slug/fields
필드 생성
POST /_emdash/api/schema/collections/:slug/fields
Content-Type: application/json
요청 본문
{
"slug": "price",
"label": "Price",
"type": "number",
"required": true,
"validation": {
"min": 0
}
}
필드 업데이트
PATCH /_emdash/api/schema/collections/:collectionSlug/fields/:fieldSlug
Content-Type: application/json
필드 삭제
DELETE /_emdash/api/schema/collections/:collectionSlug/fields/:fieldSlug
필드 재정렬
POST /_emdash/api/schema/collections/:slug/fields/reorder
Content-Type: application/json
요청 본문
{
"fieldSlugs": ["title", "content", "author", "publishedAt"]
}
스키마 내보내기
스키마 내보내기 (JSON)
GET /_emdash/api/schema
Accept: application/json
스키마 내보내기 (TypeScript)
GET /_emdash/api/schema?format=typescript
Accept: text/typescript
모든 컬렉션의 TypeScript 인터페이스를 반환합니다.
플러그인 엔드포인트
플러그인 목록
GET /_emdash/api/plugins
플러그인 가져오기
GET /_emdash/api/plugins/:pluginId
플러그인 활성화
POST /_emdash/api/plugins/:pluginId/enable
플러그인 비활성화
POST /_emdash/api/plugins/:pluginId/disable
오류 코드
| 코드 | HTTP 상태 | 설명 |
|---|---|---|
NOT_FOUND | 404 | 리소스를 찾을 수 없음 |
VALIDATION_ERROR | 400 | 잘못된 입력 데이터 |
UNAUTHORIZED | 401 | 누락되거나 잘못된 토큰 |
FORBIDDEN | 403 | 권한 부족 |
CONTENT_LIST_ERROR | 500 | 콘텐츠 목록 실패 |
CONTENT_CREATE_ERROR | 500 | 콘텐츠 생성 실패 |
CONTENT_UPDATE_ERROR | 500 | 콘텐츠 업데이트 실패 |
CONTENT_DELETE_ERROR | 500 | 콘텐츠 삭제 실패 |
MEDIA_LIST_ERROR | 500 | 미디어 목록 실패 |
MEDIA_CREATE_ERROR | 500 | 미디어 생성 실패 |
SCHEMA_ERROR | 400 | 스키마 작업 실패 |
DUPLICATE_SLUG | 409 | 슬러그가 이미 존재 |
RESERVED_SLUG | 400 | 예약된 슬러그 |
검색 엔드포인트
전역 검색
GET /_emdash/api/search?q=hello+world
매개변수
| 매개변수 | 타입 | 설명 |
|---|---|---|
q | string | 검색 쿼리 (필수) |
collections | string | 쉼표로 구분된 컬렉션 슬러그 |
status | string | 상태별 필터 (기본값: published) |
limit | number | 최대 결과 수 (기본값: 20) |
cursor | string | 페이지네이션 커서 |
응답
{
"results": [
{
"collection": "posts",
"id": "01HXK5MZSN...",
"slug": "hello-world",
"title": "Hello World",
"snippet": "...this is a <mark>hello</mark> <mark>world</mark> example...",
"score": 0.95
}
],
"nextCursor": "eyJvZmZzZXQiOjIwfQ"
}
검색 제안
GET /_emdash/api/search/suggest?q=hel&limit=5
자동 완성을 위한 접두사 일치 제목을 반환합니다.
검색 색인 재구축
POST /_emdash/api/search/rebuild
전체 또는 특정 컬렉션의 FTS 색인을 재구축합니다.
검색 통계
GET /_emdash/api/search/stats
컬렉션별 색인된 문서 수를 반환합니다.
섹션 엔드포인트
섹션 목록
GET /_emdash/api/sections
GET /_emdash/api/sections?source=theme
GET /_emdash/api/sections?search=newsletter
섹션 가져오기
GET /_emdash/api/sections/:slug
섹션 생성
POST /_emdash/api/sections
Content-Type: application/json
{
"slug": "my-section",
"title": "My Section",
"keywords": ["keyword1"],
"content": [...]
}
섹션 업데이트
PUT /_emdash/api/sections/:slug
섹션 삭제
DELETE /_emdash/api/sections/:slug
설정 엔드포인트
모든 설정 가져오기
GET /_emdash/api/settings
설정 업데이트
POST /_emdash/api/settings
Content-Type: application/json
{
"siteTitle": "My Site",
"tagline": "A great site",
"postsPerPage": 10
}
메뉴 엔드포인트
메뉴 목록
GET /_emdash/api/menus
메뉴 가져오기
GET /_emdash/api/menus/:name
메뉴 생성
POST /_emdash/api/menus
Content-Type: application/json
{
"name": "footer",
"label": "Footer Navigation"
}
메뉴 업데이트
PUT /_emdash/api/menus/:name
메뉴 삭제
DELETE /_emdash/api/menus/:name
메뉴 항목 추가
POST /_emdash/api/menus/:name/items
Content-Type: application/json
{
"type": "page",
"referenceCollection": "pages",
"referenceId": "page_about",
"label": "About Us"
}
메뉴 항목 재정렬
POST /_emdash/api/menus/:name/reorder
Content-Type: application/json
{
"items": [
{ "id": "item_1", "parentId": null, "sortOrder": 0 },
{ "id": "item_2", "parentId": null, "sortOrder": 1 },
{ "id": "item_3", "parentId": "item_2", "sortOrder": 0 }
]
}
택소노미 엔드포인트
택소노미 정의 목록
GET /_emdash/api/taxonomies
택소노미 생성
POST /_emdash/api/taxonomies
Content-Type: application/json
{
"name": "genre",
"label": "Genres",
"labelSingular": "Genre",
"hierarchical": true,
"collections": ["books", "movies"]
}
용어 목록
GET /_emdash/api/taxonomies/:name/terms
용어 생성
POST /_emdash/api/taxonomies/:name/terms
Content-Type: application/json
{
"slug": "tutorials",
"label": "Tutorials",
"parentId": "term_abc",
"description": "How-to guides"
}
용어 업데이트
PUT /_emdash/api/taxonomies/:name/terms/:slug
용어 삭제
DELETE /_emdash/api/taxonomies/:name/terms/:slug
항목 용어 설정
POST /_emdash/api/content/:collection/:id/terms/:taxonomy
Content-Type: application/json
{
"termIds": ["term_news", "term_featured"]
}
위젯 영역 엔드포인트
위젯 영역 목록
GET /_emdash/api/widget-areas
위젯 영역 가져오기
GET /_emdash/api/widget-areas/:name
위젯 영역 생성
POST /_emdash/api/widget-areas
Content-Type: application/json
{
"name": "sidebar",
"label": "Main Sidebar",
"description": "Appears on posts"
}
위젯 영역 삭제
DELETE /_emdash/api/widget-areas/:name
위젯 추가
POST /_emdash/api/widget-areas/:name/widgets
Content-Type: application/json
{
"type": "content",
"title": "About",
"content": [...]
}
위젯 업데이트
PUT /_emdash/api/widget-areas/:name/widgets/:id
위젯 삭제
DELETE /_emdash/api/widget-areas/:name/widgets/:id
위젯 재정렬
POST /_emdash/api/widget-areas/:name/reorder
Content-Type: application/json
{
"widgetIds": ["widget_1", "widget_2", "widget_3"]
}
사용자 관리 엔드포인트
사용자 목록
GET /_emdash/api/admin/users
GET /_emdash/api/admin/users?role=40
GET /_emdash/api/admin/users?search=john
사용자 가져오기
GET /_emdash/api/admin/users/:id
사용자 업데이트
PATCH /_emdash/api/admin/users/:id
Content-Type: application/json
{
"name": "John Doe",
"role": 40
}
사용자 활성화
POST /_emdash/api/admin/users/:id/enable
사용자 비활성화
POST /_emdash/api/admin/users/:id/disable
인증 엔드포인트
설정 상태
GET /_emdash/api/setup/status
설정이 완료되었는지와 사용자가 존재하는지를 반환합니다.
패스키 로그인
POST /_emdash/api/auth/passkey/options
WebAuthn 인증 옵션을 가져옵니다.
POST /_emdash/api/auth/passkey/verify
Content-Type: application/json
{
"id": "credential-id",
"rawId": "...",
"response": {...},
"type": "public-key"
}
패스키를 검증하고 세션을 생성합니다.
매직 링크
POST /_emdash/api/auth/magic-link/send
Content-Type: application/json
{
"email": "[email protected]"
}
GET /_emdash/api/auth/magic-link/verify?token=xxx
로그아웃
POST /_emdash/api/auth/logout
현재 사용자
GET /_emdash/api/auth/me
사용자 초대
POST /_emdash/api/auth/invite
Content-Type: application/json
{
"email": "[email protected]",
"role": 30
}
패스키 관리
GET /_emdash/api/auth/passkey
사용자의 패스키를 나열합니다.
POST /_emdash/api/auth/passkey/register/options
POST /_emdash/api/auth/passkey/register/verify
새 패스키를 등록합니다.
PATCH /_emdash/api/auth/passkey/:id
Content-Type: application/json
{
"name": "MacBook Pro"
}
패스키 이름을 변경합니다.
DELETE /_emdash/api/auth/passkey/:id
패스키를 삭제합니다.
가져오기 엔드포인트
WordPress 내보내기 분석
POST /_emdash/api/import/wordpress/analyze
Content-Type: multipart/form-data
file: <WXR file>
WordPress 가져오기 실행
POST /_emdash/api/import/wordpress/execute
Content-Type: application/json
{
"analysisId": "...",
"options": {
"includeMedia": true,
"includeTaxonomies": true,
"includeMenus": true
}
}
속도 제한
API 엔드포인트는 배포 구성에 따라 속도가 제한될 수 있습니다. 속도가 제한되면 응답에 다음이 포함됩니다:
HTTP/1.1 429 Too Many Requests
Retry-After: 60
CORS
API는 브라우저 요청을 위한 CORS를 지원합니다. 배포에서 허용된 오리진을 구성합니다.