블로그 목록으로
소프트웨어

두 사람이 동시에 저장하면 한 사람의 수정이 사라진다: 업무 시스템 동시성 제어와 잠금 설계 가이드

재고 음수, 중복 주문 번호, 덮어써진 결재 의견은 대부분 동시 저장에서 생깁니다. 낙관적·비관적 잠금, 원자적 갱신, 멱등성 키를 업무별로 어떻게 고르고 충돌 화면은 어떻게 설계할지 정리합니다.

POLYGLOTSOFT 기술팀2026-11-027분 소요2
동시성제어낙관적잠금트랜잭션재고차감업무시스템설계

개발 서버에서는 안 보이던 버그

개발과 테스트 단계에서는 보통 한 사람이 한 화면을 씁니다. 그래서 두 사람이 같은 데이터를 거의 같은 순간에 저장할 때 생기는 문제는 운영을 시작한 뒤에야 드러납니다. 대표적인 증상은 다음과 같습니다.

  • 재고 음수: 재고가 10개일 때 두 담당자가 동시에 7개씩 출고하면, 둘 다 "10개 있음"을 확인하고 통과해 재고가 -4가 됩니다
  • 중복 주문 번호: 마지막 번호를 조회한 뒤 1을 더하는 방식이면, 동시에 들어온 두 주문이 같은 번호를 받습니다
  • 덮어써진 결재 의견: A가 의견을 쓰는 동안 B가 금액을 고쳐 저장하면, 나중에 저장한 A의 화면이 B의 수정을 지워 버립니다
  • 이중 결제: 응답이 늦어 사용자가 버튼을 다시 누르면 같은 결제가 두 번 처리됩니다
  • 이런 문제는 수십~수백 밀리초 안에 요청이 겹칠 때만 생기기 때문에 재현이 어렵습니다. 로그에는 정상 처리 두 건만 남아 있어서, 원인을 찾는 데 몇 주씩 걸리는 경우가 흔합니다.

    동시성 문제의 유형

  • 갱신 손실(Lost Update): 두 트랜잭션이 같은 값을 읽고 각자 고쳐 쓰면서 앞선 수정이 사라지는 경우입니다
  • 중복 처리: 같은 요청이 재전송이나 재시도로 두 번 실행되는 경우입니다
  • 읽기와 판단 사이의 경합: "남은 수량을 확인한 뒤 차감한다"처럼 확인과 실행 사이에 다른 요청이 끼어드는 경우입니다
  • 이 문제는 두 갈래로 나눠서 봐야 합니다. 화면 편집 충돌은 사람이 몇 분씩 붙잡고 있는 데이터에서 생기고, 백엔드 처리 경합은 밀리초 단위로 겹치는 요청에서 생깁니다. 둘은 해법이 다릅니다.

    해결 방식 비교

    낙관적 잠금

    테이블에 version 컬럼을 두고, 저장할 때 "읽었을 때의 버전과 같을 때만 수정하고 버전을 1 올린다"는 조건을 겁니다. 수정된 행이 0건이면 그사이 누군가 먼저 저장한 것이므로 사용자에게 알립니다. 평소에는 잠금을 잡지 않아 성능 부담이 거의 없습니다. HTTP API에서는 ETag와 If-Match 헤더, 412 응답으로 같은 원리를 구현할 수 있습니다.

    비관적 잠금

    SELECT ... FOR UPDATE로 행을 잠그고 처리가 끝날 때까지 다른 트랜잭션을 기다리게 합니다. 확실하지만 대기가 길어질 수 있고, 두 트랜잭션이 서로 상대의 잠금을 기다리면 교착(데드락)이 생깁니다. 잠그는 순서를 항상 같게 정하고(예: 상품 ID 오름차순), lock_timeout이나 NOWAIT로 대기 시간에 상한을 두어야 합니다.

    원자적 갱신과 제약 조건

    읽고 계산해서 쓰는 대신 한 문장으로 처리합니다. "UPDATE 재고 SET 수량 = 수량 - 7 WHERE 상품 = ? AND 수량 >= 7"처럼 쓰면 확인과 차감이 한 번에 일어나므로 재고가 음수가 되지 않습니다. 주문 번호는 DB 시퀀스로 발급하고 유니크 키를 걸어 두면, 코드에 실수가 있어도 DB가 중복을 막아 줍니다.

    멱등성 키

    클라이언트가 요청마다 고유한 키를 보내고, 서버는 이미 처리한 키면 처음 결과를 그대로 돌려줍니다. 결제·주문처럼 재시도가 잦은 API에는 사실상 필수입니다.

    업무별 적용 판단

  • 재고 차감·좌석 예약: 경합이 잦은 자원입니다. 원자적 갱신을 기본으로 하고, 여러 행을 함께 바꿔야 하면 정해진 순서로 비관적 잠금을 겁니다
  • 문서·결재 편집: 충돌은 드물지만 어느 쪽이 맞는지 사람이 판단해야 합니다. 낙관적 잠금이 맞습니다
  • 배치와 온라인 처리가 겹칠 때: 야간 정산 배치가 몇 분 동안 수천 행을 잠그면 온라인 업무가 멈춥니다. 1,000건 단위로 나눠 커밋하고, 대상 행을 버전 조건으로 갱신해 온라인 수정분을 덮어쓰지 않게 합니다
  • 사용자 경험까지 설계하기

    충돌을 감지만 하고 "저장 실패"만 띄우면 사용자는 입력한 내용을 잃습니다. "다른 사용자가 먼저 수정했습니다" 안내와 함께 내 입력값과 최신 값을 나란히 보여 주고, 항목별로 고르게 해야 합니다. 편집 시작 시 "홍길동 님이 편집 중" 같은 소프트 잠금을 표시하면 충돌 자체가 줄어듭니다. 이 표시는 브라우저를 닫아도 남지 않도록 15~30분 정도의 만료 시간을 두고 자동으로 해제해야 합니다.

    검증과 운영

    기능 테스트로는 경합을 잡을 수 없습니다. 같은 상품에 50~100개의 요청을 동시에 보내는 부하 테스트를 만들고, 끝난 뒤 재고 합계·주문 번호 중복·결제 건수가 맞는지 확인합니다. 운영에서는 버전 충돌, 잠금 대기 초과, 멱등성 키 재사용을 감사 로그로 남겨 두면 어느 화면에서 충돌이 잦은지 수치로 보고 개선할 수 있습니다.

    POLYGLOTSOFT는 ERP·MES·WMS 같은 업무 시스템을 설계할 때 동시성 제어와 충돌 안내 화면까지 함께 다룹니다. 구독형 개발 서비스로 새 시스템을 만들 때 처음부터 반영할 수 있고, 이미 운영 중인 시스템은 SM 서비스로 재고 불일치나 중복 처리 같은 문제의 원인을 찾아 개선해 드립니다. 비슷한 증상을 겪고 계시다면 편하게 문의해 주십시오.