Dify는 LLM 앱의 복잡도를 없애는 도구가 아니라, 흩어진 프롬프트, RAG, 분기 로직을 눈에 보이는 워크플로우와 운영 계층으로 옮기는 도구입니다.
Dify가 LLM 스파게티를 없앨까: DAG, Celery, DSL이 옮겨 놓은 복잡도
무엇이 실제로 단순해지는가
일반적인 LLM 앱은 프롬프트 문자열, 모델 호출, 검색 코드, 예외 처리와 대화 기록이 한 서비스에 섞이기 쉽습니다. Dify에서는 이 흐름을 LLM, 지식 검색, 코드 샌드박스, 조건 분기 같은 노드로 나누고 엣지로 연결합니다. 대시보드에서 만든 그래프는 DSL로 직렬화되어 데이터베이스에 저장됩니다.
이 구조의 장점은 역할 분리입니다. 프론트엔드는 Next.js 기반 대시보드를 통해 흐름을 편집하고, Python/FastAPI 백엔드는 실행과 API를 담당합니다. 모델이나 프롬프트를 바꿀 때 애플리케이션 코드를 매번 고치지 않아도 되고, 각 노드의 입출력과 실행 흔적을 따라 실패 지점을 좁힐 수 있습니다.
하지만 시각화는 복잡도를 제거하지 않습니다. 코드 속 조건문이 그래프의 노드와 엣지로 이동했을 뿐입니다. 노드가 많아지면 한 화면에서 전체 경로를 이해하기 어려워지고, 누가 어떤 버전을 운영에 반영했는지가 새 문제가 됩니다.
문서 한 건은 어떻게 RAG 답변이 되는가
문서 적재는 대화 요청과 분리된 비동기 작업입니다. Celery와 Redis가 작업을 받아 문서를 추출하고, 청크로 자르고, 임베딩한 뒤 벡터 데이터베이스에 넣습니다. 원문에서 언급한 ETL/Unstructured 계층은 다양한 문서 형식을 텍스트로 바꾸는 앞단을 맡습니다.
질문이 들어오면 워크플로우의 검색 노드가 관련 청크를 찾고, 설정에 따라 여러 검색 경로와 재순위를 거쳐 LLM 노드에 컨텍스트를 전달합니다. 따라서 느린 문서 전처리가 채팅 요청을 직접 막는 것을 피할 수 있습니다. 반대로 큐가 밀리거나 임베딩, 벡터 저장 단계가 실패하면 “문서를 올렸는데 검색되지 않는” 시차가 생깁니다. 운영자는 API 응답뿐 아니라 Celery 큐, Redis, 벡터 인덱스 상태도 함께 봐야 합니다.
이 API 조각만으로는 호출할 수 없다
원문에 제시된 요청 모양은 다음과 같습니다.
1
POST https://api.dify.ai/v1/chat-messages
1
2
3
4
5
{
"inputs": {},
"query": "이번 달 새로 바뀐 HR 규정 요약해 줘",
"user": "developer_123"
}
이것은 엔드포인트와 본문 형태를 보여 주는 스냅샷이지 완전한 실행 예제가 아닙니다. 인증 헤더, 앱별 설정, 오류 처리와 스트리밍 여부가 빠져 있습니다. 실제 통합에서는 먼저 입력 스키마를 고정하고, 비밀 키를 클라이언트에 노출하지 않으며, 타임아웃, 재시도, 응답 검증을 애플리케이션 계층에 둬야 합니다.
도입 후 새로 생기는 세 가지 비용
첫째는 배포와 상태 관리입니다. 자체 호스팅 구성은 PostgreSQL, Redis, 벡터 데이터베이스, Celery와 샌드박스 등 여러 서비스를 포함하며 원문은 10개가 넘는 컨테이너 구성을 지적합니다. 작은 팀이라면 모델 호출보다 플랫폼 자체의 백업, 업그레이드와 장애 대응이 더 큰 일이 될 수 있습니다.
둘째는 버전 관리입니다. 워크플로우가 데이터베이스에 저장되므로 일반 소스 코드처럼 diff와 리뷰를 자연스럽게 적용하기 어렵습니다. DSL 내보내기 규칙, 변경 승인자, 롤백 기준을 따로 정하지 않으면 UI의 편리함이 재현성 문제로 돌아옵니다.
셋째는 확장성입니다. 기본 노드로 표현되지 않는 사내 인증이나 특수 로직은 사용자 정의 노드가 필요합니다. 플랫폼 내부 규약을 이해해야 하므로 “노코드”라는 기대와 실제 개발 비용 사이에 간극이 생깁니다.
우리 팀에 맞는지 판단하는 순서
먼저 하나의 대표 흐름만 골라 시험하는 편이 안전합니다. 문서 적재부터 검색 결과, 모델 응답까지 각 단계의 지연과 실패를 기록하고, 동일 질문에서 검색 청크가 재현되는지 확인합니다. 이어서 워크플로우 내보내기와 복구를 실제로 해 보고, PostgreSQL, Redis, 벡터 저장소의 백업 책임자를 정합니다.
프롬프트와 검색 정책을 비개발자도 자주 바꾸고 여러 LLM 앱이 같은 운영 기능을 공유한다면 Dify의 가치가 큽니다. 반대로 호출 하나와 단순 검색만 필요한 서비스라면 플랫폼을 유지하는 비용이 직접 구현보다 클 수 있습니다. 판단 기준은 그래프가 예뻐 보이는지가 아니라, 변경 추적과 장애 복구까지 포함한 총운영비입니다.
노드 사이 계약을 먼저 고정해야 하는 이유
시각적 그래프도 각 노드의 입력과 출력이 느슨하면 스파게티가 됩니다. 검색 노드가 문서 목록을 내놓는지 문자열 하나를 내놓는지, LLM 노드가 자유 문장인지 구조화된 값을 반환하는지 명시해야 합니다. 노드 이름과 화면 배치만 보고 계약을 추측하면 모델이나 플러그인을 교체했을 때 뒤 노드가 조용히 잘못된 값을 받을 수 있습니다.
대표 입력, 정상 출력, 비어 있는 결과와 오류 출력을 노드별로 저장해 두면 변경 후 회귀를 확인할 수 있습니다. 예를 들어 검색 결과가 0개일 때 LLM이 일반 지식으로 답할지, 답변을 보류할지, 사람에게 연결할지를 그래프에 명시합니다. 오류 경로가 없으면 성공 화면은 단순해 보여도 실제 장애에서는 무한 재시도나 근거 없는 답으로 이어질 수 있습니다.
코드 노드와 외부 도구는 더 엄격한 경계가 필요합니다. 허용된 라이브러리, 실행 시간, 네트워크 접근, 비밀 값의 전달 범위를 제한하고 결과 크기에도 상한을 둡니다. 샌드박스가 있다는 사실만 믿지 말고 파일 접근과 외부 호출을 시도하는 테스트로 실제 격리를 확인해야 합니다.
RAG 실패는 어느 단계에서 찾을까
문서가 검색되지 않을 때는 업로드 성공 메시지부터 의심할 수 있습니다. 추출된 텍스트가 비어 있지 않은지, 청크가 어떤 경계로 나뉘었는지, 임베딩 작업이 끝났는지, 인덱스가 현재 앱과 연결됐는지를 차례로 봅니다. Celery 작업 실패와 검색 점수 부족을 같은 “답변 오류”로 묶으면 더 큰 모델을 붙여도 문제가 해결되지 않습니다.
평가 질문에는 정답뿐 아니라 정답이 있는 문서와 구간을 표시합니다. 검색 단계에서는 필요한 청크가 상위 결과에 들어왔는지, 생성 단계에서는 제공된 근거만으로 답했는지를 따로 채점합니다. 검색은 맞았는데 답이 틀리면 프롬프트나 모델 문제이고, 검색 자체가 비었으면 파서, 청킹, 임베딩, 필터 조건을 먼저 고쳐야 합니다.
문서 갱신도 시험해야 합니다. 규정의 한 문장을 바꾼 뒤 오래된 청크가 검색에서 사라지는지, 삭제한 문서가 캐시나 벡터 인덱스에 남지 않는지 확인합니다. 업로드 시점과 검색 가능 시점 사이의 지연을 사용자에게 보여 주지 않으면 아직 처리 중인 문서를 두고 모델 품질 문제로 오해하기 쉽습니다.
워크플로우 변경을 어떻게 리뷰하고 되돌릴까
운영 그래프를 UI에서 바로 고치게 두면 누가 어떤 조건을 바꿨는지 추적하기 어렵습니다. DSL을 저장소로 내보내고 변경 전후를 리뷰하며, 배포된 버전과 실행 로그에 같은 식별자를 남기는 절차가 필요합니다. DSL 안의 노드 ID나 순서가 불필요하게 바뀌어 diff가 커지는지 먼저 확인하고 사람이 읽을 수 있는 변경 요약을 함께 둡니다.
롤백은 내보낸 파일이 있다는 사실만으로 끝나지 않습니다. 이전 DSL을 새 환경에 가져왔을 때 모델 연결, 지식베이스 ID, 비밀 값과 플러그인 버전이 함께 복구되는지 시험해야 합니다. 플랫폼 업그레이드 전에는 대표 워크플로우를 복제 환경에서 불러와 실행하고, 데이터베이스와 벡터 저장소의 백업도 같은 시점으로 맞춥니다.
승인 권한도 역할에 맞게 나눕니다. 프롬프트 편집자는 문구를 바꿀 수 있어도 외부 도구 권한이나 비밀 값을 열 수 없게 하고, 운영 반영은 별도 승인자가 확인하도록 설계할 수 있습니다. 편집 편의성과 배포 권한을 같은 계정에 주면 노코드 화면이 변경 통제를 우회하는 통로가 될 수 있습니다.
자체 호스팅의 장애 범위를 어떻게 줄일까
채팅 API가 살아 있어도 Redis, Celery 또는 벡터 저장소가 멈추면 일부 기능만 조용히 실패할 수 있습니다. 각 의존성의 상태를 따로 확인하고, 문서 처리 적체, 검색 오류, 모델 호출 실패를 다른 경보로 구분합니다. 큐 길이와 가장 오래 기다린 작업, 실패 재시도 횟수를 보면 사용자가 검색 불가능한 문서를 계속 올리는 상황을 일찍 찾을 수 있습니다.
데이터베이스 복원과 애플리케이션 복원을 함께 연습해야 합니다. 대화 기록만 돌아오고 워크플로우나 지식 인덱스가 다른 시점이면 재현되지 않는 답이 생깁니다. 장애 훈련에서는 모델 API 지연, 벡터 저장소 중단, 코드 노드 시간 초과를 일부러 만들고 사용자에게 어떤 오류가 보이는지 확인합니다.
총비용에는 서버와 모델 호출 외에 업그레이드 검증, 플러그인 유지, 백업, 복원과 관찰 도구가 들어갑니다. 단순 앱 하나를 위해 이 운영 계층을 새로 갖춰야 한다면 직접 구현보다 비쌀 수 있습니다. 반대로 여러 팀이 같은 인증, 검색, 추적 기능을 반복 만들고 있다면 플랫폼 비용을 공유할 수 있습니다.
파일럿에서 어떤 수치가 나오면 확장할까
한 워크플로우를 골라 정답률, 근거 검색 성공률, P95 지연, 작업 실패율과 변경 복구 시간을 기록합니다. 비개발자가 프롬프트를 바꾸는 데 걸린 시간만 보지 말고 변경 뒤 회귀를 발견하고 이전 버전으로 돌아가는 시간까지 포함합니다. 운영자가 장애 원인을 찾는 시간이 줄지 않으면 시각화의 이점이 충분하지 않은 것입니다.
확장 조건은 노드 수가 아니라 반복되는 운영 문제의 감소로 정하는 편이 좋습니다. 같은 인증, 모델 라우팅, 관찰 기능을 여러 앱이 안정적으로 재사용하고, DSL 리뷰와 백업이 실제로 작동할 때 두 번째 워크플로우를 옮깁니다. 반대로 사용자 정의 노드가 대부분이고 플랫폼 내부를 계속 포크해야 한다면 Dify가 복잡도를 통합하기보다 새 종속 계층을 만든 것일 수 있습니다.
최종 선택은 “코드를 쓰느냐 노코드냐”가 아닙니다. 변경이 잦은 AI 흐름을 그래프로 운영하는 이득이 플랫폼의 상태ful한 의존성과 버전 통제 비용을 넘는지를 실제 파일럿으로 확인하는 문제입니다.
참고 자료:
함께 읽으면 이해가 이어지는 글
- GitNexus는 코드를 밖으로 보내지 않나: 브라우저 Graph RAG와 MCP 경계 — GitNexus가 브라우저에서 AST, 지식 그래프를 만드는 방식과 MCP로 외부 모델을 연결할 때 달라지는 데이터 경계, 규모, 정확도 검증법을 정리합니다.
- Crawl4AI로 RAG용 Markdown을 만들 때 먼저 확인할 것 — AsyncWebCrawler로 동적 페이지를 Markdown, JSON으로 바꾸는 최소 흐름과 버전, 브라우저 의존성, 추출 정확도, 자원 비용 검증법을 정리합니다.
- 로컬 RAG에 벡터 DB 서버가 꼭 필요할까? Zvec 도입 전 5가지 확인 — 서버 없이 프로세스 안에서 동작하는 Zvec의 장점과 dense, sparse 검색, 필터링 기능을 살펴보고 운영형 벡터 DB와의 경계를 정리합니다.
←→ 키와 좌우 스와이프를 지원합니다. 읽던 페이지는 이 기기에 저장됩니다.