이 가이드에서는 Apiframe을(를) n8n에 연결해서 어떤 워크플로우에서든 AI 이미지(또는 영상, 음악 등)를 생성하는 방법을 다룹니다.
이 문서에서 다룰 내용:
- 무엇을 만들 것인지
- 사전 준비 사항
- n8n에서 Apiframe 자격 증명 만들기
- 워크플로 1 -
POST /v2/images/generate - 워크플로 2 -
GET /v2/jobs/{jobId} - 워크플로 3 - 추천: 실시간 결과를 위한 웹훅 사용
- 이 패턴을 다른 Apiframe 엔드포인트로 확장하기
모든 예제는 Midjourney POST /v2/images/generate 엔드포인트를 사용하지만, 동일한 패턴을 대부분의 다른 엔드포인트 및 모델에도 그대로 적용할 수 있습니다.
I. 무엇을 만들 것인지
우리는 두 개의 작은 n8n 연동 워크플로를 만들 것입니다:
- 요청 시(on demand) 이미지 생성
- 트리거: 수동 실행, 웹훅, 구글 시트 등 어떤 것이든
- HTTP Request:
POST https://api.apiframe.ai/v2/images/generate - 응답으로
jobId를 받아 저장하거나 로그로 남길 수 있습니다.
- 최종 이미지를 자동으로 가져오기
- GET /v2/jobs/{jobId}로 Apiframe을 폴링해서 작업이 끝날 때까지 확인하기
- 또는 (추천) 작업이 완료되면 Apiframe이 n8n 웹훅을 호출하도록 설정하기
이미지 URL이 n8n에 도착하면 무엇이든 할 수 있습니다. 예를 들어 Slack으로 전송하거나, Airtable에 저장하거나, Google Drive등.
II. 사전 준비 사항
다음이 필요합니다:
- 활성화된 Apiframe 계정과 API 키가 필요합니다. 이 키는 Apiframe 대시보드에서 확인할 수 있습니다(또는 여기를 클릭). 이 API 키는 n8n에서 요청을 보낼 때
X-API-Key헤더를 통해 인증하는 데 사용됩니다. (API 키는 afk_로 시작합니다.)
- 하나의 n8n 인스턴스(셀프 호스팅 또는 클라우드 버전)
- n8n 노드(HTTP Request, Webhook, Set, IF 등)에 대한 기본적인 이해. HTTP Request 노드는 n8n에서 어떤 REST API든 호출할 수 있는 범용 노드입니다.
III. n8n에서 Apiframe 자격 증명 만들기
한 번 자격 증명을 설정해 두면, 이후 모든 HTTP Request 노드에서 재사용할 수 있습니다.
- 1단계: n8n에서 Credentials → Create credential 로 이동합니다.
- 2단계: Header Auth (또는 사용 중인 버전에 따라 "HTTP Header Auth", "API Key in Header")를 선택합니다.
- 3단계: 아래와 같이 설정합니다:
- 헤더 이름(Header name):
X-API-Key - 값(Value): Apiframe API 키 (대시보드에 표시된 afk_로 시작하는 값을 그대로 사용)
- 헤더 이름(Header name):
- 4단계: 자격 증명 이름을 Apiframe Auth처럼 지정하고 저장합니다.
Apiframe은 다음과 같은 형식을 기대합니다:
X-API-Key: afk_your_api_key_here
Content-Type: application/jsonIV. 워크플로 1 - POST /v2/images/generate로 기본 이미지 생성하기
간단한 워크플로를 하나 만들어보겠습니다:
1. 워크플로 생성하기
- n8n에서 New workflow를 생성합니다.
- 다음 노드를 추가합니다:수동 트리거(Manual Trigger) 노드.
2. 프롬프트를 위한 "Set" 노드 추가
- Set 노드를 Manual Trigger 뒤에 추가합니다.
- 노드 설정에서 Values → Add Field → String:
- Name:
prompt - Value: 예를 들어 다음과 같이 입력합니다.
a cinematic photo of a cyberpunk city at night, ultra detailed, 4k
- Name:
- (선택 사항) 문자열 필드를 하나 더 추가합니다:
- Name:
aspect_ratio - Value:
3:2
- Name:
이제 Set 노드의 출력 JSON은 대략 다음과 같은 형태가 됩니다:
{
"prompt": "a cinematic photo of a cyberpunk city at night, ultra detailed, 4k",
"aspect_ratio": "3:2"
}
3. /imagine용 HTTP Request 노드 추가
- HTTP Request 노드를 Set 노드 뒤에 추가합니다.
- 다음과 같이 설정합니다:
- Method:
POST - URL:
https://api.apiframe.ai/v2/images/generate - 인증 방식에서 "Generic Credential type"을 선택하고, 그다음 "Header Auth"를 선택한 뒤, 앞에서 만들어 둔 “Apiframe Auth” 자격 증명을 선택합니다.
- Body 설정에서 "Send body"를 켜고, model(예: midjourney), prompt, midjourneyParams(예: aspect_ratio), 그리고 나중을 위해 선택적으로 webhookUrl 필드를 추가합니다.
- Method:
이 노드를 실행하면, Apiframe은 대략 다음과 같은 결과를 반환합니다:
{
"jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "QUEUED"
}이는 작업이 현재 대기열에 있음/처리 중이라는 의미입니다. 이미지는 비동기적으로 생성되며, 최종 URL은 POST /v2/images/generate 호출만으로는 바로 받을 수 없습니다.
이제 다음과 같은 작업을 할 수 있습니다:
- 다음 값을 로깅하거나
jobId - DB나 Google Sheet에 저장하세요
- 이 값을 “Fetch” 워크플로로 전달하세요
V. 워크플로 2 - GET /v2/jobs/{jobId}로 Apiframe 폴링하기
이제 실제 이미지 URL을 가져오기 위해 GET /v2/jobs/{jobId} 엔드포인트를 사용해 보겠습니다.
Apiframe은 GET https://api.apiframe.ai/v2/jobs/{jobId} 엔드포인트를 제공하며, 이 엔드포인트는 jobId 를 URL 경로로 받아 최종 결과 또는 status: "PROCESSING" 응답을 반환합니다.
여기서는 최소한의 “대기 후 Fetch” 플로우를 만들어 보겠습니다.
1. Wait 노드 추가
앞에서 만든 POST /v2/images/generate HTTP Request 노드 뒤에:
- 새 Wait 노드를 추가합니다.
- 예를 들어 2–3초 정도 기다리도록 설정합니다.
이 시간 동안 Apiframe이 이미지를 생성할 수 있습니다. 실제 생성 시간은 작업의 복잡도와 시스템 부하에 따라 달라집니다.
2. GET /v2/jobs/{jobId} HTTP Request 노드 추가
Wait 노드 뒤에 HTTP Request 노드를 하나 더 추가합니다:
- Method:
GET - URL:
https://api.apiframe.ai/v2/jobs/{{ $json.jobId }} - 인증은 이전과 마찬가지로 "Generic Credential type"을 선택하고, "Header Auth"를 선택한 다음 “Apiframe Auth”를 선택합니다.
- 이번 요청에는 Body가 필요 없습니다 — "Send body"는 꺼둔 채, 이전 노드에서 받은
jobId값을 URL 경로에 그대로 사용합니다.
Processing(작업이 아직 실행 중일 때):
{
"jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "PROCESSING",
"progress": 40
}
Completed(작업 완료, 이미지 URL 준비 완료):
{
"jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "COMPLETED",
"result": {
"images": [
"https://.../image1.png",
"https://.../image2.png",
"https://.../image3.png",
"https://.../image4.png"
],
"gridUrl": "https://.../grid.png"
}
}3. “PROCESSING” 상태 처리하기
빠르게 개발 환경을 구성할 때는 다음과 같이 할 수 있습니다:
- 그냥 좀 더 오래 기다렸다가 한 번만 다시 가져옵니다.
- 또는 Fetch 뒤에 간단한 IF 노드를 추가합니다:
- 조건:
status가"PROCESSING" - “true”인 경우: 다른 Wait + Fetch로 분기
- “false”인 경우: 최종 로직(Slack, Airtable 등)으로 계속 진행
- 조건:
프로덕션 환경에서는, Apiframe에서 폴링 대신 웹훅 사용을 권장합니다. 불필요한 요청을 줄이고 즉각적인 업데이트를 받을 수 있기 때문입니다.
이제 이를 직접 설정해 보겠습니다.
VI. 워크플로 3 - 웹훅 기반 결과 처리(권장 방식)
다음이 깔끔한 “실시간” 아키텍처입니다:
- 워크플로 A: 생성 요청 전송 (
webhookUrl과webhookEvents) 포함 - 워크플로 B: 웹훅 수신 — 생성이 완료되면 Apiframe에서 전송하는 웹훅을 받습니다
1. 워크플로 B 만들기 - 웹훅 수신기
- n8n에서 New workflow를 만들고 이름을 다음과 같이 지정합니다:
Apiframe – 이미지 생성 완료. - Webhook 노드를 추가합니다.
Webhook 노드를 다음과 같이 설정하세요:
- HTTP Method:
POST - Path: 예를 들어 다음과 같이 지정합니다:
apiframe/midjourney-completed - Response mode:
- 예를 들어
When Last Node Finishes로 설정하면 (원한다면 데이터를 다시 반환할 수 있습니다).
- 예를 들어
- HTTP Method:
Production URL 을 복사합니다. 이 값은 Apiframe에서 webhookUrl 로 설정할 값입니다.
2. 웹훅 서명 검증: X-Webhook-Signature
Apiframe은 웹훅을 호출할 때 X-Webhook-Signature 헤더에 요청 본문의 HMAC-SHA256 서명을, X-Webhook-Event 헤더에 이벤트 이름을 담아 보냅니다. 서명에 사용하는 시크릿은 API 키 자체가 아니라 API 키를 SHA-256으로 해시한 값입니다.
- Webhook 노드 설정에서 "Raw Body" 옵션을 켠 뒤, 그 뒤에 Code 노드를 추가합니다.
- Code 노드에 다음 스크립트를 붙여넣어 서명을 검증합니다:
const crypto = require('crypto');
// 서명에 사용하는 시크릿은 API 키 자체가 아니라, API 키를 SHA-256으로 해시한 값입니다.
const secret = crypto
.createHash('sha256')
.update('afk_your_api_key_here')
.digest();
// Webhook 노드에서 "Raw Body" 옵션이 켜져 있어야 합니다.
const raw = $json.rawBody ?? JSON.stringify($json.body);
const expected = crypto
.createHmac('sha256', secret)
.update(raw)
.digest('hex');
const received = $json.headers['x-webhook-signature'] ?? '';
const a = Buffer.from(expected);
const b = Buffer.from(received);
if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
throw new Error('Invalid webhook signature');
}
return $input.all();- raw: 요청 원문 본문 (rawBody)
- expected: secret으로 raw를 HMAC-SHA256 해시한 값
- received: 요청 헤더의
x-webhook-signature - expected와 received가 일치하지 않으면 Code 노드에서 에러를 던져 이후 노드 실행을 중단시킵니다.
이렇게 하면 Apiframe에서 온, 위조되지 않은 웹훅만 처리되도록 보장할 수 있습니다.
3. 웹훅 페이로드에서 이미지 URL에 접근하기
웹훅 바디는 앞에서 본 “completed” 페이로드와 동일한 형태입니다:
{
"jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "COMPLETED",
"result": {
"images": [
"https://.../image1.png",
"https://.../image2.png",
"https://.../image3.png",
"https://.../image4.png"
],
"gridUrl": "https://.../grid.png"
}
}이제 이 안에서 이미지 URL에 접근한 뒤, 다음 노드들로 넘겨서 사용할 수 있습니다:
- Slack 노드 (URL을 포함한 메시지 전송)
- Airtable / Notion (URL을 저장)
- HTTP Request (앱의 백엔드로 전송)
4. 웹훅을 사용하도록 워크플로 A 업데이트하기
다시 워크플로 A(에서, POST /v2/images/generate) 엔드포인트를 호출하고 있는 워크플로)로 돌아가 HTTP Request 노드의 body에 다음 필드를 추가합니다:
webhookUrlwebhookEvents
HTTP Request 노드에서 사용할 예시 JSON body:
{
"model": "midjourney",
"prompt": "a cinematic photo of a cyberpunk city at night, ultra detailed, 4k",
"midjourneyParams": {
"aspect_ratio": "3:2"
},
"webhookUrl": "https://your-n8n-domain.com/webhook/apiframe/midjourney-completed",
"webhookEvents": ["completed"]
}이제 전체 흐름은 다음과 같습니다:
- 워크플로 A →
POST /v2/images/generate호출 (webhookUrl+webhookEvents - Apiframe이 백그라운드에서 이미지를 생성
- 완료되면 Apiframe이 여러분의 웹훅(워크플로 B)을 최종 이미지 URL과 함께 호출
- 워크플로 B가 이 URL들을 처리해 원하는 곳으로 전송
폴링도, 추가 HTTP 요청도 필요 없습니다 – 순수 이벤트 기반입니다.
VII. 이 패턴을 다른 Apiframe 엔드포인트로 확장하기
좋은 점은,한 번 n8n → Apiframe 연동을 만들어두면, 같은 패턴을 모든 기능에 재사용할 수 있다는 것입니다.
예시 아이디어:
- Variations:
POST https://api.apiframe.ai/v2/images/midjourney/action엔드포인트에 원본 이미지의parentJobId와 action: "variation"(그리고 새로운 프롬프트가 있다면 함께)을 전달합니다. - 업스케일(Upscales): 동일한 인증을 사용하지만, action 값을 "upscale"로 바꿔서 같은 엔드포인트(POST /v2/images/midjourney/action)로 요청합니다.
- 페이스스왑(Faceswap): 두 개의 이미지 URL(소스 & 타겟)을
faceswap엔드포인트로 전송합니다. - Describe: 이미지 URL을 넣으면 Apiframe이 그에 대한 프롬프트를 반환해 주는 n8n 워크플로우를 만듭니다.
- 기타 미디어: Flux, Ideogram, Luma, Suno 등 영상·음악 등 다른 미디어도(예: POST /v2/videos/generate, POST /v2/music/generate) 모두 동일한 REST 패턴을 따릅니다:
POST방식으로 JSON을 보내고, 웹훅을 사용하려면webhookUrl+webhookEvents를 포함합니다.
이 각각은 모두 단지 또 하나의 HTTP Request 노드(또는 GET /v2/jobs/{jobId})를 함께 사용할 경우 두 개의 노드)일 뿐이며, 동일한 “Apiframe Auth” 자격 증명을 사용합니다.
VIII. 마무리
이제 여러분은 다음을 갖추게 되었습니다:
- 기본적인
POST /v2/images/generate워크플로우 — n8n에서 Apiframe을 통해 Midjourney를 트리거하는 플로우 - 하나의 폴링(polling) 설정 — 빠른 실험을 위해
GET /v2/jobs/{jobId}를 사용하는 방식 - 그리고 하나의 웹훅 기반 아키텍처 — 실시간, 프로덕션급 파이프라인을 위한 구조