Daily Dev Q&A

트랜잭션

awake123 2025. 12. 21. 16:58

1. 트랜잭션이란

트랜잭션(Transaction)은 데이터베이스에서 하나의 논리적인 작업 단위를 의미합니다.
여러 개의 작업이 묶여서 전부 성공하거나, 하나라도 실패하면 전부 취소되어야 할 때 사용됩니다.

 

즉 트랜잭션 = “모두 성공하거나, 모두 실패해야 하는 작업 묶음”

 

핵심 정리

  • 트랜잭션은 데이터 정합성을 지키기 위한 안전장치
  • “중간 실패를 허용하지 않는 작업”에 필수
  • 실무에서는 거의 모든 비즈니스 로직 단위에 적용됨

2. 트랜잭션을 실무에서 사용하지 못한다면.

 

트랜잭션을 실무에서 제대로 사용하지 못한다면, 문제는 단순한 “코드 스타일”이 아니라 서비스 신뢰도 자체가 무너지는 수준까지 갑니다.

 

1) 데이터가 망가진다 (정합성 붕괴)

가장 치명적인 문제

  • 일부 데이터만 저장됨
  • 서로 맞지 않는 데이터가 DB에 남음

예시

  • 주문은 생성됐는데 결제 기록 없음
  • 결제는 됐는데 주문 테이블에 데이터 없음
  • 재고는 줄었는데 주문 취소됨

=> 복구 불가능한 상태가 쌓입니다.

 

2) 장애가 “재현 불가”해진다

트랜잭션이 없으면:

  • 오류가 중간 단계에서 발생
  • 로그상으로는 정상처럼 보이기도 함

결과:

  • “가끔 안 됩니다”
  • “어제는 안됐는데 오늘은 됩니다”
  • QA·운영·개발 모두 원인 파악 불가

=> 실무에서 제일 싫어하는 유형의 장애

 

3) 동시성 문제 폭발

트랜잭션 + 격리 개념이 없으면:

  • 중복 주문
  • 재고 마이너스
  • 포인트 두 번 적립
  • 동시에 수정 → 마지막 값만 남음 (Lost Update)

=> 사용자가 늘어날수록 확률적으로 터짐

 

4) 장애 대응 비용이 급증

트랜잭션이 없으면:

  • DB 직접 수정
  • 수작업 롤백
  • 고객 문의 폭증
  • 운영자 야근

=> “코드 한 줄 아낀 대가로 운영 지옥”

 

5) 비즈니스 신뢰도 하락

실제 서비스에서 이런 일들이 발생합니다:

  • “결제됐는데 상품이 없어요”
  • “주문 내역이 사라졌어요”
  • “환불이 안 됐어요”

=> 사용자는 다시 안 옵니다
=> 회사는 개발자를 신뢰하지 않게 됩니다

 

3. 트랜잭션이 제공하는 효율성과 최적화

 

1) 불필요한 I/O 감소 (성능 최적화)

트랜잭션이 없을 때

  • 쿼리마다 즉시 commit
  • 매 쿼리마다 디스크 flush 발생 가능

트랜잭션이 있을 때

  • 여러 쿼리를 한 번에 commit
  • 디스크 I/O 묶음 처리

결과

  • DB 디스크 접근 횟수 감소
  • TPS(초당 처리량) 증가

=> 실무에서 체감 성능 차이 큼

 

2) 락 관리 최적화 (동시성 효율)

트랜잭션은 락을 무작정 오래 잡는 게 아니라,
일관된 규칙으로 관리합니다.

트랜잭션 없이 직접 제어

  • 락 시점 불명확
  • 예상 못한 데드락
  • 중복 락 획득

트랜잭션 사용 시

  • 락 획득 → 작업 → 커밋/롤백 → 해제
  • 생명주기 명확

결과

  • 데드락 감소
  • 동시 처리 안정성 증가

3) 실패 비용 최소화 (롤백 최적화)

트랜잭션이 없는 경우

  • 중간 실패 → 수동 복구
  • DB 직접 수정
  • 운영 인력 투입

트랜잭션이 있는 경우

  • 실패 → 자동 롤백
  • 별도 복구 불필요

결과

  • 장애 대응 시간 감소
  • 운영 비용 절감
  • 사람 실수 감소

4) 배치·대량 처리 성능 향상

예: 대량 데이터 저장

 
1건 저장 + commit × 10,000

vs

 
10,000건 저장 + commit 1번

 

차이

  • 네트워크 왕복 감소
  • 로그 기록 최소화
  • 처리 시간 수십 배 차이 가능

=> 그래서 배치 작업은 트랜잭션 설계가 핵심

 

5) 캐시·지연 쓰기(write-behind) 활용

트랜잭션이 있으면 DB는:

  • 변경 내용을 즉시 디스크에 쓰지 않고
  • 메모리에 모아두었다가
  • 커밋 시 한 번에 반영

📌 결과

  • 메모리 활용 극대화
  • 디스크 병목 완화

 

6) 읽기 전용 트랜잭션 최적화

 
@Transactional(readOnly = true)

DB와 ORM이 인식하는 의미:

  • 변경 감지(Dirty Checking) 비활성화
  • 스냅샷 최소화
  • 일부 DB는 락 전략 완화

결과

  • 조회 성능 개선
  • 메모리 사용 감소

=> 조회 API에 readOnly 안 쓰는 건 성능 포기에 가깝다.

 

=> JPA 기준으로는 Dirty Checking과 스냅샷 생성을 막는 게 핵심 효과이고,
DB 락 완화는 DB 구현체에 따라 다르다.

 

7) 시스템 설계 단순화 (간접적 효율)

트랜잭션이 없으면:

  • 예외 케이스 코드 폭증
  • 실패 분기 처리 증가
  • 테스트 경우의 수 폭발

트랜잭션이 있으면:

  • 성공 / 실패 두 가지 흐름
  • 코드 단순
  • 테스트 비용 감소

결과

  • 개발 생산성 증가
  • 유지보수 비용 감소

 

4. 면접관의 질문 의도.

 

1) 기본기 확인 (개념 암기 vs 이해)

 

-> “이 사람이 트랜잭션을 정의 수준으로만 아는가,
아니면 왜 존재하는지 이해하는가”

 

 

2) 성능 감각이 있는 개발자인지

-> “이 사람이 서비스가 느려졌을 때
DB부터 의심할 수 있는가?”

 

3) 실무 경험 유무 판별

경험 없는 답변

  • “데이터 무결성을 보장합니다”
  • “안정성을 위해 필요합니다”

경험 있는 답변

  • “트랜잭션 없으면 운영에서 복구 지옥입니다”
  • “락 관리 실패하면 동시성에서 터집니다”
  • “외부 API를 안에 넣으면 장애가 커집니다”

4) 시스템 사고 능력 확인

-> “이 사람은 코드만 보나, 시스템을 보나?”

 

즉 트랜잭션 질문 =
데이터·성능·운영을 동시에 생각하는 개발자인지 확인하는 질문

 

 

꼬리 질문

 

Q1. @Transactional 안에서 예외가 왜 롤백되는가?

A1. Spring은 “비정상 종료”라고 판단되는 경우 트랜잭션을 롤백한다

 

Q2.체크 예외는 왜 롤백 안 되는가?

A2. 체크 예외는 “예측 가능한 비즈니스 예외”로 간주되기 때문

-> Spring은 기본적으로 선언적 트랜잭션에서 비즈니스 예외까지 롤백하면
과도한 롤백이 발생한다고 판단
해서 unchecked 예외만 롤백 대상으로

 

Q3.트랜잭션 범위를 왜 Service에 두는가?

A3. 트랜잭션은 “비즈니스 단위”이기 때문

 

Q4.외부 API 호출을 트랜잭션 안에 두면 왜 위험한가?

A4. 트랜잭션이 “길어지고”, “락이 유지된 채로 멈출 수 있기 때문”

 

1) @Transactional이 동작하지 않는 경우는 언제인가요?

  • private 메서드
  • 같은 클래스 내부 메서드 호출(self-invocation)
  • 프록시를 거치지 않는 호출
  • final 클래스 / final 메서드

-> Spring의 @Transactional은 AOP 프록시 기반이라 프록시를 거치지 않으면 동작하지 않습니다.


2) 왜 Service 계층에만 @Transactional을 붙여야 하나요?

  • Controller → 요청 단위
  • Repository → 단일 쿼리
  • Service → 비즈니스 단위
  • 트랜잭션 경계의 명확성

-> 트랜잭션은 비즈니스 로직 단위여야 하기 때문입니다.


3) 트랜잭션 전파(PROPAGATION)란 무엇인가요?

  • 트랜잭션이 이미 존재할 때
  • 새 트랜잭션을 만들지
  • 기존 트랜잭션에 참여할지
    결정하는 규칙

-> 이미 트랜잭션이 존재할 때
새로 만들지, 기존 것을 사용할지를 결정하는 규칙입니다.


4) REQUIRED와 REQUIRES_NEW의 차이는?

  • REQUIRED: 있으면 참여, 없으면 생성
  • REQUIRES_NEW: 무조건 새 트랜잭션
  • 로그, 이력 저장에서 자주 사용

->

  • REQUIRED
    → 기존 트랜잭션에 참여
  • REQUIRES_NEW
    → 기존 트랜잭션을 중단하고 새로 생성

 


5) 트랜잭션 격리 수준(ISOLATION)은 왜 필요한가요?

  • Dirty Read
  • Non-repeatable Read
  • Phantom Read
    이런 문제를 방지하기 위해

-> 동시에 실행되는 트랜잭션 간 데이터 간섭 문제를 제어하기 위해서입니다.


6) 읽기 전용 트랜잭션(readOnly=true)은 정확히 무엇을 최적화하나요?

  • Dirty Checking 비활성화
  • 스냅샷 비용 감소
  • 일부 DB에서 락 전략 완화

->

  • JPA 기준으로:
    • Dirty Checking 비활성화
    • 변경 감지용 스냅샷 생성 안 함
  • 조회 전용임을 명확히 선언

 


7) 트랜잭션 안에서 외부 API를 호출하면 왜 안 되나요?

  • DB 락 장기 유지
  • 외부 시스템은 롤백 불가
  • 장애 전파 위험

->

 

  • DB 락을 잡은 채 외부 응답 대기
  • 외부 시스템은 롤백 불가
  • 장애가 트랜잭션 전체로 전파

 


8) 트랜잭션 범위를 크게 잡으면 생기는 문제는?

  • 락 점유 시간 증가
  • 데드락 가능성 증가
  • 성능 저하

->

  • 락 점유 시간 증가
  • 동시 처리량 감소
  • 데드락 가능성 증가

 


9) @Transactional과 @Async를 같이 쓰면 어떻게 되나요?

  • 스레드가 달라짐
  • 트랜잭션 컨텍스트 전달 안 됨
  • 별도 트랜잭션으로 실행됨

->

  • @Async는 별도 스레드에서 실행
  • 트랜잭션 컨텍스트는 스레드 로컬
  • 기존 트랜잭션이 전달되지 않음

 


10) JPA에서 flush와 commit의 차이는?

  • flush: SQL을 DB로 전송
  • commit: 트랜잭션 종료 + 확정
  • flush는 롤백 가능, commit은 불가

->

  • flush
    → SQL을 DB로 전송 (트랜잭션 유지)
  • commit
    → 트랜잭션 종료 + 확정

-> flush는 쿼리 동기화, commit은 트랜잭션 종료

 

 

 

 

'Daily Dev Q&A' 카테고리의 다른 글

spring의 예외처리방식  (0) 2025.12.30
스프링 프레임 워크  (0) 2025.12.22
람다표현식  (0) 2025.12.19
자바의 접근 제어자(Access Modifier)  (0) 2025.12.09
Collections Framework  (0) 2025.12.09