7주차 Unit 5.1 — 트랜잭션이 비즈니스 로직과 결합

Psj·2026년 6월 1일

F-lab

목록 보기
229/240

Unit 5.1 — 트랜잭션이 비즈니스 로직과 결합

F-LAB JAVA · 7주차 · Phase 5 · 수동 트랜잭션의 한계
🔧 Phase 5 시작 + 🔄 Part B 시작 — 트랜잭션 추상화의 진화


📌 학습 목표

이 Unit을 끝내면 다음을 답할 수 있어야 한다.

  • 수동 트랜잭션 의 정의는?
  • 6주차 ACID 의 코드화는?
  • tx.begin / commit / rollback 패턴은?
  • 비즈니스 로직 + 인프라 혼재 의 문제는?
  • 5주차 SoC (관심사 분리) 위반 은?
  • 수동 트랜잭션의 보일러플레이트 는?
  • 같은 패턴이 모든 메서드에 반복 의 폐해는?
  • "트랜잭션 자동화" 의 동기는?
  • @Transactional 의 등장 예고는?

🎯 핵심 한 문장

수동 트랜잭션 코드 (tx.begin / try / commit / catch / rollback / finally / close) 는 6주차 ACID 의 자바 코드화로 비즈니스 로직과 인프라 코드 (트랜잭션 관리) 가 한 메서드에 섞여 5주차 SoC (관심사 분리) 를 위반하고 모든 메서드에 같은 패턴이 반복되는 보일러플레이트 폭증을 일으키며, 이 결합을 분리하려는 욕구가 @Transactional 등장의 직접적 동기다.
수동 트랜잭션 은 개발자가 트랜잭션 시작·커밋·롤백을 명시적으로 코딩 하는 방식이다.
6주차에서 학습한 ACID (Atomicity / Consistency / Isolation / Durability) 를 자바로 직접 구현하는 패턴 — tx.begin() (시작) → try (비즈니스 로직) → tx.commit() (성공 시 커밋) → catch (예외) → tx.rollback() (실패 시 롤백) → finally (close) — 가 한 메서드에 모두 있다.
결정적 문제 3가지 — (1) SoC 위반 — 비즈니스 로직 (3 줄) 과 인프라 코드 (15 줄) 가 한 메서드 (5주차 디자인 패턴 위반), (2) 보일러플레이트 폭증 — 모든 메서드에 같은 try/catch/finally 패턴 반복 (1020 메서드 → 같은 코드 ×1020), (3) 실수 위험 — rollback 누락·commit 위치 오류·중첩 트랜잭션 어려움.
5주차 디자인 패턴의 정신 (DI / OCP / 템플릿+전략) 으로 보면 — "변하는 부분 (비즈니스 로직) 과 변하지 않는 부분 (트랜잭션 시작·커밋·롤백) 을 분리 해야 하는데 수동 방식은 분리 X.
이 결합을 풀려는 욕구가 @Transactional 의 등장 동기 — 어노테이션 1줄로 트랜잭션 자동화 + 5주차 패턴의 결정체 + 6주차 ACID 의 추상화 (Phase 7 ★ 깊이).

비유 — 셰프가 직접 매번 식기 세척

수동 트랜잭션 = 셰프가 매번 식기 세척:

상황:
  - 셰프 (개발자) 의 본업: 요리 (비즈니스 로직)
  - 보조 작업: 식기 세척 (트랜잭션 관리)

수동:
  매 요리 시:
    1. 식기 가져오기 (tx.begin)
    2. 요리 (비즈니스 로직)
    3. 손님 만족 → 식기 정리 (commit)
    4. 사고 → 식기 안 정리 (rollback)
    5. 식기 반납 (close)

  문제:
    - 본업 (요리) 보다 보조 (식기) 더 많음
    - 모든 요리에 같은 식기 작업
    - 매번 같은 일

이상적 (@Transactional):
  - 셰프는 요리만
  - 식기 관리는 자동 (도와주는 직원)
  - "이 식당은 자동" 선언만

5주차 디자인 패턴 정신:
  - 변하는 부분: 요리 (메뉴별)
  - 변하지 않는 부분: 식기 작업
  - 분리해야

수동의 문제 3가지:
  1. SoC 위반 (한 사람이 모두)
  2. 보일러플레이트 (같은 일 반복)
  3. 실수 위험 (식기 누락 등)

6주차 ACID 의 코드화:
  - try { ... commit }
  - catch { rollback }
  - finally { close }
  - = 식기 관리 패턴

@Transactional 예고:
  - 어노테이션 1줄
  - 셰프는 요리만
  - 자동화

ILIC:
  - 수동 → 1020 메서드 × 같은 패턴 = 폭증
  - @Transactional → 어노테이션만

→ 수동 트랜잭션 = 본업 + 인프라 결합, SoC 위반, 보일러플레이트, @Transactional 동기.


🧭 9개 섹션 로드맵

1. 수동 트랜잭션 정의
2. 6주차 ACID 의 코드화
3. tx.begin / commit / rollback
4. 비즈니스 로직 + 인프라 혼재
5. SoC 위반 (5주차)
6. 보일러플레이트 폭증
7. 같은 패턴이 모든 메서드에 반복
8. 트랜잭션 자동화 동기
9. @Transactional 예고

1️⃣ 수동 트랜잭션 정의

1.1 정의

수동 트랜잭션:

  개발자가 직접 트랜잭션 시작 / 커밋 / 롤백을
  명시적으로 코딩:
    - tx.begin()
    - tx.commit()
    - tx.rollback()
    - try / catch / finally

→ "손으로 트랜잭션 관리"

1.2 JDBC 직접 (6주차)

// JDBC 직접 (6주차)
public void transfer(Long fromId, Long toId, BigDecimal amount) {
    Connection conn = null;
    try {
        conn = dataSource.getConnection();
        conn.setAutoCommit(false);   // 트랜잭션 시작
        
        // 비즈니스 로직
        update(conn, fromId, amount.negate());
        update(conn, toId, amount);
        
        conn.commit();   // 성공 시 커밋
    } catch (Exception e) {
        try {
            if (conn != null) conn.rollback();   // 실패 시 롤백
        } catch (SQLException ex) {
            throw new RuntimeException(ex);
        }
        throw new RuntimeException(e);
    } finally {
        try {
            if (conn != null) {
                conn.setAutoCommit(true);
                conn.close();
            }
        } catch (SQLException e) {
            // 무시
        }
    }
}
DataSource dataSource;
interface DataSource { Connection getConnection() throws SQLException; }
void update(Connection conn, Long id, java.math.BigDecimal amount) {}

1.3 JPA 의 수동

// JPA 의 수동 트랜잭션
public void transfer(Long fromId, Long toId, BigDecimal amount) {
    EntityManager em = emf.createEntityManager();
    EntityTransaction tx = em.getTransaction();
    
    try {
        tx.begin();   // 트랜잭션 시작
        
        // 비즈니스 로직
        Account from = em.find(Account.class, fromId);
        Account to = em.find(Account.class, toId);
        from.withdraw(amount);
        to.deposit(amount);
        
        tx.commit();   // 성공 시 커밋
    } catch (Exception e) {
        if (tx.isActive()) tx.rollback();   // 실패 시 롤백
        throw new RuntimeException(e);
    } finally {
        em.close();   // 자원 정리
    }
}
EntityManagerFactory emf;
class EntityManagerFactory { EntityManager createEntityManager() { return null; } }
class EntityManager {
    EntityTransaction getTransaction() { return null; }
    <T> T find(Class<T> c, Object id) { return null; }
    void close() {}
}
class EntityTransaction {
    void begin() {}
    void commit() {}
    void rollback() {}
    boolean isActive() { return false; }
}
class Account {
    void withdraw(java.math.BigDecimal amount) {}
    void deposit(java.math.BigDecimal amount) {}
}

1.4 공통 패턴

공통 패턴:

  begin / try / commit / catch / rollback / finally / close
  
  - 7 줄 (트랜잭션 관리)
  - vs 비즈니스 로직 (3-5 줄)
  - 본말 전도

1.5 ILIC 의 맥락

// ILIC 가 수동 트랜잭션이면 (가정)
@Service
public class ShipmentService {
    @Autowired EntityManagerFactory emf;
    
    public void processShipment(Long id) {
        EntityManager em = emf.createEntityManager();
        EntityTransaction tx = em.getTransaction();
        
        try {
            tx.begin();
            
            // 비즈니스 로직 (3 줄)
            Shipment s = em.find(Shipment.class, id);
            s.markAsShipped();
            em.merge(s);
            
            tx.commit();
        } catch (Exception e) {
            if (tx.isActive()) tx.rollback();
            throw new RuntimeException(e);
        } finally {
            em.close();
        }
    }
    
    // 1020 메서드 모두 비슷한 패턴!
}
class Shipment { void markAsShipped() {} }
EntityManagerFactory emf;
class EntityManagerFactory { EntityManager createEntityManager() { return null; } }
class EntityManager {
    EntityTransaction getTransaction() { return null; }
    <T> T find(Class<T> c, Object id) { return null; }
    <T> T merge(T t) { return null; }
    void close() {}
}
class EntityTransaction {
    void begin() {}
    void commit() {}
    void rollback() {}
    boolean isActive() { return false; }
}

1.6 자기 점검 답변

수동 트랜잭션의 정의는?

:
1. 수동:

  • 명시적 코딩
  1. 패턴:

    • begin / commit / rollback
  2. JDBC / JPA:

    • 둘 다 가능
  3. 부담:

    • 보일러플레이트

2️⃣ 6주차 ACID 의 코드화

2.1 6주차 학습 회상

6주차 학습 회상 (ACID):

  Atomicity (원자성):
    - 모두 성공 / 모두 실패
    - 부분 성공 X

  Consistency (일관성):
    - 트랜잭션 전후 상태 일관

  Isolation (격리성):
    - 동시 실행 시 영향 X

  Durability (지속성):
    - commit 후 영구

→ DB 의 핵심 원칙

2.2 ACID 의 자바 코드화

ACID → 자바 코드:

  Atomicity:
    - try { ... commit } catch { rollback }
    - 모두 성공 (commit) 또는 모두 실패 (rollback)

  Consistency:
    - 비즈니스 규칙 / 제약
    - DB / 애플리케이션 검증

  Isolation:
    - tx.begin (격리 레벨 명시)
    - DB 의 락 / MVCC

  Durability:
    - commit 시 DB 가 보장
    - WAL / fsync

→ 트랜잭션 코드 = ACID 구현

2.3 begin / commit / rollback 의 의미

의미:

  begin:
    - 트랜잭션 경계 시작
    - 격리 시작
    - 메모리에서 변경 추적

  commit:
    - 트랜잭션 성공
    - 변경 영구화 (Durability)
    - 다른 트랜잭션에 visible

  rollback:
    - 트랜잭션 실패
    - 변경 되돌림 (Atomicity)
    - 메모리 정리

2.4 6주차 인프라 위에

6주차 인프라 위에:

  Phase 4 — Connection Pool (HikariCP)
  Phase 5 — DataSource (인터페이스)
  Phase 6 — ACID 원칙
  Phase 7 — JdbcTemplate (SQL Mapper)

  7주차 Part B:
    - 위 인프라 위에
    - 트랜잭션 추상화
    - 수동 → 자동

2.5 ILIC 의 맥락

// ILIC 의 ACID 자바 코드 (수동, 가정)

@Service
public class TransferService {
    @Autowired DataSource dataSource;
    
    public void transfer(Long fromId, Long toId, BigDecimal amount) {
        Connection conn = null;
        try {
            conn = dataSource.getConnection();
            conn.setAutoCommit(false);   // ① ACID 의 begin
            
            // ② 비즈니스 로직 (Atomicity 필요)
            updateBalance(conn, fromId, amount.negate());
            updateBalance(conn, toId, amount);
            
            conn.commit();   // ③ Atomicity + Durability
            // 둘 다 성공 → 모두 반영
        } catch (Exception e) {
            try {
                if (conn != null) conn.rollback();   // ④ Atomicity
                // 하나라도 실패 → 모두 취소
            } catch (SQLException ex) {
                throw new RuntimeException(ex);
            }
            throw new RuntimeException(e);
        } finally {
            close(conn);
        }
    }
}
DataSource dataSource;
interface DataSource { Connection getConnection() throws SQLException; }
void updateBalance(Connection conn, Long id, java.math.BigDecimal a) {}
void close(Connection conn) {}

2.6 자기 점검 답변

6주차 ACID 의 코드화는?

:
1. ACID:

  • DB 원칙
  1. 자바 코드화:

    • try/commit/catch/rollback
  2. begin / commit / rollback:

    • 의미
  3. 6주차 인프라:

    • 위에

3️⃣ tx.begin / commit / rollback

3.1 전형적 패턴

// 전형적 수동 트랜잭션 패턴
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();

try {
    tx.begin();
    
    // === 비즈니스 로직 ===
    // (이 부분만 매번 다름)
    
    tx.commit();
} catch (Exception e) {
    if (tx.isActive()) {
        tx.rollback();
    }
    throw e;
} finally {
    em.close();
}
EntityManagerFactory emf;
class EntityManagerFactory { EntityManager createEntityManager() { return null; } }
class EntityManager {
    EntityTransaction getTransaction() { return null; }
    void close() {}
}
class EntityTransaction {
    void begin() {}
    void commit() {}
    void rollback() {}
    boolean isActive() { return false; }
}

3.2 패턴의 7 단계

패턴의 7 단계:

  ① em / conn 획득
  ② tx 객체
  ③ tx.begin (또는 setAutoCommit(false))
  ④ try (비즈니스 로직)
  ⑤ tx.commit
  ⑥ catch + rollback
  ⑦ finally + close

→ 항상 같음 (변하지 않는 부분)

3.3 비즈니스 로직만 변함

비즈니스 로직만 변함:

  매 메서드:
    - 위 7 단계 동일
    - 비즈니스 로직만 다름

  비유:
    - "옷 같음 (트랜잭션 패턴)"
    - "사람 다름 (비즈니스 로직)"

  5주차 패턴:
    - 변하는 부분 ↔ 변하지 않는 부분
    - 분리해야 (템플릿+전략)

3.4 단순 예시

// 단순 메서드도 7 단계 매번
public Shipment get(Long id) {
    EntityManager em = emf.createEntityManager();
    EntityTransaction tx = em.getTransaction();
    
    try {
        tx.begin();
        Shipment s = em.find(Shipment.class, id);   // 1 줄
        tx.commit();
        return s;
    } catch (Exception e) {
        if (tx.isActive()) tx.rollback();
        throw new RuntimeException(e);
    } finally {
        em.close();
    }
}

// 비즈니스 로직 1 줄 vs 트랜잭션 관리 ~15 줄
// 본말 전도
class Shipment {}
EntityManagerFactory emf;
class EntityManagerFactory { EntityManager createEntityManager() { return null; } }
class EntityManager {
    EntityTransaction getTransaction() { return null; }
    <T> T find(Class<T> c, Object id) { return null; }
    void close() {}
}
class EntityTransaction {
    void begin() {}
    void commit() {}
    void rollback() {}
    boolean isActive() { return false; }
}

3.5 ILIC 의 맥락

// ILIC 의 수동 패턴 (가정)
@Service
public class ShipmentService {
    @Autowired EntityManagerFactory emf;
    
    // 모든 메서드 같은 7 단계
    public Shipment get(Long id) {
        EntityManager em = emf.createEntityManager();
        EntityTransaction tx = em.getTransaction();
        try {
            tx.begin();
            Shipment s = em.find(Shipment.class, id);   // 비즈니스
            tx.commit();
            return s;
        } catch (Exception e) {
            if (tx.isActive()) tx.rollback();
            throw new RuntimeException(e);
        } finally {
            em.close();
        }
    }
    
    public void update(Shipment s) {
        EntityManager em = emf.createEntityManager();
        EntityTransaction tx = em.getTransaction();
        try {
            tx.begin();
            em.merge(s);   // 비즈니스
            tx.commit();
        } catch (Exception e) {
            if (tx.isActive()) tx.rollback();
            throw new RuntimeException(e);
        } finally {
            em.close();
        }
    }
    
    // 같은 패턴 × N 메서드
}
class Shipment {}
EntityManagerFactory emf;
class EntityManagerFactory { EntityManager createEntityManager() { return null; } }
class EntityManager {
    EntityTransaction getTransaction() { return null; }
    <T> T find(Class<T> c, Object id) { return null; }
    <T> T merge(T t) { return null; }
    void close() {}
}
class EntityTransaction {
    void begin() {}
    void commit() {}
    void rollback() {}
    boolean isActive() { return false; }
}

3.6 자기 점검 답변

tx.begin / commit / rollback 패턴은?

:
1. 패턴:

  • 7 단계
  1. 항상 같음:

    • 변하지 않는 부분
  2. 비즈니스:

    • 변함
  3. 5주차:

    • 분리 필요

4️⃣ 비즈니스 로직 + 인프라 혼재

4.1 한 메서드에 두 관심사

한 메서드에 두 관심사:

  1. 비즈니스 관심사:
     - "Shipment 를 SHIPPED 로"
     - 도메인 의미

  2. 인프라 관심사:
     - 트랜잭션 시작 / 커밋 / 롤백
     - 자원 관리

  → 한 메서드에 섞임

4.2 코드 비율

코드 비율:

  비즈니스 로직: 3-5 줄
  인프라 코드 (트랜잭션): 12-15 줄

  비율: 인프라 > 비즈니스
  → 본말 전도

4.3 가독성 ↓

가독성 ↓:

  메서드 읽을 때:
    - "이 메서드 뭐 하는가?"
    - 비즈니스 로직 찾기 어려움
    - try/catch 사이에 묻힘

  의도 파악 ↓

4.4 수정 부담 ↑

수정 부담 ↑:

  비즈니스 로직 수정:
    - try/catch 사이 코드 찾기
    - 주변 인프라 코드 노이즈

  트랜잭션 정책 수정:
    - 모든 메서드 수정 필요
    - 누락 위험

  유지보수 ↓

4.5 5주차 정신 위반

5주차 정신 위반:

  5주차 학습:
    - DI (Dependency Injection)
    - OCP (Open-Closed Principle)
    - SoC (Separation of Concerns)
    - 템플릿+전략

  수동 트랜잭션은:
    - 비즈니스 + 인프라 결합
    - SoC 위반
    - 5주차 가르침 위반

4.6 ILIC 의 맥락

// ILIC 메서드 (수동) - 두 관심사 혼재 (가정)
public void processShipment(Long id, String reason) {
    EntityManager em = emf.createEntityManager();     // 인프라
    EntityTransaction tx = em.getTransaction();        // 인프라
    
    try {
        tx.begin();                                     // 인프라
        
        // === 비즈니스 ===
        Shipment s = em.find(Shipment.class, id);       // 비즈니스
        if (!s.canShip()) {                              // 비즈니스
            throw new IllegalStateException(reason);     // 비즈니스
        }
        s.markAsShipped();                               // 비즈니스
        em.merge(s);                                     // 비즈니스
        // === 비즈니스 끝 ===
        
        tx.commit();                                    // 인프라
    } catch (Exception e) {                             // 인프라
        if (tx.isActive()) tx.rollback();              // 인프라
        throw new RuntimeException(e);                  // 인프라
    } finally {                                        // 인프라
        em.close();                                    // 인프라
    }
}

// 비즈니스 5 줄, 인프라 12 줄
// 비즈니스 의도 묻힘
class Shipment {
    boolean canShip() { return false; }
    void markAsShipped() {}
}
EntityManagerFactory emf;
class EntityManagerFactory { EntityManager createEntityManager() { return null; } }
class EntityManager {
    EntityTransaction getTransaction() { return null; }
    <T> T find(Class<T> c, Object id) { return null; }
    <T> T merge(T t) { return null; }
    void close() {}
}
class EntityTransaction {
    void begin() {}
    void commit() {}
    void rollback() {}
    boolean isActive() { return false; }
}

4.7 자기 점검 답변

비즈니스 로직 + 인프라 혼재 문제는?

:
1. 혼재:

  • 두 관심사
  1. 비율:

    • 인프라 > 비즈니스
  2. 가독성:

  3. 5주차 정신:

    • 위반

5️⃣ SoC 위반 (5주차)

5.1 SoC 원칙

SoC (Separation of Concerns):

  관심사 분리:
    - 각 코드 = 한 가지 책임
    - 다른 관심사는 분리
    - 모듈성 ↑
    - 유지보수 ↑

  5주차 학습:
    - 5주차 디자인 패턴의 기본
    - SOLID 의 SRP (단일 책임)

5.2 수동 트랜잭션의 위반

수동 트랜잭션 = SoC 위반:

  한 메서드:
    - 비즈니스 책임
    - 트랜잭션 관리 책임
    - 자원 관리 책임

  → 3 책임 = SRP 위반
  → 5주차 정신 위반

5.3 분리 했을 때 (이상)

분리 했을 때 (이상):

  비즈니스 코드:
    - 도메인 로직만
    - 깔끔
    - 의도 명확

  트랜잭션 관리:
    - 별도 위치 (어노테이션 / AOP)
    - 자동
    - 인프라 책임

→ 분리 = 깔끔

5.4 6주차 → 7주차 진화

6주차 → 7주차 진화:

  6주차 JdbcTemplate:
    - 자원 관리만 분리 (Connection)
    - 트랜잭션은 여전히 수동

  7주차 @Transactional:
    - 트랜잭션도 분리
    - 어노테이션 1줄
    - 완전한 SoC

→ 추상화 진화

5.5 5주차 패턴 매칭

5주차 패턴 매칭:

  - 템플릿+전략 패턴:
    - 변하지 않는 부분 (템플릿)
    - 변하는 부분 (전략)

  수동 트랜잭션:
    - 변하지 않는 부분: 트랜잭션 패턴 (begin/commit/rollback)
    - 변하는 부분: 비즈니스 로직

  → 템플릿+전략 적용 가능
  → JdbcTemplate (6주차) 와 같은 사상

5.6 ILIC 의 맥락

// ILIC 의 SoC 분리 (이상)

// Bad (수동 트랜잭션)
public void processShipment(Long id) {
    EntityManager em = ...;
    EntityTransaction tx = em.getTransaction();
    try {
        tx.begin();
        // 비즈니스 로직
        tx.commit();
    } catch (Exception e) {
        if (tx.isActive()) tx.rollback();
        throw e;
    } finally {
        em.close();
    }
}

// Good (@Transactional, 미리)
@Transactional
public void processShipment(Long id) {
    // 비즈니스 로직만!
    Shipment s = repo.findById(id).orElseThrow();
    s.markAsShipped();
}

// → 비즈니스만
// → 트랜잭션 분리 (인프라)
// → SoC ✓
class Shipment { void markAsShipped() {} }
EntityManager em;
class EntityManager {
    EntityTransaction getTransaction() { return null; }
    void close() {}
}
class EntityTransaction {
    void begin() {}
    void commit() {}
    void rollback() {}
    boolean isActive() { return false; }
}
@interface Transactional {}
ShipmentRepository repo;
interface ShipmentRepository { java.util.Optional<Shipment> findById(Long id); }

5.7 자기 점검 답변

SoC 위반 (5주차) 은?

:
1. SoC:

  • 관심사 분리
  1. 수동 = 위반:

    • 3 책임
  2. 분리 시:

    • 깔끔
  3. 5주차 패턴:

    • 적용 가능

6️⃣ 보일러플레이트 폭증

6.1 보일러플레이트

보일러플레이트:

  같은 코드의 반복:
    - 의미 없는 반복
    - 같은 패턴
    - 시간 낭비

  수동 트랜잭션:
    - 모든 메서드에 같은 7 단계
    - 폭증

6.2 메서드별 부담

메서드별 부담:

  1 메서드:
    - 비즈니스 3 줄
    - 트랜잭션 12 줄
    - 총 15 줄

  10 메서드:
    - 비즈니스 30 줄
    - 트랜잭션 120 줄
    - 총 150 줄

  100 메서드:
    - 비즈니스 300 줄
    - 트랜잭션 1200 줄
    - 총 1500 줄

6.3 ILIC 의 규모

ILIC 의 규모:

  102 테이블 × 평균 10 메서드 = 1020 메서드

  수동 트랜잭션:
    - 메서드당 트랜잭션 12 줄
    - 총 1020 × 12 = 12,240 줄 보일러플레이트

  @Transactional:
    - 어노테이션 1 줄
    - 총 1020 줄

  → 12,000 줄 절약!

6.4 코드 품질

코드 품질:

  보일러플레이트 ↑:
    - 가독성 ↓
    - 유지보수 ↓
    - 버그 ↑

  깔끔 코드:
    - 비즈니스만
    - 의도 명확
    - 안전

6.5 변경의 부담

변경의 부담:

  트랜잭션 정책 변경 시 (예: 격리 레벨):
    - 모든 메서드 수정 필요
    - 1020 곳 변경
    - 누락 위험
    - 시간 폭증

  @Transactional:
    - 어노테이션 옵션만
    - 또는 클래스 레벨

6.6 ILIC 의 맥락

// ILIC 의 보일러플레이트 (수동)

// 메서드 1
public void method1(Long id) {
    EntityManager em = emf.createEntityManager();
    EntityTransaction tx = em.getTransaction();
    try {
        tx.begin();
        // 비즈니스 1
        tx.commit();
    } catch (Exception e) {
        if (tx.isActive()) tx.rollback();
        throw e;
    } finally {
        em.close();
    }
}

// 메서드 2 (같은 패턴!)
public void method2(Long id) {
    EntityManager em = emf.createEntityManager();
    EntityTransaction tx = em.getTransaction();
    try {
        tx.begin();
        // 비즈니스 2
        tx.commit();
    } catch (Exception e) {
        if (tx.isActive()) tx.rollback();
        throw e;
    } finally {
        em.close();
    }
}

// ... 메서드 1020개 모두 같은 패턴

// 시간 / 인지 부담 / 실수 위험 폭증

6.7 자기 점검 답변

수동 트랜잭션의 보일러플레이트는?

:
1. 보일러플레이트:

  • 같은 코드 반복
  1. 메서드별:

    • 7 단계 매번
  2. ILIC:

    • 12,000 줄
  3. 부담:

    • 가독성/유지보수/버그

7️⃣ 같은 패턴이 모든 메서드에 반복

7.1 반복의 위험

반복의 위험:

  1. 실수 위험:
     - rollback 누락
     - close 누락
     - 누수

  2. 일관성 위험:
     - 일부 다르게 작성
     - 어떤 메서드는 try-with-resources
     - 어떤 메서드는 try/catch/finally

  3. 변경 위험:
     - 정책 변경 시 모든 메서드 수정
     - 누락

7.2 실수 예시

// 실수 1: rollback 누락
try {
    tx.begin();
    // 비즈니스
    tx.commit();
} catch (Exception e) {
    // tx.rollback();   ← 빠뜨림!
    throw e;
}
// 트랜잭션 leak

// 실수 2: close 누락
try {
    // ...
    tx.commit();
} catch (Exception e) {
    tx.rollback();
}
// em.close();   ← 빠뜨림!
// 메모리 누수

// 실수 3: commit 후 코드
try {
    tx.begin();
    // 비즈니스
    tx.commit();
    
    // 이후 작업 (트랜잭션 X)
    sendEmail();   // ← 메일 보내고 실패 시?
} catch (Exception e) {
    tx.rollback();   // 이미 commit, 의미 X
}
class EntityTransaction {
    void begin() {}
    void commit() {}
    void rollback() {}
}
EntityTransaction tx;
void sendEmail() {}

7.3 일관성 ↓

// 같은 코드 다르게 (일관성 X)

// 메서드 A
try {
    tx.begin();
    // ...
    tx.commit();
} catch (Exception e) {
    if (tx.isActive()) tx.rollback();
    throw new RuntimeException(e);
} finally {
    em.close();
}

// 메서드 B (조금 다름)
tx.begin();
try {
    // ...
    tx.commit();
} catch (RuntimeException e) {
    tx.rollback();
    throw e;
} finally {
    if (em.isOpen()) em.close();
}

// 두 메서드 미묘하게 다름
// 일관성 ↓
EntityTransaction tx;
class EntityTransaction {
    void begin() {}
    void commit() {}
    void rollback() {}
    boolean isActive() { return false; }
}
EntityManager em;
class EntityManager {
    void close() {}
    boolean isOpen() { return false; }
}

7.4 코드 리뷰 부담

코드 리뷰 부담:

  매 PR:
    - 모든 메서드의 트랜잭션 코드 확인
    - 누락 / 일관성 / 실수 검사
    - 시간 ↑

  자동화 시:
    - @Transactional 만 확인
    - 빠르고 안전

7.5 ILIC 의 맥락

ILIC 의 위험 시나리오 (수동, 가정)

ILIC 의 1020 메서드:
  - 모두 트랜잭션 코드 반복
  - 일부 미묘하게 다름
  - 일부 실수 (rollback 누락 등)
  - 운영 사고 위험

박승제 의 코드 리뷰 부담:
  - 매 PR 트랜잭션 확인
  - 시간 ↑
  - 놓침 가능

  → @Transactional 도입 동기

@Transactional 시:
  - 어노테이션만 확인
  - 누락 X (어노테이션 없으면 명백)
  - 일관 (Spring 이 처리)
  - 안전

7.6 자기 점검 답변

같은 패턴이 모든 메서드에 반복의 폐해는?

:
1. 실수 위험:

  • rollback/close 누락
  1. 일관성 ↓:

    • 미묘하게 다름
  2. 변경 부담:

    • 1020 곳
  3. 리뷰 부담:

    • 시간 ↑

8️⃣ 트랜잭션 자동화 동기

8.1 자연스러운 욕구

자연스러운 욕구:

  수동 트랜잭션 사용하다 보면:
    "트랜잭션을 자동화할 순 없을까?"
    "이 패턴 반복 안 했으면..."
    "비즈니스만 작성하고 싶다"

→ 자동화 필요

8.2 5주차 패턴의 답

5주차 패턴의 답:

  템플릿+전략:
    - 변하지 않는 부분 (템플릿)
    - 변하는 부분 (전략)

  트랜잭션:
    - 변하지 않는 부분: begin/commit/rollback
    - 변하는 부분: 비즈니스

  적용:
    - 템플릿: TransactionTemplate (Spring)
    - 또는 @Transactional (더 자동)

8.3 6주차 ↔ 7주차 같은 사상

6주차 ↔ 7주차 같은 사상:

  6주차 JdbcTemplate:
    - 변하지 않는 부분: Connection / Statement / ResultSet 관리
    - 변하는 부분: SQL + RowMapper
    - 자원 관리 자동

  7주차 @Transactional:
    - 변하지 않는 부분: tx.begin/commit/rollback
    - 변하는 부분: 비즈니스 로직
    - 트랜잭션 자동

→ 같은 정신 (분리 + 자동화)

8.4 자동화의 가치

자동화의 가치:

  1. 코드 ↓:
     - 보일러플레이트 X
     - 비즈니스만

  2. 안전 ↑:
     - 실수 X (rollback 누락)
     - 일관

  3. 변경 ↑:
     - 정책 변경 쉬움
     - 한 곳 (어노테이션 옵션)

  4. SoC ✓:
     - 비즈니스 / 인프라 분리

  5. 5주차 정신:
     - 디자인 패턴 결정체

8.5 Spring 의 답

Spring 의 답:

  1. TransactionTemplate (수동 + 자동 중간):
     - 람다로 비즈니스 전달
     - 트랜잭션 자동

  2. @Transactional (완전 자동):
     - 어노테이션 1 줄
     - AOP / 프록시 사용
     - 가장 자동

→ @Transactional 표준

8.6 TransactionTemplate 미리보기

// TransactionTemplate (수동과 자동 사이)
@Autowired TransactionTemplate transactionTemplate;

public void process(Long id) {
    transactionTemplate.execute(status -> {
        // 비즈니스만!
        Shipment s = repo.findById(id).orElseThrow();
        s.markAsShipped();
        return null;
    });
    // 자동 commit / rollback
}

// 람다 패턴 + 5주차
class TransactionTemplate {
    <T> T execute(java.util.function.Function<Object, T> f) { return null; }
}
TransactionTemplate transactionTemplate;
class Shipment { void markAsShipped() {} }
ShipmentRepository repo;
interface ShipmentRepository { java.util.Optional<Shipment> findById(Long id); }

8.7 @Transactional 미리보기

// @Transactional (완전 자동)
@Service
public class ShipmentService {
    @Autowired ShipmentRepository repo;
    
    @Transactional   // 이거 1 줄
    public void process(Long id) {
        // 비즈니스만!
        Shipment s = repo.findById(id).orElseThrow();
        s.markAsShipped();
        // 자동:
        // - tx.begin (전)
        // - 메서드 실행
        // - 정상 → commit
        // - 예외 → rollback
        // - 자원 close
    }
}

// 어노테이션 1 줄
// SoC 완전
// 보일러플레이트 X
class Shipment { void markAsShipped() {} }
ShipmentRepository repo;
interface ShipmentRepository { java.util.Optional<Shipment> findById(Long id); }
@interface Transactional {}

8.8 ILIC 의 맥락

ILIC 의 @Transactional 도입

ILIC = @Transactional 사용 (실제):
  - 102 테이블 × 1020 메서드
  - 모두 @Transactional 어노테이션
  - 비즈니스 로직만

코드 양:
  - 수동: 15,000 줄+ 트랜잭션
  - @Transactional: ~1,020 줄 (어노테이션만)
  - 90% 절약

안정성:
  - rollback 누락 X
  - 일관 패턴
  - 검증 (어노테이션 = AOP)

생산성:
  - 비즈니스 집중
  - 박승제 + 1 풀스택 = 2 명 운영 가능
  - 102 테이블 관리

→ Spring 의 결정적 가치

8.9 자기 점검 답변

트랜잭션 자동화의 동기는?

:
1. 욕구:

  • 비즈니스만 작성
  1. 5주차 패턴:

    • 템플릿+전략
  2. 6주차 사상:

    • 같은 정신
  3. Spring 답:

    • @Transactional

9️⃣ @Transactional 예고

9.1 Phase 5-7 흐름

Phase 5-7 흐름:

  Phase 5 — 수동의 한계
    - 5.1 결합 문제 ← 여기
    - 5.2 수동의 3 함정

  Phase 6 — PlatformTransactionManager
    - 6.1 인터페이스 추상화
    - 6.2 3 구현체
    - 6.3 사용 전후 비교

  Phase 7 — @Transactional (★ 모두)
    - 7.1 프록시 패턴 ★
    - 7.2 동작 원리 ★★★
    - 7.3 5가지 함정 ★

→ 진화: 수동 → 추상화 → 자동화

9.2 Part B 의 가치

Part B 의 가치:

  - 6주차 ACID 의 추상화 (자동)
  - 5주차 디자인 패턴의 결정체
  - 자바 백엔드의 또 다른 정점
  - 면접 단골 (프록시 / AOP / 함정)

  특히 Phase 7:
    - 7.2 ★★★
    - @Transactional 의 동작 원리
    - 프록시 + AOP
    - 백엔드의 핵심

9.3 5주차 + 6주차 연계

5주차 + 6주차 + 7주차 Part B:

  5주차 (디자인 패턴):
    - DI / OCP / 템플릿+전략
    - 프록시 패턴

  6주차 (DB 접근):
    - DataSource / ACID / JdbcTemplate

  7주차 Part B:
    - DataSource (6) + 디자인 패턴 (5)
    - = PlatformTransactionManager 추상화
    - + 프록시 (5) + 트랜잭션 (6)
    - = @Transactional 동작 원리

→ 모든 학습의 응축

9.4 박승제의 학습 의의

박승제의 학습 의의

ILIC 의 @Transactional:
  - 매일 사용 (102 테이블)
  - 동작 원리는?
  - 함정은?
  - 효과적 사용?

Phase 7 학습으로:
  - 원리 이해
  - 함정 회피
  - 최적 활용

  → 운영 안정성
  → 디버깅 능력
  → 면접 대비

9.5 면접 단골 (Phase 7)

면접 단골 (Phase 7):

  - @Transactional 동작 원리?
  - 프록시 패턴?
  - AOP?
  - 5가지 함정?
    1. private 메서드
    2. self-invocation
    3. checked exception
    4. 트랜잭션 전파
    5. readOnly

9.6 면접 단골 질문 매핑

Q핵심 답변
수동 트랜잭션?tx.begin/commit/rollback
패턴?7 단계
ACID 코드화?try/catch/finally
혼재?비즈니스 + 인프라
SoC 위반?5주차 정신
보일러플레이트?12,000 줄
실수?rollback/close 누락
자동화 동기?5주차 정신
Spring 답?@Transactional
Phase 7?★ 깊이

9.7 자기 점검 체크리스트

수동 트랜잭션

  • 정의

ACID 코드화

  • 의미

7 단계 패턴

  • 익숙

혼재

  • 두 관심사

SoC 위반

  • 5주차

보일러플레이트

  • 폭증

반복 위험

  • 실수/일관성

자동화 동기

  • 5주차 정신

@Transactional 예고

  • Phase 7

9.8 추가 심화 질문

Q1: try-with-resources?

답:

  • Java 7+
  • AutoCloseable 자동 close
  • finally 의 close 자동
  • 하지만 트랜잭션 commit/rollback 은 여전히 수동

Q2: setAutoCommit(true/false)?

답:

  • Connection 의 옵션
  • true: 매 SQL 자동 commit (트랜잭션 X)
  • false: 수동 commit / rollback (트랜잭션)

Q3: nested transaction?

답:

  • 트랜잭션 안 트랜잭션
  • JDBC: SAVEPOINT
  • JPA: 별도 (PROPAGATION)
  • 수동으로는 어려움

Q4: 트랜잭션 격리 레벨?

답:

  • READ UNCOMMITTED / READ COMMITTED / REPEATABLE READ / SERIALIZABLE
  • 동시성 트레이드오프
  • 수동 시 tx.setIsolationLevel
  • @Transactional 시 옵션

Q5: 트랜잭션 propagation?

답:

  • 메서드 호출 시 트랜잭션 전파 정책
  • REQUIRED / REQUIRES_NEW / NESTED / ...
  • @Transactional 옵션
  • Phase 7 에서 상세

🎯 핵심 요약 — 3줄 정리

1. 수동 트랜잭션 = 결합 코드

  • tx.begin / try / commit / catch / rollback / finally / close 패턴
  • 6주차 ACID 의 자바 코드화
  • 비즈니스 로직 (3 줄) + 인프라 (12 줄) 혼재

2. 결정적 문제 3가지

  • (1) SoC 위반: 비즈니스 + 인프라 한 메서드 (5주차 정신 X)
  • (2) 보일러플레이트 폭증: 1020 메서드 × 12 줄 = 12,240 줄
  • (3) 실수 위험: rollback/close 누락, 일관성 ↓

3. 자동화의 동기

  • 5주차 템플릿+전략 패턴의 자연 적용
  • 6주차 ↔ 7주차 같은 사상 (JdbcTemplate / @Transactional)
  • Spring 의 답: @Transactional 어노테이션 1 줄 (Phase 7 ★)

📚 다음으로...

Unit 5.2 — 수동 트랜잭션 3가지 함정 (Phase 5 완주)

이번 Unit에서 수동의 결합을 봤다면, 다음은 3가지 함정 (Phase 5 마지막).

  • 함정 1: 실수 누락 (rollback / close)
  • 함정 2: 트랜잭션 경계 모호
  • 함정 3: 트랜잭션 전파 어려움
  • → Phase 6 (PlatformTransactionManager) 예고

Phase 5 진행 상황

🔧 Phase 5 — 수동 트랜잭션의 한계
  ✅ Unit 5.1 트랜잭션이 비즈니스 로직과 결합 ← 여기
  ⏭ Unit 5.2 수동 트랜잭션 3가지 함정 — Phase 5 완주

7주차 누적 진행

🗂️ Part A — 데이터 모델링과 ORM
  ✅ Phase 1 (5)
  ✅ Phase 2 (2)
  ✅ Phase 3 (4)
  ✅ Phase 4 (5)

🔄 Part B — 트랜잭션 추상화의 진화
  🔧 Phase 5 (1/2)

총: 17/24 Unit (71%)

🔧 Phase 5 시작 + 🔄 Part B 시작 — 트랜잭션 추상화의 진화

profile
Software Developer

0개의 댓글