정보처리기사 실기 트랜잭션·동시성·회복 | COMMIT과 격리 수준 예제

2026. 10. 14. 10:46ㆍWORK/커리어

저녁 버스 정류장에서 서류 봉투를 들고 기다리는 사람

이 글은 LIFE ARCHITECTURE의 WORK 카테고리 ‘일하며 공부하는 구조’ 기록입니다. 퇴근 후 짧은 시간에 자격증 공부를 이어가려면 의지보다 ‘한 번에 끝낼 수 있는 작은 단위’로 공부를 쪼개는 구조가 필요합니다. 이번에는 트랜잭션·동시성·회복을 예로, 두 작업이 겹치거나 중간에 실패했을 때 데이터가 어떻게 달라지는지 순서대로 정리했습니다.

퇴근길에 공부할 시간이 20분 남았다. 트랜잭션의 ACID 네 글자를 다시 외우려다가도, 두 사람이 같은 좌석을 동시에 예약하면 무엇이 먼저 반영되는지에서 막히기 쉽다. 답은 용어를 따로 외우는 데 있지 않다. 한 작업의 경계, 다른 작업이 볼 수 있는 값, 장애 뒤 남을 값을 구분하면 된다. 아래 예제는 실제 기출이 아닌 설명을 위해 만든 연습문제다.

트랜잭션은 함께 성공하거나 함께 취소할 작업의 경계다

가정한 사례에서 교육 신청 시스템은 남은 좌석을 한 자리 줄이고 신청 내역을 하나 추가한다. 이 둘을 별개의 확정 작업으로 처리하면 좌석만 줄어든 뒤 신청 내역 저장에 실패할 수 있다. 트랜잭션으로 묶으면 성공한 두 변경을 함께 확정하거나, 확정 전 실패한 변경을 취소할 수 있다. PostgreSQL에서는 BEGIN으로 경계를 열고 COMMIT으로 확정하며 ROLLBACK으로 취소한다.

BEGIN;
UPDATE classes SET remaining = remaining - 1
WHERE id = 7 AND remaining > 0;
INSERT INTO registrations (class_id, student_id)
VALUES (7, 42);
COMMIT;

이 코드는 개념을 보여 주는 예시다. 실제 서비스는 UPDATE가 정확히 한 행에 적용됐는지 확인하고, 중복 신청을 막는 제약 조건과 오류 처리를 두어야 한다. 좌석이 0개라 UPDATE가 아무 행도 바꾸지 않았는데 INSERT를 그대로 실행하면 논리상 잘못된 신청이 생길 수 있다. 즉 트랜잭션은 둘을 묶는 틀이고, 업무 규칙이 맞는지는 별도로 검사해야 한다.

ACID는 같은 사건을 네 방향에서 묻는 말이다

원자성은 묶은 변경을 부분만 확정하지 않는 성질이다. 일관성은 트랜잭션 전후에 정의한 제약을 유지한다는 요구다. 격리성은 여러 트랜잭션이 겹칠 때 서로의 중간 상태가 어떻게 보이는지에 관한 성질이다. 지속성은 성공적으로 확정된 결과가 장애 뒤에도 보존되는 성질이다. 네 단어를 한 문장에 쌓기보다, 사례에 각각 질문을 붙여 보자. 신청 기록이 실패하면 좌석 변경도 취소되는가. 남은 좌석이 음수가 되지 않도록 규칙을 검사했는가. 다른 사람은 아직 확정되지 않은 신청을 보는가. 확정 직후 서버가 멈추면 무엇이 남는가.

질문 연결 개념 좌석 신청 사례에서 확인할 일
둘 중 하나만 저장될 수 있는가 원자성 실패 시 함께 취소
규칙을 어긴 값이 생기는가 일관성 좌석 수·중복 신청 제약 검사
동시 사용자에게 무엇이 보이는가 격리성 읽기 시점과 잠금·충돌 처리
확정 뒤 장애가 나면 남는가 지속성 로그와 복구 설정 확인

COMMIT 전의 변경과 이후의 변경을 나눠 읽는다

초기 좌석 수가 3이라고 가정한다. A가 트랜잭션 안에서 2로 바꾸고 아직 확정하지 않았다. A 자신은 자신의 변경을 볼 수 있지만, PostgreSQL의 기본 Read Committed 격리 수준에서 B의 일반 조회는 A의 미확정 값 2를 읽지 않는다. A가 ROLLBACK하면 저장된 값은 3으로 돌아간다. A가 COMMIT한 뒤 B가 새 SELECT 문장을 시작하면 B는 확정된 값 2를 볼 수 있다. 다만 B가 어느 격리 수준에서 언제 읽었는지에 따라 결과 설명은 달라진다.

이 구분은 앞선 정규화와 함수 종속 예제에서 테이블의 구조를 나눴던 것과 다른 질문이다. 정규화가 데이터의 배치와 중복을 다룬다면, 여기서는 그 데이터를 바꾸는 여러 작업의 시간 순서를 살핀다.

동시성 문제는 읽은 시점과 쓴 시점을 표시하면 보인다

가정한 사례에서 좌석이 1개 남았다. A와 B가 거의 동시에 “1개 남음”을 읽고 각각 신청을 시도한다. 둘 다 화면의 숫자를 봤다는 사실만으로 둘 다 신청을 허용해서는 안 된다. 조건부 UPDATE의 영향받은 행 수를 확인하고, 신청 레코드의 제약과 오류를 같은 트랜잭션에서 처리해야 한다. 어떤 데이터베이스와 격리 수준을 쓰는지에 따라 잠금 대기나 직렬화 실패가 나타날 수 있으므로, 충돌 시 재시도 정책도 설계 대상이다.

시험용으로 현상을 읽을 때는 세 가지를 구별한다. 더티 리드는 남이 아직 확정하지 않은 값을 읽는 것, 반복 불가능 읽기는 같은 트랜잭션에서 같은 행을 다시 읽었는데 다른 트랜잭션의 확정으로 값이 달라지는 것, 팬텀 읽기는 같은 조건의 행 집합을 다시 조회할 때 새 행 등이 나타나는 것이다. 이 명칭만 보고 모든 DBMS가 같은 격리 수준에서 똑같이 움직인다고 단정하면 오답이 된다.

격리 수준은 읽기 결과를 묻는 조건표다

PostgreSQL 문서 기준으로 Read Committed는 각 SELECT 문장이 시작할 때까지 확정된 데이터의 스냅샷을 읽는다. 그래서 같은 트랜잭션에서 첫 SELECT 뒤 다른 트랜잭션이 확정하고, 두 번째 SELECT를 시작하면 결과가 달라질 수 있다. Repeatable Read는 트랜잭션의 첫 일반 명령 시점의 스냅샷을 기준으로 읽으므로 같은 값의 재조회가 안정적이다. PostgreSQL의 Repeatable Read는 표준이 요구하는 최소 수준보다 강해서 팬텀 읽기도 막는다. Serializable은 동시 실행 결과가 어떤 직렬 실행과 동등하도록 검사하며, 직렬화 오류가 나면 응용 프로그램이 트랜잭션 전체를 재시도해야 할 수 있다.

다음 표는 PostgreSQL을 전제로 한 문제 풀이 메모다. 다른 제품의 기본값이나 구현은 해당 제품 문서를 따로 확인해야 한다.

수준 같은 트랜잭션의 새 SELECT 풀이할 때 주의할 점
Read Committed 문장마다 새로운 확정 상태를 볼 수 있음 미확정 값은 읽지 않음
Repeatable Read 같은 스냅샷을 기준으로 읽음 PostgreSQL에서는 팬텀도 막음
Serializable 직렬 실행과 동등한 결과를 목표로 검사 충돌하면 전체 재시도가 필요할 수 있음

장애 뒤의 회복은 확정 기록을 기준으로 생각한다

전원이 끊겼다는 문장을 보면 먼저 장애가 COMMIT 전인지 후인지 표시한다. 확정 전 작업은 완료된 결과로 취급하지 않는다. 확정 뒤라면 지속성의 관점에서 복구된 데이터에 그 변경이 남아야 한다. PostgreSQL의 선행 기록 로그(WAL)는 데이터 페이지가 디스크에 기록되기 전 변경을 로그에 남겨, 장애 후 필요한 변경을 재적용하는 바탕이 된다. 다만 실제 내구성은 저장 장치와 서버 설정에 영향을 받는다. 예컨대 관련 안전 설정을 끄면 성공 응답 뒤 장애 시 손실 가능성이 달라진다. 시험의 추상적 원칙과 특정 시스템의 설정을 섞지 말자.

SAVEPOINT는 긴 트랜잭션 안에서 일부 작업을 되돌리는 지점이다. 그 지점으로 돌아갔다고 해서 바깥 트랜잭션이 자동으로 COMMIT되는 것은 아니다. 문제에서 ROLLBACK TO SAVEPOINT가 나오면 그 이후의 변경만 되돌리는지, 이후 COMMIT까지 제시되는지 순서를 끝까지 읽어야 한다.

짧은 연습은 시간표 한 줄로 검산한다

자체 연습문제다. PostgreSQL의 Read Committed, 초기 classes.remaining = 3으로 가정한다. A가 BEGIN 후 2로 UPDATE하되 아직 COMMIT하지 않았다. B가 SELECT를 한 번 실행한다. 그다음 A가 COMMIT하고 B가 같은 트랜잭션에서 새 SELECT를 실행한다. B의 두 결과는 무엇인가?

첫 결과는 3, 두 번째 결과는 2다. 첫 문장이 시작할 때 A의 2는 미확정이므로 보이지 않는다. 두 번째 문장 전에 A가 확정했으므로 새 문장의 스냅샷에는 2가 포함된다. B가 Repeatable Read였다면 첫 일반 조회의 스냅샷을 계속 사용해 두 번째도 3을 보게 된다. 답을 쓸 때는 값뿐 아니라 “문장 시작 시점”과 “격리 수준”을 함께 적으면 검산이 쉽다.

  • 각 세션의 BEGIN, UPDATE, COMMIT, ROLLBACK 순서를 두 줄로 적는다.
  • 읽는 순간 다른 세션의 변경이 확정됐는지 동그라미 친다.
  • 격리 수준과 스냅샷 기준이 문장별인지 트랜잭션별인지 표시한다.
  • 장애가 있으면 COMMIT 전후를 가른 다음 남을 값을 쓴다.
  • 제약 위반이나 갱신 행 수 확인이 필요한 업무 규칙은 별도 표시한다.

헷갈리는 예외를 질문으로 남겨 둔다

Q. ROLLBACK은 다른 사람이 이미 COMMIT한 변경도 취소하는가?
아니다. 자신의 트랜잭션에서 아직 확정하지 않은 변경을 취소한다. 다른 세션의 확정까지 되돌리는 명령으로 해석하지 않는다.

Q. 트랜잭션만 쓰면 좌석 초과 신청이 절대 생기지 않는가?
그렇지 않다. UPDATE가 실제 한 행을 바꿨는지 확인하고, 중복을 막는 제약과 동시 요청 충돌 처리가 필요하다. 원자성과 업무 규칙 검증은 서로 다른 역할이다.

Q. Repeatable Read라면 모든 DB에서 팬텀 읽기가 같은가?
제품별 구현을 확인해야 한다. 이 글의 표와 연습 결과는 PostgreSQL 문서를 기준으로 했다. 문제에 DBMS가 특정되면 그 조건을 먼저 적용한다.

외울 문장을 더 늘리기 전에 두 사람의 작업을 나란히 적어 보자. 데이터는 어느 순간 읽혔고, 어느 순간 확정됐는가. 이 두 표시가 있으면 트랜잭션은 긴 용어 목록보다 훨씬 작은 문제로 바뀐다.

자료 확인: 2026년 10월 14일. 종목 정보는 Q-Net 정보처리기사 종목 안내에서 확인했다. 동작 설명은 PostgreSQL 트랜잭션 안내, 격리 수준 문서, WAL·회복 문서를 기준으로 했다. 예제의 좌석 수와 세션은 학습용 가정이다.

반응형