티스토리 뷰
0. 배경
자바 EE 어플리케이션 개발 시, 로컬 트랜잭션과 글로벌 트랜잭션의 필요성에 의해 각각의 스펙과 구현들이 등장
트랜젝션의 방식 | 내용 |
1. 로컬 트랜잭션 (Local Transaction) | - 단일 데이터소스(단일 데이터베이스) + 단일 Connection 에 종속 :: 하나의 DB 연결만을 다루며, 트랜잭션이 하나의 DB 내에서만 수행 - JDBC 기반에서 흔히 사용 |
2. 글로벌 트랜잭션 (Global Transaction) | - 다수 데이터소스(다수 데이터베이스) + 단일 Connection 종속성 제거 :: 여러 DB에서의 트랜젝션 - 2PC (2-Phase Commit Protocol) 또는 XA 프로토콜로 커밋 / 롤백 - JTA와 JNDI 기술을 통해 트랜잭션과 데이터소스를 연결하고 관리 |
- 구현체마다 동일한 로직은 AbstarctPlatformTransactionManager 내 일부분이 구현 -> 재사용
1) 기본 구현체는 DataSourceTransactionManager
2) Spring Data JPA 사용 시,JpaTransactionManager로 설정
- Spring Transaction :: 로컬 / 글로벌 트랜잭션의 단점을 보완한 Spring 표준 트랜잭션
- 트랜잭션 동기화
:: 트랜잭션을 유지하려면 Connection 유지가 필요 -> 별도의 공간에 보관
- 기존에는 다수 데이터소스에 대한 트랜잭션을 위해 Connection 객체를 파라미터로 계속 넘겨줌
- 보완 내용 :: TransactionSynchronizationManager + DataSourceUtils 를 통해 Connection 보관 및 사용 - 트랜잭션 추상화
:: 트랜잭션의 공통 부분을 추상화 -> 어떤 기술, 로컬 / 글로벌 트랜잭션 모두 사용 가능한 표준형상
- 기존에는 트랜잭션 기술마다 각 정의된 트랜잭션 세부 구현체들을 가져다 구현이 필요했음
- PlatformTransactionManager 인터페이스 구현체 통해 트랜잭션 시작과 끝 Commit, Rollback을 표기
- JTA TransactionManager 와 구별 위해 PlatformTransactionManager라 명칭
- 트랜잭션 동기화
상세한 내용들은 아래에 기술
1. 트랜잭션 동기화
:: TransactionSynchronizationManager내 Connection 보관
:: 이전 포스팅의 JDBC API에서는 트랜잭션 동기화를 위해 매번 Connection을 파라미터로 전달
:: Spring은 트랜잭션의 동기화를 위해 Connection을 꺼내 사용할 수 있는 중간 저장소를 제공
구분 | 내용 |
TransactionSynchronizationManager | :: 스레드 기반으로 Connection 을 저장 - 내부적으로 ThreadLocal 을 사용하여, 스레드마다 독립적으로 Connection 객체 보관 -> 멀티 스레드 환경에서도 유리 |
DataSourceUtils | :: 저장된 Connection 을 꺼내어 사용할 수 있도록 도움 onnection 가져올때 DataSource 가 필요하기 때문에 설정 필요 |
// application.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/database-name
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
1.1 예시
항목 | 내용 | ||
- TransactionSynchronizationManager + DataSourceUtils | :: ThreadLocal 기반 Connection 보관을 하여, :: (1) 트랜잭션 생성 + (3) 트랜잭션 처리 ← 명시적 |
||
- DataSourceUtils | :: 어디서든 Connection 꺼내쓸 수 있게 제공하여, :: (2) 트랜잭션 참여 ← 명시적 |
===> 위를 통해 아래와 같이 단축
과정 | 전 | 후 |
1) 트랜잭션 생성 | Connection 생성 | TransactionSynchronizationManager가 관리 |
2) 트랜잭션 참여 | 기존에 앞서 생성된 Connection 에 연결 | DataSourceUtils (파라미터 미사용) |
3) 트랜잭션 처리 | 성공 시 Commit 혹은 실패 시 Rollback | TransactionSynchronizationManager가 관리 |
EX)
1) 기존 코드 MessagejdbcApiDao.java
// ...
public List<Message> save(final Connection connection, Integer userId, String message) throws SQLException {
if (true) {
throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "트랜잭션 롤백 여부를 확인하기 위한 의도된 예외");
}
// ...
- 변경 후의 코드
// ...
public List<Message> save(/* final Connection connection, */Integer userId, String message) throws SQLException {
if (true) {
throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "트랜잭션 롤백 여부를 확인하기 위한 의도된 예외");
}
Connection connection = DataSourceUtils.getConnection(dataSource);
// ...
2) 기존의 코드 UserService.java
public UserResponseDto save(String name, Integer age, String job, String specialty) {
Connection connection = null;
try {
connection = dataSource.getConnection(); // Connection 생성
connection.setAutoCommit(false); // Connection Auto-Commit 옵션 끄기
User user = userJdbcRepository.save(connection, name, age, job, specialty);
List<Message> messages = messageJdbcRepository.save(connection, user.getId(), user.getName() + "님 가입을 환영합니다.");
connection.commit(); // (A) Commit
UserResponseDto result = UserResponseDto.from(user);
result.setMessages(messages);
return result;
} catch (SQLException e) {
try {
connection.rollback(); // (B) Rollback
} catch (final SQLException ignored) {
}
throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "자원 반납 시 문제가 있습니다.");
} finally {
try {
connection.close(); // (C) Close
} catch (final SQLException ignored) {
}
}
}
- 이후 적용 코드
public UserResponseDto save(String name, Integer age, String job, String specialty) {
// 트랜잭션 초기화
TransactionSynchronizationManager.initSynchronization();
// Connection 생성
Connection connection = DataSourceUtils.getConnection(dataSource);
try {
connection.setAutoCommit(false); // Connection Auto-Commit 옵션 끄기
// 사용자 정보 저장
User user = userJdbcRepository.save(name, age, job, specialty);
// 메시지 저장
List<Message> messages = messageJdbcRepository.save(user.getId(), user.getName() + "님 가입을 환영합니다.");
connection.commit(); // (A) Commit
// 응답 DTO 생성
UserResponseDto result = UserResponseDto.from(user);
result.setMessages(messages);
return result;
} catch (SQLException e) {
try {
connection.rollback(); // (B) Rollback
} catch (final SQLException ignored) {
}
// 예외 처리
throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "자원 반납 시 문제가 있습니다.");
} finally {
// Connection 반납
DataSourceUtils.releaseConnection(connection, dataSource);
}
}
1.2 트랜잭션 추상화
- Spring의 DB 접근 기술 :: 순수 JDBC, JPA, R2DBC, 등 - 서로 다른 트랜잭션 처리 방법임
1) 기술을 바꿀 경우, 트랜잭션을 사용하는 코드를 모두 수정 필요.. ㅠㅠ
2) 이에 Spring은 TransactionManager 라는 트랜잭션 공통 부분을 추상화 솔루션을 통해 일부분 공통화
- 트랜잭션의 공통 부분인 PlatformTransactionManager 인터페이스를 각 기술별 구현체로 사용
- Spring은 각 DB 기술에 대한 TransactionManager 구현체를 자동으로 Bean에 등록
기술 | 내용 |
1) DataSourceTransactionManager (로컬) | Spring 의 DataSource Bean 사용 및 MyBatis 의 SqlSessionTemplate 에도 적용 |
2) JpaTransactionManager (로컬) | - |
3) HibernateTransactionManager (로컬) | - |
4) JtaTransactionManager (글로벌) | 2PC + XA 프로토콜이 적용된 JTA 사용하여 다수 데이터소스에 글로벌 트랜잭션을 적용 |
5) JmsTransactionManager (로컬) | JMS(Java Message System) 메세지 큐 서버에 트랜잭션을 적용하고싶을때 |
1.2.1 예시
@Service
@RequiredArgsConstructor
public class SomeService {
// 환경에 맞는 트랜잭션 매니저 Bean 주입도 가능
// private final PlatformTransactionManager transactionManager;
private final DataSource dataSource;
public UserResponseDto save(String name, Integer age, String job, String specialty) {
// 여기에서는 Bean 주입이 아닌 직접 정의하여 사용
PlatformTransactionManager transactionManager = new DataSourceTransactionManager(dataSource);
// 트랜잭션 세부 정의 : Propagation, Isolation, Timeout, ReadOnly
TransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
// 옵션 정의 예시
// TransactionDefinition definition = new DefaultTransactionDefinition() {{
// setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); // Propagation :: 전파 범위, 새로운 트랜잭션 시작
// setIsolationLevel(TransactionDefinition.ISOLATION_READ_UNCOMMITTED); // Isolation Level :: 고립 레벨, 가장 낮은 고립 수준
// setTimeout(30); // Timeout :: 롤백되는 한계시간 표기, 30초 후 타임아웃
// setReadOnly(true); // Read-only :: 읽기 전용 트랜잭션
// }};
// 1. 트랜잭션 시작
TransactionStatus status = transactionManager.getTransaction(transactionDefinition);
try {
// 비즈니스 로직 실행
...
// 2.1. 트랜잭션 정상 종료
transactionManager.commit(transactionStatus);
} catch (Exception e) {
// 2.2. 트랜잭션 오류 종료
transactionManager.rollback(transactionStatus);
}
}
}
=> 이처럼 추상화 도입을 통해 부모에서 Connection에 대한 처리를 안해도 된다.. (Spring의 자동 주입)
=> 이해를 위해, React와 비교하자면 전역 상태 관리였던 Redux안에 저장된 값을 공유하듯이 여기서는 ThreadLocal에서 전역적으로 연결을 관리(= 하나의 연결에 대해 부모에서 Prop Drilling처럼 내려주기를 안해도 된다! 개꿀)
1.2.2 PlatformTransactionManager 사용 시 JdbcTemplate 의 의의
- 1 쿼리 : 1 트랜잭션은 변화가 없지만, N 쿼리 : 1 트랜잭션의 경우,
DataSourceUtils 없이도 트랜잭션의 추상화 / 동기화 / 고도화가 가능해졌다.- 트랜잭션 추상화는 (1) PlatformTransactionManager을 통해 해결
- 트랜잭션 추상화 + 트랜잭션 동기화 (TransactionSyncrhonizationManager)
- (2-1) 선언적(Declarative) 트랜잭션 관리 :: @Transactional (Spring AOP 사용)
- (2-2) 프로그래밍형(Programmatic) 트랜잭션 관리 :: TransactionTemplate
- 즉, JDBC Template에서 PlatformTransactionManager를 통해,
1) 명시해줘야했던 Connection을 내부에서 사용함으로써, Connection의 내부화(트랜잭션 참여의 내재화 / 동기화)
2) 모든 트랜잭션 로직의 구현체가 무엇이든 상관없이 일관된 처리가 가능해졌다.
Ex) PlatformTransactionManager + JDBC Template을 통한 고수준의 동기화
- 추가적인 예시

절차 | 내용 |
1) Transaction 시작 | - 새 PlatformTransactionManager 생성 |
2) Datasource 에서 Connection 획득 | - 구현체가 DataSourceTransactionManager이므로, PlatformTransactionManager.getTransaction(definition) |
3) 획득한 Connection 을 TransactionSynchronizationManager 의 ThreadLocal 영역에 저장 | |
4) Repository 에서 TransactionSynchronizationManager 을 통해 Connection 꺼내와서 처리 | - JDBC API 사용 시 :: DataSourceUtils 에서 Connection - JdbcTemplate 사용 시 :: 알아서 자동으로 Connection |
5) 로직 수행 완료 | |
6) Transaction commit or rollback | - PlatformTransactionManager.commit(status) - PlatformTransactionManager.rollback(status) |
7) TransactionSynchronizationManager 의 ThreadLocal 영역에서 Connection 제거 | |
8) Datasource 에 Connection 반환 | = DataSourceUtils.releaseConnection(connection, dataSource) |
참고
ASAC 수업자료
'정리용 > DB' 카테고리의 다른 글
[DB 기초] 7-2. 선언형 트랜잭션의 주의점 (0) | 2025.03.25 |
---|---|
[DB 기초] 7-1. Spring Transaction 방법 2가지 : 프로그래밍형 / 선언형 (0) | 2025.03.24 |
[DB 기초] 6-2. JDBC Template (0) | 2025.03.19 |
[DB 기초] 6-1. JDBC API / Driver (0) | 2025.03.19 |
[DB 기초] 6. DB 설정 및 연결 (0) | 2025.03.19 |
- Total
- Today
- Yesterday
- memo
- asac#asac7기
- acac
- useState
- git
- useMemo
- asac7#asac
- useEffect
- useReducer
- Nginx
- asac7기
- useLayoutEffect
- ASAC
- ssh
- useRef
- asac7
- acas#acas7기
- react
- useCallback
- useContext
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 |