부분 실패는 없다, 모 아니면 도
결제나 송금 과정에서 치명적인 오류를 막기 위해, 모든 작업이 완전히 성공하거나 완전히 취소되도록 보장하는 트랜잭션과 동시성의 개념을 배웁니다.
Part 3에서는 서비스에 사용자가 몰려 트래픽이 커졌을 때, 시스템이 무너지지 않고 버텨내는 '백엔드 체력'에 대해 배웁니다. 쇼핑몰에서 고객이 결제 버튼을 누르는 순간을 상상해 봅시다. 재고는 차감되었는데 고객의 통장에서 돈이 빠져나가기 직전에 서버 컴퓨터의 전원이 툭 꺼져버렸습니다. 재고는 줄었는데 회사는 돈을 받지 못한 최악의 상황입니다. 이렇게 '일부분만' 작업이 진행되어 데이터가 엉망이 되는 것을 완벽하게 막아주는 마법의 보호막, 트랜잭션(Transaction)에 대해 알아봅니다.
은행 송금의 딜레마
A의 계좌에서 B의 계좌로 1만 원을 송금하는 과정은 우리 눈에는 하나의 버튼 클릭이지만, 데이터베이스 입장에서는 반드시 두 개의 독립된 쿼리가 실행되는 작업입니다.
- A의 계좌 잔액을 1만 원 깎습니다. (`UPDATE`)
- B의 계좌 잔액을 1만 원 늘립니다. (`UPDATE`)
송금 도중 일어날 수 있는 최악의 시나리오
- A의 계좌 잔액을 1만 원 깎습니다. (성공)
- 번개가 쳐서 서버의 전원이 꺼집니다.
- B의 계좌에 1만 원을 늘리는 명령을 내리지 못했습니다. A의 돈만 깎이고 B는 돈을 받지 못한 최악의 '부분 실패'가 발생합니다.
개발자가 뒤늦게 서버를 고쳐 켰지만, 이 1만 원이 어디서 어떻게 날아갔는지 100만 건의 데이터 속에서 찾아내는 것은 불가능에 가깝습니다. 이런 '부분 실패(Partial Failure)'는 시스템의 신뢰도를 바닥으로 떨어뜨립니다.
모 아니면 도: 트랜잭션의 마법
이런 치명적인 사고를 막기 위해 데이터베이스에는 트랜잭션(Transaction)이라는 강력한 봉투 기능이 있습니다. 여러 개의 쿼리들을 하나의 봉투(트랜잭션) 안에 담아서 묶어버리는 것입니다. 이 봉투에는 아주 극단적이고 엄격한 규칙이 적용됩니다. "봉투 안에 있는 모든 명령이 하나도 빠짐없이 100% 완벽하게 성공하거나, 아니면 단 하나도 실행되지 않은 것처럼 원상 복구(0%) 해라. 99% 성공은 없다!"
트랜잭션의 실행 흐름
sql```sql
BEGIN; -- 봉투 열기
UPDATE accounts SET balance = balance - 10000 WHERE name = 'A';
UPDATE accounts SET balance = balance + 10000 WHERE name = 'B';
COMMIT; -- 봉투 닫고 확정하기 (만약 에러 시 ROLLBACK 실행)
```- 11. 봉투 열기 (BEGIN): '지금부터 내가 내리는 명령들은 함부로 진짜 저장하지 말고 일단 봉투 안에 모아둬!'라고 선언합니다.
- 22. 쿼리 실행: A의 돈을 깎고, B의 돈을 올리는 UPDATE 쿼리를 날립니다. (이때 DB는 진짜로 깎지 않고 기억만 해둡니다.)
- 33-A. 완전 성공 (COMMIT): 모든 명령이 에러 없이 무사히 끝났다면, '봉투를 닫고 진짜로 쾅 도장을 찍어서 영원히 저장해!'라고 선언합니다.
- 43-B. 원상 복구 (ROLLBACK): 중간에 하나라도 에러가 났거나 서버가 꺼졌다면, DB는 스스로 '에잇, 방금 봉투 안에 넣었던 짓거리들 싹 다 무효! 처음 상태로 되돌려!'라고 외치며 롤백합니다.
트랜잭션을 걸어두면, 1번 쿼리 실행 직후 번개가 쳐서 서버가 꺼지더라도 안심할 수 있습니다. 데이터베이스 엔진이 재부팅되는 순간 "어? 아까 닫히지 않은 찜찜한 봉투(COMMIT 되지 않은 트랜잭션)가 있네? 무조건 롤백(ROLLBACK)시켜서 A의 돈을 원래대로 돌려놔!"라고 스스로 복구 작업을 수행하기 때문입니다.
바이브코딩 시 AI의 가장 위험한 생략
이처럼 결제, 포인트 차감, 게시글 등록과 해시태그 등록 등 '반드시 두 개 이상의 테이블이 동시에 수정되어야 하는 작업'에는 무조건 트랜잭션 봉투를 씌워야 합니다.
하지만 바이브코딩 시 AI에게 코드를 짜달라고 하면, AI는 '기능이 동작하게 만드는 데' 급급하여 이 귀찮은 트랜잭션 씌우기 작업을 쏙 빼먹는 경우가 허다합니다. 개발할 때는 번개가 칠 일이 없으니 에러 없이 잘 돌아가는 것처럼 보이기 때문입니다.
실전 트랜잭션 방어 수칙
- 1AI가 데이터베이스의 데이터를 수정(INSERT, UPDATE, DELETE)하는 코드를 여러 줄 짜주었을 때, 그 코드 앞뒤로 트랜잭션을 시작(BEGIN/Start)하고 확정(COMMIT)하는 로직이 있는지 반드시 눈으로 검사하세요.
- 2만약 없다면 AI에게 '이 두 작업은 무조건 하나의 트랜잭션으로 묶어서 중간에 실패하면 롤백되도록 코드를 수정해 줘'라고 지시해야 합니다.
- 3트랜잭션이 너무 길어지면 다른 유저들이 데이터에 접근하지 못하고 락(Lock)이 걸려 대기해야 하므로, 아주 꼭 필요한 수정 작업들만 작게 묶어서 봉투에 담아야 합니다.
비개발자인 우리가 직접 SQL의 `BEGIN`과 `COMMIT`을 타이핑할 일은 많지 않지만, 시스템이 '어떻게 부분 실패를 막고 돈을 안전하게 보호하는가'에 대한 원리를 아는 것과 모르는 것은 바이브코딩으로 만든 서비스의 생사를 가르는 결정적 차이입니다.
개념 퀴즈
퀴즈 답을 맞춰야 학습 완료가 됩니다.우리 서비스에서 유저가 5,000 포인트를 소모하여 VIP 등급을 구매하는 기능을 만들었습니다. 백엔드에서 1) 유저의 포인트를 5,000점 차감하는 쿼리, 2) 유저의 등급을 'VIP'로 변경하는 쿼리를 순서대로 실행합니다. 이 두 쿼리 중 하나라도 실패할 경우, 안전하게 전체 작업을 취소하고 원래 상태로 되돌려주어 부분 실패를 막아주는 데이터베이스의 기능은 무엇인가요?
3줄 요약
- 1여러 테이블의 데이터를 동시에 변경하는 작업 도중 에러나 사고가 발생하면, 일부 데이터만 변경되어 심각한 오류(부분 실패)가 발생합니다.
- 2데이터베이스의 트랜잭션(Transaction)은 여러 개의 명령어를 묶어 '100% 완전 성공' 아니면 '0% 원상 복구(Rollback)' 둘 중 하나의 결과만 존재하도록 강제합니다.
- 3바이브코딩 시 결제나 포인트 차감 등 중요한 연속 수정 작업에 AI가 트랜잭션 코드를 빼먹지 않도록 반드시 지시하고 확인해야 합니다.