F-LAB JAVA · 7주차 · Phase 6 · PlatformTransactionManager
🏆 Phase 6 완주 — 점진적 추상화의 흐름
이 Unit을 끝내면 다음을 답할 수 있어야 한다.
수동 트랜잭션 (begin/commit/rollback 직접 = 15 줄) → PlatformTransactionManager 직접 사용 (인터페이스 의존 = 12 줄) → TransactionTemplate (람다 + try/catch 자동 = 5 줄) → @Transactional (어노테이션 1줄 + AOP) 의 4 단계 진화로 코드 양은 15 → 1 로 줄고 SoC / 안전성 / 일관성 모두 점진적으로 향상되며, 이 진화의 마지막 정점이 Phase 7 의 @Transactional + 프록시 + AOP ★★★ 학습이다.
트랜잭션 처리의 진화는 4 단계 — 각 단계마다 코드 양은 줄고 추상화 수준은 높아진다.
(1) Before (수동 트랜잭션, Phase 5) —tx.begin / try / commit / catch / rollback / finally / close매번 작성, 15 줄 보일러플레이트, SoC 위반 / 함정 위험.
(2) After 1 (PlatformTransactionManager 직접) — 인터페이스 의존 (DIP), 어떤 TM 이든 같은 코드, 12 줄 (수동 대비 약간 개선), 여전히 명시적 commit/rollback.
(3) After 2 (TransactionTemplate) — 람다 (Callback) 로 비즈니스 전달, 5 줄 (try/catch/finally 자동), 5주차 템플릿+전략 패턴 / 6주차 JdbcTemplate 와 같은 정신.
(4) After 3 (@Transactional, Phase 7 ★★★) — 어노테이션 1 줄 + AOP/프록시 자동, 완전 자동화, SoC 완벽, 현대 표준.
진화의 의미 — 5주차 디자인 패턴 (DI/DIP/OCP/템플릿+전략) + 6주차 DataSource 추상화 + 자바 진영 추상화 정신 의 결정체로, ILIC 의 1020 메서드가 모두 @Transactional 어노테이션 1 줄만으로 안전하게 동작하는 게 이 진화의 결과다.
트랜잭션 처리 진화 = 자동차 진화:
[Before] 손수레 (수동 트랜잭션):
- 모든 부품 직접 조립
- 매번 같은 과정
- 실수 위험
- 운반: 가능하지만 부담
[After 1] 수동 변속기 자동차 (PlatformTM 직접):
- 부품 표준화 (인터페이스)
- 운전법 같음 (어떤 차든)
- 하지만 변속 직접
- 운반: 더 효율적
[After 2] 자동 변속기 (TransactionTemplate):
- 변속 자동
- 운전자: 가속 / 핸들만
- 람다 (Callback) = 운전자 의도
- 운반: 편함
[After 3] 자율주행 (@Transactional):
- 어노테이션 1줄 = "목적지"
- 모든 동작 자동
- 완벽한 추상화
- 운반: 최상
진화의 의미:
- 5주차 (디자인) + 6주차 (DataSource) 정신
- 인터페이스 추상화
- 점진적 자동화
- 5+6+7주차 응축
ILIC:
- 1020 메서드 = 모두 @Transactional 1줄
- 박승제 + 1 풀스택 = 운영 가능
- 자동화의 결과
→ 4 단계 진화, 코드 15 → 1, 점진적 추상화, @Transactional 정점.
1. 사용 전후 비교 개요
2. Before — 수동 트랜잭션
3. After 1 — PlatformTransactionManager 직접
4. After 2 — TransactionTemplate (람다)
5. After 3 — @Transactional 미리보기
6. 단계별 코드 비교 (한눈에)
7. 5+6주차 학습 응축
8. 진화의 의미
9. Phase 6 완주 + Phase 7 ★★★ 예고
4 단계 진화:
① Before (Phase 5):
- 수동 트랜잭션
- tx.begin / commit / rollback
- 15 줄
② After 1 (Phase 6.1):
- PlatformTransactionManager 직접
- 인터페이스 의존
- 12 줄
③ After 2 (Phase 6.1-2):
- TransactionTemplate
- 람다
- 5 줄
④ After 3 (Phase 7):
- @Transactional
- 어노테이션
- 1 줄
코드 양:
Before: 15 줄 (트랜잭션) + 3 줄 (비즈니스)
After 1: 12 줄 (트랜잭션) + 3 줄 (비즈니스)
After 2: 5 줄 (트랜잭션) + 3 줄 (비즈니스)
After 3: 1 줄 (@Transactional) + 3 줄 (비즈니스)
→ 15 → 1
→ 93% 감소
추상화 수준:
Before:
- 가장 저수준
- JDBC 직접 또는 JPA 직접
- 트랜잭션 객체 명시
After 1:
- PlatformTM 인터페이스
- 구현체 모름
- 5주차 DIP
After 2:
- 람다 + 자동
- 5주차 템플릿+전략
After 3:
- 어노테이션 + AOP
- 완전 자동
- 5주차 프록시
ILIC = After 3 사용
ILIC 의 모든 1020 메서드:
- @Transactional 어노테이션
- 1 줄
- 비즈니스만
수동 (Before) 이었으면:
- 15,000 줄+ 트랜잭션 코드
- 함정 위험
- 운영 사고
@Transactional 덕분:
- 1020 줄
- 안전
- 자동
→ 93% 절약 + 안전
사용 전후 비교 개요는?
답:
1. 4 단계:
코드:
추상화:
현재 표준:
// Before: 수동 트랜잭션
@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(); // ⑧ 자원
}
}
}
// → 15 줄 트랜잭션 코드
// → 3 줄 비즈니스
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; }
}
@interface Service {}
@interface Autowired {}
문제점 (Phase 5):
1. SoC 위반:
- 비즈니스 (3 줄) + 인프라 (15 줄)
- 한 메서드
2. 보일러플레이트:
- 매 메서드 같은 패턴
- 1020 메서드 × 12 줄 = 12,240 줄
3. 함정 위험:
- rollback 누락
- close 누락
- 일관성 ↓
4. 의도 모호:
- 비즈니스 코드 묻힘
- 가독성 ↓
ILIC 의 부담 (수동, 가정)
102 테이블 × 평균 10 메서드 = 1020 메서드
각 메서드:
- 비즈니스: 3-10 줄
- 트랜잭션: 12-15 줄
총:
- 비즈니스: 약 6,000 줄
- 트랜잭션: 약 13,000 줄
비율:
- 트랜잭션 > 비즈니스
- 본말 전도
유지보수:
- 모든 메서드 트랜잭션 확인
- 시간 폭증
- 실수 위험
Before — 수동 트랜잭션 코드는?
답:
1. 수동:
줄 수:
문제:
ILIC:
// After 1: PlatformTransactionManager 직접
@Service
public class ShipmentService {
@Autowired PlatformTransactionManager tm; // 인터페이스
@Autowired ShipmentRepository repo;
public void processShipment(Long id) {
TransactionStatus status = tm.getTransaction(
new DefaultTransactionDefinition()
); // ① 시작
try {
// === 비즈니스 ===
Shipment s = repo.findById(id).orElseThrow();
s.markAsShipped();
// === 비즈니스 끝 ===
tm.commit(status); // ② 커밋
} catch (Exception e) {
tm.rollback(status); // ③ 롤백
throw new RuntimeException(e);
}
}
}
// → 12 줄 트랜잭션
// → 인터페이스 의존
class Shipment { void markAsShipped() {} }
ShipmentRepository repo;
interface ShipmentRepository { java.util.Optional<Shipment> findById(Long id); }
class PlatformTransactionManager {
TransactionStatus getTransaction(TransactionDefinition d) { return null; }
void commit(TransactionStatus s) {}
void rollback(TransactionStatus s) {}
}
PlatformTransactionManager tm;
class TransactionStatus {}
class TransactionDefinition {}
class DefaultTransactionDefinition extends TransactionDefinition {}
@interface Service {}
@interface Autowired {}
개선점:
1. 인터페이스 의존:
- PlatformTransactionManager 만
- 구현체 모름
- 5주차 DIP ✓
2. 자원 관리 ↓:
- em.close() X (TM 이 처리)
3. finally 불필요:
- TM 이 내부적으로 자원 정리
4. 일관 인터페이스:
- JDBC / JPA / Hibernate 모두 같음
하지만:
- 여전히 명시적 commit / rollback
- try / catch 필요
- 12 줄
남은 문제:
1. 여전히 보일러플레이트:
- 매 메서드 try/catch
- 12 줄 반복
2. 인지 부담:
- try / catch / rollback / throw
- 매번 작성
3. SoC 부분적:
- 인프라 코드 줄었지만 여전히 있음
→ 다음 단계 필요 (TransactionTemplate)
Before:
EntityManager em = emf.createEntityManager(); ← 자원
EntityTransaction tx = em.getTransaction(); ← 자원
try {
tx.begin(); ← 시작
// 비즈니스
tx.commit(); ← 커밋
} catch (Exception e) {
if (tx.isActive()) tx.rollback(); ← 안전 + 롤백
throw new RuntimeException(e);
} finally {
em.close(); ← 자원 정리
}
After 1:
TransactionStatus status = tm.getTransaction(...); ← 시작
try {
// 비즈니스
tm.commit(status); ← 커밋
} catch (Exception e) {
tm.rollback(status); ← 롤백
throw new RuntimeException(e);
}
// finally 없음 (TM 이 처리)
→ 자원 / finally 자동
→ 하지만 try/catch 여전
// ILIC 의 After 1 (만약 사용했다면)
@Service
public class ShipmentService {
@Autowired PlatformTransactionManager tm;
@Autowired ShipmentRepository repo;
public Shipment get(Long id) {
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setReadOnly(true); // 읽기 전용
TransactionStatus status = tm.getTransaction(def);
try {
Shipment s = repo.findById(id).orElseThrow();
tm.commit(status);
return s;
} catch (Exception e) {
tm.rollback(status);
throw new RuntimeException(e);
}
}
}
// → 1020 메서드 × 12 줄 = 12,240 줄 보일러플레이트
// → 여전히 부담
// → 다음 단계 필요
class Shipment {}
ShipmentRepository repo;
interface ShipmentRepository { java.util.Optional<Shipment> findById(Long id); }
class PlatformTransactionManager {
TransactionStatus getTransaction(TransactionDefinition d) { return null; }
void commit(TransactionStatus s) {}
void rollback(TransactionStatus s) {}
}
PlatformTransactionManager tm;
class TransactionStatus {}
class TransactionDefinition {}
class DefaultTransactionDefinition extends TransactionDefinition { void setReadOnly(boolean b) {} }
@interface Service {}
@interface Autowired {}
After 1 — PlatformTransactionManager 직접 코드는?
답:
1. PlatformTM:
줄 수:
개선:
남은 문제:
// After 2: TransactionTemplate
@Service
public class ShipmentService {
@Autowired TransactionTemplate transactionTemplate;
@Autowired ShipmentRepository repo;
public void processShipment(Long id) {
transactionTemplate.executeWithoutResult(status -> {
// === 비즈니스만 ===
Shipment s = repo.findById(id).orElseThrow();
s.markAsShipped();
// === 비즈니스 끝 ===
});
// 트랜잭션 자동:
// - 시작 / commit / rollback / 자원 정리
}
}
// → 5 줄 (lambda 포함)
// → try/catch 자동
class Shipment { void markAsShipped() {} }
ShipmentRepository repo;
interface ShipmentRepository { java.util.Optional<Shipment> findById(Long id); }
class TransactionTemplate {
void executeWithoutResult(java.util.function.Consumer<TransactionStatus> c) {}
<T> T execute(java.util.function.Function<TransactionStatus, T> f) { return null; }
}
TransactionTemplate transactionTemplate;
class TransactionStatus {}
@interface Service {}
@interface Autowired {}
람다의 마법:
변하지 않는 부분 (TransactionTemplate):
- 트랜잭션 시작
- try / catch / finally
- commit / rollback
- 자원 정리
변하는 부분 (람다):
- 비즈니스 로직만
→ 5주차 템플릿+전략 패턴
→ 6주차 JdbcTemplate 와 같은 정신
// 반환값 있을 때 (execute)
public Shipment getShipment(Long id) {
return transactionTemplate.execute(status -> {
return repo.findById(id).orElseThrow();
});
}
// 또는 (Java 17+)
public Shipment getShipment(Long id) {
return transactionTemplate.execute(status ->
repo.findById(id).orElseThrow()
);
}
class Shipment {}
ShipmentRepository repo;
interface ShipmentRepository { java.util.Optional<Shipment> findById(Long id); }
class TransactionTemplate {
<T> T execute(java.util.function.Function<TransactionStatus, T> f) { return null; }
}
TransactionTemplate transactionTemplate;
class TransactionStatus {}
// 명시적 rollback (조건부)
transactionTemplate.executeWithoutResult(status -> {
Shipment s = repo.findById(id).orElseThrow();
if (!s.canProcess()) {
status.setRollbackOnly(); // ← rollback 예약
return;
}
s.process();
});
// 자동:
// - rollbackOnly 면 commit 시 rollback
class Shipment {
boolean canProcess() { return false; }
void process() {}
}
ShipmentRepository repo;
interface ShipmentRepository { java.util.Optional<Shipment> findById(Long id); }
class TransactionTemplate {
void executeWithoutResult(java.util.function.Consumer<TransactionStatus> c) {}
}
TransactionTemplate transactionTemplate;
class TransactionStatus { void setRollbackOnly() {} }
Long id;
// TransactionTemplate 의 동작 (개념)
public <T> T execute(TransactionCallback<T> action) {
TransactionStatus status = transactionManager.getTransaction(definition);
T result;
try {
result = action.doInTransaction(status); // ← 람다 호출
} catch (Exception e) {
transactionManager.rollback(status);
throw e;
}
transactionManager.commit(status);
return result;
}
// 5주차 템플릿+전략 패턴 완벽 적용
class TransactionCallback<T> { T doInTransaction(TransactionStatus s) { return null; } }
class PlatformTransactionManager {
TransactionStatus getTransaction(Object d) { return null; }
void commit(TransactionStatus s) {}
void rollback(TransactionStatus s) {}
}
class TransactionStatus {}
PlatformTransactionManager transactionManager;
Object definition;
6주차 JdbcTemplate ↔ 7주차 TransactionTemplate:
JdbcTemplate (6주차):
- 변하지 않는 부분: Connection / Statement / ResultSet
- 변하는 부분: SQL / RowMapper (람다)
- 자원 관리 자동
TransactionTemplate (7주차):
- 변하지 않는 부분: begin / commit / rollback / close
- 변하는 부분: 비즈니스 (람다)
- 트랜잭션 자동
→ 정확히 같은 패턴
→ 5주차 템플릿+전략 + 3주차 람다
개선점 (After 1 → After 2):
1. 코드 ↓:
- 12 줄 → 5 줄
2. try/catch 제거:
- 자동 처리
3. 가독성 ↑:
- 비즈니스 명확
- 람다 = 의도 명확
4. 람다 활용:
- 3주차 함수형
- 5주차 패턴
하지만:
- 메서드 시그니처 노이즈 (Consumer / Function)
- 람다 안에서 예외 처리 미묘
- 메서드 전체 트랜잭션 아닐 때만 진가
// ILIC 의 TransactionTemplate 활용 (배치 등)
@Service
public class ShipmentBatchService {
@Autowired TransactionTemplate transactionTemplate;
@Autowired ShipmentRepository repo;
// 배치: 개별 트랜잭션 (실패해도 다른 거 계속)
public void processBatch(List<Long> ids) {
for (Long id : ids) {
try {
transactionTemplate.executeWithoutResult(status -> {
Shipment s = repo.findById(id).orElseThrow();
s.process();
});
// 개별 트랜잭션 (1건 실패해도 나머지 계속)
} catch (Exception e) {
log.error("Failed processing {}", id, e);
}
}
}
}
// → ILIC 도 가끔 사용 (배치 / 세밀 제어)
// → 일반은 @Transactional 표준
class Shipment { void process() {} }
ShipmentRepository repo;
interface ShipmentRepository { java.util.Optional<Shipment> findById(Long id); }
class TransactionTemplate {
void executeWithoutResult(java.util.function.Consumer<TransactionStatus> c) {}
}
TransactionTemplate transactionTemplate;
class TransactionStatus {}
org.slf4j.Logger log;
@interface Service {}
@interface Autowired {}
After 2 — TransactionTemplate (람다) 코드는?
답:
1. TransactionTemplate:
줄 수:
패턴:
6주차 JdbcTemplate:
// After 3: @Transactional
@Service
public class ShipmentService {
@Autowired ShipmentRepository repo;
@Transactional // ← 이거 1 줄!
public void processShipment(Long id) {
// === 비즈니스만 ===
Shipment s = repo.findById(id).orElseThrow();
s.markAsShipped();
// === 비즈니스 끝 ===
}
// 자동:
// - 트랜잭션 시작
// - 메서드 실행
// - 정상 → commit
// - 예외 → rollback
// - 자원 정리
}
// → 1 줄
// → 비즈니스만
class Shipment { void markAsShipped() {} }
ShipmentRepository repo;
interface ShipmentRepository { java.util.Optional<Shipment> findById(Long id); }
@interface Service {}
@interface Autowired {}
@interface Transactional {}
어노테이션의 마법:
@Transactional:
- 단순 어노테이션 (메타데이터)
- 동작 X (자체로는)
Spring 의 AOP / 프록시:
- 어노테이션 감지
- 프록시 객체 생성
- 호출 시 트랜잭션 자동
→ 다음 Phase 7 ★★★ 상세
호출 흐름:
① 클라이언트:
shipmentService.processShipment(1L);
② Spring 의 프록시 (Spring 이 생성):
- 진짜 ShipmentService 의 자식 또는 인터페이스 구현
- 호출 가로채기 (AOP)
③ 프록시:
- PlatformTransactionManager.getTransaction()
- 진짜 메서드 호출
④ 진짜 메서드:
- 비즈니스 로직
⑤ 프록시:
- 정상 → tm.commit()
- 예외 → tm.rollback()
- 자원 정리
→ 모두 자동
// @Transactional 의 옵션 (다양)
@Transactional(
propagation = Propagation.REQUIRED, // 전파
isolation = Isolation.READ_COMMITTED, // 격리
timeout = 30, // 타임아웃 (초)
readOnly = false, // 읽기 전용
rollbackFor = {Exception.class} // 어떤 예외 시 rollback
)
public void process() {
// ...
}
// 모든 옵션이 @Transactional 어노테이션 1줄에
@interface Transactional {
Propagation propagation() default Propagation.REQUIRED;
Isolation isolation() default Isolation.DEFAULT;
int timeout() default -1;
boolean readOnly() default false;
Class<?>[] rollbackFor() default {};
}
enum Propagation { REQUIRED }
enum Isolation { DEFAULT, READ_COMMITTED }
5가지 함정 (Phase 7):
1. private 메서드:
- 프록시 적용 X
- @Transactional 무시
2. self-invocation:
- 같은 클래스 내 호출
- 프록시 우회
- 무시
3. checked exception:
- 기본 rollback X
- rollbackFor 명시 필요
4. 트랜잭션 전파:
- REQUIRED / REQUIRES_NEW / NESTED 등
- 7가지
5. readOnly:
- 진짜 읽기 전용?
- 최적화 X 케이스
// ILIC 의 @Transactional 표준
@Service
public class ShipmentService {
@Autowired ShipmentRepository repo;
// 쓰기
@Transactional
public void process(Long id) {
Shipment s = repo.findById(id).orElseThrow();
s.markAsShipped();
}
// 읽기 전용 (최적화)
@Transactional(readOnly = true)
public List<Shipment> findAll() {
return repo.findAll();
}
// 새 트랜잭션 (로그 등)
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void logEvent(Long id, String event) {
eventLogRepo.save(new EventLog(id, event));
}
}
// → 102 테이블 × 1020 메서드 모두 이 패턴
// → 1020 줄 (어노테이션)
class Shipment { void markAsShipped() {} }
ShipmentRepository repo;
interface ShipmentRepository {
java.util.Optional<Shipment> findById(Long id);
java.util.List<Shipment> findAll();
}
class EventLog { EventLog(Long id, String e) {} }
EventLogRepository eventLogRepo;
interface EventLogRepository { void save(EventLog e); }
@interface Service {}
@interface Autowired {}
@interface Transactional {
boolean readOnly() default false;
Propagation propagation() default Propagation.REQUIRED;
}
enum Propagation { REQUIRED, REQUIRES_NEW }
After 3 — @Transactional 미리보기는?
답:
1. @Transactional:
자동:
AOP / 프록시:
5가지 함정:
═══════════════════════════════════
Before (수동) — 15 줄
═══════════════════════════════════
public void process(Long id) {
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
try {
tx.begin();
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();
}
}
═══════════════════════════════════
After 1 (PlatformTM) — 12 줄
═══════════════════════════════════
public void process(Long id) {
TransactionStatus status = tm.getTransaction(
new DefaultTransactionDefinition()
);
try {
Shipment s = repo.findById(id).orElseThrow();
s.markAsShipped();
tm.commit(status);
} catch (Exception e) {
tm.rollback(status);
throw new RuntimeException(e);
}
}
═══════════════════════════════════
After 2 (TransactionTemplate) — 5 줄
═══════════════════════════════════
public void process(Long id) {
transactionTemplate.executeWithoutResult(status -> {
Shipment s = repo.findById(id).orElseThrow();
s.markAsShipped();
});
}
═══════════════════════════════════
After 3 (@Transactional) — 1 줄
═══════════════════════════════════
@Transactional
public void process(Long id) {
Shipment s = repo.findById(id).orElseThrow();
s.markAsShipped();
}
줄 수 진화:
Before: 15 줄 (트랜잭션) + 3 줄 (비즈니스) = 18 줄
After 1: 12 줄 (트랜잭션) + 3 줄 (비즈니스) = 15 줄
After 2: 3 줄 (트랜잭션) + 2 줄 (비즈니스, 람다) = 5 줄
After 3: 1 줄 (@Transactional) + 2 줄 (비즈니스) = 3 줄
→ 18 → 3
→ 83% 감소
ILIC 의 규모 (1020 메서드):
Before:
1020 × 15 = 15,300 줄 (트랜잭션)
+ 1020 × 3 = 3,060 줄 (비즈니스)
총: 18,360 줄
After 1:
1020 × 12 = 12,240 줄 (트랜잭션)
+ 1020 × 3 = 3,060 줄 (비즈니스)
총: 15,300 줄
After 2:
1020 × 3 = 3,060 줄 (트랜잭션)
+ 1020 × 2 = 2,040 줄 (비즈니스, 람다)
총: 5,100 줄
After 3:
1020 × 1 = 1,020 줄 (@Transactional)
+ 1020 × 2 = 2,040 줄 (비즈니스)
총: 3,060 줄
→ 18,360 → 3,060
→ 83% 절약
→ 약 15,000 줄 줄임!
| 단계 | 비즈니스 | 인프라 | SoC |
|---|---|---|---|
| Before | 3 줄 | 15 줄 | ❌ (인프라 > 비즈니스) |
| After 1 | 3 줄 | 12 줄 | ❌ |
| After 2 | 2 줄 | 3 줄 | △ |
| After 3 | 2 줄 | 0 줄 (어노테이션만) | ✓ |
| 단계 | rollback 누락 | close 누락 | 일관성 |
|---|---|---|---|
| Before | 위험 | 위험 | ↓ |
| After 1 | 위험 (try/catch 수동) | 자동 | △ |
| After 2 | 자동 (TransactionTemplate) | 자동 | ↑ |
| After 3 | 자동 (@Transactional) | 자동 | ✓ |
단계별 코드 비교 (한눈에) 는?
답:
1. 줄 수:
ILIC:
SoC:
안전성:
5주차 + 6주차 + 7주차 응축:
5주차 (디자인 패턴):
- DI / DIP / OCP
- 템플릿+전략
- 프록시
- SoC
6주차 (DB 접근):
- DataSource 추상화
- ACID
- JdbcTemplate
7주차 Part B (트랜잭션):
- PlatformTransactionManager
- TransactionTemplate
- @Transactional (프록시)
- 5+6주차 정신 응축
5주차 패턴 매핑:
- DI:
@Autowired PlatformTransactionManager tm;
어노테이션 1줄
- DIP:
PlatformTransactionManager (인터페이스 의존)
구현체 모름
- OCP:
구현체 교체 가능
JpaTM / DataSourceTM 등
- 템플릿+전략:
TransactionTemplate (템플릿)
람다 (전략)
- 프록시:
@Transactional 의 동작
Phase 7 ★★★
→ 5주차 모든 패턴 응축
6주차 정신 매핑:
6주차 DataSource:
- 인터페이스 + 구현
- HikariDataSource 등
7주차 PlatformTM:
- 인터페이스 + 구현
- JpaTransactionManager 등
→ 정확히 같은 패턴
6주차 JdbcTemplate:
- 자원 관리 자동
7주차 TransactionTemplate:
- 트랜잭션 관리 자동
→ 같은 정신
학습의 누적 가치:
1주차 → 7주차:
- 자바 OOP
- JVM
- 함수형 (3주차)
- 동시성
- 디자인 패턴 (5주차)
- DB 접근 (6주차)
- ORM + 트랜잭션 (7주차)
각 주차가 다음 주차의 기반:
- 3주차 → 람다 (TransactionTemplate)
- 5주차 → 디자인 패턴 (PlatformTM)
- 6주차 → DataSource (PlatformTM)
→ 7주차 = 모든 학습의 정점
박승제의 학습 흐름
1주차 OOP →
2주차 JVM →
3주차 람다 →
4주차 동시성 →
5주차 디자인 패턴 →
6주차 DB 접근 →
7주차 ORM + 트랜잭션 (지금)
각 주차 단단히 →
7주차 깊은 이해
Phase 7 (@Transactional ★★★):
- 5주차 프록시 활용
- 6주차 PlatformTM 위에
- 7주차 정점
→ 면접 / 운영 / 코드 리뷰
→ 박승제의 자산
ILIC = 5+6+7주차 결정체
ILIC 의 코드:
@Service
public class ShipmentService {
@Autowired ShipmentRepository repo;
@Transactional
public void process(Long id) {
Shipment s = repo.findById(id).orElseThrow();
s.markAsShipped();
}
}
내부 동작:
- 5주차: @Service 빈, @Autowired DI, 프록시
- 6주차: HikariCP, DataSource, JDBC
- 7주차: Spring Data JPA, JpaTransactionManager,
@Transactional, EntityManager
모든 학습이 이 1 메서드에 응축
단순하지만 깊음
→ 자바 백엔드의 정점
5+6주차 학습 응축 의미는?
답:
1. 5주차:
6주차:
7주차 Part B:
결정체:
진화의 의미:
자바 진영의 정신:
- 추상화 + 표준 + 자동화
- 점진적 진화
- DI / DIP / OCP
트랜잭션의 진화:
- 수동 → 인터페이스 → 람다 → 어노테이션
- 코드 ↓ + 안전 ↑ + SoC ✓
추상화의 가치:
- 보일러플레이트 ↓
- 비즈니스 집중
- 실수 ↓
추상화의 비용:
장점:
- 위 가치
비용:
- "마법" 느낌
- 내부 모르면 디버깅 ↓
- 함정 (Phase 7)
- 학습곡선
대처:
- 깊은 이해 (이번 학습)
- 5+6+7주차 정신
다른 진화 사례:
자원 관리:
- 수동 (try/catch/finally) →
- try-with-resources →
- @AutoClosable
의존성 주입:
- new 직접 →
- 팩토리 →
- DI 컨테이너 (Spring)
매핑:
- JDBC ResultSet →
- RowMapper (람다) →
- JPA 어노테이션
→ 모두 같은 정신 (점진적 자동화)
실무의 가치:
코드 품질 ↑:
- 비즈니스 집중
- 가독성 ↑
- 유지보수 ↑
생산성 ↑:
- 빠른 개발
- 안전한 코드
- 적은 인력 운영
운영 안전 ↑:
- 함정 자동 회피
- 일관 패턴
→ "현대 자바 백엔드의 표준"
박승제의 가치
ILIC 의 박승제:
- 102 테이블 + 1020 메서드
- 풀스택 (혼자 + 1 명)
- 어떻게 운영?
@Transactional 의 가치:
- 1020 어노테이션
- 비즈니스만 작성
- 함정 자동 회피
- 코드 리뷰 효율
Phase 7 학습 후:
- 동작 원리 이해
- 함정 회피
- 트러블슈팅 능력
- 면접 우위
→ Spring Boot 개발자의 핵심
진화의 의미는?
답:
1. 진화:
자바 정신:
다른 사례:
가치:
🎯 Phase 6 — PlatformTransactionManager
Unit 6.1 — 인터페이스 추상화:
- PlatformTransactionManager
- 3 메서드
- 5주차 DIP + 6주차 DataSource 정신
Unit 6.2 — 3가지 구현체:
- DataSourceTM / HibernateTM / JpaTM
- 선택 기준
- 혼용 (JPA + JdbcTemplate)
Unit 6.3 — 사용 전후 비교 ← 여기:
- 4 단계 진화
- Before / After 1 / 2 / 3
- Phase 6 완주
Phase 6 핵심 메시지:
"PlatformTransactionManager 는 Spring 의 트랜잭션 추상화 인터페이스로
5주차 DIP / 6주차 DataSource 와 같은 사상이며,
인터페이스 (PlatformTM) + 구현체 (DataSource/Hibernate/Jpa TM)
+ 활용 (직접 / TransactionTemplate / @Transactional) 의
완벽한 추상화 흐름이 자바 백엔드 트랜잭션의 진화다."
✨ Phase 7 — @Transactional (3 Unit, 모두 ★ 깊이)
Unit 7.1 — 프록시 패턴 ★:
- 5주차 프록시 패턴 활용
- JDK 동적 프록시 / CGLIB
- Spring AOP 의 기반
Unit 7.2 — @Transactional 동작 원리 ★★★:
- 어노테이션 → 프록시 → 트랜잭션
- Bean Post Processor
- 진짜 메서드 호출 흐름
- 면접 정점
Unit 7.3 — @Transactional 5가지 함정 ★:
- private 메서드
- self-invocation
- checked exception
- propagation
- readOnly
→ 자바 백엔드의 정점
→ 7주차 종합 졸업 시험 24문항
Phase 7 의 가치:
1. 면접 단골:
- "@Transactional 동작 원리?"
- "프록시 패턴?"
- "5가지 함정?"
2. 실무 핵심:
- 매일 사용
- 함정 회피 필수
- 트러블슈팅
3. 깊은 이해:
- 5+6+7주차 응축
- 자바 백엔드 정수
4. 7주차 정점:
- 마지막 ★★★ Phase
- 졸업 시험 24문항
🗂️ Part A — 데이터 모델링과 ORM (완주)
✅ Phase 1-4 (16)
🔄 Part B — 트랜잭션 추상화의 진화
✅ Phase 5 (2)
✅ Phase 6 (3) ← 완주!
⏭ Phase 7 (3) — 모두 ★ 깊이
총: 21/24 Unit (88%, Phase 6 완주!)
ILIC 의 다음 단계 (학습)
Phase 7 학습 후:
- @Transactional 동작 원리 이해
- 프록시 / AOP 디버깅 능력
- 5가지 함정 회피
- propagation 활용
ILIC 의 1020 메서드:
- 더 깊은 이해
- 안전한 운영
- 효율적 코드 리뷰
박승제 의 면접 우위:
- 시니어 개발자 시험
- 깊은 답변
| Q | 핵심 답변 |
|---|---|
| 4 단계 진화? | Before/After1/2/3 |
| Before? | 수동 (15 줄) |
| PlatformTM 직접? | 12 줄, 인터페이스 |
| TransactionTemplate? | 람다 (5 줄) |
| @Transactional? | 1 줄 (AOP) |
| SoC 점진적? | After3 완벽 |
| 5주차 패턴? | DI/DIP/OCP/템플릿+전략/프록시 |
| 6주차 정신? | DataSource 동일 |
| 코드 절약? | ILIC 15,000+ |
| Phase 7? | ★★★ 정점 |
답:
답:
답:
답:
답:
1. 4 단계 진화
2. 5+6주차 응축
3. Phase 7 ★★★ 정점
🎯 Phase 6 — PlatformTransactionManager
✅ Unit 6.1 인터페이스 추상화
✅ Unit 6.2 3가지 구현체 (DataSourceTM/HibernateTM/JpaTM)
✅ Unit 6.3 사용 전후 비교 ← 여기, Phase 6 완주
→ Spring 의 트랜잭션 추상화 완전 정복
→ 5주차 + 6주차 정신 응축
→ Phase 7 (@Transactional ★★★) 의 기반
✨ Phase 7 — @Transactional (마지막)
Unit 7.1 — 프록시 패턴 ★
Unit 7.2 — @Transactional 동작 원리 ★★★
Unit 7.3 — @Transactional 5가지 함정 ★
+ 종합 졸업 시험 24문항
Phase 7 의 가치:
🗂️ Part A — 데이터 모델링과 ORM (완주)
✅ Phase 1-4 (16)
🔄 Part B — 트랜잭션 추상화의 진화
✅ Phase 5 (2)
✅ Phase 6 (3) ← 완주
⏭ Phase 7 (3) — 모두 ★ 깊이
총: 21/24 Unit (88%, Phase 6 완주!)
🏆 Phase 6 완주 — 점진적 추상화의 흐름