DS log

트랜잭션 아웃박스 패턴을 이용하여 데이터 정합성 확보하기 (회원탈퇴) 본문

트러블 슈팅

트랜잭션 아웃박스 패턴을 이용하여 데이터 정합성 확보하기 (회원탈퇴)

DS log 2026. 4. 5. 15:21
728x90

 

회사에서 회원 탈퇴 기능에 대한 요구사항을 받았습니다.

데이터베이스 하나에 삭제해야하는 데이터가 다 있으면, 처리하기가 쉽겠지만 그렇지 않았습니다.  서비스와 연동하는 다른 서비스의 데이터 또한 제거를 해야만 했습니다. 

 

제거 요구사항은 각 데이터의 성격에 따라서 Hard Delete 처리를 하거나 Soft Delete 처리를 수행해야했습니다. 운이 좋게도?  담당자분들이 모종의 이유로 없었기 때문에 외부 서버의 API까지 개발할 수 있었습니다.

오늘은 이 요구사항을 처리하면서 고민했던 것들을 정리해보려고 합니다.

요구사항 정리

  1. 회원 탈퇴 시 해당 사용자와 관련된 데이터를 제거해야한다. (내부 서비스)
  2. 회원 탈퇴 시 해당 사용자와 관련된 데이터를 제거해야한다. (외부 서비스)
  3. 회원 탈퇴 시 해당 사용자와 관련된 이미지를 제거해야한다. (외부 서비스)
  4. 데이터의 성격에 따라 Hard Delete를 수행하거나, Soft Delete를 수행해야한다.

어떻게 처리하면 좋을까?

처리할 수 있는 방법은 다양합니다. 그 중에서 대표적인 케이스를 비교해보면 아래와 같습니다.

  1. 트랜잭션 안에서 외부 API를 직접 호출합니다.
  2. 트랜잭션 커밋 후 외부 API를 즉시 1회만 호출합니다.
  3. 트랜잭션 아웃박스 패턴을 이용해 비동기로 API를 호출하고, 실패시 배치로 재시도합니다.

 

트랜잭션 안에서 외부 API 직접 호출하기

 

'트랜잭션 안에서 외부 API 직접 호출하기' 는 구현이 가장 단순하고 흐름이 직관적인 장점(?) 이 있지만 아래와 같은 단점 때문에 보통 사용하지 않습니다.

  1. 외부 API 호출이 성공했으나 내부 서비스의 트랜잭션이 롤백이 됬다면?
  2. 내부 서비스의 트랜잭션이 커밋이 됬는데, 외부 API 호출이 유실 됬다면?
  3. 외부 API 응답 지연이 발생해 내부 서비스의 트랜잭션이 길어진다면? (장애 전파)

그러나, 외부 API에 대한 응답이 꼭 있어야만 가능한 경우는 사용할 수 있을 듯합니다. 

 

 

트랜잭션 커밋 후 외부 API를 즉시 1회만 호출하기

 

이 방법은 트랜잭션 밖에서 외부 API를 호출함으로써 '트랜잭션 안에서 외부 API를 직접 호출'의 몇몇 단점을 커버했지만, 외부 API 호출이 실패했을 경우에 대한 대책이 없는 한계가 있습니다.


트랜잭션 아웃박스 패턴

 

트랜잭션 아웃박스 패턴은 내부 트랜잭션 내에서 일어하는 상태 변경과 외부 API 호출 기록을 같은 트랜잭션 내에 묶는 방법입니다.

이 방법은 외부 API 호출 기록을 저장하기 때문에 실패 시 재시도 하고 최종 실패를 추적할 수 있어 운영 시 복구가 용이하다는 장점이 있습니다.

그러나, 이 방법은 최종 일관성을 맞추는 구조이고 즉각적인 일관성은 없기 때문에 이에 관해서 어떻게 할 지 충분히 생각을 해야 합니다.

그외에도 외부 API 호출 멱등성, 모니터링 등 고려해야할 것이 많습니다.

트랜잭션 아웃박스 패턴을 채택

 

회원 탈퇴 케이스의 경우 내부 서비스의 데이터만 즉각적으로 처리하고, 외부 서비스의 데이터는 최종적으로 데이터가 지워지면 되니 문제가 없다고 생각했습니다.

 

(개인정보처리방침에 대해서 기획자와 이야기를 충분히 나누면 좋습니다.)


핵심 흐름은 아래와 같습니다.

  1. 회원 탈퇴의 기준 상태를 내부 DB에서 먼저 확정합니다.
  2. 외부 호출 "의도"를 outbox 테이블에 같은 트랜잭션으로 저장합니다.
  3. 커밋 이후 비동기 1회 시도와 배치 재시도로 결국 처리합니다.
[하나의 로컬 트랜잭션]

회원 상태 변경 + outbox INSERT
-> commit

[커밋 이후]

즉시 비동기 1회 전송
실패 시 PENDING 유지
배치가 재시도
최종 SUCCESS 또는 최종 FAIL

 

 

이외에 고민했던 부분들..

 

  • 배치로 외부 API 호출을 재시도 해야하는 데 배치의 주기는 어떻게 할까?

    먼저, 수행 시간을 측정하고 추적하면서 배치 주기를 점차 수정해나가야합니다.
    Fixed Delay를 사용하지 말고, 지수 백오프를 사용해서 외부 서버의 복구 시간을 확보하는 전략이 좋을 듯 하지 않을까 생각하는데, 맞는 지는모르겠네요.

  • 외부 API 호출 재시도 실패 시 개발자가 관여해서 수동으로 복구해야하는 시점은 언제일까?

    재시도 횟수를 ?회 정도로 제한해야하지 않을까 싶습니다.
  • 멱등성 문제는 없을까?

    API를 개발할 때 멱등성 키를 Param으로 받아서 중복된 요청의 경우 실제로 처리가 안 되도록 해야합니다.
        
  • 모니터링은 어떻게 할까?

    Micreometer 등을 활용해 outbox failure count와 같은 커스텀 메트릭을 등록해서 Grafana 대시보드에서 이를 볼 수 있도록 만들어야합니다.

       등등등,,, 잡다한 것들이 많아서 이만 마무리하겠습니다. 감사합니다!

 

728x90