본문 바로가기
Q&A해결2026년 4월 30일1분 읽기

Drizzle ORM에서 트랜잭션 안에 트랜잭션을 쓰면 어떻게 되나요

O
ORM학습자
조회 738댓글 3

Drizzle로 비즈니스 로직 짜다 보니 트랜잭션 함수 안에서 다른 트랜잭션 함수를 호출하게 됩니다. 이게 문제 없나요? 안쪽 트랜잭션이 따로 commit 되거나, 바깥쪽이 롤백돼도 안쪽이 살아남거나 하는 일이 있나요?

async function createOrder(...) {
  return db.transaction(async tx => {
    // ...
    await chargePayment(tx, ...)  // 이게 또 transaction 시작
  })
}

async function chargePayment(tx, ...) {
  return tx.transaction(async tx2 => {
    // ...
  })
}

댓글 3

김김영삼6개월 전

안전합니다. Drizzle의 중첩 transaction()은 PostgreSQL의 SAVEPOINT로 자동 변환됩니다. 별도 트랜잭션이 아니라 같은 트랜잭션 안의 부분 마커입니다.


실제 SQL 흐름


BEGIN;                          -- 바깥 transaction()
-- ...
SAVEPOINT sp1; -- 안쪽 transaction()
-- ...
RELEASE SAVEPOINT sp1; -- 안쪽 commit
-- ...
COMMIT; -- 바깥 commit

안쪽이 throw 하면


BEGIN;
SAVEPOINT sp1;
-- ... error!
ROLLBACK TO SAVEPOINT sp1; -- 안쪽만 롤백
-- ... 바깥은 계속
COMMIT;

안쪽 에러를 try/catch로 잡으면 안쪽만 부분 롤백하고 바깥은 계속 진행 가능. 이게 SAVEPOINT의 강점.


실용 패턴 — 부분 실패 허용


async function bulkProcess(items) {
return db.transaction(async tx => {
const results = []
for (const item of items) {
try {
const result = await tx.transaction(async sub => {
return await processItem(sub, item)
})
results.push({ ok: true, item, result })
} catch (e) {
// 이 item만 롤백, 다음 item 계속
results.push({ ok: false, item, error: e })
}
}
return results
})
}

한 가지 주의 — 안쪽 에러를 try/catch로 잡지 않으면 바깥까지 전파돼서 전체 롤백됩니다. "이 단계는 실패해도 OK"임을 명시적으로 처리해야 부분 롤백이 동작합니다.

OORM학습자(guest)6개월 전
SAVEPOINT 동작 명확해졌습니다. 추가 질문 — 그럼 바깥 트랜잭션 없이 안쪽 함수만 단독 호출하면 어떻게 되나요? tx가 없는데 tx.transaction() 호출하면?
김김영삼6개월 전

안 되는 게 정답이고, 그래서 함수를 tx 받게 만들지 db 받게 만들지 하나로 정하는 게 핵심입니다.


패턴 1: 항상 tx 받기 (재사용성↑)


// chargePayment는 항상 tx 필요
async function chargePayment(tx: Tx, ...) {
return tx.transaction(async sub => { ... })
}

// 단독 호출
db.transaction(tx => chargePayment(tx, ...))

패턴 2: db 또는 tx 둘 다 받기 (편의성↑)


type Executor = typeof db | Tx

async function chargePayment(executor: Executor, ...) {
return executor.transaction(async tx => { ... })
}

// 둘 다 동작
chargePayment(db, ...) // 새 transaction
chargePayment(tx, ...) // SAVEPOINT

Drizzle은 db와 Tx의 transaction 메서드 시그니처가 같아서 패턴 2가 깔끔합니다. 도메인 함수는 항상 Executor를 받게 통일하세요.

Ctrl+Enter로 등록