EmDash의 가져오기 시스템은 플러그 가능한 소스 아키텍처를 사용합니다. 각 소스는 특정 플랫폼에서 콘텐츠를 탐색, 분석, 가져오는 방법을 알고 있습니다.
가져오기 소스
| 소스 ID | 플랫폼 | 탐색 | OAuth | 전체 가져오기 |
|---|---|---|---|---|
wxr | WordPress 내보내기 파일 | 아니오 | 아니오 | 예 |
wordpress-com | WordPress.com | 예 | 예 | 예 |
wordpress-rest | 자체 호스팅 WordPress | 예 | 아니오 | 탐색만 |
WXR 파일 업로드
가장 완전한 가져오기 방법입니다. WordPress eXtended RSS(WXR) 내보내기 파일을 관리 대시보드에 직접 업로드합니다.
기능:
- 모든 글 타입(사용자 정의 포함)
- 모든 메타 필드
- 초안 및 비공개 글
- 전체 택소노미 계층
- 미디어 첨부 메타데이터
WXR 파일 얻는 방법:
- WordPress 관리자에서 도구 → 내보내기로 이동합니다
- 모든 콘텐츠 또는 특정 글 타입을 선택합니다
- 내보내기 파일 다운로드를 클릭합니다
.xml파일을 EmDash에 업로드합니다
WordPress.com OAuth
WordPress.com에 호스팅된 사이트의 경우 수동 파일 내보내기 없이 OAuth로 연결해 가져올 수 있습니다.
- WordPress.com 사이트 URL을 입력합니다
- Connect with WordPress.com을 클릭합니다
- WordPress.com 팝업에서 EmDash를 승인합니다
- 가져올 콘텐츠를 선택합니다
포함 항목:
- 게시 및 초안 콘텐츠
- 비공개 글(인증 포함)
- API를 통한 미디어 파일
- REST API에 노출된 사용자 정의 필드
WordPress REST API 탐색
URL을 입력하면 EmDash가 사이트를 탐색해 WordPress를 감지하고 사용 가능한 콘텐츠를 보여줍니다:
감지: WordPress 6.4
├── 게시물: 127 (게시됨)
├── 페이지: 12 (게시됨)
└── 미디어: 89개 파일
참고: 초안과 비공개 콘텐츠는 인증이나
WXR 전체 내보내기가 필요합니다.
REST 탐색은 정보 제공용입니다. 완전한 가져오기를 위해 WXR 파일 업로드 또는 OAuth 연결(WordPress.com)을 제안합니다.
가져오기 흐름
모든 소스는 동일한 흐름을 따릅니다:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 연결 │────▶│ 분석 │────▶│ 준비 │────▶│ 실행 │
│ (탐색/ │ │ (스키마 │ │ (스키마 │ │ (콘텐츠 │
│ 업로드) │ │ 확인) │ │ 생성) │ │ 가져오기) │
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
1단계: 연결
URL을 입력해 탐색하거나 파일을 직접 업로드합니다.
URL 탐색은 등록된 모든 소스를 병렬로 실행합니다. 가장 높은 신뢰도 일치가 다음 동작을 결정합니다:
- WordPress.com 사이트 → OAuth 연결 제안
- 자체 호스팅 WordPress → 내보내기 안내 표시
- 알 수 없음 → 파일 업로드 제안
2단계: 분석
소스가 콘텐츠를 파싱하고 스키마 호환성을 확인합니다:
글 타입:
├── post (127) → posts [새 컬렉션]
├── page (12) → pages [기존, 호환]
├── product (45) → products [필드 3개 추가]
└── revision (234) → [건너뜀 - 내부 타입]
필요한 스키마 변경:
├── 컬렉션 생성: posts
├── pages에 필드 추가: featured_image
└── 컬렉션 생성: products
각 글 타입의 상태:
| 상태 | 의미 |
|---|---|
| 준비됨 | 컬렉션이 호환 가능한 필드와 함께 존재 |
| 새 컬렉션 | 자동으로 생성됨 |
| 필드 추가 | 컬렉션 존재, 누락 필드 추가 |
| 비호환 | 필드 타입 충돌(수동 수정 필요) |
3단계: 스키마 준비
Create Schema & Import를 클릭하면:
- SchemaRegistry를 통해 새 컬렉션 생성
- 올바른 열 타입으로 누락 필드 추가
- 인덱스가 있는 콘텐츠 테이블 설정
4단계: 가져오기 실행
콘텐츠가 순차적으로 가져옵니다:
- Gutenberg/HTML이 Portable Text로 변환
- WordPress 상태가 EmDash 상태에 매핑
- WordPress 작성자가 소유권(
authorId)과 표시용 바이라인에 매핑 - 택소노미가 생성되고 연결
- 재사용 블록(
wp_block)이 섹션으로 가져옴 - 실시간 진행 상황 표시
작성자 가져오기 동작:
- 작성자 매핑이 EmDash 사용자를 가리키면 소유권이 해당 사용자로 설정되고 동일 사용자를 위한 연결된 바이라인이 생성/재사용됩니다.
- 사용자 매핑이 없으면 WordPress 작성자 정보에서 게스트 바이라인이 생성/재사용됩니다.
- 가져온 항목은 순서가 있는 바이라인 크레딧을 받으며 첫 번째 크레딧이
primaryBylineId로 설정됩니다.
5단계: 미디어 가져오기(선택)
콘텐츠 후 선택적으로 미디어를 가져옵니다:
-
분석 — 유형별 첨부 파일 수 표시
발견된 미디어: ├── 이미지: 75개 파일 ├── 동영상: 10개 파일 └── 기타: 4개 파일 -
다운로드 — WordPress URL에서 진행 상황과 함께 스트리밍
미디어 가져오기 중... ├── 89개 중 45개 (50%) ├── 현재: vacation-photo.jpg └── 상태: 업로드 중 -
URL 재작성 — 새 URL로 콘텐츠 자동 업데이트
미디어 가져오기는 콘텐츠 해싱(xxHash64)으로 중복을 제거합니다. 여러 글에서 사용된 같은 이미지는 한 번만 저장됩니다.
소스 인터페이스
가져오기 소스는 표준 인터페이스를 구현합니다:
interface ImportSource {
/** 고유 식별자 */
id: string;
/** 표시 이름 */
name: string;
/** URL 탐색(선택) */
probe?(url: string): Promise<SourceProbeResult | null>;
/** 이 소스의 콘텐츠 분석 */
analyze(input: SourceInput, context: ImportContext): Promise<ImportAnalysis>;
/** 콘텐츠 항목 스트리밍 */
fetchContent(input: SourceInput, options: FetchOptions): AsyncGenerator<NormalizedItem>;
}
입력 타입
소스는 다양한 입력 타입을 받습니다:
// 파일 업로드(WXR)
{ type: "file", file: File }
// 선택적 토큰이 있는 URL(REST API)
{ type: "url", url: string, token?: string }
// OAuth 연결(WordPress.com)
{ type: "oauth", url: string, accessToken: string }
정규화된 출력
모든 소스는 동일한 정규화 형식을 생성합니다:
interface NormalizedItem {
sourceId: string | number;
postType: string;
status: "publish" | "draft" | "pending" | "private" | "future";
slug: string;
title: string;
content: PortableTextBlock[];
excerpt?: string;
date: Date;
author?: string;
authors?: string[];
categories?: string[];
tags?: string[];
meta?: Record<string, unknown>;
featuredImage?: string;
}
API 엔드포인트
가져오기 시스템은 다음 엔드포인트를 노출합니다:
URL 탐색
POST /_emdash/api/import/probe
Content-Type: application/json
{ "url": "https://example.com" }
감지된 플랫폼과 제안 동작을 반환합니다.
WXR 분석
POST /_emdash/api/import/wordpress/analyze
Content-Type: multipart/form-data
file: [WordPress 내보내기 .xml]
스키마 호환성과 함께 글 타입 분석을 반환합니다.
스키마 준비
POST /_emdash/api/import/wordpress/prepare
Content-Type: application/json
{
"postTypes": [
{ "name": "post", "collection": "posts", "enabled": true }
]
}
컬렉션과 필드를 생성합니다.
가져오기 실행
POST /_emdash/api/import/wordpress/execute
Content-Type: multipart/form-data
file: [WordPress 내보내기 .xml]
config: { "postTypeMappings": { "post": { "collection": "posts" } } }
지정된 컬렉션으로 콘텐츠를 가져옵니다.
미디어 가져오기
POST /_emdash/api/import/wordpress/media
Content-Type: application/json
{
"attachments": [{ "id": 123, "url": "https://..." }],
"stream": true
}
다운로드/업로드 중 NDJSON 진행 업데이트를 스트리밍합니다.
URL 재작성
POST /_emdash/api/import/wordpress/rewrite-urls
Content-Type: application/json
{
"urlMap": { "https://old.com/image.jpg": "/_emdash/media/abc123" }
}
새 미디어 URL로 Portable Text 콘텐츠를 업데이트합니다.
오류 처리
복구 가능한 오류
- 네트워크 타임아웃 — 백오프로 재시도
- 단일 항목 파싱 실패 — 로그 남기고 건너뜀, 가져오기 계속
- 미디어 다운로드 실패 — 수동 처리 필요로 표시
치명적 오류
- 잘못된 파일 형식 — 오류 메시지와 함께 가져오기 중단
- 데이터베이스 연결 끊김 — 가져오기 일시 정지, 재개 허용
- 스토리지 용량 초과 — 가져오기 중단, 사용량 표시
오류 보고서
가져오기 후:
가져오기 완료
✓ 125개 게시물 가져옴
✓ 12개 페이지 가져옴
✓ 85개 미디어 참조 기록
⚠ 2개 항목 경고:
- 게시물 "Special Characters ñ" - 제목 인코딩 수정
- 페이지 "About" - 중복 slug가 "about-1"로 변경
✗ 1개 항목 실패:
- 게시물 ID 456 - 콘텐츠 파싱 오류(초안으로 저장)
실패한 항목은 검토를 위해 _importError에 원본 콘텐츠가 포함된 초안으로 저장됩니다.
사용자 정의 소스 만들기
다른 플랫폼용 소스를 만듭니다:
import type { ImportSource } from "emdash/import";
export const mySource: ImportSource = {
id: "my-platform",
name: "My Platform",
description: "Import from My Platform",
icon: "globe",
canProbe: true,
async probe(url) {
// URL이 플랫폼과 일치하는지 확인
const response = await fetch(`${url}/api/info`);
if (!response.ok) return null;
return {
sourceId: "my-platform",
confidence: "definite",
detected: { platform: "my-platform" },
// ...
};
},
async analyze(input, context) {
// 콘텐츠 파싱 및 분석
// ImportAnalysis 반환
},
async *fetchContent(input, options) {
// 각 콘텐츠에 대해 NormalizedItem yield
for (const item of items) {
yield {
sourceId: item.id,
postType: "post",
title: item.title,
content: convertToPortableText(item.body),
// ...
};
}
},
};
EmDash 구성에 소스를 등록합니다:
import { mySource } from "./src/import/custom-source";
export default defineConfig({
integrations: [
emdash({
import: {
sources: [mySource],
},
}),
],
});
다음 단계
- WordPress 마이그레이션 — WordPress 마이그레이션 전체 가이드
- 플러그인 포팅 — WordPress 플러그인을 EmDash로 포팅