정산 데이터 불변성 설계

웹 · 앱 개발5분 읽기

정산 자동화에서 계산만큼 중요한 게 "한 번 확정한 값이 바뀌지 않는 것" 이에요. 매달 정산을 다시 돌리는데, 이미 지급을 끝낸 정산이 재계산으로 슬쩍 바뀌면 그건 사고예요. 케이터링 식자재 ERP에서 정산 데이터 불변성을 설계 중심에 둔 이유가 여기 있어요. 전체 그림은 케이터링 식자재 ERP 구축 사례에, 정산 계산 자체는 다중 정산 자동화에 있습니다.

정산 데이터 불변성은 무엇을 보장하나요

정산 데이터 불변성은 일단 확정된 정산은 이후 어떤 자동 계산으로도 바뀌지 않음을 보장하는 성질이에요. 정산은 살아 있는 데이터예요. 주문이 추가되고 취소되고 금액이 바뀔 때마다 그 달 정산을 다시 계산해야 하죠. 그런데 재계산이 모든 정산을 무차별로 갈아엎으면, 이미 확정해 지급까지 끝낸 건이 뒤늦게 흔들려요.

그래서 정산에 상태(state) 를 두고, 상태에 따라 "바뀔 수 있는 것"과 "잠긴 것"을 갈랐어요. 정산은 세 상태를 순서대로만 지나가요.

상태 전이는 한 방향으로만

핵심은 상태를 되돌릴 수 없게 만드는 거예요. 대기에서만 확정으로, 확정에서만 지급완료로 갈 수 있고, 그 반대나 건너뛰기는 막았어요. 각 전이 메서드가 현재 상태를 먼저 확인하고, 조건이 안 맞으면 아무 일도 하지 않아요.

정산은 대기에서 확정으로, 확정에서 지급완료로만 한 방향으로 전이하며 확정 이후에는 잠긴다대기재계산 자유확정확정잠김, 재계산 안 됨지급지급완료잠김, 최종 상태

정산은 대기, 확정, 지급완료 순서로만 전이하고 확정 이후에는 잠깁니다.

이렇게 하면 "확정된 정산을 수정"하는 경로 자체가 코드에 존재하지 않아요. 누가 실수로 재계산 버튼을 눌러도, 확정·지급완료 건은 상태 검사에 걸려 그대로 지나가요.

주문이 바뀌면 '대기 정산만' 다시 계산

정산 자동화 서비스도 같은 원칙을 지켜요. 재계산은 먼저 정산 행을 잠그고 상태를 확인해, 확정이나 지급완료 상태면 아무 것도 바꾸지 않고 그대로 반환합니다. 대기 상태일 때만 기존 상세를 지우고 처음부터 다시 쌓아요.

그래서 주문을 수정하거나 취소해도, 그 달에 아직 대기 상태인 정산만 다시 계산돼요. 이미 확정한 정산은 주문이 바뀌든 말든 그대로 남죠. 근무 기록을 지워도 마찬가지예요. 대기 정산이면 해당 근무 상세만 빼고 총액을 다시 잡지만, 확정된 정산이면 건드리지 않아요. 정산이 근무에서 흘러오는 과정은 근무 시간이 시급·정산으로 자동 흐르게에서 볼 수 있어요.

정산 상세 요약 화면

정산 상세 요약 화면

동시성까지 챙긴 트랜잭션 락

한 가지 더, 재계산은 트랜잭션 안에서 정산 행을 잠그고(row lock) 진행해요. 여러 트리거가 같은 정산을 동시에 다시 계산하려 들면 상세가 중복되거나 총액이 어긋날 수 있는데, 잠금으로 한 번에 하나만 처리되게 막은 거예요. 여기에 "사용자 + 연 + 월 + 정산 유형"이 유일하도록 제약을 걸어, 같은 달 같은 유형 정산이 둘로 갈라지는 일도 원천 차단했어요.

외부 데이터 임포트에도 같은 원칙

외부에서 정산 데이터를 엑셀로 들여올 때도 이 원칙은 흔들리지 않아요. 임포트는 그 달 정산이 이미 있으면 대기 상태일 때만 값을 덮어쓰고, 확정·지급완료된 건은 건너뛰어요. 그래서 관리자가 실수로 지난달 확정 정산이 담긴 엑셀을 다시 올려도, 확정된 값은 그대로 보존돼요. 자동 계산이든 수동 임포트든, 확정 이후의 정산에는 어느 경로로도 손이 닿지 않게 막은 거죠.

왜 이렇게까지 잠그나요

이렇게까지 하는 이유는 정산이 돈과 신뢰가 걸린 데이터이기 때문이에요. 파트너에게 "지난달 정산이 왜 바뀌었냐"는 질문을 받는 순간 시스템은 신뢰를 잃어요. 반대로 "한 번 확정하면 절대 안 바뀐다"가 보장되면, 파트너도 담당자도 그 숫자를 믿고 일할 수 있어요. 구체적인 시나리오로 보면 이래요. 어떤 파트너의 3월 매출 정산을 확정해 지급까지 끝냈는데, 4월에 3월 주문 하나가 뒤늦게 취소됐다고 해볼게요. 이때 이미 확정한 3월 정산을 소급해 깎으면 분쟁이 나요. 우리 설계에서는 확정된 3월 정산은 그대로 두고, 필요하면 조정분을 다음 정산이나 기타정산으로 따로 반영해요. 과거를 고쳐 쓰는 게 아니라 앞으로 바로잡는 방식이죠.

정리하면, 정산 데이터 불변성은 세 겹으로 지켜져요. 상태 머신으로 전이를 한 방향으로 묶고, 재계산이 상태를 존중해 대기 건만 갱신하고, 트랜잭션 락과 유일성 제약으로 동시성까지 막는 거죠. 이 세 겹이 실운영에서 정산을 믿을 수 있는 데이터로 만들었어요. 불변성은 기능처럼 눈에 띄지 않지만, 정산을 두고 매달 벌어지던 실랑이를 없애는 건 결국 이런 보이지 않는 설계예요. 화면 하나를 더 그리는 것보다, 확정한 숫자가 절대 바뀌지 않는다는 약속을 코드로 지키는 쪽이 실운영에서는 훨씬 값진 기능이었어요.

자주 묻는 질문

확정된 정산을 어떻게 잠그나요?

정산에 대기·확정·지급완료 상태를 두고, 상태 전이 메서드가 현재 상태를 먼저 검사합니다. 확정은 대기에서만, 지급완료는 확정에서만 가능하고 수정도 대기에서만 허용돼요. 그래서 확정된 정산을 바꾸는 경로 자체가 코드에 존재하지 않습니다.

주문이 바뀌면 이미 확정한 정산도 다시 계산되나요?

아니요. 재계산은 대기 상태 정산만 갱신합니다. 주문을 수정·취소하거나 근무 기록을 지워도 그 달에 아직 대기인 정산만 다시 계산되고, 이미 확정·지급완료된 정산은 그대로 잠겨 있어요.

동시에 재계산이 일어나면 정산이 꼬이지 않나요?

재계산은 트랜잭션 안에서 정산 행을 잠그고 진행해 한 번에 하나씩만 처리됩니다. 또 사용자·연·월·정산 유형 조합에 유일성 제약을 걸어, 같은 달 같은 유형 정산이 둘로 갈라지는 상황도 데이터 차원에서 막았어요.

#정산시스템설계#데이터불변성#상태머신#트랜잭션락#시스템

업무자동화 구축이 필요하신가요?

무료 상담 신청