'분류 전체보기' 카테고리의 글 목록 :: 잡다한 프로그래밍
반응형

Spring Batch 전체 구조를 이해하기 전에

Spring Batch는 단순히 “데이터를 읽고-쓰는 도구”가 아니다.
Job → Step → Reader/Processor/Writer로 이어지는 표준 처리 플로우를 중심으로,
실행/재시작/상태 관리까지 포함한 하나의 배치 실행 플랫폼이다.

이 시스템을 제대로 이해하려면 먼저 가장 핵심적인 두 개념부터 잡아야 한다.


Job과 Step

Job — 배치 처리의 최상위 단위

하나의 Job은 “하나의 배치 작업 전체”를 의미한다.

  • 매일 0시에 실행되는 '일일 매출 집계'
  • 매주 일요일마다 처리되는 '휴면 회원 정리'
  • 매월 1일에 실행되는 '정기 결제'
  • 필요시점에 실행하는 '대용량 데이터 이관'

즉, Job은 “무엇을 처리할 것인가”를 가장 큰 단위에서 정의한다.


Step — Job을 구성하는 실행 단위

Step은 Job을 구성하는 실행 단위로 하나의 Job은 하나 이상의 Step으로 구성된다. 예를 들어 '일일 매출 집계' Job은 다음과 같은 Step들로 이뤄질 수 있다.

1. 매출 집계 Step

  • 전일 주문 데이터를 읽고(read)
  • 결제 완료된 것만 필터링하여(process)
  • 상품별/카테고리별로 집계해서 저장(write)

2. 알림 발송 Step

  • 집계 요약 정보를 생성하여 관리자에게 전달

3. 캐시 갱신 Step

  • 집계된 데이터로 캐시 정보 업데이트

  1.  

각 Step은 순차적으로 실행되며,


이 중 하나라도 실패하면 Job은 실패한다.


Spring Batch 시스템을 이해하는 관점

Spring Batch는 크게 두 가지 영역으로 나눠 보면 훨씬 이해가 쉽다.

✔ Spring Batch가 제공하는 영역

  • Job / Step API (JobBuilder, StepBuilder)
  • JobLauncher: Job을 실행하고 실행에 필요한 파라미터를 전달하는 역할을 한다. 배치 작업 실행의 시작점이라고 할 수 있다.
  • JobRepository: 배치 처리의 모든 메타데이터를 저장하고 관리하는 핵심 저장소로 Job과 Step의 실행 정보(시작/종료 시간, 상태, 결과 등)를 기록한다. 이렇게 저장된 정보들은 배치 작업의 모니터링이나 문제 발생 시 재실행에 활용된다.
  • ExecutionContext: Job과 Step 실행 중의 상태 정보를 key-value 형태로 담는 객체다. Job과 Step 간의 데이터 공유나 Job 재시작 시 상태 복원에 사용된다
  • 데이터 처리 컴포넌트 구현체: 앞서 0장에서 설명했듯이 Spring Batch는 데이터를 '읽기-처리-쓰기' 방식으로 처리하며, 이를 위한 다양한 구현체를 제공한다.
    • ItemReader 구현체: JdbcCursorItemReader, JpaPagingItemReader, MongoCursorItemReader 등 다양한 데이터 소스로부터 데이터를 읽어올 수 있다.
    • ItemWriter 구현체: JdbcBatchItemWriter, JpaItemWriter, MongoItemWriter 등을 통해 처리된 데이터를 저장할 수 있다.

이 영역은 프레임워크가 책임진다.
우리는 “조합해서 사용”만 하면 된다.


✔ 개발자가 제어하는 영역

  • @Configuration을 사용해 Job과 Step의 실행 흐름을 정의한다.
  • 각 Step의 실행 순서와 조건을 설정하고, Spring 컨테이너에 등록해 배치 잡의 동작을 구성한다.
  • Spring의 DI(의존성 주입)를 활용해 ItemReader, ItemProcessor, ItemWriter 등 배치 작업에 필요한 컴포넌트들을 조합하고 배치 플로우를 완성한다.
  • 아래 예제는 가장 간단한 형태의 Spring Batch 잡 구성 코드를 보여준다. 이처럼 개발자는 Job과 Step을 빈으로 정의하고, 필요한 ItemReader와 ItemWriter를 조합해 배치 흐름을 구성하기만 하면 된다.
@Bean
public Job dataTerminationJob(Step terminateStep) {
    return new JobBuilder("dataTerminationJob", jobRepository)
            .start(terminateStep)
            .build();
}

@Bean
public Step terminateStep(ItemReader<String> itemReader, ItemWriter<String> itemWriter) {
    return new StepBuilder("terminateStep", jobRepository)
            .<String, String>chunk(10, transactionManager)
            .reader(itemReader)
            .writer(itemWriter)
            .build();
}

@Bean
public ItemReader<String> itemReader() {
    // return ItemReader 구현체
}

@Bean
public ItemWriter<String> itemWriter() {
    // return ItemWriter 구현체
}

즉, 비즈니스 로직 자체에만 집중하면 되고,
실행/상태관리/재시작 같은 복잡한 인프라는 Batch가 전담한다.


Spring Boot 기반 구성만 남긴 이유

Spring Batch는 순수 Spring으로도 구성할 수 있지만
실제로는 Spring Boot 기반으로 사용하는 경우가 99%다.

이유:

  • JobRepository, TransactionManager, DataSource 자동 구성
  • JobLauncherApplicationRunner 자동 실행
  • H2 같은 기본 메타데이터 DB 자동 초기화

결론: Spring Boot면 바로 로직부터 작성할 수 있다.

그렇기 때문에 원본 글의 “순수 Spring 설정” 부분은 전부 제거하고
여기서는 Spring Boot 기준만 정리한다.


Spring Boot 기반 Spring Batch 기본 구조

아래는 Boot 환경에서 가장 기본적인 Batch 설정 흐름이다.

1) Job/Step 구성

@Configuration
public class SystemTerminationConfig {

    private final JobRepository jobRepository;
    private final PlatformTransactionManager transactionManager;

    private AtomicInteger processesKilled = new AtomicInteger(0);
    private final int TERMINATION_TARGET = 5;

    public SystemTerminationConfig(JobRepository jobRepository,
                                   PlatformTransactionManager transactionManager) {
        this.jobRepository = jobRepository;
        this.transactionManager = transactionManager;
    }

    @Bean
    public Job systemTerminationSimulationJob() {
        return new JobBuilder("systemTerminationSimulationJob", jobRepository)
                .start(enterWorldStep())
                .next(meetNPCStep())
                .next(defeatProcessStep())
                .next(completeQuestStep())
                .build();
    }

Spring Boot는 JobRepository와 TransactionManager를 자동 설정해주기 때문에
우리는 생성자 주입만 하면 된다.


2) Step 정의

Step 1

@Bean
public Step enterWorldStep() {
    return new StepBuilder("enterWorldStep", jobRepository)
            .tasklet((contribution, chunkContext) -> {
                System.out.println("System Termination 시뮬레이션 세계에 접속했습니다!");
                return RepeatStatus.FINISHED;
            }, transactionManager)
            .build();
}

Step 2

@Bean
public Step meetNPCStep() {
    return new StepBuilder("meetNPCStep", jobRepository)
            .tasklet((contribution, chunkContext) -> {
                System.out.println("시스템 관리자 NPC를 만났습니다.");
                System.out.println("첫 번째 미션: 좀비 프로세스 " + TERMINATION_TARGET + "개 처형하기");
                return RepeatStatus.FINISHED;
            }, transactionManager)
            .build();
}

Step 3 (반복 Step)

@Bean
public Step defeatProcessStep() {
    return new StepBuilder("defeatProcessStep", jobRepository)
            .tasklet((contribution, chunkContext) -> {
                int terminated = processesKilled.incrementAndGet();
                System.out.println("좀비 프로세스 처형 완료! (" + terminated + "/" + TERMINATION_TARGET + ")");

                return terminated < TERMINATION_TARGET
                        ? RepeatStatus.CONTINUABLE
                        : RepeatStatus.FINISHED;
            }, transactionManager)
            .build();
}
 
Step 4
@Bean
public Step completeQuestStep() {
    return new StepBuilder("completeQuestStep", jobRepository)
            .tasklet((contribution, chunkContext) -> {
                System.out.println("미션 완료!");
                return RepeatStatus.FINISHED;
            }, transactionManager)
            .build();
}

Spring Boot에서 배치 실행

실행 방법

./gradlew bootRun --args='--spring.batch.job.name=systemTerminationSimulationJob'

JobLauncherApplicationRunner가 spring.batch.job.name프로퍼티에 지정된 Job을 찾아 실행해준다.


실행 흐름 정리

  1. JobRepository와 필수 인프라가 Boot 자동 설정으로 구성됨
  2. Job/Step만 등록하면 됨
  3. bootRun 시 JobLauncherApplicationRunner가 Job 실행
  4. Step 순서대로 실행
  5. 상태/메타데이터는 DB(H2 등)에 자동 저장

핵심 정리

  • Spring Batch는 Job 단위의 배치 실행 플랫폼
  • Job은 Step들의 묶음
  • Step은 tasklet 또는 chunk 기반
  • Boot 환경에서는 인프라 전부 자동 구성
  • 개발자는 로직 + Reader/Writer 설정만 담당
  • 실행은 --spring.batch.job.name로 제어
  • 재시작/상태관리/에러복구는 Batch가 자동 처리
반응형
반응형

배치 처리란 무엇인가?

대부분의 애플리케이션 로직은 웹 요청을 받고 즉시 응답하는 형태(Online Transaction Processing)로 구성되어 있다.
하지만 서비스가 일정 규모를 넘어서면 대량의 데이터를 한 번에 처리해야 하는 작업이 필연적으로 등장한다.

예를 들어:

  • 매일 0시에 실행되는 일일 정산
  • 월간 리포트 생성
  • 로그 분석 및 통계 집계
  • 데이터 마이그레이션
  • DB 백업 및 정제 작업

이처럼 지정된 시점에 대량의 데이터를 자동으로 처리하는 방식이 바로 배치 처리(batch processing)다.


왜 배치 처리가 필요한가?

웹 애플리케이션(REST API) 방식만으로는 대응하기 어려운 경우가 있다.
몇 가지 대표적인 문제를 보면 배치의 필요성이 명확해진다.

1) 대량 데이터 처리의 비효율성

웹 요청은 일반적으로 한 번에 소량의 데이터를 처리한다.
하지만 수백만 건 이상의 데이터를 연속적이고 안정적으로 처리하려면 별도의 처리 구조가 필요하다.

2) 정기적/반복적 작업 자동화

매일 반복되는 데이터 집계나 백업을 사람이 직접 실행할 수는 없다.
배치가 이런 작업을 스케줄링 기반으로 자동화한다.

3) 시스템 부하 관리

실시간 API에서 대량 데이터를 처리하면 서버 자원이 급격히 소모될 수 있다.
배치는 보통 **비사용 시간대(야간)**에 실행되어 서비스 운영과 분리된다.

4) 높은 신뢰성과 복구 가능성

장시간 실행되는 작업은 도중에 실패할 가능성이 높다.
배치는 체크포인트, 재시작, 재시도 등 복구 기능이 내장되어 있어 안정적이다.


웹 애플리케이션과 배치의 차이

구분웹 애플리케이션배치 애플리케이션

 

구분 웹 애플리케이션 배치 애플리케이션
실행 방식 요청 시 즉시 실행 스케줄 기반 자동 실행
처리량 소량 데이터 대량 데이터
응답 시간 짧아야 함 길어도 됨
에러 처리 바로 반환 재시도·체크포인트·재시작
리소스 항상 켜져 있음 작업 시에만 동작
목적 사용자 중심 기능 제공 시스템 데이터 처리 및 관리

웹과 배치는 서로 대체 관계가 아니라 서로를 보완하는 구조다.
서비스 사용자에게 필요한 기능은 웹에서 제공하고,
서비스 운영을 위해 필요한 대량 처리 작업은 배치가 담당한다.


스프링 배치(Spring Batch)를 사용하는 이유

스프링 배치는 배치 작업을 표준화하고 안정적으로 실행하기 위한 프레임워크이다.
일반적으로 배치 처리를 직접 구현하면 다음 요소들을 모두 직접 만들어야 한다:

  • Reader / Writer / Processor 구성
  • 스케줄링
  • 재시도 및 예외 처리
  • 트랜잭션 관리
  • 상태 관리 및 체크포인트
  • 실패 시 재시작 로직
  • 모니터링

스프링 배치는 이러한 기능을 이미 잘 정립된 설계 패턴과 구조로 제공한다.

핵심 구성 요소

  • Job: 배치 작업 전체 단위
  • Step: Job 내부의 실행 단위
  • ItemReader: 데이터 읽기
  • ItemProcessor: 처리/변환
  • ItemWriter: 저장
  • JobRepository: 실행 정보 저장
  • JobLauncher: Job 실행

덕분에 개발자는 비즈니스 로직에만 집중할 수 있다.


스프링 배치의 주요 특징

1) 대용량 처리에 최적화된 구조

Chunk 단위로 데이터를 잘라서 처리함으로써
메모리 사용량을 제어하고 일정한 처리 속도를 유지한다.

2) 강력한 트랜잭션 및 복구 기능

  • 체크포인트
  • Step 재시작
  • 실패 이력 관리
  • 재시도/Skip 정책

대량 처리 중간에 장애가 나도, 처음부터 다시 하지 않고 중단된 지점부터 재개할 수 있다.

3) 다양한 데이터 소스 지원

CSV, XML, JSON, JDBC, JPA, MongoDB, Redis 등
거의 모든 데이터 저장소와 연동할 수 있다.

4) 확장성

  • 멀티스레드 Step
  • 병렬 Step
  • 여러 서버에서 배치처리를 동시에 분산 처리(Partitioning)

 

정리

배치 처리는 서비스가 커질수록 반드시 필요해지는 영역이다.
그리고 스프링 배치는 이 배치 작업을 안정적이고 일관된 방식으로 구현할 수 있도록 도와준다.

핵심은 다음과 같다:

  • 배치는 대량 데이터 자동 처리를 위한 구조
  • 웹과 배치는 목적이 다르며 서로를 보완한다
  • 스프링 배치는 배치 처리의 모든 패턴을 표준화된 방식으로 제공한다
  • 재시작/실패 복구/트랜잭션 등 복잡한 로직을 프레임워크가 책임진다
  • 개발자는 비즈니스 로직에만 집중할 수 있다

배치를 처음 접하더라도, 위 개념들을 이해하면
스프링 배치로 안정적인 데이터 처리 시스템을 만들 수 있다.

반응형
반응형

Temporal에서 클라이언트 개발자는 워크플로우(Workflow)액티비티(Activity) 두 가지만 신경 쓰면 된다.

  • 워크플로우 (Workflow): 작업의 순서를 정의하는 “관리자”
  • 액티비티 (Activity): 실제 비즈니스 로직을 실행하는 “노동자”

 

워크플로우는 일반 함수가 아니다 — “죽지 않는 프로그램”

보통의 함수는 메모리에서 실행되고 프로그램이 종료되면 사라진다.
하지만 Temporal의 워크플로우는 절대 죽지 않는다.

  • 네트워크가 끊겨도
  • 서버가 재시작되어도
  • 시간이 며칠, 몇 달이 지나도

Temporal 서버가 중단된 시점부터 다시 이어서 실행시킨다.

 

어떻게 이런 게 가능할까? — 결정론적 실행(Deterministic Execution)

Temporal의 핵심은 바로 결정론적 실행 보장이다.

동일한 입력(Input)에 대해 항상 동일한 결과(Output)을 보장한다.
즉, “멱등성(Idempotency)”이 내장되어 있다.

예를 들어 주문 처리 워크플로우를 생각해보자:

public void processOrderWorkflow() {
  reserveInventory();
  processPayment();
  sendReceipt();
}
  • 이 워크플로우가 네트워크 오류로 중단되더라도
  • Temporal은 마지막 성공 지점(세이브 포인트)을 기준으로
    자동으로 재실행하면서 동일한 결과를 복원한다.

즉, “다시 실행하더라도 결과는 언제나 같다.”
이게 Temporal 워크플로우의 결정론적 실행 모델이다.

 

시간 제약이 없다 — 장기 실행 가능한 워크플로우

워크플로우는 몇 초, 몇 분짜리 함수가 아니다.
며칠, 몇 주, 심지어 몇 달 동안도 실행 가능하다.

Temporal은 내부적으로 각 단계별 상태를 DB에 저장해두기 때문에,
스프링 배치(Spring Batch)처럼 세이브 포인트(Save Point) 에서 재개할 수 있다.

예를 들어 “3일 후 결제 확인” 같은 장기 지연 로직도
워크플로우 안에서 자연스럽게 작성할 수 있다.

Workflow.sleep(Duration.ofDays(3));
checkPaymentStatus();

 

버전 호환성 — 실행 중인 워크플로우는 그대로 유지된다

Temporal은 새로운 워크플로우 버전을 배포하더라도
이미 실행 중인 워크플로우는 중단되지 않고 기존 버전대로 동작한다.

이 덕분에:

  • 프로세스 개선이나 코드 리팩토링이 쉬움
  • 배포 중에도 안정성 유지
  • 긴 워크플로우 실행에도 버전 충돌 없음

즉, “시간에 독립적인 시스템” 이다.

 

단점 — 예측 가능성과 외부 의존성의 제약

Temporal 워크플로우는 결정론적이어야 하기 때문에
다음과 같은 비결정적 코드 사용이 금지된다:

비결정적 동작이유
System.currentTimeMillis() 실행 시점마다 다름
UUID.randomUUID() 호출할 때마다 달라짐
외부 API 호출 네트워크 상태, 응답 값이 매번 달라짐
비즈니스 로직 계산 외부 상태에 의존 가능성 있음

이런 코드는 결과가 실행마다 달라질 수 있기 때문에
워크플로우 내부에선 사용 불가하다.

 

그래서 등장한 개념 — 액티비티(Activity)

이 문제를 해결하기 위해 Temporal은
워크플로우와 별도로 액티비티(Activity) 라는 개념을 제공한다.

역할설명
워크플로우 작업의 순서와 상태를 관리하는 컨트롤러
액티비티 실제 외부 연산(DB, API, 파일 등)을 수행하는 작업자

즉,

워크플로우는 “무엇을 할지” 정의하고
액티비티는 “어떻게 할지” 수행한다.

워크플로우는 직접 DB나 외부 API를 호출하지 않고,
Temporal 서버를 통해 액티비티를 호출한다.

이때 Temporal이 자동으로 다음을 처리한다:

  • 액티비티 실패 시 재시도
  • 타임아웃, 백오프, 장애 복구
  • 로그 및 히스토리 저장

결과적으로 워크플로우는 순수 함수처럼, 액티비티는 비즈니스 로직처럼 동작한다.

 

워크플로우와 액티비티 호출 흐름

[Workflow 코드]
    ↓
[Temporal Server] ← 상태 저장, 재시도, 장애 복구 관리
    ↓
[Activity Worker] ← 실제 DB/API 호출 수행
 
 

워크플로우는 액티비티를 직접 실행하지 않는다.
Temporal 서버가 중간에서 조율하며, 재시도 / 대기 / 복구 로직을 대신 관리한다.

반응형
반응형

분산 시스템, 특히 MSA(마이크로서비스 아키텍처)나 EDA(Event-Driven Architecture) 기반 시스템을 운영하다 보면
비즈니스 로직의 상태 관리, 에러 처리, 재시도, 보상 트랜잭션 같은 복잡한 문제가 반드시 등장한다.

 

이런 복잡성을 단순화하기 위해 등장한 것이 바로 Temporal이다.

Temporal이란 무엇인가?

Temporal은 “복잡한 분산 시스템에서 안정적이고 확장 가능한 애플리케이션을 만들기 위한 오픈소스 플랫폼”이다.
핵심은 단순하다.

개발자는 비즈니스 로직에만 집중하고, 나머지 복잡한 분산 처리(재시도, 상태관리, 장애복구)는 Temporal이 책임진다.

 

왜 필요한가? — 분산 시스템에서의 고질적인 문제들

EDA 아키텍처의 장점은 명확하다.
서비스 간 결합도가 낮고, 각 서비스가 자율적으로 확장 가능하며, 병렬처리가 가능하다.

하지만 현실적인 문제도 많다:

에러 핸들링 메시지 중간 실패 시 재시도 로직을 직접 구현해야 함
상태 관리 워크플로우가 어디까지 진행됐는지 추적하기 어려움
보상 트랜잭션(Saga) 실패 시 이전 단계 롤백 로직을 직접 관리해야 함
데이터 일관성 분산된 서비스 간 데이터 동기화 어려움
장애 복구 중간 장애 시 작업 재개, 순서 보장 어려움

 

기존 방식 vs Temporal 방식

✈️ 예시: 항공사 예약 시스템

기존 방식

 
예약 요청 → 결제 → 마일리지 적립 → SMS 전송

각 단계가 다른 서비스로 구현되어 있다면,

  • 결제가 실패했을 때 예약을 취소해야 하고
  • 마일리지 적립이 실패하면 재시도해야 하며
  • SMS 발송 실패 시 보상 로직을 추가해야 한다.

즉, 에러 핸들링 / 상태 관리 / 재시도 / 롤백을 모두 직접 작성해야 한다.

Temporal 방식

예약Workflow() {
  reserveFlight();
  processPayment();
  addMileage();
  sendSms();
}​

각 단계를 함수(Workflow Activity) 로 정의하면,
Temporal이 다음을 자동으로 처리한다:

  • 단계별 상태 저장 (히스토리 DB)
  • 재시도 및 복구
  • 중간 실패 시 워크플로우 재개
  • 장애 복구 및 로그 유지

개발자는 단순히 “무엇을 수행할지”만 정의하면 된다.

 

내부 아키텍처 — Temporal이 어떻게 동작하나?

Temporal은 서버(Temporal Server)클라이언트(Worker, SDK) 로 구성된다.

 

Frontend Service 모든 요청의 진입점. 클라이언트 요청을 내부 서비스로 분배
History Service 워크플로우의 상태 및 이벤트 로그를 모두 저장
Matching Service 대기 중인 작업과 워커를 매칭, 큐 관리 및 분배 수행
Worker Service (System Worker) 시스템 내부 관리 작업 수행 (아카이빙, 데이터 정리 등)
Persistent Layer (DB) 워크플로우 상태, 실행 히스토리, 이벤트 로그를 저장

 

클라이언트 구성

템퍼럴 플랫폼과 상호작용하고, 워커플로우 시작, 조회, 쿼리실행 같은 작업을 하게해주는 구성

반응형
반응형

금융 서비스에서는 거래가 발생하는 즉시 이를 감지하고 처리해야 하는 요구가 매우 높습니다.

따라서 Debezium으로 데이터 파이프라인을 구축할 수 있습니다.

 

예를 들어,

  • 거래 발생 → 잔고 변경
  • 거래 패턴 → 이상 탐지 / 사기 탐지
  • 거래 내역 → 신용 점수 / 한도 관리

 

 

MSA 아키텍처와 CDC의 궁합

마이크로서비스 환경에서 Debezium은 데이터 동기화의 이상적인 패턴을 제공합니다.

서비스 간 결합도 각 서비스가 필요한 이벤트만 구독 → 느슨한 결합
확장성 신규 서비스 추가 시 기존 서비스 영향 없음
장애 격리 이벤트 단위 격리 → 부분 장애가 전체로 전파되지 않음
시스템 안정성 서비스 간 직접 DB 접근 제거 → 일관성 향상
성능 EDA 기반의 비동기/병렬 이벤트 처리로 고성능 보장

 

플랫폼 서버 개발자 입장에서 좋은 패턴: 캐시 무효화

금융 서비스에서 DB와 캐시 간 데이터 불일치는 치명적이다.

Debezium을 활용하면 이를 구조적으로 해결할 수 있다.

 

예시 흐름

  1. 계좌 DB의 balance가 변경됨
  2. Debezium이 변경 이벤트를 감지하여 Kafka에 전송
  3. “Cache Invalidator” 서비스가 해당 이벤트를 구독
  4. 관련 캐시(Key)에 대한 무효화 및 갱신 수행

이 패턴을 통해,

  • TTL 기반 캐시 지연 문제 해결
  • 수동 갱신 코드 제거
  • 일관성 높은 캐시 시스템 유지 가능

즉, Debezium은 “캐시 갱신 트리거 역할을 하는 CDC 엔진”이 된다.

실시간 분석과 데이터 웨어하우스 동기화

배치 구조의 경우 지연시간이 발생하다보니 금융데이터에 대한 리스크 분석이 어렵다.

하지만 CDC 기반으로 구조를 만들면, 실시간 동기화가 가능하기 때문에 변경될때마다 데이터를 분석해서 실시간 대시보드에 띄우는등 작업이 가능하다.

 

 

반응형
반응형

로그 기반 CDC vs 배치 처리 — 데이터 변경 감지 방식의 본질적 차이

대표적인 접근은 배치(Polling)로그 기반 CDC(Change Data Capture) 두 가지다.
이번 글에서는 두 방식의 구조, 장단점, 그리고 실무적 선택 기준을 살펴보자

 

 

배치 기반의 CDC

- 폴링 처리로 5분마다 데이터 조회 = 실시간에 적합하지 않음

로그기반의 CDC

- 모든 트랜잭션을 로깅 하기 때문에 바로바로 이벤트를 받고 처리할 수 있음

 

 

지연시간과 CPU 부하의 상관관계로그기반의 CDC

폴링 = 짧은 주기를 가지면 지연시간이 줄어들지만 CPU 사용량이 늘어난다.

반대로 CPU 사용량이 줄어들면 실시간성이 떨어진다.

로그기반 CDC

변경된 로그를 자동으로 받아, 이벤트 처럼 처리할 수 있고

변경이 없으면 CPU 사용이 거의 없기 때문에 지연시간도 낮고 DB영향이 최소화된다.

 

삭제처리에 유용함

배치처리 CDC의 경우 로우가 삭제되는경우 레코드가 삭제 > 5분뒤 배치처리에서 조회해도 데이터가 삭제되어 조회가 불가능

 

로그기반 DELETE 트랜잭션도 로깅되기 때문에 처리가능

반응형
반응형

복잡한 비즈니스 로직, Temporal 워크플로우로 다루기

현대 시스템은 단순히 함수를 호출하고 끝나는 구조가 아닙니다.
결제 → 승인 → 재고 차감 → 알림 발송처럼,
여러 개의 트랜잭션이 순차적 혹은 병렬적으로 동작하는 복잡한 프로세스가 많습니다.

이런 구조를 단순 코드나 큐 기반으로 관리하면 점점 복잡성이 증가 합니다.

기존 워크플로우 관리 방식의 한계

  • 상태 관리 복잡: 각 단계의 상태 (진행중, 실패, 완료)를 직접 추적해야한다
  • 장애 복구 어려움: 중간 단계 실패 시, 어디서 멈췄는지 파악하기 어려움
  • 확장성 부족: 새로운 로직이 추가되면 기존 코드 수정 필요
  • 네트워크 불안정에 취약: 외부 API 장애나 타임아웃 시 재처리 로직 직접 구현 필요

구현이 불가능 하진 않지만 비즈니스로직 구현이 어려워지거나 불편해질 수 있습니다.

 

Temporal 워크플로우

앞선 문제들에 공감하고, 더 안정적으로 비즈니스로직을 운영 및 구현하고싶다면 워크플로우 오케스트레이션을 도입해볼 수 있습니다.

Temporal 적용 시 내부적으로 일어나는 일

1. Client가 Workflow 시작 요청 → Temporal Cluster 저장
→ 모든 실행 상태(Event History)가 DB에 기록됨
2. Worker가 Task Queue에서 Activity 실행
→ 네트워크 장애나 예외 발생 시 자동 리트라이
3. Activity 성공 시 다음 단계로 진행
→ 실패한 단계만 재시도 가능 (partial rollback 지원)
4. 전체 완료 시 Workflow 성공으로 마킹

이작업은 여러작업을 순서대로 or 병렬 실행하면서 복잡한 비즈니스 프로세스를 자동화 하는것 을 의미함

 

반응형
반응형

앞으로 CDC 패턴에 대해서 공부해 볼것이다.

CDC(Change Data Capture)란 무엇인가?

**CDC(Change Data Capture)**는 말 그대로

“데이터베이스에서 일어나는 변경(Insert / Update / Delete) 을 실시간으로 감지하여, 이를 다른 시스템으로 전달하는 기술”

즉, 기존의 DB가 ‘변했다’는 사실을 캡처(Capture) 해서 이를 이벤트(Event) 로 만들어 보내주는 구조입니다.

왜 CDC가 필요한가?

오늘날 기업 시스템은 단일 DB에 머무르지 않습니다.
서비스가 분리되고, 분석·알림·검색·캐시 등 다양한 하위 시스템이 함께 움직입니다.

그런데 만약 주문 데이터 하나가 생성될 때마다,

  • 검색엔진(Elasticsearch)에 반영해야 하고,
  • 캐시(Redis)를 갱신해야 하고,
  • 분석용 데이터 웨어하우스(BigQuery 등)에 전달해야 한다면?
  • 이를 매번 배치(batch) 로 돌린다면 지연(latency)이 발생하고 DB 부하가 커지고 실시간성이 확보되지 않습니다.

즉 CDC 하나의 데이터로 파생되는 수많은 데이터를 서비스마다 독립적으로 유연하게 처리할 수 있다는 장점이 있습니다.

 

예시 흐름

  1. DB에서 주문이 생성됨 (INSERT)
  2. CDC 시스템이 트랜잭션 로그에서 해당 변경사항을 캡처
  3. 이를 이벤트(Event) 로 Kafka에 발행
  4. 다른 시스템(Elasticsearch, Redis, 알림 시스템 등)이 이 이벤트를 구독(consume) 하여 데이터 반영

반응형

+ Recent posts