개발자

Nodejs API 동시요청은 어떻게 처리하나요?

2023년 08월 16일조회 805

이제 막 nodejs 백엔드를 입문한 주니어입니다 Sequelize와 Mysql을 이용해서 웹서버를 개발중인데 이용자의 API동시 요청 때문에 애를 먹고 있습니다 예를 들어 회원의 쿠폰사용 승인 API를 두개의 컴퓨터에서 동시에 호출을 할 경우 회원의 쿠폰사용이 중복으로 처리가 됩니다 이를 방지하기 위해서 express-rate-limit모듈도 적용을 시켰는데 가끔씩 중복으로 처리가 됩니다 다른 분들은 어떻게 처리를 하고 계신지 궁금합니다

이 질문이 도움이 되었나요?
'추천해요' 버튼을 누르면 좋은 질문이 더 많은 사람에게 노출될 수 있어요. '보충이 필요해요' 버튼을 누르면 질문자에게 질문 내용 보충을 요청하는 알림이 가요.
profile picture
익명님의 질문

답변 2

인기 답변

손정현님의 프로필 사진

안녕하세요! 정확한 요구사항이 뭔지는 모르지만 혹시 트랜잭션 사용하는건 고려해보셨나요? 대부분의 RDB는 트랜잭션 기능을 지원할겁니다. 쿠폰 사용을 허가 하기전에 쿠폰이 있는지 확인하는 로직이 있을거라고 생각됩니다. 쿠폰의 존재를 확인하고 쿠폰이 있으면 쿠폰 사용함을 업데이트 해주는 로직을 하나의 트랜잭션으로 묶으면 되지 않을까 싶네요. 잠깐 찾아보니 sequelize에도 transaction 기능이 지원되네요! 한번 참고해보세요. - https://sequelize.org/docs/v6/other-topics/transactions/ 현업에서는 여러가지 방법을 혼합해서 쓰는 것 같고 트랜잭션도 그 중 하나의 방법인 것 같아요 :)

인기 답변

김도열님의 프로필 사진

DBMS 바깥의 원격 서비스에서 트랜잭션을 사용하는 것은 DBA 관점에서 안티 패턴입니다. 가능한 한 백엔드 서버에서 트랜잭션은 사용하지 않아야 합니다. 그 보다는 다른 해결 방법이 있을 것 같은데요. 쿠폰 사용을 승인하기 전에, 쿠폰의 사용 여부를 SELECT 한 후 어떤 로직을 처리하고 마지막에 사용 완료를 UPDATE로 처리하고 계시죠? 사용 여부 확인에 SELECT를 사용하지 말고, 즉시 UPDATE를 실행하고 ROW_COUNT() 함수로 affected row count를 확인하는 방식을 고려해 보세요. 쿠폰이 이미 사용되었다면 UPDATE로 인해 영향을 받은 행 수가 0이 되기 때문에 굳이 SELECT를 미리 해 보지 않아도 됩니다. 대부분의 동시성 문제는 이런 방식으로 해결할 수 있으니 참고하시기 바랍니다.

손정현님의 프로필 사진

손정현

엔지니어2023년 09월 21일

좋은 답변 감사합니다 도열님! 혹시 괜찮다면 질문 하나만 드려도 될까요? "DBMS 바깥의 원격 서비스에서 트랜잭션을 사용하는 것은 DBA 관점에서 안티 패턴입니다. 가능한 한 백엔드 서버에서 트랜잭션은 사용하지 않아야 합니다." -> 왜 안티 패턴인지, 어떤 부분이 문제가 되는지 여쭤봐도 될까요? 제가 DBA 관점에서 문제를 접근해본 경험이 적어서 궁금하네요 ㅎㅎ

김도열님의 프로필 사진

김도열

DB Designer & SQLer2023년 09월 21일

겸손하시네요. 이미 알고 계신 내용과 별 다르지 않을 거라고 생각합니다. 아시는 것처럼 트랜잭션을 시작하고 커밋 (또는 롤백) 할 때의 원칙은 트랜잭션의 시작은 최대한 늦추고 커밋이나 롤백은 최대한 빨리 실행시켜서 잠금 시간을 최소화 하는 것입니다. 그런데 로컬과 달리 원격 서버의 경우 트랜잭션 블록 안의 쿼리 문들이 네트워크를 타고 왕복을 하고, 중간 중간 백엔드에서 비즈니스 로직 처리가 끝나기를 기다리게 되기 때문에 상대적으로 잠금 시간이 길어집니다. (다른 세션의 잠금 대기가 발생하고, 상황에 따라선 데드락까지도 유발) 그래서 가능한 다른 방법을 최대한 찾아 보고, 방법이 있다면 백엔드 서버에서 트랜잭션을 시작하는 작업은 하지 않는 것이 좋다는 의미로 드린 말씀입니다. 참고로 본문의 쿠폰과 같은 예에서, 보통은 트랜잭션을 사용해서 이렇게 해결하는 것 같습니다. {백엔드에서 트랜잭션 시작} SELECT coupon_id, is_used FROM coupon WHERE coupon_key = 'abc' FOR UPDATE; {백엔드에서 쿠폰 존재 여부, 쿠폰 사용 여부에 대한 exception 처리} UPDATE coupon SET is_used = 'y', ... WHERE coupon_id = ... + 그 밖의 몇 가지 데이터 수정 {백엔드에서 Commit} 이걸 트랜잭션을 사용하지 않고 해결하는 방식도 있는데요. UPDATE coupon SET is_used = 'y', ... WHERE coupon_key = 'abc' AND is_used = 'n'; SELECT ROW_COUNT() AS affected_rows; 여기서 affected_rows가 0이면 존재하지 않는 쿠폰이거나, 이미 사용된 쿠폰이라는 의미가 되기 때문에, 조금이라도 늦게 도착한 API 호출에 의해서는 쿠폰 사용이 승인되지 않습니다.

손정현님의 프로필 사진

손정현

엔지니어2023년 09월 21일

제품 개발에 집중하다보면 기본적인 걸 스쳐 지나가는 경우가 많은 것 같은데 역시 기본적이면서 당연한게 중요하고 문제를 쉽게 푸는데 도움이 되네요. 다시 한번 깨닫게 됩니다. 상세한 답변 감사합니다. 좋은 밤 되세요 :)

지금 가입하면 모든 질문의 답변을 볼 수 있어요!

현직자들의 명쾌한 답변을 얻을 수 있어요.

또는

이미 회원이신가요?

목록으로

지금 가입하면 모든 질문의 답변을 볼 수 있어요!