문제의 배경
비즈니스 논리 처리 작업에서는 종종 여러 관련 테이블에서 데이터를 변경해야 합니다. 만약 작업이 실패할 경우(예: 제약 조건 위반), 마지막 작업뿐만 아니라 논리 내의 모든 작업을 롤백하는 것이 중요합니다. 이를 위해 트랜잭션 메커니즘과 오류 처리를 사용합니다.
문제점
모든 DBMS가 기본적으로 다단계 프로시저에서 오류 발생 시 롤백을 보장하지 않습니다 (특히 TRY/CATCH, EXCEPTION 핸들러를 사용할 경우). 트랜잭션 논리를 잘못 작성하면 "부분적인" 변경이 발생할 수 있으며 (일부 데이터는 업데이트되고 일부는 업데이트되지 않음), 이는 비즈니스 무결성을 훼손합니다.
해결책
정확한 처리를 위해 명시적으로 트랜잭션을 시작하고 종료합니다 (BEGIN TRANSACTION, COMMIT/ROLLBACK) 및 오류를 포착합니다. 다양한 DBMS에서 구문 및 기능이 다릅니다.
MS SQL Server의 예:
CREATE PROCEDURE update_balances(@from INT, @to INT, @amount DECIMAL(10,2)) AS BEGIN BEGIN TRY BEGIN TRANSACTION; UPDATE accounts SET balance = balance - @amount WHERE id = @from; UPDATE accounts SET balance = balance + @amount WHERE id = @to; COMMIT TRANSACTION; END TRY BEGIN CATCH ROLLBACK TRANSACTION; INSERT INTO error_log(err_message, err_date) VALUES(ERROR_MESSAGE(), GETDATE()); THROW; -- 오류를 다시 던진다 END CATCH END;
PostgreSQL의 경우:
CREATE OR REPLACE FUNCTION update_balances(from_id INT, to_id INT, amount NUMERIC) RETURNS void AS $$ BEGIN UPDATE accounts SET balance = balance - amount WHERE id = from_id; UPDATE accounts SET balance = balance + amount WHERE id = to_id; -- 예외가 발생하면 자동으로 트랜잭션이 롤백됩니다 EXCEPTION WHEN OTHERS THEN INSERT INTO error_log(err_message, err_date) VALUES(SQLERRM, NOW()); RAISE; END; $$ LANGUAGE plpgsql;
주요 특징:
저장 프로시저 내부의 오류가 항상 자동으로 트랜잭션을 롤백할 수 있나요? 아니요! 예를 들어, MS SQL Server에서는 이러한 롤백이 보장되지 않으며 명시적인 ROLLBACK이 필요합니다. PostgreSQL에서는 모든 오류가 발생하면 전체 트랜잭션이 "손상"되고 종료가 필요합니다.
하나의 프로시저 내에서 "부분적으로" 커밋할 수 있나요? 안 좋은 관행입니다. 비즈니스 작업의 끝에서 단 한번만 commit/rollback을 하는 것을 권장합니다. 일부 플랫폼에서는 "savepoints"를 허용하지만, 이는 특수 작업을 위한 것입니다.
SQL에서 TRY/CATCH/EXCEPTION을 통해 잡을 수 없는 오류 유형은 무엇인가요? 일부 시스템 장애(예: 서버와의 연결 끊김)는 핸들러에 도달하지 못할 수 있으며, 롤백을 불가능하게 만들 수 있습니다.
저장 프로시저가 세 개의 테이블을 업데이트하고 오류 처리가 없는 COMMIT으로 종료되었습니다. 결과적으로 두 번째 테이블에서 오류가 발생할 때 첫 번째 테이블은 이미 변경되었고, 백업에서 수작업으로 데이터를 "롤백"하는 데 몇 시간이 걸렸습니다.
장점:
간단하고 "작동"하나, 실패가 발생할 때까지.
단점:
불일치 위험; 오류 발생 시 빠르게 복구할 수 없음.
명시적인 오류 처리 및 트랜잭션 롤백을 도입하고 모든 실패를 별도의 로그에 기록했습니다. 데이터베이스의 원래 상태로 복귀하는 데 몇 초밖에 걸리지 않으며, 오류가 분석되고 최소화됩니다.
장점:
장애 발생 시에도 데이터의 무결성을 보장; 투명한 논리.
단점:
초보자에게는 약간 복잡하며, 코드에서의 규율이 필요합니다.