한 묶음으로 처리하기

결제는 됐는데 주문은 안 생기는 사고를 막는 장치예요. 여러 기록을 한 묶음으로 묶어서 전부 되거나 전부 없던 일이 되게 해요. 반쯤 처리된 어정쩡한 상태를 아예 만들지 않는 게 핵심이에요.

쉽게 말하면

계산대에서 손님 하나를 받는 일은 실제로 동작 세 개예요. 카드 승인을 받고, 주문서를 주방에 넘기고, 재고를 하나 깎아요. 승인만 받고 주문서를 주방에 못 넘기면 손님은 돈을 냈는데 음식이 안 나와요. 그래서 일 잘하는 사장님은 이 셋을 한 동작으로 다뤄요. 중간에 뭐가 하나 어긋나면 승인까지 취소하고 처음부터 다시 해요. 컴퓨터에서 이렇게 묶는 것을 트랜잭션이라고 불러요. 우리 말로는 한 묶음 처리예요.

컴퓨터는 기록을 한 번에 한 곳씩 고쳐요. 세 곳을 고쳐야 하면 세 번에 나눠 고치는 거예요. 그 사이에 서버가 죽거나 인터넷이 끊기거나 손님이 브라우저를 닫으면, 앞의 한두 곳만 고쳐진 상태로 멈춰요. 이 어정쩡한 상태가 사고의 정체예요.

따로 처리
1) 카드 승인 성공 2) 주문 만들기 ... 실패

손님 카드에서는 돈이 빠졌는데 주문 목록에는 아무것도 없어요. 사장님은 항의 전화를 받고 나서야 알게 돼요.

한 묶음 처리
[ 카드 승인 + 주문 만들기 + 재고 차감 ] 하나라도 실패하면 전부 없던 일

손님은 "결제에 실패했어요"를 봐요. 기분은 나쁘지만 돈은 그대로예요. 다시 누르면 돼요.

반쯤 처리되면 실제로 무슨 일이 생기나

이 사고의 무서운 점은 화면에 오류가 안 뜬다는 거예요. 서비스는 멀쩡하게 돌아가고, 기록만 조용히 어긋나 있어요. 그래서 발견이 늦어요.

묶이지 않은 자리사장님이 보는 증상손해
결제 + 주문 만들기결제는 됐는데 주문 목록에 없어요손님이 카드사에 이의를 제기해요. 이의제기가 쌓이면 결제사 심사에 걸려요
주문 + 재고 차감재고가 계속 줄어드는데 팔린 기록이 없어요실물과 장부가 매달 어긋나요. 원인을 못 찾아 재고 관리를 포기하게 돼요
포인트 차감 + 상품 지급포인트는 빠졌는데 물건이 안 왔대요보상해 주고 나면 이번엔 포인트가 두 번 나가요
주문 만들기 + 결제주문은 생겼는데 결제 기록이 없어요공짜 주문이 섞여요. 매출과 정산이 안 맞아요
보낸 쪽 차감 + 받은 쪽 증가한쪽에서만 빠지고 다른 쪽에 안 들어와요돈이 사라져요. 신뢰가 즉시 무너지는 종류의 사고예요
  1. 1손님이 결제 버튼을 눌러요. 카드 승인이 정상으로 떨어져요.
  2. 2그다음 줄에서 주문 기록을 만들려는 순간, 서버가 잠깐 답을 못 해요. 화면에는 500 같은 표지판이 뜨거나, 아무 일도 없이 멈춘 것처럼 보여요.
  3. 3손님은 결제가 안 된 줄 알고 다시 눌러요. 이번엔 성공해요. 카드에서 두 번 빠졌어요.
  4. 4사장님은 며칠 뒤 정산 금액이 주문 합계보다 많은 걸 발견해요. 어느 주문이 중복인지 대조하는 데 반나절이 들어요.
  5. 5환불해 주면서 재고까지 손으로 되돌려요. 이 과정에서 또 하나 빠뜨려요.

이 사고의 비용은 수리비가 아니에요

코드를 고치는 데 드는 시간보다 어긋난 기록을 사람 손으로 맞추는 시간이 훨씬 커요. 한 번 어긋난 장부는 다음 달에도 어긋난 채로 넘어가요. 그래서 돈이 오가는 자리는 손님이 열 명일 때부터 묶어 두는 게 이득이에요.

한 묶음이 지켜 주는 약속 네 가지

개발자들이 이 네 가지를 묶어 부르는 이름이 있어요. 영어 머리글자를 따서 ACID라고 해요. 외울 필요는 없지만, 견적서나 연동 문서에서 이 단어를 보면 "기록이 어긋나지 않게 하는 장치" 얘기를 하고 있다는 뜻이에요.

약속무슨 뜻인가이게 없으면
전부 아니면 전무묶음 안의 동작이 다 되거나 하나도 안 돼요. 절반은 없어요결제는 됐는데 주문이 없는 그 상태가 만들어져요
규칙은 늘 지켜짐재고가 음수가 되거나 잔액이 마이너스가 되는 상태로는 저장이 안 돼요화면에는 재고 3개인데 실제로는 이미 다 팔린 상황이 생겨요
서로 안 끼어듦두 손님이 같은 순간에 눌러도, 한 명씩 차례로 처리한 것과 같은 결과가 나와요마지막 하나를 두 사람에게 팔아요
됐다고 했으면 남아요"주문 완료"를 띄운 뒤에는 전원이 나가도 그 기록이 살아 있어요"주문했는데요"와 "기록이 없는데요"가 맞서요

"됐다"고 말하는 시점이 전부예요

묶음 처리에는 끝맺는 순간이 하나 있어요. 개발자들은 이걸 커밋이라고 불러요. 그 순간 전까지는 아무리 많은 작업을 했어도 되돌릴 수 있고, 그 순간 이후에는 전원이 나가도 남아 있어요. 손님에게 "주문 완료"를 보여주는 시점은 반드시 이 순간 여야 해요. 순서가 뒤바뀌면 완료 화면을 본 손님의 주문이 사라질 수 있어요.

무엇을 묶고 무엇은 밖에 두나

묶는 기준은 딱 하나예요. 한쪽만 되면 손해가 나는 것끼리 묶어요. 한쪽만 돼도 나중에 다시 하면 되는 일은 묶음 밖에 둬요. 이 구분만 되면 이 문서의 실무 부분은 끝난 거예요.

동작묶음 안이유
주문 기록 만들기이게 없으면 결제가 허공에 뜬 돈이 돼요
재고 하나 깎기실물과 장부가 어긋나면 되돌릴 방법이 없어요
포인트 차감과 적립손님 자산이라 한쪽만 되면 곧바로 분쟁이에요
주문 확인 문자와 메일늦게 가도 되고, 실패하면 다시 보내면 돼요
영수증 그림 만들기오래 걸리는 일이에요. 묶음 안에 들어오면 그 시간만큼 기록이 잠겨요
통계 숫자 올리기몇 분 늦게 반영돼도 손해가 없어요
광고 성과 전송남의 서비스라 애초에 내 묶음에 들어오지 않아요
순서가 뒤바뀐 경우
"주문 완료" 화면 먼저 보여주기 그다음 기록 저장 시도

손님은 완료 화면을 봤는데 기록이 없어요. 사장님이 아무리 설명해도 손님은 화면을 기억해요.

순서가 맞는 경우
기록을 끝맺고 나서 "주문 완료" 화면 보여주기

저장이 실패하면 손님은 실패 화면을 봐요. 다시 누르면 되고, 어긋난 기록도 안 남아요.

밖에 둔 일은 잊지 않게 적어 둬요

묶음 밖으로 뺀 일은 실패해도 조용해요. 문자가 안 갔는데 아무도 모르는 상태가 될 수 있어요. 그래서 밖에 둔 일은 실패하면 기록에 남기고 나중에 다시 시도하게 만들어요. 묶음 밖으로 뺀다는 건 안 해도 된다는 뜻이 아니라 나중에 해도 된다는 뜻이에요.

같은 순간에 두 명이 누르면

진열대에 케이크가 하나 남았어요. 두 손님이 각자 다른 직원에게 동시에 주문해요. 직원 둘이 각각 진열대를 보고 "하나 있네요" 하고 둘 다 받아요. 그리고 둘 다 주방에 주문서를 넣어요. 케이크는 하나뿐인데 팔린 건 둘이에요. 사람 사이에서는 눈으로 보고 눈치채지만, 서버 안에서는 눈치챌 사람이 없어요.

  1. 1손님 A와 손님 B가 거의 같은 순간에 결제를 눌러요. 차이는 0.01초예요.
  2. 2서버는 두 요청을 나란히 처리해요. 둘 다 재고를 읽고 "1개 남음"을 확인해요.
  3. 3둘 다 "1에서 1을 뺀 0"을 저장해요. 두 번 뺐는데 재고는 1만 줄었어요.
  4. 4주문은 두 건이 만들어져요. 재고는 0인데 팔린 건 2개예요.
  5. 5다음 날 사장님은 재고 하나가 비어 있는 걸 보고 도난을 의심해요. 사실은 코드가 만든 구멍이에요.

이걸 막는 방법은 결국 줄을 세우는 것이에요. 재고를 건드리는 동안에는 다른 요청이 그 줄을 못 만지게 잠깐 잠가 둬요. 개발자들은 이 잠그는 일을 락 또는 잠금이라고 불러요. 인기 상품 하나에 요청이 몰리면, 그 줄 앞에 사람들이 서서 기다리게 돼요. 그래서 잠금은 안전을 사고 속도를 지불하는 거래예요.

데드락이라는 말을 들으면

두 작업이 서로가 잠근 것을 기다리면서 영원히 멈추는 상황이에요. 좁은 복도에서 두 사람이 서로 비켜 주기를 기다리는 것과 똑같아요. 데이터베이스는 이걸 감지하면 한쪽을 강제로 취소시키고 오류를 내요. 그래서 데드락은 데이터가 깨진 게 아니라 한쪽 작업이 실패한 것이에요. 개발자가 "데드락 났어요"라고 하면 "기록이 상했다"가 아니라 "묶는 순서를 정리해야 한다"는 뜻으로 들으세요.

지금은 신경 쓰지 않아도 되는 때

정직하게 말할게요. 손님이 하루 열 명인 가게에서 잠금이며 격리 수준이며 따지고 있으면 그건 시간 낭비예요. 같은 순간에 두 요청이 겹칠 확률 자체가 거의 없고, 어긋난 기록이 생겨도 눈으로 세어 고칠 수 있는 분량이에요. 이 문서의 절반은 아직 안 읽어도 되는 얘기예요.

이런 상황이면왜 안 해도 되나대신 할 일
주문이 하루 몇 건, 겹칠 일이 없음동시에 눌리는 일이 안 생겨요. 잠금이 막을 사고가 애초에 없어요주문 목록을 하루 한 번 눈으로 보세요. 그게 더 빨라요
문의 폼, 방명록, 후기처럼 돈이 안 걸린 기록반쯤 저장돼도 손해가 거의 없어요. 손님이 다시 쓰면 돼요저장 실패를 손님에게 제대로 보여주기만 하면 충분해요
고칠 곳이 딱 한 군데인 작업한 줄만 고치는 일은 그 자체로 이미 통째예요. 묶을 게 없어요묶음 처리를 넣어도 달라지는 게 없어요. 넘어가세요
아직 손님이 없는 준비 단계지금 필요한 건 정확한 기록이 아니라 팔리는지 확인하는 거예요화면을 먼저 내보내세요. 최소 기능 편이 순서를 다뤄요
예약 신청을 받고 확정은 사장님이 직접사람이 중간에 확인하니까 어긋나도 그 자리에서 잡혀요확정 버튼을 누르는 그 순간만 묶으면 돼요

예외는 딱 하나예요

돈과 재화가 오가는 자리는 규모와 무관해요. 손님이 하루 세 명이어도 결제와 주문을 따로 처리하면 그 셋 중 한 명에게 사고가 나요. 그리고 첫 손님과 다투는 일은 천 번째 손님과 다투는 일보다 훨씬 아파요. 나머지는 미뤄도 되지만 이건 첫날부터예요.

견적서와 연동 문서를 읽는 법

이 배치의 진짜 값어치는 여기예요. 아래 표에 있는 말들은 개발자끼리 쓰는 말이지만, 견적서와 결제사 연동 문서에는 그대로 적혀 나와요. 뜻을 알면 견적이 성실한지 대충인지 구분할 수 있어요.

문서에 적힌 말사장님 말로 옮기면이때 물어볼 것
트랜잭션으로 묶어 처리합니다여러 기록을 한 묶음으로 다뤄서 반쯤 처리를 없애겠다는 뜻어느 동작들을 한 묶음으로 묶는지 이름을 대 달라고 하세요
커밋 후 응답을 반환합니다기록을 확실히 남긴 다음에 손님에게 완료를 보여준다는 뜻. 순서가 맞아요"완료 화면이 먼저 뜨는 경로는 없나요"가 좋은 질문이에요
실패 시 롤백합니다중간에 어긋나면 앞서 한 것까지 없던 일로 되돌린다는 뜻결제사 승인까지 되돌리는지, 내 기록만 되돌리는지 구분해서 물으세요
행 단위 잠금을 사용합니다그 기록을 만지는 동안 다른 요청을 잠깐 기다리게 한다는 뜻인기 상품에 몰릴 때 얼마나 기다리게 되는지 물어보세요
멱등키를 적용합니다같은 요청이 두 번 들어와도 한 번만 처리한다는 뜻. 중복 결제를 막는 장치예요결제 재시도 상황에서 실제로 검증해 봤는지 물으세요
웹훅으로 최종 상태를 맞춥니다결제사가 나중에 알려주는 결과를 받아 내 기록을 바로잡는다는 뜻웹훅을 못 받았을 때의 대비가 있는지 물으세요. 웹훅 편에 이유가 있어요
정산 대조 배치를 돌립니다하루 한 번 결제사 기록과 내 기록을 맞춰 보고 어긋난 건을 뽑아낸다는 뜻어긋난 건이 나오면 누가 어떻게 처리하는지 절차까지 받으세요
격리 수준을 올렸습니다동시 처리 때 서로 끼어드는 정도를 더 엄격하게 잡았다는 뜻그만큼 느려지거나 실패가 늘지 않았는지 숫자로 받으세요

장면 1 · 견적서에 이 항목이 없다

결제 연동 3일, 주문 관리 화면 5일입니다.

견적 자체가 이상한 건 아니에요. 다만 결제와 주문을 잇는 부분이 한 줄도 없어요. "결제 성공과 주문 생성을 한 묶음으로 처리하는 작업도 포함인가요"라고 물어보세요. 포함이라고 하면 좋고, "그건 별도예요"라고 하면 지금 알게 된 거라 이득이에요. 나중에 사고가 나고 추가 견적을 받는 것보다 훨씬 싸요.

장면 2 · 개발자가 반대한다

거기까지 묶으면 느려집니다. 지금 규모에서는 과한 설계예요.

이건 트집이 아니라 진짜 판단일 수 있어요. 판단 기준은 그 자리에 돈이 걸렸는지예요. 결제와 재화가 걸린 자리면 느려도 묶는 게 맞고, 방문 기록이나 후기 저장이면 개발자 말이 맞아요. 그 자리에 무엇이 걸렸는지는 사장님이 가장 잘 알아요. 그래서 이 대화는 개발자에게 맡길 게 아니라 사장님이 결정할 일이에요.

장면 3 · 사고가 이미 났다

결제된 주문 몇 건이 목록에 안 보여요.

순서가 있어요. 먼저 결제사 관리자 화면에서 승인 목록을 뽑아 내 주문 목록과 대조해요. 여기서 진짜 손해액이 나와요. 그다음 해당 손님에게 먼저 연락해요. 손님이 먼저 항의하기 전에 연락이 가면 사고가 사과로 끝나요. 코드 수리는 세 번째예요. 이 순서는 장애 대응 편의 원칙과 같아요.

AI에게 그대로 말할 문장

전문어를 쓸 필요 없어요. 증상을 그대로 말하는 게 가장 정확한 요청이에요. 왼쪽 칸에서 내 상황을 찾아 오른쪽 문장을 그대로 옮겨 쓰세요.

내가 본 증상그대로 말할 문장
결제는 됐는데 주문이 안 생겼다결제 성공과 주문 생성을 한 묶음으로 처리해 주세요. 주문 생성이 실패하면 결제도 취소되게 해 주세요
재고가 팔린 기록 없이 줄어든다주문 생성과 재고 차감을 한 묶음으로 묶어 주세요. 둘 중 하나만 되는 경우가 없게 해 주세요
같은 결제가 두 번 잡혔다손님이 결제 버튼을 두 번 눌러도 주문이 한 건만 만들어지게 해 주세요
마지막 하나가 두 명에게 팔렸다재고가 1개 남았을 때 두 사람이 같은 순간에 주문하면 한 명만 성공하게 해 주세요
포인트만 빠지고 상품이 안 갔다포인트 차감과 상품 지급을 한 묶음으로 처리하고, 실패하면 포인트를 돌려주세요
어디가 안 묶였는지 모르겠다지금 코드에서 기록 두 곳 이상을 함께 고치는 곳을 찾아서, 묶어야 하는 자리를 목록으로 알려 주세요

직접 해보기

내 서비스에서 묶어야 할 자리를 찾아보세요

지금 고칠 필요는 없어요. 표 마지막 줄 문장을 그대로 붙여 넣어서 목록만 받아 보는 것으로 충분해요. 목록을 보면 내 서비스에서 돈이 걸린 자리가 몇 군데인지 처음으로 눈에 들어와요.

스튜디오 열기

자주 묻는 것

Q. 바이브캠퍼스로 만들면 이건 알아서 되나요?
주문이나 결제가 있는 화면을 만들면 기본 처리는 함께 잡혀요. 사장님이 할 일은 돈과 재화가 걸린 자리를 말해 주는 것이에요. "포인트로 결제되는 기능이 있어요"처럼 한 줄만 알려 주면, 묶어야 할 자리가 그 자리라는 걸 알 수 있어요.
Q. 결제사 승인까지 한 묶음으로 되돌릴 수 있어요?
완전히는 안 돼요. 결제사는 남의 장부라 내 묶음 안에 들어오지 않아요. 그래서 실무에서는 승인을 취소 요청으로 되돌려요. 없던 일로 만드는 게 아니라 반대 방향으로 한 번 더 처리하는 거예요. 취소 요청이 실패할 수도 있으니 그 경우 오류 기록에 남기고 사람이 확인하는 절차가 필요해요.
Q. 손님이 결제 중에 창을 닫으면요?
가장 흔한 사고 경로예요. 카드 승인은 결제사 쪽에서 끝났는데 내 서버는 결과를 못 받은 상태가 돼요. 이건 묶음 처리만으로는 안 막혀요. 결제사가 나중에 결과를 알려주는 통보를 받아서 맞추는 방식이 필요해요. 결제 연결 편과 함께 보면 그림이 맞아요.
Q. 묶으면 서비스가 느려진다는 게 사실이에요?
사실이지만 오해가 섞여 있어요. 묶음 자체는 거의 공짜예요. 느려지는 건 묶음이 길 때예요. 묶음 안에서 이미지 변환이나 외부 서비스 호출처럼 오래 걸리는 일을 하면 그동안 그 기록이 잠겨 있어요. 그래서 정석은 묶음을 짧게 유지하고, 오래 걸리는 일은 묶음 밖으로 빼는 거예요.
Q. 그럼 전부 다 묶어 두면 가장 안전한가요?
아니에요. 크게 묶으면 잠기는 범위가 넓어져서 손님들이 서로 기다리게 되고, 데드락도 늘어요. 기준은 함께 맞아야 하는 것끼리만 묶는 거예요. 결제와 주문은 함께 맞아야 하고, 주문과 후기 저장은 함께 맞을 필요가 없어요.
Q. 이미 어긋난 기록은 어떻게 정리해요?
손으로 고치기 전에 먼저 백업을 떠 두세요. 정리하다 잘못 지우면 되돌릴 방법이 없어져요. 그다음 결제사 기록을 기준으로 대조하세요. 내 기록이 아니라 결제사 기록이 기준이에요. 순서와 방법은 백업과 복구 편에 있어요.
Q. 이 얘기는 데이터베이스가 있어야만 해당되나요?
기록을 저장하는 곳이 있으면 다 해당돼요. 다만 묶음 처리라는 장치는 데이터베이스가 제공하는 기능이라, 스프레드시트 같은 곳에 기록을 쌓고 있으면 이 장치가 없어요. 그 경우는 사람이 대조하는 절차로 메워야 해요.

확인해 보세요

포인트로 상품을 사는 기능을 만들었어요. 무엇과 무엇을 한 묶음으로 묶어야 할까요?

하나 더

하루 주문 다섯 건, 후기 저장 기능만 있는 소개 사이트예요. 지금 잠금과 격리 수준을 손봐야 할까요?

더 깊이 (안 읽어도 괜찮아요)

계산대 비유가 어디부터 다른가 · 계산대 비유는 내 가게 안에서만 정확해요. 내 장부(데이터베이스) 안의 기록끼리는 진짜로 한 묶음이 되고, 하나가 실패하면 앞의 것까지 깨끗하게 없던 일이 돼요. 그런데 카드사와 배송사는 남의 장부예요. 남의 장부에 이미 적힌 줄을 내가 지울 수는 없어요. 그래서 밖으로 나간 일은 취소 요청이라는 반대 방향 동작으로 메워요. 개발자들은 이걸 보상 처리라고 불러요. 비유가 어긋나는 지점이 정확히 여기라서, 결제 사고가 유독 까다로운 거예요.

"됐다"는 답이 안 왔을 때가 가장 위험해요 · 실패했다는 답이 오면 오히려 쉬워요. 안 된 걸 아니까요. 문제는 아무 답도 안 오는 경우예요. 성공했는데 답만 못 받은 건지, 애초에 실패한 건지 구분이 안 돼요. 이때 무턱대고 다시 보내면 중복 처리가 돼요. 그래서 실무에서는 요청마다 고유한 표를 붙여 보내고, 받는 쪽이 "이 표는 이미 처리했다"고 판단하게 만들어요. 이게 견적서에 적히는 멱등키예요. 시간 초과와 재시도 편과 같은 문제를 다른 각도에서 보는 거예요.

격리 수준이라는 눈금 · 동시 처리에서 서로 얼마나 끼어들지 못하게 할지는 켜고 끄는 스위치가 아니라 눈금이에요. 느슨하게 두면 빠르지만 드물게 이상한 결과가 나오고, 조이면 정확하지만 기다림과 실패가 늘어요. 데이터베이스마다 기본 눈금이 다르고, 대개 기본값이 일상 서비스에는 적당해요. 그래서 이 눈금을 만지는 것은 마지막 처방이에요. 개발자가 처음부터 격리 수준을 올려서 해결하겠다고 하면, 묶는 자리를 잘못 잡은 게 아닌지 먼저 확인해 보세요.

이것만 기억하세요

  • ·함께 맞아야 하는 기록끼리 한 묶음으로 묶어요. 전부 되거나 전부 없던 일이에요
  • ·손님에게 완료를 보여주는 시점은 기록이 확실히 남은 뒤예요. 순서가 뒤바뀌면 주문이 사라져요
  • ·돈과 재화가 걸린 자리는 규모와 무관하게 첫날부터, 나머지는 미뤄도 돼요
  • ·묶음은 짧게. 오래 걸리는 일은 묶음 밖으로 빼요. 크게 묶는 게 안전한 게 아니에요
  • ·결제사는 내 묶음 밖이에요. 되돌리기가 아니라 취소 요청과 대조로 메워요
VibeCampus新規ビルド