본문 바로가기
Dev

기술 면접에서 털리고 쓰는 동시성 제어와 멱등성

by Day-T 2026. 3. 27.

 

 

최근 해외 송금 시스템 관련 과제를 수행하며 데이터 정합성과 시스템 안정성에 대해 고민할 기회가 있었다.

 

통장에서 돈이 출금되어 환전이 이루어지고, 이 결과가 해외 파트너망으로 전송되기까지의 모든 과정을 안전하게 보장해야 했다. 특히 도메인 특성상 여러 네트워크를 거치며 네트워크 단절이나 타임아웃이 빈번하게 발생하는 환경이었기 때문에, 중복 출금을 막고 장애를 방지하기 위한 동시성 제어와 멱등성 설계가 핵심 과제였다.

 

이참에 고민했던 동시성 제어와 멱등성 관련 내용을 정리해 본다.


동시성 제어

다수의 스레드가 공유 자원을 읽고, 수정하고, 다시 쓰는 과정에서 OS 커널의 선점형 스케줄러가 개입하면 어떻게 될까? 연산이 무작위로 교차하며 데이터 정합성이 박살 나는 경쟁 상태가 발생하게 된다.

 

선점형 스케줄러는 프로세스가 CPU를 계속 사용하고 싶어 하더라도 다음과 같은 상황에서 개입해 주도권을 빼앗는다.

  • 할당 시간 만료: 프로세스에 부여된 '타임 슬라이스(Time Slice)'가 끝났을 때
  • 우선순위 변화: 더 높은 우선순위를 가진 프로세스가 등장했을 때
  • 인터럽트 발생: 입출력 완료 등 외부 신호가 들어왔을 때

이러한 OS 레벨의 변수들을 통제하여 데이터 정합성을 지키는 것이 동시성 제어의 핵심이다.

 

 

① Synchronized와 Spring AOP의 충돌

 

가장 흔히 겪는 문제는 애플리케이션의 락 범위와 데이터베이스 트랜잭션 범위의 불일치로 인해 발생한다.

 

Java의 synchronized는 메서드 실행이 끝나면 즉시 락을 해제한다. 하지만 Spring의 @Transactional은 AOP 프록시 방식으로 동작하기 때문에, 비즈니스 메서드가 끝난 뒤에 프록시가 commit을 호출한다.

💡 갱신 손실(Lost Update) 발생 시나리오

스레드 1이 락을 풀었지만 아직 DB에는 커밋되지 않은 '찰나의 시간'이 존재한다. 이때 스레드 2가 락을 획득하고 진입하면, 스레드 1이 과거 데이터를 읽게 되어 갱신 손실이 발생한다.

 

 

또한 락을 제어하는 과정에서 User-Kernel 모드 전환 비용이라는 큰 대가를 치러야 한다.

  • 컨텍스트 스위칭(Context Switching): 실행 중이던 스레드의 레지스터 상태를 메모리에 저장하고 커널 환경을 세팅하는 비용
  • 캐시 오염(Cache Pollution): CPU L1/L2 캐시에 있던 기존 데이터가 무효화되어 이후 연산 속도가 급감하는 현상

 

(이미지 참조: 5번 ~ 6번 과정)

  • 스레드 차단(Block) 시점 (6번 이전): 락을 획득하지 못한 스레드 2는 OS에 의해 강제로 재워지며(System Call), User → Kernel 전환이 일어나 막대한 CPU 사이클이 소모된다.
  • 스레드 깨우기(Wake up) 시점 (5번 이후): 스레드 1이 락을 해제하면 잠자던 스레드 2를 깨우기 위해 다시 Kernel → User 모드 전환이 발생한다.

 

 

② 비관적 락과 PostgreSQL MVCC

 

PostgreSQL은 별도의 인메모리 락 매니저 외에도 MVCC(Multi-Version Concurrency Control) 설계를 락킹에 적극 활용한다. 덕분에 메모리 부족으로 인한 락 에스컬레이션(Row 락이 Table 락으로 격상되는 현상)이 발생하지 않는다.

  • 동작 원리: 튜플(행) 헤더에는 xmin(생성 Tx ID)과 xmax(삭제/업데이트 Tx ID) 필드가 존재한다.
    • SELECT ... FOR UPDATE가 수행되면 해당 튜플의 xmax를 현재 트랜잭션 ID로 채워 배타적 점유를 표시한다.
  • 트레이드오프: 락 에스컬레이션이 없다는 강력한 장점이 있지만, 락 획득 자체가 쓰기 작업(Write I/O)을 유발하므로 WAL(Write Ahead Log) 부하가 미세하게 증가할 수 있다.

 

(이미지 참조: 10번 ~ 13번 Wait Queue 대기 상태)

비관적 락이 데이터 정합성을 지키는 핵심 디테일은 버전 재검사(Re-fetch)에 있다.

  1. Tx1이 커밋되면 Wait Queue에 있던 Tx2가 풀려난다.
  2. 이때 Tx2는 Tx1이 수정한 최신 데이터(Updated Data)를 다시 읽어온 뒤 연산을 시작하여 원자성을 보장한다.

 

 

🎯 실무 적용 팁: @Lock(LockModeType.PESSIMISTIC_WRITE)

 

비관적 락은 선점적이다.

 

즉, 내가 자원을 점유하는 동안 다른 누구도 접근하지 못하게 막는다. 그렇기에 이 '막음'이 '장애'로 이어지지 않게 하려면 다음과 같은 전략적 접근이 필요하다.

 

 

1. 무한 대기 방지: 타임아웃과 획득 전략

 

기본적으로 비관적 락은 다른 트랜잭션이 락을 해제할 때까지 무한정 대기한다. 하지만 동시 접속자가 많은 환경에서 무한 대기는 곧 커넥션 풀 고갈과 직결된다.

 

락을 얻기 위해 기다릴 최대 시간을 지정해야 하는데 JPA에서는 쿼리 힌트를 통해 이를 제어한다.

 

 

상황에 따라 아예 기다리지 않는 전략이 유리할 수 있다.

 

NOWAIT:

  • 다른 트랜잭션이 락을 쥐고 있다면 즉시 에러를 발생시킨다. "지금 안 되면 바로 실패"해야 하는 비즈니스에 적합하다.
  • 특정 계좌에 대한 설정 변경. (누군가 이미 수정 중이라면, "잠시 후 다시 시도해 주세요"라고 즉시 응답하는 게 무한 대기보다 낫다.)

 

 

SKIP LOCKED:

  • 이미 락이 걸린 행은 무시하고 지나간다. 여러 워커 스레드가 동시에 작업을 처리해야 할 때 유용하다. 이미 락이 걸린 행은 투명하게 무시하고, 락이 걸리지 않은 행들만 결과셋으로 가져온다.
  • 선착순 쿠폰 발급 시스템 혹은 메시지 발송 큐(Queue)
    • 워커 1: 1~10번 쿠폰 중 락 없는 것 5개 가져가서 발급 중.
    • 워커 2: 1~10번 쿼리 시 워커 1이 잡은 건 빼고 남은 5개만 쏙 가져감.

 

 

2. 네트워크 I/O 배제

 

가장 빈번하게 발생하는 장애 포인트다. @Transactional 안에서 외부 API(RestTemplate, FeignClient 등)를 호출하는 것은 위험하다.

⚠️ 경고: 외부 API 지연은 DB 전체의 마비로 이어진다. 비관적 락을 획득한 상태로 외부 API 응답을 기다리면, 그 시간 동안 DB 커넥션과 해당 데이터의 로우 락(Row Lock)이 반납되지 않는다. 외부 API가 10초간 지연되면, DB 커넥션 풀은 10초 동안 서서히 잠식당하며 결국 시스템 전체가 먹통이 된다.

 

해결책: 락이 필요한 DB 연산은 트랜잭션의 가장 마지막 단계로 몰거나, 외부 API 호출을 트랜잭션 외부로 분리하여 락을 점유하는 시간(Lock Holding Time)을 최소화해야 한다.

 

 

3. 데드락 방어를 위한 락 획득 순서 정렬

 

두 트랜잭션이 서로가 가진 자원을 기다리며 무한히 교차 대기하는 데드락은 애플리케이션 레벨에서 방어할 수 있다. 가장 확실한 방법은 자원 획득 순서의 일관성을 강제하는 것이다.

 

문제 상황:

  • 트랜잭션 A: 계좌 1 락 획득 -> 계좌 2 락 시도
  • 트랜잭션 B: 계좌 2 락 획득 -> 계좌 1 락 시도 (교차 대기 발생!)

 

항상 ID가 작은 계좌부터 락을 획득하도록 정렬한다. 순환 대기 조건을 파괴하는 것이 핵심이다.

 

 

4. 인덱스 활용

 

FOR UPDATE 쿼리는 내부적으로 인덱스를 타고 검색을 수행한다. 만약 WHERE 조건에 사용된 컬럼에 인덱스가 없다면 어떤 일이 벌어질까?

  • DB 엔진은 업데이트 대상을 찾기 위해 테이블 전체를 훑게 된다. 이 과정에서 검색된 모든 행에 락을 시도할 수 있으며, 최악의 경우 행 단위 락(Row Lock)이 아닌 테이블 전체가 잠기는 효과를 초래한다.
  • 비관적 락이 걸리는 모든 쿼리의 WHERE 조건 컬럼은 반드시 인덱스가 적절히 생성되어 있는지 확인해야 한다.

 

요약하자면, 비관적 락은 단순히 데이터의 일관성을 지켜주는 도구가 아니다.

  1. 타임아웃으로 커넥션 풀을 보호하고,
  2. 네트워크 I/O를 격리하여 점유 시간을 줄이며,
  3. 락 정렬을 통해 데드락을 방지하고,
  4. 인덱스로 락의 범위를 최소화해야 한다.

이 네 가지 방어막이 구축되었을 때 비로소 비관적 락은 안전한 무기가 된다.

 

 

③ 낙관적 락의 함정

낙관적 락은 물리적 DB 락을 배제하고 CPU의 CAS(Compare-And-Swap) 개념을 차용해 업데이트 시점에 버전 일치 여부만 판별(WHERE id = ? AND version = ?)하는 Lock-free 방식이다.

 

블로킹이 없어 빠를 것 같지만, 송금 시스템처럼 동시성 경합이 빗발치는 도메인에서는 독이 된다. 버전 충돌로 실패한 수많은 트랜잭션이 지수 백오프를 통해 미친 듯이 재시도를 폭격하게 되고, 결국 RDBMS의 CPU 스파이크를 유발하며 시스템을 마비시킨다.

 

 

④ 분산 락: 스핀 락(Lettuce) vs Pub/Sub(Redisson)

 

우리가 앞서 살펴본 synchronized나 비관적 락은 '단일 서버' 혹은 '단일 DB'라는 울타리 안에서는 강력하다. 하지만 서버가 수십 대인 분산 환경으로 넘어오면 이야기가 달라지게 되는데 JVM을 넘어 서버와 서버 사이의 '임계 구역'을 관리해야 하기 때문이다.

 

이때 Redis를 활용하게 되는데, Lettuce(스핀 락) 방식과 Redisson(Pub/Sub) 방식을 고려할 수 있다.

 

Lettuce (Spin Lock의 한계)

Lettuce로 분산 락을 구현하면 보통 SETNX(Set if Not eXists) 명령어를 루프 안에서 계속 실행한다. 이 방식의 문제는 락을 획득하지 못한 스레드가 계속 CPU를 점유하며 재시도 로직을 태우는 행위로 인해 CPU 오버헤드와 네트워크 I/O 병목을 일으킬 확률이 높다는 점이다.

 

Redisson

위 시퀀스 다이어그램을 보면 Lettuce 방식에 비해 Redisson은 다르게 동작하는데 핵심은 Redis의 Pub/Sub 메커니즘과 Semaphore의 결합에 있다.

  • 구독(Subscribe) 단계 (5~6번): 락 획득에 실패한 스레드(App Instance 2)는 Redis에게 "락 해제되면 lock_channel로 알려줘!"라고 구독 요청을 보낸다.
  • 대기(Waiting) 단계 (7번): 알림을 기다리는 동안 스레드는 무작위로 재시도하는 것이 아니라, OS 레벨에서 Semaphore를 이용해 대기 상태로 들어간다. 이때 CPU를 점유하지 않으므로 애플리케이션의 리소스 낭비가 거의 없다.
  • 알림(Publish) 및 재시도 (10~15번): 락 홀더(App Instance 1)가 작업을 마치고 락을 해제(DEL)하면, Redis가 구독자들에게 "락 풀렸다!"라고 메시지를 발행한다. 메시지를 받은 스레드만 깨어나 락 획득을 다시 시도한다.

 

 

Redisson은 tryLock(3, 5, TimeUnit.SECONDS) 부분처럼 락에 유효시간을 설정해 서버가 죽더라도 일정 시간 뒤에는 락이 자동으로 풀리게 설계되어 있다. 거기에 더불어 Redisson은 Watchdog이라는 백그라운드 스레드가 락의 만료 시간을 주기적으로 연장해 준다. 그렇기에 만약 비즈니스 로직이 설정한 시간보다 길어져도 로직이 안전하게 끝날 때까지만 락을 유지하고, 프로세스가 죽으면 연장을 멈춰 자연스럽게 락이 해제되도록 돕는 이점이 있다.

 

그렇기에 분산 락 방식은 단순히 '락을 건다'는 목적을 넘어 분산 환경의 불확실성을 제어할 수 있는 기법 중에 하나다.


멱등성

멱등성이란 f(f(x)) = f(x)라는 수학적 정의처럼, 연산을 여러 번 중복해서 수행해도 시스템의 최종 상태가 1회 수행한 것과 동일하게 유지되는 성질을 말한다.

 

해외 송금 시스템처럼 네트워크 단절과 재시도가 빈번한 환경에서는, 중복 요청에 대해 필수적으로 고려 해야되는데 다음과 같은 방식으로 해결할 수 있다.

 

2-Tier 이중 방어 아키텍처 (Redis + PostgreSQL)

 

송금 버튼을 "따닥" 하고 연타하거나, 클라이언트가 타임아웃 때문에 재시도를 보낼 때 가장 부하가 높은 곳은 DB다.

 

RDBMS의 UNIQUE 제약조건만 믿고 모든 요청을 DB까지 통과시키면, 무거운 B-Tree 인덱스 탐색과 트랜잭션 롤백 비용으로 인해 시스템 전체가 마비될 수 있다. 이를 방지하기 위해 메모리(Redis)와 디스크(PostgreSQL)를 결합한 이중 방어막을 세운다.

 

 

Tier 1: Redis 기반의 Application-level Fast-fail

 

먼저, 애플리케이션 진입점에서는 Redis를 활용해 불필요한 데이터베이스 트랜잭션의 생성을 사전에 차단한다.

 

Redis의 SETNX(SET if Not eXists) 명령어를 통해 특정 멱등키의 존재 여부를 O(1) 시간 복잡도로 원자적으로 확인하며, 이미 처리 중인 요청으로 판명될 경우 비즈니스 로직을 실행하지 않고 즉시 응답을 반환한다. 이 과정은 데이터베이스 커넥션 풀의 점유를 방지하고, B-Tree 인덱스 탐색에 필요한 CPU 자원을 보존하여 시스템의 가용성을 높이는 역할을 한다.

 

또한, 멱등키에 3초 내외의 짧은 만료 시간(TTL)을 설정함으로써 메모리 누수를 방지하는 동시에 중복 요청을 제어한다.

 

 

Tier 2: PostgreSQL 기반의 Database-level Integrity

 

이어지는 데이터베이스 단계는 시스템의 최종적인 정합성을 확정하며, Redis의 휘발성이나 분산 환경에서 발생할 수 있는 일시적인 정합성 이슈를 보완하는 최후의 보루가 된다.

 

핵심 거래 테이블의 request_id 컬럼에 설정된 UNIQUE INDEX는 데이터 삽입 시점에 물리적인 키 충돌을 검출하여 데이터 오염을 차단한다. 만약 Redis의 TTL이 만료된 후 지연 유입된 중복 요청이 삽입을 시도하더라도, 데이터베이스 엔진 수준에서 DataIntegrityViolationException을 발생시켜 무결성을 유지한다.

 

최종적으로 애플리케이션은 이 예외를 캡처한 뒤, 기존에 성공적으로 처리되었던 응답 값을 다시 조회(SELECT)하여 클라이언트에게 반환한다. 이로써 서버 내부의 상태 변화 없이 클라이언트 관점에서의 완전한 멱등성을 완성하게 된다.

 

 

 

비동기 EDA 환경의 순서 역전

송금 시스템처럼 긴 호흡이 필요한 프로세스는 단순히 멱등성의 보장은 "요청을 안전하게 DB에 저장했다"고 해서 끝나지 않고 보통 Kafka와 같은 이벤트 큐를 통해 비동기(EDA)로 전달된다. 이때 Kafka와 같은 이벤트 큐를 사용하는 환경에서는 파티션 리밸런싱 등으로 인해 필연적으로 이벤트 순서가 꼬일 수 있다. (예: '송금 승인'보다 '송금 취소'가 먼저 도착)

 

이를 해결하기 위해 이벤트 Payload에 파트너망 발송 시점의 타임스탬프(논리적 시계)를 삽입한다. 컨슈머는 DB에 기록된 마지막 이벤트 시간과 인입된 이벤트의 시간을 대조하여, 과거의 이벤트(Stale Event)로 판명될 경우 도메인 로직을 우회(Bypass)하고 오프셋만 커밋하여 메시지를 안전하게 폐기한다.

 

요약하자면, 송금 시스템의 멱등성은 API 진입 시점의 중복(2-Tier 방어)을 막는 것에서 시작하여, 비동기적으로 흘러오는 상태 변경 이벤트의 순서(타임스탬프 방어)를 바로잡는 것으로 완성된다.


아래 질문/답변은 이번에 정리한 내용에 대해 면접처럼 꼬리 질문을 해봤다.

 

Self Dive

Q1. synchronized와 @Transactional을 함께 사용할 때 발생하는 동시성 누수에 대해 OS 스레드와 트랜잭션 생명주기 관점에서 설명해 주세요.

Spring @Transactional은 프록시 기반으로 동작합니다.

비즈니스 메서드 종료 시 synchronized 락은 즉시 풀리지만, 프록시가 물리적 COMMIT을 날리기까지 필연적인 시간차가 존재합니다. 이 틈에 다른 OS 스레드가 진입해 커밋되지 않은 과거 데이터를 읽게 되며 갱신 유실(Lost Update)이 발생합니다.

 

 

Q2. 금융 도메인에서 비관적 락을 표준으로 삼은 이유와 PostgreSQL의 물리적 구현 방식은?

롤백 비용이 극도로 높은 송금 코어 특성상 충돌을 원천 차단하는 비관적 락이 적합합니다.

PostgreSQL은 별도 메모리 락 테이블 대신, 데이터 페이지 내 튜플 헤더의 xmax(트랜잭션 ID) 필드를 직접 갱신하여 배타적 락을 구현하며, 타 트랜잭션은 Wait Queue에서 대기하게 됩니다.

 

 

Q3. 낙관적 락을 송금 코어 로직에 적용했을 때의 부작용은?

경합이 심할 때 낙관적 락은 다수의 OptimisticLockException을 발생시킵니다.

이를 복구하기 위한 애플리케이션의 잦은 재시도가 무의미한 Read/Update 쿼리 폭격을 낳고, 결과적으로 DB 커넥션 풀 고갈과 CPU 스파이크를 유발합니다.

 

 

Q4. 분산 락 구현 시 Lettuce 대신 Redisson을 선택한 이유는? (OS 스레드 관점)

Lettuce는 SETNX를 계속 호출하는 스핀 락 방식이라 Redis 이벤트 루프를 블로킹하고 네트워크 부하를 줍니다.

Redisson은 Pub/Sub 기반으로, 락 획득 실패 시 OS 스레드를 멈춰두었다가(Sleep) 락 해제 이벤트 수신 시 스레드를 깨워 재시도하므로 리소스를 극적으로 최적화합니다.

 

 

Q5. 교착 상태(Deadlock)를 막기 위해 '락 정렬'을 강제했다고 하셨는데, 구체적인 코드 구현은?

데드락 4대 조건 중 '순환 대기'를 깬 것입니다.

계좌 PK의 대소 관계를 판별해 항상 작은 ID부터 락을 획득하도록 강제합니다.

Long firstId = Math.min(fromId, toId);
Long secondId = Math.max(fromId, toId);

accountRepository.findByIdWithPessimisticLock(firstId); accountRepository.findByIdWithPessimisticLock(secondId);

 

 

Q6. JPA에서 비관적 락을 걸기 전 일반 findById로 먼저 조회하면 생기는 문제는?

JPA 1차 캐시로 인해 Repeatable Read가 보장되어 버립니다. 이후 findByIdWithPessimisticLock을 호출하더라도 JPA는 DB에서 가져온 최신 데이터를 버리고 1차 캐시의 과거 스냅샷을 반환합니다.
따라서 최초 로드 시점부터 비관적 락을 걸어야 합니다.

 

 

Q7. 타임아웃이 잦은 환경에서 트랜잭션 롤백만으로 부족하고 멱등성이 필수적인 이유는?

네트워크 타임아웃 시 클라이언트는 서버 내 트랜잭션 성공 여부를 알 수 없어 맹목적인 재시도를 합니다.
멱등성이 보장되어야만 서버가 이 중복 로직을 재실행하지 않고, 캐싱해 둔 기존의 '성공 응답'만 안전하게 반환할 수 있습니다.

 

 

Q8. RDBMS UNIQUE 제약조건만 쓰지 않고 Redis를 1-Tier로 둔 이유는?

'따닥' 트래픽이 디스크 기반의 B-Tree 인덱스를 찌르면 즉각적인 DB 리소스(CPU/Connection) 소모로 이어집니다. 앞단에 Redis를 두어 O(1) 메모리 연산으로 DB 도달 전에 요격을 해내는 것이 목적입니다.

 

 

Q9. Redis에 장애가 나거나 TTL 만료 후 중복 요청이 들어오면 어떻게 방어하나요?

Redis는 성능 최적화 수단이며 최종 무결성은 DB가 보장합니다.

핵심 원장 테이블의 transaction_request_id UNIQUE 인덱스가 물리적 키 충돌을 검출해 DataIntegrityViolationException을 발생시켜 완벽히 차단합니다.

 

 

Q10. EDA 환경에서 '송금 승인'보다 '송금 취소'가 먼저 도달하는 역전 현상 방어법은?

이벤트 페이로드에 논리적 시계(타임스탬프)를 포함합니다.

DB에 기록된 최신 이벤트 시간보다 유입된 이벤트의 시간이 더 과거(Stale Event)라면 비즈니스 로직을 Bypass하고 오프셋만 커밋하여 메시지를 버리는(Discard) 방식으로 결과적 일관성을 지킵니다.

출처

https://jhzlo.tistory.com/85

 

[Redis] 분산락을 깊게 다뤄보자. (RedLock, Redisson)

🧐 들어가기전Redis에 대한 개념을 되짚어보다가, 분산락을 다시 한 번 살펴보게 되었고 문득 다음과 같은 궁금증이 생기게 되었다. "분산락은 어떤 구조로 이루어져있지? 우리가 흔하게 쓰는 Red

jhzlo.tistory.com

https://helloworld.kurly.com/blog/distributed-redisson-lock/

 

풀필먼트 입고 서비스팀에서 분산락을 사용하는 방법 - Spring Redisson - 컬리 기술 블로그

어노테이션 기반으로 분산락을 사용하는 방법에 대해 소개합니다.

helloworld.kurly.com

https://astor-dev.com/blog/posts/620536739584282624/

 

결제 도메인에서의 멱등성 보장 | 블로그

결제 도메인과 같이 일관성이 중요하면서도 PG사에 통신이 들어가는 등 일련된 비즈니스 로직을 하나의 트랜잭션에 담기 어려운 경우 멱등성 처리가 굉장히 중요해집니다. 일관성과 안전성을

astor-dev.com

https://leapcell.io/blog/ko/myeokdeungseong-eul-yonghan-jungbok-jag-eob-bangji-API-geuchuk

 

멱등성을 이용한 중복 작업 방지 API 구축 | Leapcell

이 문서는 백엔드 개발에서 의도치 않은 중복 작업을 방지하는 데 중요한 멱등성 API의 설계 및 구현에 대해 자세히 설명합니다. API 안정성을 보장하기 위한 핵심 개념, 실용적인 전략 및 코드 예

leapcell.io

https://www.snap-tool.com/blog/subscription-idempotency

 

안전한 구독 결제 시스템 만들기: 중복 결제 방지

구독 서비스에서 발생하는 결제 오류와 중복 결제 문제를 해결하는 핵심 설계 전략을 소개합니다.

www.snap-tool.com

https://monday9pm.com/%EC%84%9C%EB%B9%84%EC%8A%A4%EC%97%90%EC%84%9C-%EB%A9%B1%EB%93%B1%EC%84%B1%EC%9D%84-%EC%9C%A0%EC%A7%80%ED%95%98%EA%B8%B0-%EC%9C%84%ED%95%B4-fa6bfffa3861

 

API의 멱등성(idempotence) 이야기

안녕하세요. 이번 글에서는 분산 서비스 환경에서의 멱등성에 대해 정리하였습니다. 정의만 알았던 멱등성을 실제 서비스에서 고민하고 찾아봤던 내용에 대해 이야기하고자 합니다.

monday9pm.com

https://medium.com/@rkdwldnjs878/kafka-%EB%A9%94%EC%8B%9C%EC%A7%80-%ED%95%9C%EB%B2%88%EB%A7%8C-%EC%B2%98%EB%A6%AC%ED%95%98%EA%B8%B0-52bcd20a0706

https://sabarada.tistory.com/175