아키텍처
운영자와 기여자를 위해 Nodaro의 구조를 설명합니다. 편집기, Fastify API, BullMQ 워커, Redis, 스토리지, 세 가지 인증 방식, 에디션을 다룹니다.
Nodaro는 REST 우선 AI 워크플로 엔진입니다. 사용자는 브라우저 편집기에서 이미지와 동영상 생성, 동영상 합성, 텍스트 모델, 오디오 같은 AI 노드를 연결해 그래프를 만듭니다. 그래프는 Supabase Postgres에 저장되며, 그래프를 실행하면 Fastify API가 그래프를 정렬하고 각 노드의 작업을 Redis의 큐 워커에 넘깁니다. 생성은 모델 공급자가 하고, 결과는 S3 호환 스토리지에 저장됩니다.
같은 코드가 Community, Business, Cloud의 세 에디션을 제공하며, 세 종류의 액세스 토큰을 받습니다. 이 페이지는 Nodaro를 운영하는 운영자와 Nodaro의 코드를 바꾸는 기여자를 위한 것입니다.
시스템 한눈에 보기
저장소
공개 저장소인 github.com/nodaroai/app.nodaro.ai는 npm workspaces를 쓰는 모노레포 하나입니다.
| 폴더 | 들어 있는 것 |
|---|---|
backend/ | Fastify API, BullMQ 워커, 오케스트레이터입니다. Node.js 22, TypeScript를 씁니다. |
frontend/ | 브라우저 앱으로, 편집기, 앱 실행기, 관리자 패널이 들어 있습니다. React 19, React Router 7, React Flow를 씁니다. |
packages/ | 공유 코드, 공개된 npm 패키지 @nodaro/shared, @nodaro/sdk, @nodaro/cli, 그리고 Remotion 동영상 컴포지션입니다. |
supabase/ | 데이터베이스 스키마로, 순방향으로만 적용하는 SQL 마이그레이션으로 관리합니다. |
docs/ | 설계 노트를 포함한 제품 문서입니다. |
tools/ | 운영자용 스크립트입니다. 키 생성기, 계약 검사 프로브, 백업과 복원 스크립트가 있습니다. |
examples/ | 예제입니다. 예를 들어 SSH로 서버를 업데이트하는 GitHub Actions 워크플로가 있습니다. |
프론트엔드
프론트엔드는 Vite 단일 페이지 앱입니다. Docker 이미지에서는 웹 서버인 Caddy가 이 앱을 정적 파일로 제공합니다. 한 번들에 네 가지 제품이 들어 있습니다.
- 편집기 — React Flow 캔버스, 노드 계열별 설정 패널, 그리고 편집 중에 브라우저에서 워크플로를 실행하는 그래프 실행기입니다.
- 앱 실행기 — 게시된 워크플로를 입력과 결과로 이루어진 간단한 양식으로 보여 줍니다.
- OAuth 동의 화면 — 사용자가 서드파티 앱과 MCP 클라이언트를 승인하는 화면으로,
/oauth/authorize에 있습니다. - 관리자 패널 — Business 에디션과 Cloud 에디션에만 있습니다.
서버 상태는 React Query에, 인터페이스 상태는 Zustand에, 캔버스 상태는 React Flow 자체 스토어에 있습니다.
API
API는 TypeScript로 작성한 Node.js 22 기반 Fastify 서버입니다. 라우트는 Fastify 플러그인이며, 모든 엔드포인트는 요청과 응답의 스키마를 선언합니다. 같은 스키마가 각 요청을 검증하고, /v1/openapi.json의 OpenAPI 3.1 문서도 만듭니다. GET /v1/nodes는 그 설치에서 실행할 수 있는 노드 유형을 나열합니다.
모든 라우트보다 먼저 인증 훅 하나가 실행됩니다. 인증을 참고하세요.
프로세스와 큐
백엔드는 이미지 하나에 담긴 Node.js 프로세스 다섯 개로 제공됩니다.
| 프로세스 | 역할 |
|---|---|
| API 서버 | HTTP API입니다. |
| 미디어 워커 | 큐에서 노드별 작업을 받아 모델 공급자를 호출하고, 결과를 스토리지에 업로드합니다. 이미지와 동영상 생성, ffmpeg 처리, 오디오 등 40가지가 넘는 작업 유형을 처리합니다. 대부분 네트워크를 기다리므로 기본 동시 실행 수는 50입니다. |
| 렌더 워커 | 컴포지션, 모션 그래픽, Lottie 오버레이, 3D 타이틀, 합성을 렌더링하는 Remotion 렌더러입니다. 헤드리스 Chrome을 실행하며 CPU에 따라 처리량이 제한됩니다. 기본 동시 실행 수는 2입니다. |
| 오케스트레이터 | 워크플로 전체를 실행합니다. 실행을 읽고, 그래프를 정렬하고, 레벨별로 실행합니다. 기본 동시 실행 수는 20입니다. |
| 파이프라인 워커 | 스토리 → 동영상 파이프라인입니다. 모든 에디션에 포함되지만 Nodaro Cloud에서만 동작하며, 다른 곳에서는 바로 종료됩니다. |
Docker에서는 시작 스크립트 하나가 모든 프로세스를 띄우고, Caddy가 API 앞단을 맡습니다. 확장한 설치에서는 각 프로세스를 별도 컨테이너에서 실행할 수 있습니다. 프로세스들은 Redis의 BullMQ 큐와 Postgres의 실행 기록을 통해서만 서로 조율합니다. 확장을 참고하세요.
워크플로가 실행되는 방식
사용자가 편집기에서 실행을 클릭하면 경로는 두 가지입니다.
- 브라우저에서. 편집 중에는 편집기가 직접 그래프를 실행하고, 노드마다 API 엔드포인트를 하나씩 호출합니다. 각 결과는 해당 노드가 끝나는 즉시 볼 수 있습니다.
- 서버에서. Webhook, 일정, API로 트리거된 실행, 앱 실행, 명시적인 서버 실행은 오케스트레이터로 갑니다. 오케스트레이터는 진행 상황을 실행 기록에 저장하므로, 어떤 클라이언트든 실행을 따라갈 수 있습니다.
오케스트레이터는 다음과 같이 동작합니다.
- 워크플로의 노드와 연결을 불러옵니다.
- 그래프를 레벨별로 정렬합니다. 한 레벨에는 입력이 모두 준비된 노드가 들어갑니다.
- 각 레벨의 노드를 병렬로 실행합니다. 상한은 사용자 자신의 병렬 수와 서버 한도인
MAX_CONCURRENT_NODES_PER_EXECUTION입니다. - 각 노드를 세 가지 방식 중 하나로 실행합니다.
| 방식 | 위치 | 예시 | 이유 |
|---|---|---|---|
| 큐 경유 | 미디어 워커나 렌더 워커의 작업 | 이미지 생성, 동영상 생성, 텍스트 음성 변환, 동영상 합치기, 동영상 렌더링 | 오래 걸리는 작업이나 외부 호출로, 백프레셔를 위해 오케스트레이터와 분리합니다 |
| 직접 HTTP | API 라우트로 보내는 내부 호출 | 텍스트 생성 - 모든 LLM, 모션 그래픽, 프롬프트 도우미, 소셜 게시 노드 | 빠른 텍스트 모델 호출로, 큐 오버헤드가 없습니다 |
| 인라인 | 오케스트레이터 내부 | 텍스트 합치기, 텍스트 분할, 레이어 합성 | 외부 호출이 없는 순수 로직입니다 |
- 각 노드가 끝나면 그 출력을 의존하는 노드에 넘기고, 노드마다 데이터베이스 쓰기 한 번으로 실행 진행 상황을 저장합니다.
실행이 멈추는 방식은 두 가지입니다. 취소됨은 즉시 멈춥니다. 이미 실행 중인 노드는 버려지고, 이미 청구된 크레딧은 되돌려지지 않습니다. 중지 중은 현재 레벨을 끝낸 뒤 다음 레벨 전에 멈춥니다.
한도. 각 노드는 최대 90분, 실행 전체는 최대 120분까지 걸릴 수 있습니다. 자체 시간 예산을 선언하는 노드는 대신 그 예산을 받고, 실행의 한도도 같은 만큼 늘어납니다. 현재 그런 노드는 EDL 적용이며, 예산은 렌더링하는 편집의 규모로 정해집니다. 실행 중에 오케스트레이터가 다시 시작되면, 예산이 있는 노드는 그 워커가 아직 살아 있을 경우 두 번 실행되지 않고 이어서 진행됩니다. 하위 워크플로는 순환을 감지하면서 최대 5단계 깊이까지 재귀적으로 실행됩니다.
스토리지와 데이터베이스
- 미디어 — 생성된 모든 이미지, 동영상, 오디오 파일은 S3 호환 스토리지에 저장되고 키로 참조됩니다. Nodaro는 저장된 미디어를 스스로 삭제하지 않습니다.
- 데이터베이스 — Supabase Postgres에는 프로필, 프로젝트, 워크플로, 실행과 진행 상황, 작업, 미디어 기록, 크레딧 거래, OAuth 앱과 토큰, 개인 API 토큰, 암호화된 소셜 연결이 저장됩니다. 모든 테이블에 행 수준 보안이 적용되므로, 데이터베이스 자체가 각 사용자를 자신의 행으로 제한합니다.
- 마이그레이션 — 스키마는
supabase/migrations/에 있으며, 파일 이름 순서대로 순방향으로만 적용됩니다. - Redis — 큐와 작은 공유 캐시를 보관합니다. Redis의 작업 상태는 오래 유지되지 않습니다. 백업과 복원을 참고하세요.
인증
API는 세 종류의 Bearer 토큰을 받고, 형태로 구분합니다.
| 토큰 | 출처 | 동작 주체 |
|---|---|---|
eyJ…, JWT | 편집기의 Supabase 로그인 세션 | 로그인한 사용자 |
ndr_app_<64 hex> | 사용자가 승인한 OAuth 앱 | 앱을 승인한 사용자. 부여된 스코프로 제한됩니다 |
ndr_<64 hex> | 개인 API 토큰 | 토큰 소유자 |
한 설치의 프로세스들은 INTERNAL_ORCHESTRATOR_SECRET으로도 서로 인증하며, 이 값은 상수 시간으로 비교됩니다. 웹 서버는 외부 요청에서 그 헤더를 제거합니다.
API는 요청마다 다음 순서로 확인합니다.
- 공개 라우트는 인증을 건너뜁니다.
/v1/openapi.json과 들어오는 Webhook이 그 예입니다. - 내부 시크릿: 프로세스 간 호출에 씁니다.
- OAuth 앱 토큰: 취소되거나 만료되지 않은 토큰이어야 합니다.
- Supabase 세션 토큰: 사용자의 역할은 프로필에서 읽고 5분 동안 캐시합니다.
- 그 밖의 모든 경우는
401로 응답합니다.
개인 API 토큰은 그 토큰을 받는 라우트에서 확인합니다. 이 라우트들은 요청 한도도 적용하고, 토큰을 해당 워크플로로 제한합니다. 인증과 OAuth 앱을 참고하세요.
코드 속 에디션
코드베이스 하나가 세 에디션을 모두 제공합니다.
- Community(
EDITION=community, 기본값) — 셀프 호스팅이며, 관리자 패널, 크레딧 원장, 결제가 없습니다. 가입한 사람은 누구나 일반 사용자입니다. - Business(
EDITION=business) — 관리자 패널과 사용자 관리가 추가됩니다. 여전히 셀프 호스팅이며 결제는 없습니다. - Cloud(
EDITION=cloud) — 크레딧, 결제, 관리자가 정하는 크레딧 가격이 추가됩니다. app.nodaro.ai를 구동하는 에디션이며, 셀프 호스팅용이 아닙니다.
라우트는 에디션의 이름이 아니라 기능을 확인합니다. 관리자 라우트는 관리자 패널이 있는 에디션에서만, 크레딧 코드는 크레딧이 있는 에디션에서만 실행됩니다. 이 확인은 라우트에서 다른 어떤 로직보다 먼저 이루어집니다. 브라우저 앱은 빌드 시점에 VITE_EDITION에서 에디션을 읽습니다. 에디션과 서피스 프로필을 참고하세요.
상태와 스트리밍
작업과 실행의 상태는 2~5초마다 폴링합니다. 상태는 초 단위로 바뀌고, 폴링은 배포하기 간단하기 때문입니다. 스트리밍 출력, 즉 텍스트 모델의 텍스트, 워크플로 실행, 파이프라인에는 서버 전송 이벤트를 씁니다. 그래서 Nodaro 앞에 둔 리버스 프록시는 응답을 버퍼링하면 안 됩니다.
이렇게 선택한 이유
- Fastify — 일급 TypeScript 타입과 스키마 기반 검증을 제공하며, 플러그인으로 각 라우트를 자체 범위에 둡니다.
- BullMQ — 재시도, 백오프, 동시 실행 제어를 갖춘 Redis 기반의 성숙한 큐로, 같은 Node.js 스택에서 동작합니다.
- REST — 라우트별로 OAuth 스코프를 정하기 쉽고, OpenAPI 문서를 생성할 수 있으며, 서드파티 클라이언트에게 단순한 모델입니다.
- Supabase — Postgres, 로그인, 실시간 기능, 행 수준 보안을 한 서비스에서 제공합니다. 행 수준 보안이 많은 맞춤 권한 부여 코드를 대신합니다.
- React Flow — 노드 그래프용으로 검증된 캔버스이며, Nodaro는 이를 크게 확장해 씁니다.
- Remotion — 선언적인 React 컴포지션으로, 서버에서도 편집기 미리보기와 똑같이 렌더링됩니다.
- 하나의 공급자 레이어 — 모든 모델 호출이 하나의 추상화를 거치므로, 기능 코드를 바꾸지 않고 모델을 다른 공급자로 옮길 수 있습니다.
자주 묻는 질문
Nodaro는 무엇으로 만들어졌나요?
React Flow 캔버스를 갖춘 Vite와 React 19 기반의 단일 페이지 앱, TypeScript로 작성한 Node.js 22 기반 Fastify API, Redis 위의 BullMQ 큐, Postgres와 로그인을 맡는 Supabase, 미디어용 S3 호환 스토리지, 동영상 합성을 맡는 Remotion으로 만들어졌습니다.
서버에서 워크플로는 어떻게 실행되나요?
오케스트레이터가 워크플로를 불러와 그래프를 레벨별로 정렬하고, 각 레벨의 노드를 동시 실행 한도까지 병렬로 실행합니다. 오래 걸리거나 외부 호출이 필요한 작업은 Redis를 거쳐 미디어 워커와 렌더 워커로 보내고, 빠른 텍스트 호출과 순수 로직은 바로 실행합니다.
Nodaro API는 어떤 토큰을 받나요?
편집기의 Supabase 세션 토큰, ndr_app_으로 시작하는 개발자 앱의 OAuth 액세스 토큰, ndr_로 시작하는 개인 API 토큰을 받습니다. 각 토큰은 한 사용자에 대응하며, OAuth 토큰은 스코프도 함께 가집니다.
실행 한 번에 시간이 얼마나 걸릴 수 있나요?
각 노드는 최대 90분, 실행 전체는 최대 120분까지 걸릴 수 있습니다. EDL 적용처럼 자체 시간 예산을 선언하는 노드는 대신 그 예산을 받고, 실행 전체의 한도도 같은 만큼 늘어납니다.
설치의 OpenAPI 문서는 어디에 있나요?
내 설치의 /v1/openapi.json에 있습니다. 모든 엔드포인트는 스키마로 입력을 검증하며, 같은 스키마로 OpenAPI 3.1 문서를 만듭니다. GET /v1/nodes는 그 설치에서 실행할 수 있는 노드 유형을 나열합니다.
관련 문서
확장
기여
인증
TypeScript SDK
에디션과 서피스 프로필
최종 업데이트