1.0.0 • Published 5 months ago
@jrjr519/saga v1.0.0
LocalTransaction Utility
개요
해당 유틸리티는 Node.js 환경에서 SAGA 패턴에 대응하는 로컬 트랜잭션(Local Transaction) 을 간단하고 선언적인 방식으로 구현하기 위한 유틸리티입니다.
- 주요 목적
- 단계적(체이닝) 작업을 수행하다가, 어떤 단계에서 실패(에러)하면 이전 단계들을 보상(rollback) 할 수 있도록 지원합니다.
- 데코레이터(@LocalTransaction) 를 이용하면 메서드 범위에서 트랜잭션 컨텍스트를 자동 생성 및 에러 발생 시 rollbackAll 실행을 보장합니다.
- withRollback 함수를 통해 동기/비동기 작업을 프로미스로 래핑하고, 역순 실행되는 보상 함수를 간편하게 등록할 수 있습니다.
프로젝트 구조
이 프로젝트는 monorepo 구조로 구성되어 있습니다:
saga/
├── src/ # 라이브러리 소스 코드
│ ├── index.ts # 메인 export 파일
│ └── LocalTransaction/ # LocalTransaction 유틸리티들
├── example/ # 예제 애플리케이션 (별도 패키지)
│ ├── package.json # example 패키지 설정
│ ├── index.ts # 예제 실행 파일
│ └── *.ts # 예제 서비스 파일들
├── package.json # 루트 패키지 (workspaces 설정)
└── dist/ # 컴파일된 라이브러리 출력설치 및 실행
의존성 설치:
pnpm install라이브러리 빌드:
pnpm build예제 실행:
cd example pnpm start
특징
- AsyncLocalStorage 사용
- 각 요청/비동기 흐름마다 독립된 트랜잭션 컨텍스트를 관리.
- 데코레이터가 없을 때도 수동으로
localTransactionContextStorage.run(...)으로 컨텍스트 범위를 지정할 수 있음.
- 동기/비동기 함수 모두 처리 가능
withRollback(action)에서 동기/비동기action과,.rollback(fn)에서 동기/비동기fn을 모두 지원
- 단순화된 "부분 롤백"
- 단계별 보상 로직(rollbackFn)을 등록하고, 에러 발생 시 진행 상황까지의 동작을 등록 순서의 역순으로 실행
사용 예시
1. TypeScript + 데코레이터 방식
아래 예시는 TypeScript 코드에서 @LocalTransaction 데코레이터를 사용해 로컬 트랜잭션을 자동으로 생성하고,withRollback 체이닝을 통해 보상 로직(rollback)을 등록하는 대표적인 구조입니다.
import {LocalTransaction} from './LocalTransactionDecorator';
import {withRollback} from './withRollback';
@LocalTransaction({catchUnhandledError: true})
async function sagaMethod(): Promise<void> {
// 1) 동기 작업 + rollback 등록
const syncResult = await withRollback(getFoo())
.rollback(() => syncRollback());
console.log('syncResult =', syncResult); // 'Foo'
// 2) 비동기 작업 + rollback 등록
const asyncResult = await withRollback(gerBar())
.rollback(() => asyncRollback());
console.log('asyncResult =', asyncResult); // 'getBar'
throw new Error('[sagaMethod] error for testing');
}설명
@LocalTransaction데코레이터가 메서드 실행 전후로 트랜잭션 컨텍스트를 생성/획득합니다.- 내부에서 에러가 발생하면, 이미 등록된 rollback 함수들이 역순으로 호출됩니다.
- 동기 작업/비동기 작업 모두
withRollback(...)+.rollback(...)체이닝으로 보상 로직을 붙일 수 있습니다.
2. JavaScript + (No Decorator) 방식
데코레이터를 사용할 수 없는 환경(혹은 Plain JS)이라면, 직접 컨텍스트를 생성하고 withRollback을 호출해 보상 로직을 등록할 수 있습니다.
const {localTransactionContextStorage} = require('./LocalTransactionContextStorage');
const {LocalTransactionContext} = require('./LocalTransactionContext');
const {withRollback} = require('./withRollback');
async function sagaMethodPlainJS() {
// 1) 새 컨텍스트 만들기
const ctx = new LocalTransactionContext();
try {
// 2) localTransactionContextStorage.run을 통해 이 컨텍스트 범위 내에서 작업
await localTransactionContextStorage.run(ctx, async () => {
// 동기
const syncResult = await withRollback(getFoo())
.rollback(() => syncRollback());
console.log('syncResult =', syncResult);
// 비동기
const asyncResult = await withRollback(getBar())
.rollback(() => asyncRollback());
console.log('asyncResult =', asyncResult);
// 에러발생
throw new Error('[sagaMethodPlainJS] error for testing');
});
} catch (error) {
console.error('[ERROR] sagaMethodPlainJS:', error.message);
// 3) rollbackAll() 실행
if (ctx.hasRollbacks() && !ctx.isExecuted()) {
console.log('[ROLLBACK] Executing rollbackAll');
await ctx.rollbackAll();
}
}
}설명
localTransactionContextStorage.run(ctx, callback): 해당 콜백 실행 중에는ctx가 활성 컨텍스트가 됩니다.withRollback(...)이.rollback(...)을 호출할 때 현재 활성 컨텍스트에 보상 함수를 추가합니다.- 콜백 내부에서 에러가 발생하면, 상위에서 수동으로
ctx.rollbackAll()을 호출하거나, 별도의 에러 핸들링 로직에 포함시킬 수 있습니다. (데코레이터 없이 직접 제어 가능)
await 위치에 대한 주의사항
withRollback는 항상 Promise를 반환하므로, 결과(
T)를 얻기 위해서는await해야 합니다.const result = await withRollback(someAction());동기 함수도
withRollback(fn())형태로 감싸면,- 내부적으로
Promise.resolve(fn())처리 → 반환값도 Promise. - 따라서
await으로 풀어야 결과를 사용할 수 있습니다.
- 내부적으로
rollback 함수는 에러가 발생했을 때 내부에서 자동 실행되므로, 롤백 함수 호출에 대해 직접
await할 필요는 없습니다..rollback(fn)은 "보상 로직 등록"만 수행.- 실제 실행은 "에러 발생 시점" → "데코레이터나 수동 rollbackAll()에서 역순 호출"
구조
flowchart TD
subgraph Application Layer
A[애플리케이션 코드비즈니스 로직]
B[서비스 메서드 @LocalTransaction 적용]
end
subgraph Transaction Utilities
C[LocalTransactionDecorator]
D[withRollback Utility]
E[LocalTransactionContext]
F[Rollback Functions Stack]
end
subgraph Context Management
G[AsyncLocalStorage]
end
A --> B
B --> C
C -->|컨텍스트 생성/획득|G
G --> E
C -->|메서드 실행 시 에러 캐치|E
D -->|보상 함수 등록|E
E --> F
E -->|rollbackAll 호출|F
F -->|역순 실행|E
E -->|에러 발생 시 rollbackAll|C
%% Additional flows:
B --- D1.0.0
5 months ago