결제 흐름 안의 물류 API 호출을 걷어내기 – 같은 트랜잭션 아웃박스 + 배치 릴레이 + Kafka 로 OMS 에 주문 넘기기

결제·취소 흐름 안에서 메시지를 바로 발행하고 물류 API 를 부르던 구조를, 주문과 주문 아웃박스를 한 MongoDB 트랜잭션에 저장하고 커머스의 배치 릴레이가 아웃박스를 폴링·CAS 선점해 Kafka 로 OMS 에 발행하는 구조로 바꾸는 설계다. 이미 운영 중인 조각과 앞으로 만들 조각을 나눠 적었다. 그리고 Kafka 를 고른 이유, exactly-once 대신 at-least-once + 멱등 소비를 고른 이유, CDC 를 쓰지 않은 이유, 장애 지점별 동작을 정리했다.

· 25분 읽기

5KB 응답을 만들려고 2.5MB 를 읽던 홈 API – 모니터링으로 원인을 좁히고, DataLoader·projection·2중 캐시로 32배

앱 첫 화면 API 가 1코어에서 초당 10건을 넘기면 무너졌다. Grafana 로 CPU 포화와 Mongo 전송량을 보고 Mongo 프로파일러로 요청 1건을 해부해, 병렬화 대신 DataLoader 배치·projection 으로 덜 읽고 2중 캐시로 안 읽게 바꾼 과정을 단계별 측정값과 함께 정리했다.

· 10분 읽기

00시, 새로 뜬 pod 만 무너졌다 – 프로파일러로 Spring Data 의 숨은 락을 찾고, 고친 뒤에도 무너진 이유까지

기획전 오픈 00시 트래픽을 재현하자 막 띄운 서버만 무너졌다. async-profiler 의 lock 모드로 Spring Data SimplePropertyPath 의 실패를 캐시하지 않는 락을 찾아 고쳤지만, 막 뜬 서버는 여전히 무너졌다. single-flight 캐시와 적응형 동시성 제한을 더해 버티게 된 과정을 정리했다.

· 15분 읽기

알림을 한 번만, 그러나 잃지 않게 – 고객등급제 알림톡 발송을 outbox / inbox 로 다시 짠 기록

배치가 비즈뿌리오에 직접 보내고 나서 내역을 남기던 고객등급제 알림을, 배치는 outbox 테이블에 적기만 하고 중계 잡이 RabbitMQ 로 넘기고 워커가 inbox(message_id 선점)로 한 번만 보내는 구조로 바꿨다. 구간마다 at-least-once 와 at-most-once 를 다르게 고른 이유와 장애 지점별 동작을 정리했다.

· 16분 읽기

PG 를 바꾸며 돈과 주문을 맞추는 법 – 아임포트(KCP)에서 토스페이먼츠 직접 연동으로

아임포트를 거쳐 KCP 로 결제하던 구조를 토스페이먼츠 직접 연동으로 바꾼 기록이다. 두 PG 의 결제건이 함께 살아 있던 전환 기간의 설계, 트랜잭션 없이 돈과 주문을 맞춘 장치, 승인 후 취소가 생기는 재고 구조, 무통장입금 만료가 석 달 반 동안 처리되지 않았던 사고를 정리했다.

· 14분 읽기

한 주문에 문이 세 개 – 결제 완료 API·PG 웹훅·취소를 주문번호 분산락 하나로 줄 세우기

토스페이먼츠 결제 흐름을 새로 만들면서 같은 주문에 결제 완료 API, PG 웹훅, 취소가 거의 같은 순간에 들어올 수 있다는 것을 보고 처음부터 주문번호 분산락을 설계했다. 락이 없으면 어떤 경합이 생기는지, 락 키가 주문번호여야 했던 이유, 대기·임대·소유자 id 의 세부, 락 뒤에 겹쳐 둔 장치, 다른 대안과 그것을 바깥 문으로 쓰지 않는 이유를 정리했다.

· 15분 읽기

문자 속 120자짜리 링크를 7자 코드로 – 자체 단축 URL 을 백엔드·웹·어드민에 걸쳐 만든 기록

외부 단축 URL 서비스가 종료된 뒤 대체 문자(LMS)에 긴 원본 주소와 카카오용 UTM 이 그대로 실리던 문제를 정리했다. 워커가 문자를 만들 때 7자 코드로 줄이고, 웹이 302 로 풀고, 어드민에서 카카오 UTM 과 문자 UTM 을 나눠 저장하도록 바꿨다.

· 13분 읽기

먼저 ack 하던 알림 소비자 – 코루틴 launch 가 메시지를 잃는 이유와 처리 후 ack·멱등 처리

RabbitMQ 알림 리스너가 CoroutineScope.launch 로 일을 넘기고 바로 반환해, 알림을 보내기 전에 ack 가 나가던 문제를 정리했다. runBlocking 으로 처리 후 ack 하도록 바꾸고, 그로 인해 생기는 재배달을 수신 원장과 업무 키로 막았다.

· 16분 읽기

타임아웃 정리 – 왜 나고, 어떤 종류가 있고, 코틀린에서는 어떻게 다룰까

외부 API 연동에서 몇 번 만난 타임아웃을 계기로 정리했다. 타임아웃이 왜 나는지, connect·read·call·풀 대기·코루틴 타임아웃이 각각 무엇을 재는지, 값은 어떻게 정하고 난 뒤에는 무엇을 하는지를 코틀린 코드와 실행 결과로 확인했다.

· 17분 읽기