모든 글

전부 성공 아니면 전부 취소 — 트랜잭션과 ACID

포인트 이체에서 차감만 되고 적립이 실패하면 데이터가 깨진다. "절반만 반영된 상태"가 아예 존재할 수 없게 만드는 트랜잭션과 ACID를, FTP로 파일 절반만 올라가던 그 아찔함에 빗대 정리한다.

서버에서 DB를 다루다 보면 여러 쓰기 작업이 반드시 함께 성공해야 하는 순간이 와요.

포인트 이체가 대표적이에요.

A에서 차감, B에 적립 — 둘 중 하나만 실행되면 데이터가 깨져요.

퍼블리싱 시절, FTP로 파일 여러 개를 올리다 절반만 올라가서 사이트가 깨져 보이던 그 아찔함, 기억나시죠.

트랜잭션은 바로 그 “절반만 반영된 상태”가 아예 존재할 수 없게 만드는 장치예요.

작업들을 하나의 묶음으로 묶어서, 전부 성공하면 반영(커밋), 하나라도 실패하면 전부 취소(롤백)해요.

// Next.js Server Action + Prisma
'use server';
export async function transferPoints(fromId: string, toId: string, amount: number) {
  await prisma.$transaction(async (tx) => {
    await tx.user.update({ where: { id: fromId }, data: { points: { decrement: amount } } });
    await tx.user.update({ where: { id: toId },   data: { points: { increment: amount } } });
  });   // 안에서 하나라도 throw되면 둘 다 롤백
}

만약 $transaction 없이 update를 그냥 두 번 실행하면, 각 update가 독립적으로 즉시 커밋돼요.

두 번째에서 에러가 나도 첫 번째 차감은 이미 반영된 뒤라 되돌릴 수 없고요.

”저장은 됐는데 데이터가 반쯤 이상하다”는 버그의 상당수가 여기서 나와요.

이 성질을 정리한 말이 ACID예요.

글자성질뜻
A원자성(Atomicity)전부 아니면 전무
C일관성(Consistency)규칙을 위반한 상태는 만들지 않음
I격리성(Isolation)진행 중인 중간 상태를 다른 요청이 못 봄
D지속성(Durability)커밋된 건 장애가 나도 남음

특히 격리성이 실무에서 헷갈려요.

”차감은 됐는데 적립은 아직 안 된” 중간 상태를, 동시에 들어온 다른 요청이 읽지 못하게 막는 성질이에요.

이체 도중에 잔액을 조회한 사람이 이상한 값을 보지 않도록 하는 거죠.

Server Action이나 API 라우트에서 두 테이블 이상을 함께 수정하는 순간 트랜잭션이 필요해요.

DB 인덱스가 읽기의 기본기라면, 트랜잭션은 쓰기의 기본기고요.

여러 쓰기를 함께 해야 할 때 물어보세요.

하나만 성공하면 데이터가 깨지나.

그렇다면 트랜잭션으로 묶어서, 절반만 반영된 상태가 애초에 불가능하게 만들면 돼요.

N
NACRE
Writing on less · Seoul
다른 글 보기

댓글

  1. 불러오는 중…