워크플로우 시스템의 두 세계: 선언적 DAG와 명령적 코드

On the way to step functions: the two worlds

작성자
발행일
2026년 01월 20일

핵심 요약

  • 1 워크플로우 시스템은 선언적 DAG 정의와 명령적 노드 실행 코드라는 두 가지 독립적인 세계로 구성되며, 이들의 엄격한 분리가 견고한 시스템의 핵심입니다.
  • 2 두 세계를 통합하려는 시도는 '3색 함수'와 같은 복잡성을 야기하며, 비결정성, 비멱등성, 이해하기 어려운 문제를 초래하여 사용자에게 더 큰 고통을 줍니다.
  • 3 Nuke, Terraform과 같은 성공적인 시스템들은 DAG를 오케스트레이션 계층으로 명확히 분리하여 병렬 처리, 캐싱, 재시도 등 강력한 이점을 제공합니다.

도입

워크플로우 및 내구성 있는 컴퓨테이션 엔진의 핵심 원리를 탐구하는 이 글은 DAG(Directed Acyclic Graph)가 모든 워크플로우 엔진의 기반임을 강조하며, 특히 '두 가지 세계'의 존재를 조명합니다. 전문 그래픽 및 오디오 애플리케이션에서 사용되는 DAG는 작업 공간이 엄격하고 제한적인 인터페이스로 분리된 두 세계로 구성된다는 특징을 가집니다. 본 글은 이러한 분리의 중요성을 설명하고, 이를 무시할 때 발생하는 문제점들을 지적합니다.

본 글은 워크플로우 시스템의 핵심을 이루는 두 가지 세계를 Nuke와 Terraform 사례를 통해 상세히 설명합니다.

1. 두 가지 세계의 정의

  • 선언적 DAG (Declarative DAG): 워크플로우의 구조와 흐름을 정의하는 부분입니다. Nuke에서는 Tcl 스크립트, Terraform에서는 HCL이 이에 해당합니다. 이는 ‘무엇을 해야 하는가’를 기술합니다.

  • 명령적 노드 코드 (Imperative Node Code): DAG 내 각 노드에서 실제로 작업을 수행하는 코드입니다. Nuke에서는 C++로 컴파일된 네이티브 객체, Terraform에서는 Go로 작성된 ‘프로바이더’가 이에 해당합니다. 이는 ‘어떻게 수행하는가’를 기술하며, 멱등성을 가져야 합니다.

2. 엄격한 분리의 중요성

이 두 세계는 DAG에 노출된 속성을 통해서만 상호작용하며, 명령적 코드가 선언적 DAG를 직접 제어할 수 없습니다. 엔진은 값을 한 방향으로만 노드에 전달하고, 결과는 수집됩니다. 이러한 분리는 다음과 같은 이점을 제공합니다:

  • 명확한 책임 분리: 오케스트레이션(DAG)과 실행(노드)의 역할을 명확히 구분합니다.

  • 멱등성 및 예측 가능성: 노드 코드가 멱등성을 유지하고, 엔진은 결과를 캐싱하거나 재시도할 수 있습니다.

  • 견고성: 각 단계가 독립적으로 해결, 캐싱, 재시도 및 오케스트레이션될 수 있어 시스템의 안정성이 향상됩니다.

3. 통합 시도의 문제점: ‘3색 함수’

ActiveJob Continuation, Temporal, Vercel 워크플로우와 같이 두 세계를 융합하려는 시도는 다음과 같은 문제점을 야기합니다:

  • 복잡한 실행 흐름: 동기(red), 비동기(blue)에 더해 워크플로우(green) 코드가 혼합되어 ‘3색 함수’를 생성합니다.

  • 비결정성: performance.now()와 같은 비결정적 함수가 워크플로우 코드 내에서 사용될 경우, 일시 중단 및 재개 시 의미 없는 결과를 초래할 수 있습니다.

  • 불확실한 보장: 체크포인트, 멱등성, 재시도 로직이 코드에 내재되어 추론하기 어렵고, 예상치 못한 버그를 유발합니다.

  • 잘못된 친숙함: 단일 코드 블록으로 작성되어 익숙하게 느껴지지만, 실제로는 완전히 다른 실행 환경에서 작동하므로 오해를 불러일으킵니다.

4. YAML 사용에 대한 경고

DAG 정의를 YAML로 작성하려는 유혹이 있지만, YAML은 ‘최소 공통분모’로서 다양한 언어 사용자 간의 타협점일 뿐, 특정 언어 생태계에 최적화된 선택은 아닙니다. 사용자에게 친숙하고 강력한 언어로 DAG를 정의할 수 있도록 하는 것이 더 바람직합니다.

결론

결론적으로, 워크플로우 시스템의 견고성과 이해도를 높이려면 선언적 DAG 정의와 명령적 노드 실행 코드라는 두 가지 본질적으로 다른 엔티티를 명확히 분리해야 합니다. Nuke와 Terraform 같은 성공적인 사례들이 보여주듯이, 오케스트레이션과 실행은 서로 다른 역할을 가지며, 이를 통합하려는 시도는 시스템에 불필요한 복잡성과 비결정성을 초래합니다. '모든 것이 그저 함수일 뿐'이라는 환상에서 벗어나 DAG의 힘을 인정할 때, 더 미래 지향적이고 테스트 가능하며 사용자 친화적인 워크플로우 시스템을 구축할 수 있습니다. 다음 파트에서는 이러한 원칙이 Ruby 환경에서 어떻게 구현될 수 있는지 탐구할 예정입니다.

댓글 0

로그인이 필요합니다

댓글을 작성하거나 대화에 참여하려면 로그인이 필요합니다.

로그인 하러 가기

아직 댓글이 없습니다

첫 번째 댓글을 작성해보세요!