공공부하자개발 · 영어 학습 노트
자바
실무 확장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정리‹ 이전다음 ›

6. 연습 문제

문제 1. `Tx` 에 `NOT_SUPPORTED` 전파 속성 추가하기

Tx.Propagation 에 NOT_SUPPORTED 를 추가하고, invoke() 에서 이 값이면 현재 트랜잭션을 잠시 멈추고(CURRENT.set(null)) 트랜잭션 없이 메서드를 실행한 뒤 원래 트랜잭션을 복원하세요.

java
public enum Propagation { REQUIRED, REQUIRES_NEW, NOT_SUPPORTED }

// invoke() 안, 분기 추가
if (tx.propagation() == Propagation.NOT_SUPPORTED) {
    Ctx suspended = CURRENT.get();
    CURRENT.set(null);
    try {
        return method.invoke(target, args);
    } finally {
        CURRENT.set(suspended);
    }
}

새 커넥션을 열지 않으므로 owner 판단이나 커밋·롤백 로직이 필요 없습니다.

문제 2. `AccountService` 에 낙관적 락 버전 체크 추가하기

account 테이블에 version INT 컬럼을 추가하고, addBalance 대신 버전을 함께 확인하는 갱신 메서드를 만드세요. 갱신 건수가 0이면 RuntimeException 을 던져 롤백을 유도합니다.

java
private void updateWithVersion(Connection c, String id, int delta, int seenVersion) throws SQLException {
    try (PreparedStatement ps = c.prepareStatement(
            "UPDATE account SET balance = balance + ?, version = version + 1 "
          + "WHERE id = ? AND version = ?")) {
        ps.setInt(1, delta);
        ps.setString(2, id);
        ps.setInt(3, seenVersion);
        if (ps.executeUpdate() == 0) {
            throw new RuntimeException("버전 충돌: " + id);
        }
    }
}

WHERE version = ? 조건이 핵심입니다. 조회 시점 버전과 다르면 다른 트랜잭션이 먼저 수정한 것입니다.

문제 3. 커밋 직전 로그로 트랜잭션 경계 확인하기

Tx.finish() 시작 부분에 System.out.println 으로 커밋인지 롤백인지, 어떤 스레드인지 출력하는 코드를 추가하세요. 실무의 TRACE 로그를 흉내 내는 문제입니다.

java
private static void finish(Ctx ctx, Throwable cause, Class<? extends Throwable> rollbackFor) {
    boolean matches = cause != null && rollbackFor.isInstance(cause);
    System.out.println("[tx] thread=" + Thread.currentThread().getName()
            + " action=" + (matches || ctx.rollbackOnly ? "ROLLBACK" : "COMMIT"));
    // 이후 기존 로직 그대로
    ...
}

실제 Spring 은 TransactionInterceptor 레벨에서 이런 로그를 TRACE 로 남깁니다.

연습 문제
    이전 섹션5 자주 하는 실수 (Tip)6 / 7다음 섹션7 정리