두 담당자가 같은 신청서를 열었습니다. A는 금액을 수정했고 B는 담당 부서를 수정했습니다. A가 먼저 저장한 뒤 B가 오래된 화면에서 저장하면, 시스템 구현에 따라 A의 금액 수정이 조용히 사라질 수 있습니다. 이런 분실 갱신(lost update)은 접수·결재·고객 정보 관리 화면처럼 여러 사람이 같은 레코드를 만지는 서비스에서 쉽게 발생합니다. 누가 마지막으로 버튼을 눌렀는지가 업무상 정답을 결정해서는 안 됩니다.
읽은 시점과 저장하는 시점 사이에 시간이 있다
웹 화면은 데이터를 읽고 사용자가 생각하는 몇 분 동안 열려 있습니다. 그 사이 다른 사용자가 값을 바꿀 수 있습니다. 단순한 UPDATE ... WHERE id = ?는 내가 처음 읽은 값이 여전히 최신인지 확인하지 않습니다. 해결의 출발점은 레코드에 버전 번호나 갱신 토큰을 붙이는 것입니다. 화면을 열 때 버전 7을 받았다면 저장할 때도 “버전 7인 경우에만 변경”을 요청합니다. 다른 사람이 먼저 저장해 버전이 8이 됐다면 갱신된 행 수가 0이 되어 충돌을 감지합니다.
EF Core의 동시성 처리 문서도 동시성 토큰의 원래 값과 데이터베이스의 현재 값을 비교해 충돌을 확인하는 방식과 예외 처리를 설명합니다. SQL Server의 rowversion처럼 DB가 관리하는 토큰도 있고, 애플리케이션이 관리하는 정수 버전도 있습니다. 사용하는 DB와 업데이트 경로에 맞춰 택해야 합니다. 단지 화면에 타임스탬프를 보여 주는 것으로는 충분하지 않습니다. 실제 UPDATE의 조건에 버전이 들어가야 합니다.

충돌을 발견한 다음이 더 중요하다
충돌이 나면 새 데이터를 다시 읽고 사용자에게 바뀐 필드를 보여 주세요. “다른 사용자가 수정했습니다. 새 내용을 확인하고 다시 저장하세요”라는 흐름이 필요합니다. 무조건 자동 재시도해 오래된 폼을 덮어쓰면 동시성 검사가 있어도 분실 갱신은 돌아옵니다. 서로 다른 필드만 수정했으므로 자동 병합이 가능해 보일 때도 업무 규칙을 확인해야 합니다. 금액과 담당 부서는 서로 영향이 없을 수도 있지만, 결재 단계와 승인자 변경은 함께 검토해야 할 수 있습니다.
특히 상태 전이는 필드 수정과 분리해서 생각해야 합니다. 문서가 승인된 후에 오래된 화면에서 ‘임시 저장’을 보내면 승인 상태를 되돌릴 수 있습니다. 저장 명령에 허용된 현재 상태와 버전을 함께 검사하고, 권한도 저장 시점에 다시 확인합니다. 클라이언트가 보낸 버전 숫자는 충돌 감지에 쓰는 힌트이지 권한을 부여하는 증명서가 아닙니다. 서버는 매번 실제 사용자와 객체의 관계를 검증해야 합니다.
실무에서는 충돌 메시지를 지나치게 기술적으로 쓰는 것도 문제입니다. “DbUpdateConcurrencyException”이라는 경고보다 “김 담당자가 14:32에 수정한 내용이 있습니다. 내 입력을 복사해 두고 최신 내용을 확인해 주세요”가 행동에 도움이 됩니다. 충돌 화면을 닫아도 사용자가 방금 입력한 내용이 사라지지 않도록 임시 보관하고, 개인정보가 포함된 초안이라면 저장 위치와 만료 시간을 명확히 정합니다. 충돌을 안전하게 감지해도 사용자의 입력을 잃으면 결국 다른 형태의 분실 갱신입니다.
HTTP API에서는 조건부 요청을 사용할 수 있다
REST API라면 응답에 ETag를 주고 클라이언트가 수정 시 If-Match를 보내는 패턴을 고려할 수 있습니다. 서버가 현재 ETag와 다르면 조건 실패를 반환하고, 사용자는 최신 표현을 다시 받아 비교합니다. API가 아닌 내부 폼이라도 같은 논리를 숨은 버전 필드와 서버 검증으로 구현할 수 있습니다. 중요한 것은 충돌 결과를 성공으로 위장하지 않는 것입니다. 충돌 건수와 사용자 재시도율을 기록하면 협업 화면이 실제로 얼마나 자주 부딪히는지도 알 수 있습니다.

테스트는 두 브라우저를 동시에 열어 같은 버전에서 서로 다른 변경을 저장해 보는 것으로 시작합니다. 첫 저장만 통과하고 두 번째는 명확한 충돌이 되어야 합니다. 그 뒤 삭제와 복원, 승인과 수정, 자동 배치와 수동 편집처럼 다른 경로가 같은 레코드를 건드리는 사례까지 확장합니다. 버전 검사는 한 화면만이 아니라 모든 쓰기 경로에 적용되어야 합니다. 협업 기능이 좋은 제품은 충돌이 전혀 없는 제품이 아니라 충돌이 일어났을 때 누구의 작업도 조용히 지우지 않는 제품입니다.