DB 락(Database Lock) 완벽 이해하기 - 개발자라면 꼭 알아야 할 개념
개발자 필수 개념 DB 락(Database Lock) 완벽 가이드. 동시성 문제 해결을 위한 낙관적 락과 비관적 락의 차이, 사용 사례, Spring Data JPA 적용법까지 쉽게 설명합니다. 데이터 무결성을 지키는 핵심 원리를 지금 확인하세요.
“은행 계좌에서 돈을 빼는데, 둘이 동시에 빼면 어떻게 될까?”
개발을 하다 보면 동시에 같은 데이터를 읽고, 수정하고, 삭제하는 상황을 피할 수 없습니다. 예를 들어, A와 B가 같은 은행 계좌에서 돈을 동시에 인출하려 한다고 해봅시다.
- A가 5만 원을 빼려는 순간 잔액: 10만 원
- B도 같은 순간 5만 원을 빼려는 요청을 보냄
- A와 B 모두 잔액 10만 원을 보고 인출 → 최종 잔액이 0원이 아닌 5만 원이 되어버림!
데이터 무결성(Data Integrity) 이 깨집니다.
이 문제를 해결하는 핵심 개념이 바로 DB 락(Database Lock) 입니다.
DB 락이란?
“여러 명이 동시에 같은 데이터를 건드릴 때, 꼬이지 않도록 잠시 잠가두는 안전장치”
데이터베이스는 다중 사용자가 접근하는 시스템이기 때문에, 한 사용자가 데이터를 읽거나 수정하는 동안 다른 사용자의 작업을 잠시 막는 방법이 필요합니다.
그게 바로 락(Lock) 이죠.
왜 락이 필요할까? (은행 계좌 예시)
- A: “내 계좌에서 5만 원 인출할게”
- B: “나도 5만 원 인출할래”
DB가 락을 걸지 않으면, A와 B가 동시에 잔액을 조회(10만 원) → 각자 5만 원 빼서 저장 → 최종 잔액이 5만 원이 남아버리는 문제가 생깁니다.
하지만 DB가 ‘잠깐! A 먼저 끝낼 때까지 기다려!’ 하고 락을 건다면? ➡ A의 작업이 끝나고 나서야 B가 처리되므로 정확한 결과(잔액 0원) 가 유지됩니다.
DB 락의 두 가지 철학
DB 락에는 두 가지 방식이 있습니다.
1. 낙관적 락 (Optimistic Lock)
“동시에 접근해도 괜찮겠지? 근데 마지막에 확인은 해볼게.”
- 데이터를 읽을 때 락을 걸지 않음
- 대신 버전(version) 필드를 두고, 커밋(저장)할 때 변경 여부 확인
- 누군가 먼저 수정했다면 에러(충돌) 발생 → 다시 시도해야 함
예시
- 게시글 좋아요 수 같은 기능 → 여러 명이 동시에 눌러도 조금 늦게 업데이트되면 괜찮음
비유
- “책을 빌릴 때 자물쇠 안 걸고 그냥 빌려줌. 나중에 반납할 때 ‘책이 다른 사람에 의해 수정됐나?’ 확인 후 충돌 시 다시 처리”
장점: 락을 거의 안 걸기 때문에 성능이 좋음
단점: 저장할 때 충돌 나면 재시도 로직 필요
2. 비관적 락 (Pessimistic Lock)
“혹시 모르니 내가 끝날 때까지 아무도 건들지 마!”
- 데이터를 읽는 순간부터 DB가 잠금 걸기
- 다른 트랜잭션은 읽기/쓰기 모두 대기 상태 (잠금 해제될 때까지 기다림)
- SQL에서는
SELECT FOR UPDATE로 구현
예시
- 은행 계좌, 항공권 좌석, 콘서트 티켓 → 절대 꼬이면 안 되는 데이터
비유
- “내가 책 빌리면 사서가 ‘이 책은 지금 대출 중이니 건들지 마세요’라고 딱지 붙임”
장점: 충돌이 거의 없음 (안전성 최고)
단점: 락이 오래 걸리면 다른 사용자가 기다려야 함 → 성능 저하 가능
DB 락의 동작 방식
DB 락은 트랜잭션(Transaction) 단위로 동작합니다.
- 트랜잭션 시작
- 데이터를 읽으면서 락을 걸기 (비관적 락의 경우)
- 수정 후 커밋
- 커밋이 완료되면 락 해제
그래서 락을 쓸 때는 트랜잭션을 짧게 유지하는 게 중요합니다.
언제 어떤 락을 써야 할까?
낙관적 락 (Optimistic Lock)
- 동시성이 많지만 충돌 가능성은 적을 때
- 예: 게시글 좋아요 수, 상품 조회수, 포인트 적립
비관적 락 (Pessimistic Lock)
- 꼬이면 절대 안 되는 핵심 로직
- 예: 은행 계좌 잔액 변경, 티켓 좌석 예매, 창고 재고 처리
Spring에서 DB 락 쓰는 법
Spring Data JPA에서는 @Lock 어노테이션으로 쉽게 DB 락을 사용할 수 있습니다.
1
2
3
4
5
public interface AccountRepository extends JpaRepository<Account, Long> {
@Lock(LockModeType.PESSIMISTIC_WRITE) // 비관적 락
@Query("SELECT a FROM Account a WHERE a.id = :id")
Optional<Account> findByIdForUpdate(@Param("id") Long id);
}
마무리
DB 락은 동시성 문제를 해결하는 가장 기본적인 도구입니다.
- 낙관적 락(Optimistic) -> “충돌 안 나겠지? 근데 저장할 때 체크는 할게”
- 비관적 락(Pessimistic) -> “내가 끝날 때까지 아무도 건들지 마!”
그리고 락을 쓸 땐 트랜잭션 길이를 최대한 짧게 유지하는 게 핵심!
락을 오래 잡아두면 다른 작업이 모두 기다리기 때문에 시스템 전체 성능이 떨어질 수 있습니다.
