DB 락(Database Lock) 완벽 이해하기 - 개발자라면 꼭 알아야 할 개념

“은행 계좌에서 돈을 빼는데, 둘이 동시에 빼면 어떻게 될까?”

개발을 하다 보면 동시에 같은 데이터를 읽고, 수정하고, 삭제하는 상황을 피할 수 없습니다.  예를 들어, 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) 단위로 동작합니다.

  1. 트랜잭션 시작
  2. 데이터를 읽으면서 락을 걸기 (비관적 락의 경우)
  3. 수정 후 커밋
  4. 커밋이 완료되면 락 해제

그래서 락을 쓸 때는 트랜잭션을 짧게 유지하는 게 중요합니다.


언제 어떤 락을 써야 할까?

낙관적 락 (Optimistic Lock)

  • 동시성이 많지만 충돌 가능성은 적을 때
  • 예: 게시글 좋아요 수, 상품 조회수, 포인트 적립

비관적 락 (Pessimistic Lock)

  • 꼬이면 절대 안 되는 핵심 로직
  • 예: 은행 계좌 잔액 변경, 티켓 좌석 예매, 창고 재고 처리

Spring에서 DB 락 쓰는 법

Spring Data JPA에서는 @Lock 어노테이션으로 쉽게 DB 락을 사용할 수 있습니다.

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) -> “내가 끝날 때까지 아무도 건들지 마!”

그리고 락을 쓸 땐 트랜잭션 길이를 최대한 짧게 유지하는 게 핵심!

락을 오래 잡아두면 다른 작업이 모두 기다리기 때문에 시스템 전체 성능이 떨어질 수 있습니다.




    Enjoy Reading This Article?

    Here are some more articles you might like to read next:

  • 무료 프록시, 크롤러 실패의 지름길, 유료 프록시가 필수인 7가지 기술적 이유
  • 파이썬 웹 크롤링 완벽 가이드 - 현업 데이터 엔지니어의 실전 노하우
  • AI 글쓰기 품질을 높이는 프롬프트 엔지니어링 8단계 (실전 템플릿 포함)
  • AI 시대, 경쟁력 있는 사람이 되는 법, 효과적인 프롬프트 작성 가이드
  • AI를 믿을 수 있을까? 인간이 할루시네이션을 구분할줄 알아야 한다.