에그코드 기술 이야기 · 데이터베이스 Lock 시리즈 ①
안녕하세요, 에그코드입니다.
저희는 자체 서비스를 직접 운영하며 웹·SaaS·MVP를 만드는 개발사입니다.
서비스를 키우다 보면 반드시 부딪히는 벽이 있습니다. 바로 동시성입니다.
이번 「데이터베이스 Lock」 시리즈는 총 6편에 걸쳐 이 문제를 파고듭니다. 그 시작인 1편, 낙관적 락부터 정리해 보겠습니다.

동시성 문제, 대체 언제 생길까
재고가 1개 남은 상품이 있습니다. 두 사용자가 거의 동시에 주문을 넣습니다.
둘 다 "재고 1개"를 읽고, 각자 재고를 0으로 씁니다. 결과는 재고가 꼬이거나, 주문 하나가 조용히 사라지는 겁니다.
이렇게 읽고 → 바꾸고 → 저장하는 사이에 다른 요청이 끼어드는 상황. 이걸 갱신 분실(Lost Update) 이라고 부릅니다.
잔액·포인트·재고·좋아요·동시 예약처럼, 같은 데이터를 여러 요청이 함께 고칠 때 동시성 체크가 필요합니다.
낙관적 락이란 무엇인가
낙관적 락은 이름 그대로 낙관적으로 가정합니다. "충돌은 거의 안 일어난다"고 믿고 일단 그냥 진행합니다.
데이터를 미리 잠그지 않습니다. 대신 저장하는 순간에만 확인합니다.
"내가 읽은 뒤에 누가 먼저 바꿨나?" 바뀌었다면 그때 되돌리고 다시 시도합니다.

어떻게 충돌을 알아챌까 — 버전(version)
비결은 버전 번호 하나입니다. 각 행에 version 컬럼을 두고, 수정될 때마다 1씩 올립니다.
저장할 때 SQL은 이렇게 나갑니다. "version이 내가 읽은 값과 같을 때만 수정하라."
누군가 먼저 고쳤다면 version이 이미 올라가 있어서, 바뀐 행이 0개가 됩니다. 바로 이 순간 충돌을 감지합니다.
JPA에서는 @Version 필드 하나면 끝입니다. 충돌이 나면 OptimisticLockException이 발생하고, 보통 다시 읽어서 재시도하는 식으로 처리합니다.
낙관적 락은 DB가 직접 거는 락이 아니라, 애플리케이션이 버전으로 판정하는 방법입니다.
그래서 언제 쓰면 좋을까
| 낙관적 락이 잘 맞는 곳 | 비관적 락을 고려할 곳 |
|---|---|
| 읽기가 압도적으로 많다 | 같은 행 충돌이 잦다 |
| 같은 행 충돌이 드물다 | 재시도 실패 비용이 크다 |
| 높은 동시성·빠른 응답이 중요 | 짧고 확실한 쓰기 위주 |
| 트랜잭션을 오래 못 잡는 상황 | → 다음 편(2편)에서 다룸 |
낙관적 락이 빛나는 경우는 분명합니다. 읽기가 많고 충돌은 드문 서비스입니다.
평소엔 락 비용 없이 빠르게 돌아가고, 어쩌다 부딪힐 때만 재시도하면 되니까요.
반대로 충돌이 잦으면 재시도가 계속 쌓여 오히려 느려집니다. 이럴 땐 아예 미리 잠그는 방식이 낫습니다. 그게 다음 편에서 다룰 비관적 락입니다.

정리하며
낙관적 락은 "충돌은 드물다"는 가정 위에서, 버전으로 갱신 분실을 막는 방법입니다.
물론 모든 상황의 정답은 아닙니다. 충돌 빈도와 트래픽에 따라 선택은 달라집니다.
저희 에그코드는 자체 서비스를 운영하며, 서비스마다 어떤 방식이 맞는지 매번 고민해 정합성 문제가 없도록 골라 적용합니다.

다음 편 예고 — 2편 «비관적 락». 무조건 먼저 잠그는 방식은 뭐가 다를까요?
함께 읽어보세요 — 동시성 제어, 결국 뭘 언제 쓸까 — 선택 기준 3가지 · 데드락과 라이브락, 동시성 함정 2가지 · 커넥션 풀, 몇 개로 설정해야 할까 · 에그코드 웹 개발 서비스
에그코드는 자체 서비스를 운영하며 웹·SaaS·MVP를 만듭니다. 서비스에 맞는 락 전략을 함께 고민합니다.
동시성·데이터 정합성이 걱정된다면 문의하기