트랜잭션
트랜잭션은 작업의 완전성을 보장해 준다. 즉, 논리적인 작업 셋을 모두 완벽하게 처리하거나, 처리하지 못할 경우에는 원 상태로 복구하여 작업의 일부만 적용되는 현상(Partial update)이 발생하지 않도록 해준다.
ACID
- 원자성(Atomicity)
- 트랜잭션 내에서 실행한 작업들은 모두 성공하거나 모두 실패해야 한다.
- 일관성(Consistency)
- 모든 트랜잭션은 일관성 있는 데이터베이스 상태를 유지해야 한다. 예를 들어 데이터베이스에서 정한 무결성 제약 조건을 항상 만족해야 한다.
- 격리성(Isolation)
- 동시에 실행되는 트랜잭션들이 서로에게 영향을 미치지 않도록 격리한다. 예를 들어 동시에 같은 데이터를 수정하지 못하도록 해야 한다. → 격리성은 동시성과 관련된 성능 이슈로 인해, 트랜잭션 격리 수준을 선택할 수 있다.
- 지속성(Durability)
- 트랜잭션을 성공적으로 끝내면 그 결과가 항상 기록되어야 한다. 중간에 시스템에 문제가 발생해도 데이터베이스 로그 등을 사용해서 성공한 트랜잭션 내용을 복구해야 한다.
데이터베이스 연결 구조와 DB 세션
데이터베이스의 세션(Session)은 클라이언트와 데이터베이스 간의 논리적 연결을 의미한다. 세션은 클라이언트가 데이터베이스와 상호작용하는 동안 상태와 컨텍스트를 유지하며, 이를 통해 여러 작업(쿼리 실행, 트랜잭션 관리 등)을 수행할 수 있다.

클라이언트가 데이터베이스와 연결(Connection)을 생성하면 데이터베이스 서버는 내부에 세션이라는 것을 만든다. 커넥션을 통한 모든 사용자의 요청은 이 세선을 통해서 실행된다. 즉, 세션이 생성되면 클라이언트는 해당 세션을 통해 쿼리 실행, 데이터 삽입/수정, 트랜잭션 관리 등의 작업을 수행하는 것이다. 세션은 트랜잭션의 범위와 상태를 관리하기 때문에 트랜잭션이 끝나지 않으면 세션도 유지된다.
비즈니스 로직과 트랜잭션
트랜잭션은 비즈니스 로직이 있는 서비스 계층에서 시작해야 한다. 비즈니스 로직이 잘못되면 해당 비즈니스 로직으로 인해 문제가 되는 부분을 함께 롤백해야 하기 때문이다. 이처럼 애플리케이션에서 DB 트랜잭션을 사용하려면, 트랜잭션을 사용하는 동안 같은 커넥션을 유지하여 하나의 섹션에서 비즈니스 로직을 처리해야 한다.
DB 락
DB 락(Database Lock)이란, 여러 트랜잭션이 동시에 동일한 데이터에 접근하려고 할 때 데이터 무결성과 일관성을 보장하기 위해 데이터베이스가 제공하는 동시성 제어 메커니즘이다.
동시성 문제를 해결하기 위한 방법 중 하나로 DB 락이 존재한다. DB에 락을 걸면, 세션이 트랜잭션을 시작하고 데이터를 수정하는 동안에는 커밋이나 롤백 전까지 다른 세션에서 해당 데이터를 수정할 수 없게 된다. 이러한 락은 트랜잭션이 롤백되거나 커밋되면 해제된다.
공유 락(Shared Lock, S-lock)
- 읽기 작업(Read)에 사용되기 때문에 읽기 잠금이라고도 한다.
- 다른 트랜잭션이 동시에 데이터를 읽는 것은 허용되지만, 쓰기 작업(Write)은 허용하지 않는다.
배타 락(Exclusive Lock, X-lock)
- 쓰기 작업(Write)에 사용되기 때문에 쓰기 잠금이라고도 한다.
- 배타적으로 락을 소유한 트랜잭션만 해당 데이터를 수정할 수 있으며 다른 트랜잭션은 데이터를 읽거나 수정할 수 없다.
스프링과 문제 해결 - 트랜잭션
트랜잭션 추상화

스프링은 트랜잭션 기능을 추상화하여 도중에 다른 데이터 접근 기술(JDBC, JPA 등)로 변경해도 코드의 큰 변화없이 쉽게 변경할 수 있도록 PlatformTransactionManager 라는 인터페이스를 상위에 구현해두었다.
트랜잭션 동기화
트랜잭션을 유지하기 위해서는 트랜잭션의 시작부터 끝까지 데이터베이스 커넥션을 유지해야 한다. 이를 위해 스프링은 트랜잭션 동기화 매니저를 제공한다.

트랜잭션 동기화 매니저는 쓰레드 로컬(Thread Local)을 사용해서 커넥션을 동기화 해준다. 때문에 멀티쓰레드 상황에서도 안전하게 커넥션을 동기화 할 수 있다. 트랜잭션 매니저(트랜잭션의 시작, 커밋, 롤백을 직접 관리하는 Spring의 핵심 컴포넌트)는 데이터소스를 통해 커넥션을 만들고 트랜잭션을 시작하고, 트랜잭션이 시작된 커넥션을 트랜잭션 동기화 매니저에 보관한다. 리포지토리는 트랜잭션 동기화 매니저에 보관된 커넥션을 꺼내서 사용함으로써 파라미터로 커넥션을 전달하지 않아도 커넥션을 유지할 수 있는 것이다. 모든 로직이 수행되고 트랜잭션이 종료되면, 트랜잭션 매니저는 트랜잭션 동기화 매니저에 보관된 커넥션을 통해 트랜잭션을 종료하고 커넥션을 닫는다.
쉽게 정리해서, 같은 트랜잭션을 유지하려면 같은 데이터베이스 커넥션을 사용해야 하고 이를 위해 트랜잭션 동기화 매니저가 사용되는 것이다.
트랜잭션 AOP
선언적 트랜잭션(@Transactional 어노테이션 사용)을 적용하면, 매우 편리하게 트랜잭션을 적용할 수 있다. 선언적 트랜잭션은 스프링 AOP 기술을 활용하는데 이 기술을 활용함으로써 서비스 계층에 순수한 비즈니스 로직만 남길 수 있다.

즉, 프록시를 사용하여 트랜잭션을 처리하는 객체와 비즈니스 로직을 처리하는 서비스 객체를 명확하게 분리할 수 있다. 이에 대한 더 자세한 동작과정은 DB 2편에서 다룬다.
'Spring' 카테고리의 다른 글
스프링 DB 2편 - 섹션 10~11 (0) | 2025.03.19 |
---|---|
스프링 DB 2편 - 섹션 9 (0) | 2025.03.19 |
스프링 DB 1편 - 섹션 1~2 (0) | 2025.03.18 |
스프링 핵심 원리 고급편 섹션2 (0) | 2025.03.17 |
빈으로 등록된 필터가 WebSecurity의 ignoring에 의해 무시되지 않는 이유 (0) | 2025.03.17 |