즉 여러 번 같은 요청을 넣어도 서버의 상태가 동일할 경우 이를 멱등하다고 한다. 그리고 대표적으로 멱등한 메서드는 GET, HEAD, PUT, DELETE, TRACE, OPTIONS 가 존재한다.
이러한 멱등성은 HTTP 커넥션이 끊어졌을 때 다시 해당 요청을 해도 되는지에 대한 판단 근거가 된다. 만약 결제 내역 조회 등과 같은 GET 요청의 경우 여러 번 요청을 넣어도 문제가 되지 않을 것이다. 즉, 멱등하다면 요청을 재시도할 때 같은 서버의 상태를 보장하기 때문에 문제가 없다. 하지만 결제 요청의 경우 다시 해당 요청을 넣어버리면 중복 결제 문제가 생길 수 있을 것이다. 즉, 멱등하지 않다면 재시도 요청 시 중복 요청이 보내져 문제가 발생할 수 있을 것이다.
예를 들어 멱등한 결제 API의 경우에는 안심하고 여러 번 요청할 수 있으며 중복 요청으로 발생하는 문제(중복 결제)를 방지할 수 있다. 하지만 멱등하지 않은 결제 API 경우에는 결제가 성공했는지 수동으로 확인하고 재요청해야 한다. 그렇지 않으면 중복 결제와 같은 문제가 생길 수도 있다.
실제 토스페이먼츠의 개발에서는 이러한 멱등성을 활용하여, 멱등한 요청이 들어왔을 때 한번더 처리과정을 거치는 대신 이전 기록에서 응답을 찾아내어 기록을 보내준다고 한다.
const idempotencyResponses = new Map();
let cancelReq = {
orderId: req.body.orderId
amount: req.body.amount,
};
let idempotencyKey = req.headers.idempotencyKey || null // 요청 헤더에서 멱등키를 가져옵니다.
// 멱등키가 있고 멱등 응답도 저장되어 있다면 실제 처리하지 않고 저장된 응답을 내보냅니다.
if (idempotencyKey != null && idempotencyResponses.has(idempotencyKey)) {
const response = idempotencyResponses.get(idempotencyKey);
return res.status(response.status).json(response);
};
const result = cancelProcessor.cancel(cancelReq); // 실제로 취소를 처리합니다.
// 멱등키가 있으면 멱등응답을 저장합니다.
if (idempotencyKey != null) {
idempotencyResponses.set(idempotencyKey, result);
}
const responseBody = {
message: `결제 취소 성공`,
};
return res.status(200).json(responseBody);