본문 바로가기

전략·템플릿 콜백

변하는 실행 조각을 전략과 람다 콜백으로 전달하고 싱글톤 템플릿의 스레드 안전성·예외·반환 타입을 설계합니다.

템플릿 메서드가 “상위 클래스가 하위 클래스를 호출”한다면 전략은 “컨텍스트가 협력 객체를 호출”합니다.

알고리즘 흐름과 변하는 동작을 객체 조합으로 연결해 단일 상속 제약을 피합니다.

변형이 메서드 하나면 콜백 람다로 더 작게 표현할 수 있으며 JdbcTemplate과 TransactionTemplate도 같은 사고방식을 사용합니다.

전략·템플릿 콜백

기존 TX 없음과 같은 매니저의 REQUIRED 참여를 두 독립 가정으로 표시합니다. trace·템플릿·물리 TX의 고정 역할에서 callback 완료 뒤 새 TX는 trace 안에서 물리 완료하지만 참여 TX는 trace 종료 뒤 바깥에서 완료하는 실제 차이를 추적하며 trace finished를 최종 commit 성공으로 합치지 않습니다.

TEMPLATE CALLBACK · WRAPPER NESTING

새 물리 트랜잭션을 트레이스로 감싸면 커밋까지 관찰한다

변하는 operation은 전략/콜백으로 전달합니다. 싱글톤 컨텍스트는 흐름만 소유하고 시작 시각·결과는 각 호출의 로컬 변수에 둡니다.

물리 트랜잭션의 소유 위치에 따른 커밋 관찰 범위trace범위안에서TransactionTemplate이시작또는참여상태를확보하고콜백을실행한다. 기존같은관리자의물리TX에REQUIRED로참여하면work실행뒤안쪽범위가끝나도물리커밋은나중바깥에서일어나며그점선경로는trace밖이다. 기존TX가없어새물리TX를시작했다면그commit또는rollback처리가trace안이어서그완료실패를관찰할수있다. tracefinished가항상최종DB커밋성공이라는뜻은아니다. 두가지가정은명령형일반전파모델이며sink실패와다른관리자/리액티브전파는별도다.TraceContext 범위started · 작업 시작TransactionTemplate시작/참여 상태 확보work.call()참여 범위 정상 종료물리 커밋은 아직 없음work.call()새 물리 TX 완료commit / rollback정상/실패 관찰결과·예외 전달이후 바깥 물리 TXcommit / rollback기존 물리 TX에참여하는가?예아니오이후 바깥 TX점선 뒤 커밋은이 trace의 관찰 밖
물리 트랜잭션의 소유 위치에 따른 커밋 관찰 범위trace범위안에서TransactionTemplate이시작또는참여상태를확보하고콜백을실행한다. 기존같은관리자의물리TX에REQUIRED로참여하면work실행뒤안쪽범위가끝나도물리커밋은나중바깥에서일어나며그점선경로는trace밖이다. 기존TX가없어새물리TX를시작했다면그commit또는rollback처리가trace안이어서그완료실패를관찰할수있다. tracefinished가항상최종DB커밋성공이라는뜻은아니다. 두가지가정은명령형일반전파모델이며sink실패와다른관리자/리액티브전파는별도다.TraceContext 범위started작업 시작TX 템플릿시작/참여 상태 확보work.call()참여 범위 종료물리 커밋은아직 없음work.call()새 물리 TX 완료commit /rollbacktrace 정상/실패결과·예외 전달바깥 물리 TX이후 commit또는 rollback기존 물리TX에참여하는가?예아니오바깥 TX점선 뒤 커밋은이 trace의 관찰 밖
trace가 transaction callback과 완료를 감싸는 중첩 구조외부 TraceContext 범위는 시작 기록 뒤 TransactionTemplate을 실행한다. 내부 범위는 operation 콜백을 실행하고 commit 또는 rollback을 완료한다. 그 이후 외부 trace가 정상 또는 실패를 기록하므로 커밋 실패도 관찰할 수 있다.바깥 · TraceContext.execute시작 기록 · 호출 로컬 start안쪽 · TransactionTemplate.executeoperation.call()불변 입력 · 한 작업TX 완료commit / rollback완료 뒤 정상/실패 기록 → 결과 또는 예외 전달트레이스 기록기가 실패하지 않을 때 예제의 원본 계약 유지
  1. 바깥 TraceContext · 시작 기록

    operation은 callback으로 받고 start/outcome은 호출 로컬 변수로 둡니다.

  2. 안쪽 TransactionTemplate · 대상 + 완료

    콜백에 도달하면 재시도 없이 작업을 호출하고 트랜잭션을 commit/rollback합니다.

  3. 바깥 trace · 완료 뒤 관찰

    트랜잭션 완료가 끝난 뒤 정상/실패를 기록하므로 커밋 실패도 포함됩니다. 기록기가 실패하지 않을 때 원본 결과·업무 예외가 유지됩니다.

콜백 계약을 명시

Supplier는 검사 예외를 선언하지 않습니다. checked/nullable/rollback 접근을 콜백 API에 맞게 정하고 기술 실패 번역은 어댑터가 소유합니다.

싱글톤과 공유 수집 대상

현재 작업·결과·시각을 필드에 두지 않습니다. 공유 events/sink는 스레드 안전해야 합니다. 지연 실행에는 불변 snapshot과 명시적 executor가 필요합니다.

새 물리 트랜잭션의 완료를 trace 밖에서 처리하면 커밋 실패를 놓칠 수 있습니다. 기존 바깥 경계에 참여하는 안쪽 템플릿을 감싼 경우도 나중의 물리 커밋까지 포함하지 않습니다. 래퍼 순서와 실제 전파 조건을 함께 정합니다.

이 예의 조건

  • 두 박스는 같은 실행의 진행 정보와 자료·결과를 관찰하는 고정 역할입니다. 박스의 좌우·위아래 위치를 별도 호출 순서로 해석하지 않습니다.
  • 조건별 종료는 대안입니다. 실패 뒤 정상 후속 단계를 실행하거나 독립 비교 사례를 같은 요청으로 연결하지 않습니다.
  • trace 범위 안에서 TransactionTemplate이 시작 또는 참여 상태를 확보하고 콜백을 실행한다. 기존의 같은 관리자가 소유한 물리 TX에 REQUIRED로 참여하면 work 실행 뒤 안쪽 범위가 끝나도 물리 커밋은 나중에 바깥에서 일어나며 그 점선 경로는 trace 밖이다. 기존 TX가 없어 새 물리 TX를 시작했다면 그 commit 또는 rollback 처리가 trace 안이어서 그 완료 실패를 관찰할 수 있다. trace finished가 항상 최종 DB 커밋 성공이라는 뜻은 아니다. 두 가지 가정은 명령형 일반 전파 모델이며 sink 실패와 다른 관리자/리액티브 전파는 별도다.
  • 외부 TraceContext 범위는 시작 기록 뒤 TransactionTemplate을 실행한다. 내부 범위는 operation 콜백을 실행하고 commit 또는 rollback을 완료한다. 그 이후 외부 trace가 정상 또는 실패를 기록하므로 커밋 실패도 관찰할 수 있다.
관계도의 보충 설명
원래 전체 그림의 표시 문구·조건·주석
순번원문 표시
1전략·템플릿 콜백
6trace 범위 안에서 TransactionTemplate이 시작 또는 참여 상태를 확보하고 콜백을 실행한다. 기존의 같은 관리자가 소유한 물리 TX에 REQUIRED로 참여하면 work 실행 뒤 안쪽 범위가 끝나도 물리 커밋은 나중에 바깥에서 일어나며 그 점선 경로는 trace 밖이다. 기존 TX가 없어 새 물리 TX를 시작했다면 그 commit 또는 rollback 처리가 trace 안이어서 그 완료 실패를 관찰할 수 있다. trace finished가 항상 최종 DB 커밋 성공이라는 뜻은 아니다. 두 가지 가정은 명령형 일반 전파 모델이며 sink 실패와 다른 관리자/리액티브 전파는 별도다.
22아니요
단계별 상태·값과 전환 이유
장면관찰 값전환 이유
독립 TX 가정

trace 범위가 시작되고 기존 TX 없음과 같은 매니저의 기존 TX 참여를 분리합니다.

새 TX 사례: 기존 TX 없음 · 참여 사례: 기존 물리 TX 있음 · 모델: 명령형 REQUIRED완료 소유자가 다른 두 가정을 고정합니다.
템플릿과 callback

TransactionTemplate이 새 TX를 시작하거나 기존 TX에 참여한 뒤 work callback을 실행합니다.

trace: 활성 · work: 실행 · 물리 TX: 시작 또는 참여두 사례 모두 callback 실행에 들어갑니다.
안쪽 execute 종료

새 TX는 trace 안에서 물리 commit·rollback을 처리하고 참여는 안쪽 scope만 끝냅니다.

새 TX: 물리 완료를 trace 안에서 처리 · 참여: 물리 TX 아직 바깥 소유같은 callback 완료에도 물리 완료 상태가 달라집니다.
trace 종료와 바깥 완료

trace 종료 뒤 참여 사례의 물리 완료는 바깥 경계에서 나중에 일어납니다.

새 TX 완료 실패: trace에서 관찰 가능 · 참여 최종 완료: trace 밖 · trace finished: 항상 commit 성공은 아님trace의 관찰 범위와 최종 DB 완료를 구분합니다.

전략·템플릿 콜백

기존 TX 없음과 같은 매니저의 REQUIRED 참여를 두 독립 가정으로 표시합니다. trace·템플릿·물리 TX의 고정 역할에서 callback 완료 뒤 새 TX는 trace 안에서 물리 완료하지만 참여 TX는 trace 종료 뒤 바깥에서 완료하는 실제 차이를 추적하며 trace finished를 최종 commit 성공으로 합치지 않습니다.

TEMPLATE CALLBACK · WRAPPER NESTING

새 물리 트랜잭션을 트레이스로 감싸면 커밋까지 관찰한다

변하는 operation은 전략/콜백으로 전달합니다. 싱글톤 컨텍스트는 흐름만 소유하고 시작 시각·결과는 각 호출의 로컬 변수에 둡니다.

물리 트랜잭션의 소유 위치에 따른 커밋 관찰 범위trace범위안에서TransactionTemplate이시작또는참여상태를확보하고콜백을실행한다. 기존같은관리자의물리TX에REQUIRED로참여하면work실행뒤안쪽범위가끝나도물리커밋은나중바깥에서일어나며그점선경로는trace밖이다. 기존TX가없어새물리TX를시작했다면그commit또는rollback처리가trace안이어서그완료실패를관찰할수있다. tracefinished가항상최종DB커밋성공이라는뜻은아니다. 두가지가정은명령형일반전파모델이며sink실패와다른관리자/리액티브전파는별도다.TraceContext 범위started · 작업 시작TransactionTemplate시작/참여 상태 확보work.call()참여 범위 정상 종료물리 커밋은 아직 없음work.call()새 물리 TX 완료commit / rollback정상/실패 관찰결과·예외 전달이후 바깥 물리 TXcommit / rollback기존 물리 TX에참여하는가?예아니오이후 바깥 TX점선 뒤 커밋은이 trace의 관찰 밖
물리 트랜잭션의 소유 위치에 따른 커밋 관찰 범위trace범위안에서TransactionTemplate이시작또는참여상태를확보하고콜백을실행한다. 기존같은관리자의물리TX에REQUIRED로참여하면work실행뒤안쪽범위가끝나도물리커밋은나중바깥에서일어나며그점선경로는trace밖이다. 기존TX가없어새물리TX를시작했다면그commit또는rollback처리가trace안이어서그완료실패를관찰할수있다. tracefinished가항상최종DB커밋성공이라는뜻은아니다. 두가지가정은명령형일반전파모델이며sink실패와다른관리자/리액티브전파는별도다.TraceContext 범위started작업 시작TX 템플릿시작/참여 상태 확보work.call()참여 범위 종료물리 커밋은아직 없음work.call()새 물리 TX 완료commit /rollbacktrace 정상/실패결과·예외 전달바깥 물리 TX이후 commit또는 rollback기존 물리TX에참여하는가?예아니오바깥 TX점선 뒤 커밋은이 trace의 관찰 밖
trace가 transaction callback과 완료를 감싸는 중첩 구조외부 TraceContext 범위는 시작 기록 뒤 TransactionTemplate을 실행한다. 내부 범위는 operation 콜백을 실행하고 commit 또는 rollback을 완료한다. 그 이후 외부 trace가 정상 또는 실패를 기록하므로 커밋 실패도 관찰할 수 있다.바깥 · TraceContext.execute시작 기록 · 호출 로컬 start안쪽 · TransactionTemplate.executeoperation.call()불변 입력 · 한 작업TX 완료commit / rollback완료 뒤 정상/실패 기록 → 결과 또는 예외 전달트레이스 기록기가 실패하지 않을 때 예제의 원본 계약 유지
  1. 바깥 TraceContext · 시작 기록

    operation은 callback으로 받고 start/outcome은 호출 로컬 변수로 둡니다.

  2. 안쪽 TransactionTemplate · 대상 + 완료

    콜백에 도달하면 재시도 없이 작업을 호출하고 트랜잭션을 commit/rollback합니다.

  3. 바깥 trace · 완료 뒤 관찰

    트랜잭션 완료가 끝난 뒤 정상/실패를 기록하므로 커밋 실패도 포함됩니다. 기록기가 실패하지 않을 때 원본 결과·업무 예외가 유지됩니다.

콜백 계약을 명시

Supplier는 검사 예외를 선언하지 않습니다. checked/nullable/rollback 접근을 콜백 API에 맞게 정하고 기술 실패 번역은 어댑터가 소유합니다.

싱글톤과 공유 수집 대상

현재 작업·결과·시각을 필드에 두지 않습니다. 공유 events/sink는 스레드 안전해야 합니다. 지연 실행에는 불변 snapshot과 명시적 executor가 필요합니다.

새 물리 트랜잭션의 완료를 trace 밖에서 처리하면 커밋 실패를 놓칠 수 있습니다. 기존 바깥 경계에 참여하는 안쪽 템플릿을 감싼 경우도 나중의 물리 커밋까지 포함하지 않습니다. 래퍼 순서와 실제 전파 조건을 함께 정합니다.

이 예의 조건

  • 두 박스는 같은 실행의 진행 정보와 자료·결과를 관찰하는 고정 역할입니다. 박스의 좌우·위아래 위치를 별도 호출 순서로 해석하지 않습니다.
  • 조건별 종료는 대안입니다. 실패 뒤 정상 후속 단계를 실행하거나 독립 비교 사례를 같은 요청으로 연결하지 않습니다.
  • trace 범위 안에서 TransactionTemplate이 시작 또는 참여 상태를 확보하고 콜백을 실행한다. 기존의 같은 관리자가 소유한 물리 TX에 REQUIRED로 참여하면 work 실행 뒤 안쪽 범위가 끝나도 물리 커밋은 나중에 바깥에서 일어나며 그 점선 경로는 trace 밖이다. 기존 TX가 없어 새 물리 TX를 시작했다면 그 commit 또는 rollback 처리가 trace 안이어서 그 완료 실패를 관찰할 수 있다. trace finished가 항상 최종 DB 커밋 성공이라는 뜻은 아니다. 두 가지 가정은 명령형 일반 전파 모델이며 sink 실패와 다른 관리자/리액티브 전파는 별도다.
  • 외부 TraceContext 범위는 시작 기록 뒤 TransactionTemplate을 실행한다. 내부 범위는 operation 콜백을 실행하고 commit 또는 rollback을 완료한다. 그 이후 외부 trace가 정상 또는 실패를 기록하므로 커밋 실패도 관찰할 수 있다.

관계도의 보충 설명

원래 전체 그림의 표시 문구·조건·주석
순번원문 표시
1전략·템플릿 콜백
6trace 범위 안에서 TransactionTemplate이 시작 또는 참여 상태를 확보하고 콜백을 실행한다. 기존의 같은 관리자가 소유한 물리 TX에 REQUIRED로 참여하면 work 실행 뒤 안쪽 범위가 끝나도 물리 커밋은 나중에 바깥에서 일어나며 그 점선 경로는 trace 밖이다. 기존 TX가 없어 새 물리 TX를 시작했다면 그 commit 또는 rollback 처리가 trace 안이어서 그 완료 실패를 관찰할 수 있다. trace finished가 항상 최종 DB 커밋 성공이라는 뜻은 아니다. 두 가지 가정은 명령형 일반 전파 모델이며 sink 실패와 다른 관리자/리액티브 전파는 별도다.
22아니요
단계별 상태·값과 전환 이유
장면관찰 값전환 이유
독립 TX 가정

trace 범위가 시작되고 기존 TX 없음과 같은 매니저의 기존 TX 참여를 분리합니다.

새 TX 사례: 기존 TX 없음 · 참여 사례: 기존 물리 TX 있음 · 모델: 명령형 REQUIRED완료 소유자가 다른 두 가정을 고정합니다.
템플릿과 callback

TransactionTemplate이 새 TX를 시작하거나 기존 TX에 참여한 뒤 work callback을 실행합니다.

trace: 활성 · work: 실행 · 물리 TX: 시작 또는 참여두 사례 모두 callback 실행에 들어갑니다.
안쪽 execute 종료

새 TX는 trace 안에서 물리 commit·rollback을 처리하고 참여는 안쪽 scope만 끝냅니다.

새 TX: 물리 완료를 trace 안에서 처리 · 참여: 물리 TX 아직 바깥 소유같은 callback 완료에도 물리 완료 상태가 달라집니다.
trace 종료와 바깥 완료

trace 종료 뒤 참여 사례의 물리 완료는 바깥 경계에서 나중에 일어납니다.

새 TX 완료 실패: trace에서 관찰 가능 · 참여 최종 완료: trace 밖 · trace finished: 항상 commit 성공은 아님trace의 관찰 범위와 최종 DB 완료를 구분합니다.

컨텍스트·전략 역할

컨텍스트는 트레이스 시작·종료·실패 기록을 소유합니다.

전략은 게시글 조회나 등록만 구현합니다.

생성자에 전략을 고정하면 재사용 가능한 객체 조합이고, 실행 파라미터로 받으면 호출마다 다른 콜백을 쓸 수 있습니다.

src/main/java/board/strategy/TraceContext.java
package board.strategy;

import java.util.Objects;

public final class TraceContext {
    private final TraceEvents events;

    public TraceContext(TraceEvents events) {
        this.events = Objects.requireNonNull(events);
    }

    public <T> T execute(String operation, Strategy<T> strategy) {
        long start = System.nanoTime();
        events.started(operation);
        try {
            T value = strategy.call();
            events.finished(operation, System.nanoTime() - start);
            return value;
        } catch (RuntimeException | Error failure) {
            events.failed(operation, failure, System.nanoTime() - start);
            throw failure;
        }
    }

    @FunctionalInterface
    public interface Strategy<T> {
        T call();
    }

    public interface TraceEvents {
        void started(String operation);
        void finished(String operation, long nanos);
        void failed(String operation, Throwable failure, long nanos);
    }
}

컨텍스트에 현재 작업을 필드로 저장하지 않아 싱글톤으로 공유해도 호출별 로컬 변수가 분리됩니다.

이벤트 구현도 스레드 안전해야 하며 요청 상태는 ThreadLocal 책임 주체에서 읽거나 인자로 받습니다.


람다 콜백

익명 클래스보다 람다가 짧고 대상 코드가 리포지토리 메서드 가까이에 보입니다.

캡처하는 변수는 사실상 final이어야 하며 변경 가능한 컬렉션을 공유하면 별도 동시성 문제가 생깁니다.

src/main/java/board/strategy/CallbackDemo.java
package board.strategy;

public final class CallbackDemo {
    public static void main(String[] args) {
        var context = new TraceContext(new ConsoleEvents());
        long authorId = 41L;
        String result = context.execute(
                "weekly summary",
                () -> "member=" + authorId + " postCount=12");
        System.out.println(result);
    }

    static final class ConsoleEvents implements TraceContext.TraceEvents {
        @Override
        public void started(String operation) {
            System.out.println("start=" + operation);
        }

        @Override
        public void finished(String operation, long nanos) {
            System.out.println("outcome=success");
        }

        @Override
        public void failed(String operation, Throwable failure, long nanos) {
            System.out.println("outcome=" + failure.getClass().getSimpleName());
        }
    }
}
CallbackDemo 실행
start=weekly summary
outcome=success
member=41 postCount=12

콜백이 너무 길어지면 이름을 붙인 private 메서드나 별도 전략 클래스로 추출합니다.

람다 안에 트랜잭션, 재시도, 검증을 모두 넣으면 흐름이 다시 숨겨집니다.


콜백의 검사 예외

JDK Supplier<T>는 검사 예외를 선언할 수 없습니다.

원시 JDBC 콜백처럼 검사 타입이 실제 확장 계약이면 자체 인터페이스에 throws E를 둘 수 있습니다.

그러나 호출자 전체에 일반 예외 파라미터가 퍼지는 비용을 고려합니다.

Spring 템플릿은 저수준 검사 예외를 콜백 안에서 받고 공통 계층으로 번역합니다.

애플리케이션 트레이스 컨텍스트가 기술 예외 번역까지 맡으면 관심사가 섞이므로 대상 어댑터가 먼저 런타임 규칙을 제공합니다.

콜백에서 예외를 감쌀 때 원인을 보존하고, 컨텍스트는 자신이 받은 인스턴스를 그대로 재전파하는 것이 좋습니다.

트레이싱 계층이 예외 분류 체계를 바꾸면 롤백 규칙도 예상과 달라질 수 있습니다.


TransactionTemplate 콜백

프록시 내부 호출을 피하거나 작은 블록만 트랜잭션으로 묶어야 할 때 TransactionTemplate.execute()가 유용합니다.

콜백은 TransactionStatus를 받아 롤백 전용 상태를 표시할 수 있습니다.

null 반환 허용 규칙과 예외 전파를 API 문서에서 확인합니다.

프로그래밍 방식은 전파와 타임아웃이 구성 객체에 보이지만 애노테이션보다 반복 코드가 늘어납니다.

동적으로 트랜잭션 옵션을 바꾸는 코드는 검토가 어려우므로 미리 구성한 템플릿 빈을 목적별로 둡니다.

트랜잭션 템플릿의 콜백 종료와 물리 트랜잭션의 종료는 전파 조건에 따라 달라집니다. Spring의 REQUIRED 전파 규칙은 기존 물리 트랜잭션이 있으면 그 경계에 참여합니다. 기존 경계가 없어 새 물리 트랜잭션을 만들었다면 execute 안에서 그 완료를 처리하지만, 바깥 경계에 참여했다면 최종 물리 커밋은 바깥에서 나중에 일어납니다. 프로그래밍 방식의 설정·콜백 계약을 중첩 위치와 함께 확인합니다.

리액티브 TransactionalOperator는 발행기 콜백과 Reactor 컨텍스트를 사용합니다.

명령형 ThreadLocal 트랜잭션 모델과 같은 것으로 취급하지 않습니다.


싱글톤 템플릿 상태

작업 이름, 시작 시간, 결과를 컨텍스트 필드에 두면 전략을 적용해도 ch12-1의 경쟁이 돌아옵니다.

execute()의 스택 지역 변수와 불변 상태에 보관합니다.

공유 수집 대상이 버퍼를 가진다면 동시성 컬렉션이나 추가 전용 백엔드를 사용합니다.

콜백을 나중에 실행하도록 저장하면 캡처한 엔티티와 요청 컨텍스트의 수명이 달라집니다.

즉시 실행 템플릿인지 지연된 명령인지 인터페이스 이름에서 구분합니다.

지연된 작업에는 불변 입력 스냅샷과 명시적인 실행자를 사용합니다.


패턴 선택 기준

조건템플릿 메서드템플릿 콜백
단계별 훅 다수읽기 쉬움콜백 타입 증가
한 작업 변형하위 클래스가 무거움람다 적합
런타임 조합제한적자유로움
상속 계층필요불필요
상태 소유부모 인스턴스 주의컨텍스트 로컬 권장

패턴 이름을 적용하는 것이 목표가 아니라 복사된 제어 흐름의 책임 주체를 한 곳으로 만드는 것이 목표입니다.


연습 문제

TransactionTemplate과 트레이스 콜백을 중첩해 게시글 등록을 실행하세요.

이 템플릿이 새 물리 트랜잭션을 시작하는 조건에서 어떤 래퍼가 바깥에 있어야 커밋 실패까지 트레이스에 포함되는지 결정하고, 런타임 예외 동일성과 롤백 행을 검증하세요. 기록기는 실패하지 않는다고 가정합니다. 이어 같은 트랜잭션 관리자의 기존 바깥 경계에 REQUIRED로 참여하는 경우 관찰 범위가 어떻게 달라지는지도 비교하세요.

해설 보기

트레이스 컨텍스트가 새 물리 트랜잭션의 시작과 완료를 수행하는 템플릿 바깥을 감싸면 대상 실행뿐 아니라 그 커밋 단계 예외도 관찰할 수 있습니다. 기존 물리 트랜잭션에 참여하는 경우에는 아래 중첩만으로 나중의 바깥 커밋까지 관찰하지 않습니다.

package board.strategy;

import org.springframework.transaction.support.TransactionTemplate;

public final class TracedTransaction {
    private final TraceContext trace;
    private final TransactionTemplate transactions;

    public TracedTransaction(TraceContext trace, TransactionTemplate transactions) {
        this.trace = trace;
        this.transactions = transactions;
    }

    public <T> T execute(String name, TraceContext.Strategy<T> work) {
        return trace.execute(name, () -> transactions.execute(status -> work.call()));
    }
}

반환값이 null일 수 없는 사용 사례라면 콜백 결과를 검사해 규칙 실패로 드러냅니다.

독립 확인: trace 기록기는 실패하지 않고 work는 정상 반환합니다. (가) 기존 트랜잭션이 없어 안쪽 템플릿이 새 물리 트랜잭션을 시작했고 그 커밋에서 런타임 예외 C가 납니다. (나) 같은 관리자의 바깥 트랜잭션에 REQUIRED로 참여해 안쪽 템플릿은 정상 반환했지만, trace가 끝난 뒤 바깥 커밋에서 런타임 예외 C가 납니다. 각 trace의 finished/failed와 C를 관찰하는 위치를 구하세요.

풀이 확인

(가)는 transactions.execute가 C를 던져 trace의 failed가 C를 받고 같은 C를 재전파합니다. finished에는 도달하지 않습니다. (나)는 안쪽 템플릿 반환 뒤 trace가 finished를 기록합니다. 물리 커밋 C는 이 TraceContext 밖에서 나므로 바깥 호출 경계가 관찰해야 합니다. 이 finished를 최종 DB 커밋 성공으로 해석하면 안 됩니다. 최종 커밋을 관찰하려면 트레이스가 실제 물리 경계를 감싸도록 위치를 정해야 합니다. 예외 동일성은 기록기·트랜잭션 완료 처리 자체가 원 실패를 다른 것으로 바꾸지 않는 조건이며, 실제 DB 롤백 행은 별도 런타임 검증 대상입니다.