ACID
ACID의 의미부터 확인하자.
ACID 자체의 의미
RDBMS 단일 트랜잭션을 기준으로
- Atomicity (원자성)
- Consistency (일관성)
- Isolation (격리성)
- Durability (지속성)
위 4가지 속성이 하나의 DB 커넥션 안에서 트랜잭션을 통해 보장되는 것을 말한다.
사실 데이터베이스 관점에서 트랜잭션을 정의하는 속성이므로 ACID 트랜잭션이라 말하는게 맞다.
MSA에서 ACID가 깨지는 이유
MSA의 본질은 서비스별로 독립된 데이터를 가지는 것이다. 이로인해 단일 DB 트랜잭션으로 여러 서비스의 데이터 정합성을 보장하는 것이 원칙적으로 불가능해진다.
ACID의 4가지 속성 모두가 서비스의 경계를 넘는 순간 보장할 수 없게 된다.
ACID 각 속성이 어떻게 깨지는가
Atomicity
모놀리식에서는 하나의 트랜잭션안에서 여러 테이블을 수정해도 DB가 All or Noting을 보장한다.
하지만 MSA에서 주문 생성 -> 재고 차감 -> 결제 처리 흐름이 각각 다른 서비스, 다른 DB에서 이뤄진다면 어떻게 될까?
주문은 생성 됐는데 결제 서비스가 실패했을 때 주문 서비스 DB에서 이미 커밋된 데이터는 어떻게 될까?
-> DB가 해주던 롤백을 이제 애플리케이션이 직접 해줘야 한다.
Consistency
단일 DB에서는 FK, Unique Constraint, Check Constraint 같은 제약 조건을 통해 데이터 정합성을 DB 레벨에서 강제해준다.
하지만 MSA에서 주문 서비스의 주문 상태와 결제 서비스의 결제 상태가 항상 일치해야한다는 제약 조건은 어디서 정의해아할까?
-> DB 제약 조건으로 표현할 수 없는, 서비스 간 비지니스 규칙이 된다.
Isolation
단일 DB에서는 격리 수준인 READ_UNCOMMITED, READ_COMMITED, REPEATABLE_READ 등을 통해 동시성 문제를 관리했다.
하지만 MSA에서 주문 서비스가 아직 처리중인 데이터를 결제 서비스가 읽어 간다면 어떻게 될까?
-> 서비스 간 이런 격리 매커니즘은 존재하지 않는다.
Durability
각 서비스의 DB에서는 Durability를 여전히 보장해준다.
하지만 MSA에서 각 서비스의 DB에는 커밋이 완료되어 영속화 되었지만, 주문 완료 등 이벤트가 발송되지 않을 수 있다.
주문 완료 이벤트를 수신하여 배송 서비스 등이 이뤄져야하는데 발송이 이뤄지지 않는다면 하나의 동작 관점에서는 불완전한 영속성인 것이다. (데이터는 저장되었지만 이벤트는 발송되지 않았음)
-> 일부 영속화는 되지만 전체가 된건 아니다.
ACID의 재해석: 각 속성별로 MSA는 어떻게 풀어내는가
Atomicity -> Saga 패턴
단일 DB에서는 DB 엔진이 이를 보장해줬다. MSA에서는 이를 애플리케이션 레벨로 올려야한다.
SAGA는 하나의 분산 트랜잭션을 각 서비스의 트랜잭션 체인으로 분해한다. 각 서비스의 트랜잭션은 여전히 ACID를 준수하며 동작하고, 체인 중간에 실패하면 이미 완료된 트랜잭션들을 보상 트랜잭션으로 되돌린다.
[주문 흐름]
[정상 흐름] 주문 생성(주문 서비스) → 재고 차감(재고 서비스) → 결제 처리(결제 서비스)
[결제 실패 시 보상 흐름] 결제 실패 → 재고 복원(보상) → 주문 취소(보상)
Saga에서 중요한 것은 DB 롤백과는 본질적으로 다르다는 것이다. DB 롤백은 아무일도 없었다는 것처럼 트랜잭션이 실패하면 데이터를 되돌리지만, 보상 트랜잭션은 되돌리는 행위 자체가 새로운 트랜잭션이 된다.
위 보상흐름을 보면, 주문 취소라는 이벤트가 기록으로 남으며, 재고 복원도 비지니스 로직으로 기록에 남게 된다. 따라서 의미론적 되돌림(Undo)이며 물리적 롤백은 아니다.
SAGA 구현 방식
- 안무 사가 (Choreography Saga)
- 오케스트레이션 사가 (Orchestration Saga)
안무 사가 (Choreography Saga)
안무 사가는 각 서비스가 이벤트를 발행하고 다음 서비스가 이 이벤트를 구독해서 자율적으로 반응하는 구조이다. 서비스 간 직접적인 의존이 없으므로 느슨한 결합을 유지할 수 있다는 장점이 있다.
단, 서비스가 많아지면 이벤트 흐름 추적이 어렵고 전체 플로우를 한눈에 파악하기 어렵다는 단점이 있다.
오케스트레이션 사가 (Orchestration Saga)
중앙의 Saga Orchestrator (지휘자)가 전체 흐름을 제어하는 구조이다.
흐름이 명시적이므로 디버깅과 모니터링이 용이하다는 장점이 있다.
단, Orchestrator가 단일 실패 지점(SPOF)가 될 수 있고 서비스 간 결합도가 상대적으로 높아지는 단점이 있다.
Orchestration Saga는 Temporal과 같은 워크플로우 엔진을 활용하여 구현할 수 있으며, 클러스터 구축을 통해 SPOF 와 같은 문제를 상당 부분 해소할 수 있다.
Consistency → Eventual Consistency
기존 Consistency는 트랜잭션이 끝나는 순간 모든 데이터가 일관된 상태를 의미했다. MSA에서는 이를 시간축으로 완화한다.
Eventual Consistency는 "지금 이 순간 모든 서비스의 데이터가 일치하지 않을 수 있지만, 충분한 시간이 지나면 결국 최종적으로 일치하게 된다"는 정의다.
즉, MSA 환경에서는 비지니스 관점에서 허용 가능한 일시적 불일치를 명시적으로 설계하는 것이다. 상품이 매진 되어도 즉시 미노출되지 않고, 결제가 완료된 직후에 주문 상태가 결제 대기로 일시적으로 보이는 등이다.
이런 일시적인 현상은 대부분 비지니스적으로 문제가 되지 않으며 이러한 판단이 설계의 일부가 된다.
메시지 브로커(Kafka, RabbitMQ) 등은 이 모델을 뒷받침하는 핵심 인프라이다.
Isolation → 명시적 동시성 제어
이제 단일 DB의 격리수준이 없는 상황이며, 서비스 간 동시성 문제는 애플리케이션이 직접 전략을 결정하고 적용해야 한다.
전략 1. Semantic Lock (의미론적 락)
Saga가 진행 중인 리소스에 상태 플래그를 둬서("PENDING", "PROCESSING" 등) 다른 트랜잭션이 해당 리소스를 마음대로 수정하지 못하게 막는 방법이다.
마치 DB 레벨의 row lock을 비즈니스 상태값으로 표현한것과 같다.
전략 2. Commutative Update (교환적 업데이트)
연산 순서가 바뀌어도 결과가 같도록 설계하는 방법이다.
예를 들어 재고를 9로 설정하는 요청(10에서)이 아니라, 재고를 1 줄이는 요청(델타값)으로 표현하는 것이다.
이를 통해 메시지 순서가 바뀌어도 (동시성 이슈) 최종 결과에 영향을 주지 않도록 한다.
전략 3. Version-based Optimistic Control (버전 기반 낙관적 제어)
각 리소스에 버전 번호를 부여하고, 업데이트 시 자신이 읽었던 버전과 현재 버전이 일치하는지 확인하는 방법이다. JPA에서 낙관적락에서 사용하는 @Version과 동일한 개념이며, 서비스 간 통신에 적용하는 것이다.
Durability → 변하지 않는 영역 + Outbox Pattern
Durability는 각 서비스의 트랜잭션에서 보장한다. 하지만 위에서 언급한 것 처럼 로컬 DB에는 커밋이 되었지만, 이벤트 발행은 실패한 경우의 문제가 있다.
이러한 문제는 일부 서비스의 데이터는 영속화 되었어도 다른 서비스는 이 데이터(이벤트)를 전혀 모르게 된다.
이를 해결하는 것이 Transactional Outbox Pattern이다. 비지니스의 데이터와 이벤트의 발행을 같은 트랜잭션으로 묶어서 DB에 저장(영속화)한다. 이후 별도의 프로세스 (Message Relay)가 Outbox 테이블에서 이벤트를 읽고 메시지 브로커에 발행하는 구조이다.
이를 통해 데이터 저장과 이벤트 발행의 원자성을 트랜잭션 레벨에서 보장할 수 있게 된다.
[참고] Outbox 테이블을 읽는 방법은 대표적으로 2가지가 존재한다.
- Polling Publisher(주기적 폴링)
- CDC(Change Data Capture, 대표 도구: Debezium)
- 리디 - Transactional Outbox 패턴으로 메시지 발행 보장
정리
MSA에서 ACID는 사라지는 게 아니라, 보장 주체와 보장 방식이 바뀌는 것이다.
| 속성 | 모놀리식 | MSA |
| Atomicity | DB 엔진의 트랜잭션 | Saga + 보상 트랜잭션 |
| Consistency | DB 제약조건 | Eventual Consistency + 도메인 이벤트 |
| Isolation | DB 격리 수준 | Semantic Lock, 낙관적 제어 등 애플리케이션 전략 |
| Durability | DB 엔진 | DB 엔진 + Outbox Pattern으로 이벤트 전파 보장 |
결국 MSA에서의 ACID는 DB가 해주던 것을 애플리케이션 아키텍처가 떠안는 것이고, 그 복잡성을 감수하는 대가로 서비스 독립 배포, 독립 확장, 기술 이기종성 같은 MSA의 이점을 얻는 것이다.
'기술 학습' 카테고리의 다른 글
| MSA에서 CORS 문제를 해결하는 4가지 전략 (0) | 2026.03.27 |
|---|---|
| TCP/IP 체크섬(Checksum) 내부 동작 원리 (0) | 2026.03.23 |
| Java 기반 동기/비동기, 블로킹/논블로킹 정리 (0) | 2026.03.23 |
| ANSI Isolation Level vs MySQL Isolation Level: 같은 이름, 다른 보장 (0) | 2026.03.15 |
| 객체지향 설계 원칙 - SOLID (0) | 2026.03.08 |