백엔드 튜토리얼의 함정
튜토리얼은 단순한 과정을 보여줍니다. 웹훅을 받습니다. 데이터베이스를 업데이트합니다. 200 OK를 반환합니다.
테스트할 때는 코드가 잘 작동합니다. 프로덕션에 배포합니다. 그런데 데이터베이스에 중복 레코드가 나타납니다. 사용자에게 크레딧이 두 번 지급됩니다. 데이터가 쌓여갑니다.
튜토리얼은 네트워크 장애라는 현실을 무시합니다.
문제: 신뢰할 수 없는 네트워크 네트워크는 실패합니다. 서버가 데이터를 느리게 처리할 수도 있습니다. DNS 오류로 인해 200 OK 응답이 발신자에게 도달하지 못할 수도 있습니다.
서비스가 확인 응답을 받지 못하면 재시도합니다. 동일한 웹훅을 다시 보냅니다. 코드가 모든 요청을 그대로 수락한다면, 중복 데이터가 생성됩니다.
해결책: 멱등성(Idempotency) 멱등성이란 여러 번의 동일한 요청을 보내도 단 한 번의 요청을 보낸 것과 동일한 효과를 내는 것을 의미합니다.
엘리베이터 버튼을 생각해보세요. 5층 버튼을 한 번 누르면 엘리베이터가 어디로 가야 할지 알려줍니다. 버튼을 열 번 누른다고 해서 엘리베이터가 50층으로 가지는 않습니다. 결과는 동일하게 유지됩니다.
여러분의 웹훅도 이 버튼처럼 작동해야 합니다.
해결 방법 안전한 웹훅을 구축하려면 다음 단계를 따르세요:
- 이벤트의 고유 ID를 찾습니다.
- 어떤 작업을 수행하기 전에 데이터베이스에서 해당 ID가 있는지 확인합니다.
- ID가 이미 존재한다면 중단합니다. 발신자가 재시도를 멈출 수 있도록 200 OK를 반환합니다.
- ID가 새로운 것이라면 데이터를 처리합니다.
- 즉시 이벤트 ID를 데이터베이스에 저장합니다.
Node.js 예시 로직:
const eventId = req.body.event_id;
const existingEvent = await db.processedEvents.findUnique({ where: { id: eventId } });
if (existingEvent) { return res.status(200).send('Already processed'); }
await updateUserData(req.body.data); await db.processedEvents.create({ data: { id: eventId } });
return res.status(200).send('Success');
완벽한 조건만을 위한 시스템을 만드는 것은 쉽습니다. 장애 상황을 고려한 시스템을 만드는 것이 진짜 엔지니어링입니다.
재시도로 인한 중복 데이터 문제를 겪어본 적이 있나요? 멱등성을 어떻게 처리하시나요?