콘텐츠로 건너뛰기

워크플로우 오케스트레이션 플랫폼 Temporal 알아보기

  • 테크

마이크로서비스 아키텍처가 보편화되고 AI 에이전트처럼 여러 단계가 연쇄되는 워크로드가 늘어나면서, 안정적인 분산 작업 실행에 대한 요구도 함께 커지고 있습니다.

결제 처리, 사용자 온보딩, 데이터 파이프라인처럼 여러 단계를 거치는 작업들은 중간에 서버 장애나 네트워크 오류가 발생하면 일관성을 유지하기가 생각보다 쉽지 않습니다. 단계별 재시도 로직, 상태 저장, 실패 감지와 복구까지 직접 구현하다 보면 어느새 비즈니스 로직보다 인프라 코드가 훨씬 방대해진 것을 발견하게 됩니다.

이런 문제를 해결하기 위해 등장한 것이 워크플로우 오케스트레이션 플랫폼입니다. 이번 포스팅에서는 워크플로우 오케스트레이션의 기본 개념을 살펴보고, 대표적인 플랫폼인 Temporal의 핵심 구성 요소와 동작 원리를 소개하고자 합니다.


워크플로우 오케스트레이션이란?

워크플로우 오케스트레이션(Workflow Orchestration)이란 여러 단계로 이루어진 작업의 실행 순서를 조율하고, 각 단계의 상태를 추적하며, 오류 발생 시 복구까지 자동으로 처리하는 방식을 의미합니다.

일반적으로 분산 환경에서 복잡한 작업을 처리하기 위해 개발자는 다음과 같은 것들을 직접 구현해야 합니다.

  • 실패한 작업의 재시도 로직
  • 워크플로우 상태 저장 및 복구
  • 단계별 타임아웃 처리
  • 여러 서비스 간 분산 트랜잭션 조율
  • 작업 실행 이력 추적 및 디버깅

워크플로우 오케스트레이션 플랫폼은 이러한 인프라 수준의 문제를 플랫폼이 대신 처리해 줌으로써, 개발자가 비즈니스 로직에만 집중할 수 있도록 도와줍니다.


Temporal이란?

Temporal은 장기 실행 워크플로우(Long-Running Workflow)를 안정적으로 실행하고 관리할 수 있게 해주는 오픈소스 워크플로우 오케스트레이션 플랫폼입니다. Uber에서 만든 Cadence 프로젝트에서 파생되어 2019년에 독립 플랫폼으로 출발하였으며, 현재 Stripe, Netflix, Coinbase 등 수많은 기업에서 프로덕션 환경에 활용하고 있습니다.

Temporal의 가장 핵심적인 특징은 내구성 있는 실행(Durable Execution) 입니다. 코드가 중간에 실패하거나 서버가 다운되더라도, 워크플로우는 정확히 중단된 지점부터 다시 이어서 실행되도록 보장합니다.

지금부터 Temporal을 구성하는 핵심 요소들인 Workflow, Activity, Worker, Task Queue에 대해 자세히 살펴보겠습니다.

Workflow (워크플로우)

💡 기본 개념
Workflow는 비즈니스 프로세스 전체의 흐름을 코드로 표현한 것입니다. 일반 함수처럼 작성되지만, Temporal이 실행 상태를 자동으로 저장하고 관리하기 때문에 서버가 재시작되어도 중단된 지점에서 계속 실행됩니다. 워크플로우 코드 자체는 순수하게 실행 흐름만 정의하며, 실제 외부 작업은 Activity를 통해 수행합니다.

📂 구성 요소
워크플로우 인터페이스(Workflow Interface) : 전체 비즈니스 프로세스의 실행 순서를 정의하는 인터페이스입니다. @WorkflowInterface 어노테이션으로 선언합니다.
실행 메서드(Workflow Method) : 워크플로우의 진입점으로, @WorkflowMethod 어노테이션으로 지정합니다.
Activity 호출 : 외부와의 실제 상호작용은 Workflow.newActivityStub()으로 생성한 Activity 객체를 통해 호출합니다.

동작 원리
1️⃣ 워크플로우 정의 : @WorkflowInterface로 인터페이스를 선언하고, @WorkflowMethod로 진입점 메서드를 지정합니다.
2️⃣ Activity 호출 및 대기 : 각 단계에서 Activity 메서드를 호출하여 실제 작업을 위임하고 결과를 기다립니다.
3️⃣ 상태 자동 저장 : Temporal 서버가 각 단계의 완료 여부를 이벤트로 저장합니다. 장애 발생 시 이벤트 히스토리를 기반으로 중단 시점부터 재개합니다.

@WorkflowInterface
public interface OrderWorkflow {
    @WorkflowMethod
    String processOrder(String orderId);
}

public class OrderWorkflowImpl implements OrderWorkflow {

    private final OrderActivities activities = Workflow.newActivityStub(
        OrderActivities.class,
        ActivityOptions.newBuilder()
            .setStartToCloseTimeout(Duration.ofSeconds(30))
            .build()
    );

    @Override
    public String processOrder(String orderId) {
        // 1단계: 결제 처리
        activities.processPayment(orderId);
        // 2단계: 재고 차감
        activities.deductInventory(orderId);
        // 3단계: 배송 요청
        activities.requestShipping(orderId);
        return "Order completed";
    }
}


📝 장단점
장점
▪ 복잡한 비즈니스 프로세스를 일반 코드처럼 직관적으로 표현할 수 있습니다.
▪ 서버 장애 발생 시에도 중단된 지점부터 자동으로 재개됩니다.
▪ 타임아웃, 재시도, 상태 저장 등을 별도로 구현할 필요가 없습니다.

단점
▪ 워크플로우 코드는 항상 동일하게 재실행될 수 있어야 하기 때문에, 난수 생성이나 현재 시각 조회, 외부 API 호출 등은 워크플로우 코드 안에 직접 쓸 수 없습니다.

Activity (액티비티)

💡 기본 개념
Activity는 실제로 외부 세계와 상호작용하는 작업의 최소 단위입니다. 데이터베이스 쿼리, 외부 API 호출, 파일 처리처럼 외부 시스템과 실제로 상호작용하는 모든 작업은 Activity로 정의합니다. Workflow가 “무엇을 할지”를 정의한다면, Activity는 “어떻게 할지”를 구현합니다.

📂 구성 요소
액티비티 인터페이스(Activity Interface) : @ActivityInterface 어노테이션으로 선언하는 Activity 정의 인터페이스입니다.
재시도 정책(Retry Policy) : 실패 시 재시도 횟수, 대기 시간, 타임아웃 등을 개별적으로 설정할 수 있습니다.
타임아웃(Timeout) : Activity 실행의 최대 허용 시간을 지정합니다. ActivityOptions에서 시작부터 완료까지의 시간을 설정할 수 있습니다.

동작 원리
1️⃣ Activity 정의 : @ActivityInterface로 인터페이스를 선언하고, 구현 클래스에서 실제 외부 작업 로직을 구현합니다.
2️⃣ 워크플로우에서 호출 : 워크플로우가 Activity 메서드를 호출하면 Temporal 서버가 태스크 큐에 작업을 등록합니다.
3️⃣ Worker가 실행 : 태스크 큐를 감시하던 Worker가 작업을 가져와 Activity 함수를 실행하고 결과를 반환합니다.
4️⃣ 실패 시 재시도 : Activity 실행에 실패하면 설정된 재시도 정책에 따라 자동으로 재시도됩니다.

@ActivityInterface
public interface OrderActivities {
    boolean processPayment(String orderId);
}

public class OrderActivitiesImpl implements OrderActivities {

    @Override
    public boolean processPayment(String orderId) {
        // 실제 결제 API 호출
        PaymentResult result = paymentGateway.charge(orderId);
        return result.isSuccess();
    }
}


📝 장단점
장점
▪ 외부 시스템 연동 로직을 워크플로우 흐름과 명확히 분리할 수 있습니다.
▪ 재시도 정책을 Activity별로 세밀하게 설정할 수 있습니다.
▪ 워크플로우와 독립적으로 별도 Worker에서 실행할 수 있어 확장성이 높습니다.

단점
▪ Activity는 기본적으로 최소 한 번 이상 실행될 수 있기 때문에, 같은 작업이 두 번 실행되어도 문제없도록 구현해야 합니다. 예를 들어 결제 요청이 중복으로 발생하지 않도록 처리하는 것이 대표적입니다.
▪ Activity가 많아질수록 관리해야 할 코드 단위도 늘어납니다.

Worker (워커)

💡 기본 개념
Worker는 Workflow와 Activity 코드를 실제로 실행하는 프로세스입니다. Temporal 서버에서 처리할 작업이 있는지 주기적으로 확인하여 가져오고, 해당 Workflow 또는 Activity를 실행한 뒤 결과를 다시 서버에 보고합니다. 애플리케이션 코드가 직접 Temporal 서버에 등록되는 것이 아니라, Worker를 통해 실행된다는 점이 핵심입니다.



📂 구성 요소
Workflow 목록 : Worker가 처리할 수 있는 워크플로우 클래스의 목록입니다.
Activity 목록 : Worker가 처리할 수 있는 액티비티 함수의 목록입니다.
Task Queue : Worker가 작업을 가져올 태스크 큐의 이름을 지정합니다.



동작 원리
1️⃣ Worker 초기화 : Temporal 서버에 연결하고, 처리할 Workflow와 Activity를 등록하고, 작업을 가져올 태스크 큐를 지정합니다.
2️⃣ 태스크 큐 폴링 : Worker는 지정된 태스크 큐에 처리할 작업이 있는지 지속적으로 확인합니다.
3️⃣ 코드 실행 : 작업을 가져오면 등록된 Workflow 또는 Activity 코드를 실행합니다.
4️⃣ 결과 보고 : 실행 결과를 Temporal 서버에 보고하고, 서버는 이를 이벤트 히스토리에 기록합니다.

public class OrderWorker {
    public static void main(String[] args) {
        WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs();
        WorkflowClient client = WorkflowClient.newInstance(service);

        WorkerFactory factory = WorkerFactory.newInstance(client);
        Worker worker = factory.newWorker("order-queue");

        worker.registerWorkflowImplementationTypes(OrderWorkflowImpl.class);
        worker.registerActivitiesImplementations(new OrderActivitiesImpl());

        factory.start();
    }
}


📝 장단점
장점
▪ 수평 확장이 쉬워 Worker를 여러 대 추가하는 것만으로 처리량을 늘릴 수 있습니다.
▪ Temporal 서버와 Worker가 분리되어 있어 애플리케이션 배포와 인프라가 독립적으로 관리됩니다.
▪ Worker가 다운되어도 다른 Worker가 작업을 이어 처리하므로 가용성이 높습니다.

단점
▪ 워크플로우 코드를 변경할 경우, 이미 실행 중인 워크플로우가 영향을 받지 않도록 버전 관리에 신경 써야 합니다.
▪ Worker 프로세스를 직접 운영해야 하므로 별도의 인프라 관리가 필요합니다.

Task Queue (태스크 큐)

💡 기본 개념
Task Queue는 Workflow 및 Activity 실행 요청을 Worker에게 전달하는 대기열입니다. Temporal 서버가 관리하며, 클라이언트가 Task Queue에 작업을 등록하면 해당 큐를 감시 중인 Worker가 작업을 가져가 처리합니다. Worker와 클라이언트가 직접 연결되지 않고 큐를 통해 소통하기 때문에, 서로 독립적으로 운영할 수 있습니다.



📂 구성 요소
큐 이름(Queue Name) : 태스크 큐를 식별하는 문자열입니다. Client, Workflow, Worker가 모두 같은 이름으로 참조합니다.
Workflow Task Queue : 워크플로우 실행 요청이 등록되는 큐입니다.
Activity Task Queue : Activity 실행 요청이 등록되는 큐로, 워크플로우 태스크 큐와 동일하게 설정하거나 별도로 분리할 수 있습니다.



동작 원리
1️⃣ 작업 등록 : Client 또는 Workflow가 특정 태스크 큐에 실행 요청을 등록합니다.
2️⃣ 폴링 및 작업 수령 : 해당 큐를 감시하던 Worker가 작업을 가져갑니다.
3️⃣ 부하 분산 : 동일한 태스크 큐를 바라보는 Worker가 여러 대일 경우, 작업이 자동으로 분산됩니다.

[클라이언트]
    ↓ 워크플로우 시작 요청
["order-queue"]
    ↓ 폴링
[워커 1]  [워커 2]  [워커 3]


📝 장단점
장점
▪ 특정 Worker 그룹에만 작업을 전달할 수 있어 역할별로 Worker를 분리하기 쉽습니다.
▪ 큐를 기준으로 Worker를 독립적으로 확장할 수 있습니다.
▪ Worker가 일시적으로 다운되어도 큐에 작업이 보존되어 유실되지 않습니다.

단점
▪ Task Queue 설계가 잘못되면 특정 큐에 작업이 몰려 병목이 발생할 수 있습니다.
▪ 큐 이름이 Client, Workflow, Worker 세 곳에 모두 일치해야 하므로 관리 시 주의가 필요합니다.


지금까지 워크플로우 오케스트레이션의 기본 개념부터 Temporal의 핵심 구성 요소인 Workflow, Activity, Worker, Task Queue의 역할과 장단점까지 살펴보았습니다.

Temporal은 분산 환경에서 발생하는 장애 복구, 재시도, 상태 관리 문제를 플랫폼 레벨에서 해결해 줌으로써, 개발자가 인프라 코드보다 비즈니스 로직에 집중할 수 있도록 도와줍니다. 결제 처리, 사용자 온보딩, 데이터 파이프라인처럼 여러 단계를 거치는 복잡한 작업을 다룬다면 Temporal 도입을 검토해 볼 만한 충분한 이유가 있습니다.

이번 포스팅이 Temporal과 워크플로우 오케스트레이션에 대한 이해를 높이는 데 도움이 되었기를 바라며, 여기에서 마치겠습니다. 감사합니다 😊

cf)

Temporal 공식 문서
Temporal GitHub


최신 마케팅/고객 데이터 활용 사례를 받아보실 수 있습니다.

비즈스프링 뉴스레터 구독하기 →

문의 02-6919-5516 |  sales@bizspring.co.kr