이번 글은 제가 프로젝트에서 페이서비스를 담당하며 충전,환전,결제등등 중요한 API를 다루며 고민했던 멱등성을 어떻게 보장할지, 하나의 트랜잭션을 어떻게 안전하게 처리할지 고민했던 내용과 해결했던 방법에 대해 적어보고자 합니다.
멱등성이란?
멱등하다는 표현은 개발자라면 언제 한번쯤 들어본적이 있을겁니다.
멱등하다는 것은 하나의 작업을 수행한 뒤 여러 차례 적용해도 결과를 변경시키지 않는 작업 또는 기능의 속성을 의미합니다.
멱등한 작업의 결과는 한번 수행하든 여러번 수행하든 같습니다.
멱등한 API
멱등한 API도 똑같습니다. 두 번 이상 여러변 요청해도 결과는 처음 요청과 똑같이 돌아옵니다.
단순히 값만 같은게 아닌 서버 상태(DB)에도 영향을 미치지 않습니다.
사용자가 안심하고 서비스를 이용할 수 있도록 만들게 합니다.
서버에서 어떻게 멱등한지 구별할까??
보통 API 요청에 멱등성 키(고유키)를 보냅니다. Body 혹은 Header에 붙여 서버에 보내면 서버는 그걸 확인해서 처리하는거죠.
권장사항은 헤더에 포함하는 방법을 추천합니다.
저희 프로젝트에서는 아래와 같이 헤더에 포함하는 방식으로 멱등키를 사용했습니다. 프론트에서 헤더에 추가해서 보내야겠죠.
Idempotency-Key: KG5LxwFBepaKHyUD
서버는 어떻게 처리할까요?? 저희 프로젝트에서는 페이 서비스 충전 기능이 있습니다. 이를 예시로 알아봅시다.
- PAYMENT 서버는 충전 요청마다 헤더에 멱등키가 있는지 확인
- 멱등키가 포함된 충전 요청이 들어온다면 DB에서 같은 멱등키를 가진 요청기록을 확인
- 요청이 존재한다면 서버에서 기존의 데이터를 응답
- 요청이 존재하지 않는다면 요청을 처리하고 데이터를 저장
멱등성 키 서버는 어떤걸?
멱등성의 키를 보관하는 방법을 고민해봅시다. 그전에 멱등성 키의 특징을 먼저 살펴볼까요?
- 멱등성 키는 유일성을 보장해야한다.
- 멱등성 키는 관계를 갖지 않는다.
- 멱등성 키는 일시성이 필요하다.
- 요청이 처리된 후에 멱등성 키는 일정 기간 이후 어느 요청에서도 필요하지 않는다.
저희는 기존의 프로젝트에서 사용중이던 Redis, MySql등 고민해보았습니다.
저희는 금융 서비스이다 보니 결제,결제 취소, 충전, 환전 등 모든 요청에 멱등성을 보장해야합니다.
그래서 요청을 빨리 처리하고 기간이 지나면 자동으로 삭제되며, 관계형 구조 또한 필요없는 점을 고려해 Redis를 선정하게 되었습니다.
실제 구현은 다음과 같습니다.
@Override
public String chargeBalance(ChargeBalanceCommand command) {
// 요청이 처리된적이 있는지 확인
if((redisHandler.hasIdempotencyKey(command.idempotencyKey()))) {
return redisHandler.getValue(command.idempotencyKey());
}
//비지니스 로직 처리 이후 레디스에 키 저장
}
public boolean hasIdempotencyKey(String idempotencyKey) {
return redisTemplate.hasKey(idempotencyKey);
}
public String getValue(String idempotencyKey) {
return redisTemplate.opsForSet().pop(idempotencyKey).toString();
}
글을 읽어주셔서 감사합니다. 꾸벅.
참고 :
https://docs.tosspayments.com/blog/what-is-idempotency
멱등성이 뭔가요? | 토스페이먼츠 개발자센터
생소한 표현이지만 알고 보면 쉬워요. 멱등성에 대해 이해하고 API를 멱등하게 제공하기 위한 방법도 함께 알아봐요.
docs.tosspayments.com
https://tech.kakaopay.com/post/msa-transaction/#%EB%A9%B1%EB%93%B1%EC%84%B1-api
MSA 환경에서 네트워크 예외를 잘 다루는 방법 | 카카오페이 기술 블로그
결제시스템(페이상품권)에서 분산 트랜잭션을 보장하고 안전하게 다루기 위해 고민한 내용을 공유합니다.
tech.kakaopay.com
https://stripe.com/docs/idempotency https://stripe.com/docs/api/idempotent_requests
Idempotent requests | Stripe API Reference
Updateable Stripe objects—including Account, Charge, Customer, PaymentIntent, Refund, Subscription, and Transfer have a metadata parameter. You can use this parameter to attach key-value data to these Stripe objects. You can specify up to 50 keys, with k
docs.stripe.com
'Server' 카테고리의 다른 글
| [Server] 게시글 좋아요 수 조회 전략 : COUNT 쿼리 VS 반정규화 (0) | 2026.01.01 |
|---|---|
| [Server] 도메인 서비스(Domain Service)와 애플리케이션 서비스(Application Service)에 대한 고찰 (1) | 2025.12.16 |
| [Server] 신뢰성 확보 WITH Transactional OutBox Pattern (0) | 2025.03.21 |
| [Server] 조회 쿼리 개선 (0) | 2025.03.05 |