공공부하자개발 · 영어 학습 노트
자바
실무 확장Excel · 파일 업로드 · DB 연동0/22 완료
  • 01Excel(XLSX) 구조와 순수 JDK로 읽기/쓰기
  • 02Apache POI로 Excel 업로드/다운로드
  • 03파일 업로드/다운로드 서버 (HttpServer)
  • 04JDBC 기초와 트랜잭션 (H2)
  • 05MyBatis 어노테이션 매퍼로 쿼리 연동
  • 06MyBatis XML 매퍼 · Oracle 방언 · PageHelper · Spring Boot
  • 07REST API 서버와 JSON
  • 08Vue 3 SPA 와 Java 서버 연동
  • 09@Scheduled 운영
  • 10로깅 실무: 레벨·계층, MDC 추적, 예외·성능, 마스킹, 롤링, JSON 로그
  • 11외부 API 연동
  • 12테스트 실무
  • 13암호화·개인정보 보호
  • 14인코딩·한글 실무
  • 15@Transactional 심화
  • 16긴 작업 비동기 처리와 진행률
  • 17SFTP·FTP 파일 연계
  • 18로컬 캐시와 @Cacheable
  • 19메일·알림 발송
  • 20웹 보안 체크리스트
  • 21빌드 도구와 폐쇄망 의존성 반입
  • 22성능 측정: p50·p95·p99, 측정 계층, JFR, JMH 함정, 자체 부하 테스트, 병목 순위
사이트 소개개인정보처리방침연락처
© 2026 공부하자
홈 › 실무 확장 › 15 / 22

@Transactional 심화

전파·격리·readOnly·롤백 규칙·self-invocation·REQUIRES_NEW·락
섹션 7진행 0 / 22
1왜 배우는가2핵심 원리3코드 예제4응용 변형 예제5자주 하는 실수 (Tip)6연습 문제7정리‹ 이전다음 ›

3. 코드 예제

실행 방법입니다. H2 인메모리 DB 를 쓰므로 lib 의 H2 jar 가 필요합니다.

powershell
cd java-src\extension\15_transaction
C:\project\jdk-21.0.8\bin\javac -encoding UTF-8 -cp "..\..\lib\*" *.java
C:\project\jdk-21.0.8\bin\java -Dstdout.encoding=UTF-8 -cp "..\..\lib\*;." Main   # 약 1초

Tx — 커스텀 어노테이션 + 동적 프록시 트랜잭션 매니저

java
// Tx.invoke: 프록시가 실제 호출 앞뒤로 트랜잭션을 관리한다
Ctx outer = CURRENT.get();
boolean requiresNew = tx.propagation() == Propagation.REQUIRES_NEW;
Ctx ctx;
boolean owner; // 이 호출이 커밋·롤백을 책임지는 경계인가
if (outer == null || requiresNew) {
    ctx = new Ctx(newConnection());
    CURRENT.set(ctx);
    owner = true;
} else {
    ctx = outer; // REQUIRED: 기존 트랜잭션에 그대로 참여
    owner = false;
}

REQUIRED 는 이미 열린 트랜잭션이 있으면 그 커넥션을 그대로 쓰고, REQUIRES_NEW 는 무조건 새 커넥션을 열어 ThreadLocal 에 갈아 끼웁니다. 메서드가 끝나면 finally 에서 원래 커넥션으로 되돌립니다.

rollback-only 처리

java
// 내부(owner=false) 호출이 실패하면 즉시 롤백하지 않고 표시만 한다
} else if (tx.rollbackFor().isInstance(cause)) {
    ctx.rollbackOnly = true;
}

바깥 메서드가 이 예외를 삼키고 정상 종료하면, finish() 는 rollbackOnly 를 보고 커밋 대신 롤백한 뒤 UnexpectedRollbackException 을 던집니다. 2.7 절에서 설명한 그대로입니다.

데모 실행 결과

text
1) 정상 이체 -> 커밋
  잔액: A=4000 B=1000
2) 잔액 부족 -> RuntimeException -> 전체 롤백
  예외: 잔액 부족: A balance=4000
  잔액: A=4000 B=1000
3-a) checked 예외 + 기본 rollbackFor -> 커밋됨
  예외: 사후 감사 실패(기본 규칙이라 커밋됨)
  잔액: A=3500 B=1500
3-b) checked 예외 + rollbackFor=Exception.class -> 롤백됨
  예외: 사후 감사 실패(rollbackFor=Exception 이라 롤백)
  잔액: A=3500 B=1500
4) REQUIRES_NEW 실패 로그는 바깥 롤백과 무관하게 남음
  예외: 잔액 부족: A balance=3500
  잔액: A=3500 B=1500
  failure_log 건수: 1
5) 내부 실패를 바깥이 catch -> commit 시 UnexpectedRollbackException
  예외: 내부 트랜잭션이 rollback-only 로 표시돼 커밋할 수 없습니다
  잔액: A=3500 B=1500
6) self-invocation: 프록시 없이 impl 을 직접 호출
  예외: 트랜잭션 밖에서는 DB 접근을 금지합니다(self-invocation?)

3-a 와 3-b 는 같은 이체 로직에 어노테이션 설정만 다르게 붙인 결과입니다. rollbackFor 하나로 커밋과 롤백이 갈리는 것을 잔액으로 직접 확인할 수 있습니다.

예제 직접 실행

아래 폴더를 JDK 21 로 컴파일하고 실행합니다. 외부 jar 를 쓰는 레슨은 java-src/lib 를 클래스패스에 넣습니다.

cd java-src\extension\15_transaction
javac -encoding UTF-8 *.java && java Main

:: 외부 jar 가 필요한 레슨
javac -encoding UTF-8 -cp "..\..\lib\*;." *.java && java -cp "..\..\lib\*;." Main
코드 예제
  • Tx — 커스텀 어노테이션 + 동적 프록시 트랜잭션 매니저
  • rollback-only 처리
  • 데모 실행 결과
이전 섹션2 핵심 원리3 / 7다음 섹션4 응용 변형 예제