🎯 F-lab Java 7주차 학습 커리큘럼

7주차 자료의 모든 토픽을 두 개의 큰 흐름으로 정리한 학습 경로.
1) 데이터 모델링과 ORM — SQL JOIN → ORM 패러다임 → JPA 입문 → 엔티티 매핑
2) 트랜잭션 추상화의 진화 — 수동 관리 → PlatformTransactionManager → @Transactional

6주차에서 JDBC와 JdbcTemplate으로 DB 접근을 익혔다면, 7주차는 한 단계 더 높은 추상화로 올라간다.

사전 메모: 자료 첫 부분의 "스프링 빈이란?"은 5주차 Phase 8(ApplicationContext + DI)의 복습 포인트이므로 본 커리큘럼에서는 Phase로 분리하지 않고 학습 운영 팁에서 짧게 다룬다.


📊 학습 경로 한눈에 보기

[Part A — 데이터 모델링과 ORM]
  [Phase 1] SQL JOIN — 관계형 DB의 본질
     ↓
  [Phase 2] ORM 패러다임 — 객체와 관계의 만남
     ↓
  [Phase 3] JPA 입문
     ↓
  [Phase 4] JPA 엔티티 매핑

[Part B — 트랜잭션 추상화의 진화]
  [Phase 5] 수동 트랜잭션의 한계
     ↓
  [Phase 6] PlatformTransactionManager (인터페이스 추상화)
     ↓
  [Phase 7] @Transactional (선언적 트랜잭션)

총 7 Phase × 24 Unit

🔗 1~7주차 흐름 정리

주차주제핵심 변화
1주차OOP·JVM·GC·컬렉션·I/O 개론자바 큰 그림
2주차JVM 내부·바이트코드·G1 GC"어떻게 돌아가나"
3주차컬렉션·제네릭·함수형자바 표현력
4주차멀티스레딩·동시성·Executor동시성 정복
5주차Atomic + Spring IoC/DI 입문자바 → Spring 다리
6주차테스트 + 웹 인프라 + DB 접근 진화Spring 실전 환경
7주차 (지금)ORM/JPA + 트랜잭션 추상화DB 추상화의 정점

🗓️ 권장 학습 일정 (압축 6일)

DayPhase학습 목표
1일차Phase 1SQL JOIN 4종 마스터
2일차Phase 2 + 3ORM 패러다임 + JPA 입문
3일차Phase 4JPA 엔티티 매핑 어노테이션
4일차Phase 5 + 6수동 트랜잭션 → PlatformTransactionManager
5일차Phase 7@Transactional 동작 원리
6일차종합 자기 점검 + 실습전체 정리

자료 분량이 5·6주차보다 가벼움. 6일 일정으로 충분하나, JPA 실습을 더 하려면 +3일.


🗂️ Part A — 데이터 모델링과 ORM

📚 Phase 1 — SQL JOIN: 관계형 DB의 본질

목표: 4가지 JOIN의 결과를 머릿속에서 그릴 수 있게 된다. JPA를 이해하기 위한 필수 기초.

Unit 1.1 — JOIN이 필요한 이유 (정규화의 결과)

선수 지식: 6주차 Phase 4 (DB 세션)

핵심 개념

왜 JOIN이 필요한가:

  • 관계형 DB는 정규화 를 통해 중복을 제거
  • 하나의 정보가 여러 테이블에 분산됨
  • 예: 직원과 부서가 별도 테이블에 저장
employees (직원)              departments (부서)
┌──┬───────┬───────────┐   ┌────┬──────────────┐
│id│ name  │dept_id    │   │ id │ dept_name    │
├──┼───────┼───────────┤   ├────┼──────────────┤
│ 1│Alice  │       101 │   │101 │ HR           │
│ 2│Bob    │       102 │   │102 │ Engineering  │
│ 3│Charlie│      NULL │   │103 │ Sales        │
└──┴───────┴───────────┘   └────┴──────────────┘

→ "Alice가 어느 부서?" 답하려면 두 테이블을 합쳐야 함 → JOIN

자기 점검

  • 정규화의 장단점은? (힌트: 중복 ↓, JOIN 비용 ↑)
  • "비정규화(역정규화)"를 쓰는 시나리오는? (힌트: 분석용 DB)

Unit 1.2 — INNER JOIN (교집합)

선수 지식: Unit 1.1

핵심 개념

"두 테이블에서 공통된 값 이 있는 행만 반환"

SELECT e.id, e.name, d.department_name
FROM employees e
INNER JOIN departments d ON e.department_id = d.id;

결과:

id │ name  │ department_name
───┼───────┼─────────────────
 1 │ Alice │ HR
 2 │ Bob   │ Engineering

Charlie(부서 NULL) 와 Sales(직원 없음) 는 제외됨.

자기 점검

  • INNER JOIN과 단순 SELECT FROM A, B WHERE A.x = B.y 의 차이는?
  • ON 조건과 WHERE 조건의 차이는? (힌트: OUTER JOIN에서 다름)

Unit 1.3 — LEFT JOIN과 RIGHT JOIN

선수 지식: Unit 1.2

핵심 개념

LEFT JOIN: 왼쪽 테이블의 모든 행 반환, 오른쪽 매칭 없으면 NULL

SELECT e.id, e.name, d.department_name
FROM employees e
LEFT JOIN departments d ON e.department_id = d.id;
id │ name    │ department_name
───┼─────────┼─────────────────
 1 │ Alice   │ HR
 2 │ Bob     │ Engineering
 3 │ Charlie │ NULL          ← 부서 없는 직원도 포함

RIGHT JOIN: 왼쪽/오른쪽이 반대

SELECT e.id, e.name, d.department_name
FROM employees e
RIGHT JOIN departments d ON e.department_id = d.id;
id   │ name  │ department_name
─────┼───────┼─────────────────
   1 │ Alice │ HR
   2 │ Bob   │ Engineering
NULL │ NULL  │ Sales         ← 직원 없는 부서도 포함

자기 점검

  • LEFT JOIN과 RIGHT JOIN 중 어느 쪽이 실무에서 더 자주 쓰이는가?
  • A LEFT JOIN BB RIGHT JOIN A 는 같은가?

Unit 1.4 — FULL OUTER JOIN (합집합)

선수 지식: Unit 1.3

핵심 개념

"양쪽 테이블의 모든 행 반환, 매칭 없으면 NULL"

SELECT e.id, e.name, d.department_name
FROM employees e
FULL OUTER JOIN departments d ON e.department_id = d.id;
id   │ name    │ department_name
─────┼─────────┼─────────────────
   1 │ Alice   │ HR
   2 │ Bob     │ Engineering
   3 │ Charlie │ NULL          ← 부서 없는 직원
NULL │ NULL    │ Sales         ← 직원 없는 부서

MySQL은 FULL OUTER JOIN을 지원하지 않음LEFT JOIN UNION RIGHT JOIN 으로 우회.

자기 점검

  • FULL OUTER JOIN의 결과 행 수는 INNER JOIN과 어떻게 다른가?
  • 4가지 JOIN 결과의 포함관계를 벤다이어그램으로?

Unit 1.5 — JOIN 선택 가이드

선수 지식: Unit 1.4

선택 매트릭스:

시나리오JOIN 종류
양쪽에 모두 있는 데이터만 필요INNER JOIN
왼쪽 테이블의 모든 데이터 + 매칭되는 오른쪽LEFT JOIN
두 테이블 모두의 모든 데이터FULL OUTER JOIN
"부서 없는 직원만" 같은 차집합LEFT JOIN + WHERE NULL

실무 팁:

  • LEFT JOIN이 가장 빈번 (누락 없이 다 보고 싶음)
  • INNER JOIN은 카운트·집계에 유용
  • RIGHT JOIN은 거의 안 씀 (가독성 ↓ → LEFT JOIN으로 표현)

자기 점검

  • "부서 없는 직원 찾기" 쿼리는?
  • "직원 없는 부서 찾기" 쿼리는?

📚 Phase 2 — ORM 패러다임

목표: ORM이 왜 필요한지를, 객체와 관계 모델의 본질적 미스매치로 이해한다.

Unit 2.1 — 객체-관계 미스매치

선수 지식: 1주차 Phase 1 (OOP), Phase 1 (JOIN)

핵심 개념

객체와 관계형 DB는 다른 패러다임:

측면객체 (OOP)관계형 DB
모델링상태 + 행동행과 열
상속있음없음
연관 관계참조 (order.member)외래 키 (FK)
식별객체 식별자 (==)PK
데이터 타입풍부 (List, Map, ...)제한적

이 차이를 메우는 코드를 매번 손으로 쓰는 게 ORM 등장 전의 고통

자기 점검

  • Order 객체가 List<OrderItem> 을 갖는 구조를 RDB로 어떻게 표현하는가?
  • 객체의 상속을 RDB로 표현하는 방법 3가지는? (힌트: SINGLE_TABLE, JOINED, TABLE_PER_CLASS)

Unit 2.2 — ORM의 정의와 효과

선수 지식: Unit 2.1

핵심 개념

ORM (Object-Relational Mapping):

  • 객체는 객체대로 설계
  • 관계형 DB는 DB대로 설계
  • ORM 프레임워크가 중간에서 매핑

효과:

  • 매핑 코드를 자동 생성 → 개발 속도 ↑
  • DB 변경 시 코드 변경 ↓
  • 객체지향적 코드 유지

대중적 ORM:

  • Java: Hibernate, EclipseLink
  • Python: SQLAlchemy, Django ORM
  • Ruby: ActiveRecord
  • C#: Entity Framework

자기 점검

  • ORM이 SQL을 완전히 대체하는가? (힌트: NO, 복잡 쿼리는 여전히 SQL)
  • ORM의 단점 3가지는? (힌트: 학습곡선, N+1, 성능 튜닝 어려움)

📚 Phase 3 — JPA 입문

목표: JPA가 자바 진영의 ORM 표준임을 이해하고, JdbcTemplate/MyBatis와의 결정적 차이를 잡는다.

Unit 3.1 — SQL Mapper의 한계

선수 지식: 6주차 Phase 7 (JdbcTemplate)

핵심 한계

JdbcTemplate, MyBatis 같은 SQL Mapper 는:

  • ✅ 자원 관리 자동화
  • ✅ 결과 매핑 자동화
  • SQL은 여전히 직접 작성
  • 객체-관계 매핑은 여전히 수동

예시 (JdbcTemplate):

String sql = "SELECT * FROM item WHERE id = ?";
Item item = jdbcTemplate.queryForObject(sql, itemRowMapper, id);
// ↑ SQL과 RowMapper를 직접 작성

→ "SQL 자체를 안 쓰는 방법은 없을까?"

자기 점검

  • JdbcTemplate이 해결하지 못하는 매핑 작업은?
  • 객체 그래프(예: Order → List) 를 SQL Mapper로 처리하면?

Unit 3.2 — JPA의 등장

선수 지식: Unit 3.1

핵심 개념

JPA (Java Persistence API):

  • 자바 진영의 ORM 표준 인터페이스
  • 구현체: Hibernate(가장 대중적), EclipseLink
  • 어노테이션 기반 매핑
  • SQL을 JPA가 대신 작성

SQL Mapper vs JPA:

SQL Mapper (JdbcTemplate, MyBatis)JPA
SQL 작성개발자JPA가 자동 생성
매핑RowMapper로 수동어노테이션으로 선언
객체 그래프수동 처리자동 (Lazy Loading)
학습 곡선낮음높음
복잡 쿼리자유어려움 (네이티브 쿼리 필요)

자기 점검

  • JPA를 쓰면 SQL을 전혀 안 써도 되는가? (힌트: NO, 복잡 통계 등)
  • JPA가 만든 SQL을 어떻게 확인할까? (힌트: show-sql, format-sql)

Unit 3.3 — JPA의 동작 위치

선수 지식: Unit 3.2

핵심 그림

┌─────────────────────────────────┐
│      Application Code           │
│  (Item, Member, Order 객체...)   │
└────────────┬────────────────────┘
             │ JPA API
             ↓
┌─────────────────────────────────┐
│           JPA                   │  ← 객체 ↔ SQL 변환
│     (Hibernate 구현체)           │
└────────────┬────────────────────┘
             │ JDBC API
             ↓
┌─────────────────────────────────┐
│            JDBC                 │  ← Connection, PreparedStatement
└────────────┬────────────────────┘
             │ DB Driver
             ↓
┌─────────────────────────────────┐
│            DB                   │
└─────────────────────────────────┘

핵심 위치:

  • JPA = 애플리케이션과 JDBC 사이의 중간 계층
  • 내부적으로는 여전히 JDBC를 사용
  • 6주차에서 본 DataSource, ConnectionPool은 그대로 활용

자기 점검

  • JPA를 쓰면 JDBC를 안 쓰는가? (힌트: 내부적으로 사용)
  • HikariCP는 JPA에서도 활용되는가? (힌트: YES)

Unit 3.4 — JPA 생태계 (스프링 데이터 JPA, Querydsl)

선수 지식: Unit 3.3

핵심 개념

JPA를 더 편리하게 쓰기 위한 도구들:

Spring Data JPA:

  • JPA를 더 추상화
  • Repository 인터페이스만 만들면 자동 구현
  • findByName(name) 같은 메서드 이름으로 쿼리 자동 생성
public interface ItemRepository extends JpaRepository<Item, Long> {
    List<Item> findByItemNameContaining(String keyword);
    // ↑ 구현체는 Spring이 자동 생성
}

Querydsl:

  • 타입 안전한 동적 쿼리
  • 컴파일 시점에 오류 검출
  • 복잡한 쿼리에서 강력
queryFactory
    .selectFrom(item)
    .where(item.price.gt(1000)
           .and(item.itemName.contains(keyword)))
    .fetch();

실무 조합: Spring Data JPA + Querydsl (가장 일반적)

자기 점검

  • "Spring Data JPA" 와 "JPA"는 같은 것인가? (힌트: 다름)
  • 단순 CRUD는 Spring Data JPA, 복잡 쿼리는?

📚 Phase 4 — JPA 엔티티 매핑

목표: 자바 객체를 DB 테이블과 매핑하는 어노테이션 4종을 손에 익힌다.

Unit 4.1 — @Entity와 엔티티 개념

선수 지식: Phase 3

핵심 개념

@Entity
public class Item {
    // ...
}
  • @Entity: 이 클래스가 JPA가 관리하는 객체임을 표시
  • 이 어노테이션이 있어야 JPA가 인식
  • @Entity 가 붙은 객체 = 엔티티(Entity)

엔티티의 조건:

  • @Entity 어노테이션
  • 기본 생성자 필수 (JPA가 리플렉션으로 객체 생성)
  • final 클래스 ❌ (JPA가 프록시 만들어야 함)

자기 점검

  • DTO와 Entity의 차이는?
  • 엔티티 클래스를 final로 만들면 어떤 문제가? (힌트: 프록시)

Unit 4.2 — @Id와 PK 매핑

선수 지식: Unit 4.1

핵심 개념

@Entity
public class Item {
    @Id
    private Long id;
    // ...
}
  • @Id: 테이블의 PK 컬럼과 매핑
  • 모든 엔티티는 @Id반드시 1개 (혹은 복합 키)

자기 점검

  • @Id 없이 엔티티를 만들면?
  • 복합 키는 어떻게 표현하는가? (힌트: @IdClass, @EmbeddedId)

Unit 4.3 — @GeneratedValue 전략

선수 지식: Unit 4.2

핵심 개념

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

4가지 생성 전략:

전략동작적합 DB
IDENTITYDB의 auto_increment 사용MySQL, PostgreSQL
SEQUENCEDB 시퀀스 객체 사용Oracle, PostgreSQL
TABLE키 생성용 테이블 사용모든 DB (성능 ↓)
AUTODB에 따라 자동 선택(기본값)

MySQL에서 IDENTITY 의미:

  • DB가 PK 값을 결정 (auto_increment)
  • INSERT 후에야 ID 값을 알 수 있음

자기 점검

  • IDENTITY 전략의 단점은? (힌트: 배치 INSERT 불가)
  • SEQUENCE가 IDENTITY보다 빠른 이유는? (힌트: 미리 받아둠)

Unit 4.4 — @Column과 컬럼 매핑

선수 지식: Unit 4.3

핵심 개념

@Column(name = "item_name", length = 10)
private String itemName;

주요 옵션:

  • name: DB 컬럼명 (지정 안 하면 필드명 그대로)
  • length: 문자열 최대 길이 (DDL 생성 시 활용)
  • nullable: NULL 허용 여부
  • unique: UNIQUE 제약
  • columnDefinition: 컬럼 DDL 직접 작성

예시:

@Column(name = "user_email", length = 100, nullable = false, unique = true)
private String email;

자기 점검

  • @Column 옵션이 DB 스키마에 어떻게 반영되는가? (힌트: ddl-auto 설정)
  • length만 지정하면 다른 옵션은 어떻게 되는가? (힌트: 기본값)

Unit 4.5 — 자동 매핑 규칙 (camelCase ↔ snake_case)

선수 지식: Unit 4.4

핵심 개념

Spring Boot의 JPA 통합:

  • 자바 필드: itemName (camelCase)
  • DB 컬럼: item_name (snake_case)
  • 자동 변환

:

@Column(name = "item_name")  // 명시적
private String itemName;

// 또는

private String itemName;  // 자동으로 item_name으로 매핑

→ Spring Boot 환경에서 @Column(name = "item_name") 은 생략 가능.

자기 점검

  • 이 자동 변환을 끄려면? (힌트: NamingStrategy 설정)
  • 카멜→스네이크 자동 변환의 이름은? (힌트: SpringPhysicalNamingStrategy)

🔄 Part B — 트랜잭션 추상화의 진화

📚 Phase 5 — 수동 트랜잭션의 한계

목표: "왜 @Transactional 이 필요한가" 라는 질문에 직접 코드로 답할 수 있게 된다.

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

선수 지식: 6주차 Phase 6 (트랜잭션 ACID)

핵심 시나리오

계좌 이체:

  • A계좌 10,000원, B계좌 10,000원
  • A → B로 2,000원 이체
  • 최종: A는 8,000원, B는 12,000원

원자성 보장 조건:

  • 출금과 입금이 하나의 트랜잭션 안에서 실행
  • → 같은 Connection 사용 필요
  • → Connection을 비즈니스 로직에 파라미터로 전달
public void transfer(Connection conn, ...) {  // ← Connection이 매개변수
    accountDao.withdraw(conn, fromId, amount);
    accountDao.deposit(conn, toId, amount);
}

비즈니스 로직이 Connection을 신경 써야 함 = 추상화 깨짐

자기 점검

  • Connection을 전달하지 않고 트랜잭션을 묶을 수 있는 방법은?
  • 이 문제와 5주차의 "관심사 분리" 는 어떻게 연결되는가?

Unit 5.2 — 수동 트랜잭션의 3가지 함정

선수 지식: Unit 5.1

핵심 코드

public void performTransaction() throws SQLException {
    Connection conn = null;
    try {
        conn = dataSource.getConnection();
        conn.setAutoCommit(false);  // 트랜잭션 시작

        // 비즈니스 로직 1
        PreparedStatement ps1 = conn.prepareStatement("INSERT ...");
        ps1.executeUpdate();

        // 비즈니스 로직 2
        PreparedStatement ps2 = conn.prepareStatement("UPDATE ...");
        ps2.executeUpdate();

        conn.commit();  // 모두 성공하면 커밋
    } catch (Exception e) {
        if (conn != null) conn.rollback();  // 실패 시 롤백
        throw e;
    } finally {
        if (conn != null) conn.close();  // 자원 해제
    }
}

3가지 함정:
1. 트랜잭션 누수: setAutoCommit, commit, rollback 코드 매번 반복
2. 예외 누수: SQLException이 비즈니스 로직까지 전파
3. JDBC 반복: Connection 생성·close 코드 반복

6주차의 JdbcTemplate은 부분적 해결, 트랜잭션은 그대로 남음

자기 점검

  • 위 코드에서 변하지 않는 부분과 변하는 부분은?
  • 5주차의 어떤 디자인 패턴으로 해결할 수 있을까? (힌트: 템플릿 메소드)

📚 Phase 6 — PlatformTransactionManager (트랜잭션의 추상화)

목표: Spring이 트랜잭션을 어떻게 추상화했는지를 본다. 5주차의 DataSource와 같은 사상.

Unit 6.1 — PlatformTransactionManager 인터페이스

선수 지식: Phase 5

핵심 개념

"스프링 트랜잭션 처리의 중심 인터페이스"

핵심 메서드:

public interface PlatformTransactionManager {
    TransactionStatus getTransaction(TransactionDefinition def);
    void commit(TransactionStatus status);
    void rollback(TransactionStatus status);
}

역할:

  • 트랜잭션 시작/커밋/롤백을 인터페이스로 추상화
  • 실제 동작은 구현체에 위임
  • 비즈니스 로직은 인터페이스에만 의존

자기 점검

  • 이 패턴은 6주차의 어떤 추상화와 닮았는가? (힌트: DataSource)
  • "Platform"이 이름에 들어간 이유는?

Unit 6.2 — 3가지 주요 구현체

선수 지식: Unit 6.1

핵심 구현체

구현체사용처
DataSourceTransactionManagerJDBC, JdbcTemplate, MyBatis
HibernateTransactionManagerHibernate (직접 사용)
JpaTransactionManagerJPA

상황별 선택:

  • JdbcTemplate 쓰면 → DataSourceTransactionManager
  • JPA 쓰면 → JpaTransactionManager
  • 둘 다 쓰면 → JpaTransactionManager (JPA 거 쓰면 JDBC도 같이 됨)

Spring Boot의 자동 구성:

  • 의존성에 따라 자동 선택
  • spring-boot-starter-data-jpa → JpaTransactionManager 자동 설정

자기 점검

  • 같은 애플리케이션에서 두 TransactionManager를 쓸 수 있는가?
  • 트랜잭션 매니저가 여러 개면 어떻게 구분하는가? (힌트: @Transactional("name"))

Unit 6.3 — 사용 전후 비교

선수 지식: Unit 6.2

Before (수동 관리):

Connection conn = dataSource.getConnection();
conn.setAutoCommit(false);
try {
    userDao.insertUser(conn, "John");
    accountDao.updateBalance(conn, 1, -100);
    conn.commit();
} catch (Exception e) {
    conn.rollback();
    throw e;
} finally {
    conn.close();
}

After (PlatformTransactionManager):

TransactionStatus status = transactionManager.getTransaction(
    new DefaultTransactionDefinition()
);
try {
    userDao.insertUser("John");           // Connection 안 보임
    accountDao.updateBalance(1, -100);    // Connection 안 보임
    transactionManager.commit(status);
} catch (Exception e) {
    transactionManager.rollback(status);
    throw e;
}

얻은 것:

  • Connection 매개변수 사라짐
  • DB 종류 무관 (인터페이스 의존)
  • 코드 줄어듦

아직 남은 것:

  • try/catch/commit/rollback 보일러플레이트
  • 다음 단계: @Transactional

자기 점검

  • 이 단계에서도 여전히 반복되는 코드는?
  • 5주차의 "템플릿 메소드 패턴" 으로 이 반복을 줄일 수 있을까?

📚 Phase 7 — @Transactional (선언적 트랜잭션)

목표: 트랜잭션 코드의 보일러플레이트를 어노테이션 한 줄로 줄이는 마지막 단계. AOP의 첫 만남.

Unit 7.1 — 프록시 패턴 맛보기 (AOP의 출발점)

선수 지식: Phase 6, 5주차 디자인 패턴

핵심 개념

프록시 패턴:

"원본 객체의 대리자(Proxy)를 만들어서, 원본 호출 전후에 추가 작업을 끼워 넣는 패턴"

[클라이언트] ──> [Proxy] ──┐
                          │ 트랜잭션 시작
                          ↓
                       [원본 객체.메서드()]
                          │
                          │ 커밋/롤백
                          ↓
              ┘

Spring의 활용:

  • @Transactional 이 붙은 메서드가 있는 빈은
  • 실제로는 프록시 객체가 등록됨
  • 프록시가 트랜잭션 처리를 자동으로 끼워 넣음

자기 점검

  • 프록시 패턴과 데코레이터 패턴의 차이는?
  • 6주차의 JdbcTemplate과 프록시는 어떻게 다른가? (힌트: 호출 vs 가로채기)

Unit 7.2 — @Transactional의 동작 원리

선수 지식: Unit 7.1

핵심 개념

선언적 트랜잭션:

@Service
public class TransferService {

    @Transactional
    public void transfer(Long fromId, Long toId, int amount) {
        accountDao.withdraw(fromId, amount);
        accountDao.deposit(toId, amount);
    }
}

한 줄 어노테이션으로 다음이 자동:

  • 트랜잭션 시작
  • 정상 종료 시 커밋
  • 예외 발생 시 롤백
  • Connection 관리

프록시가 하는 일 (의사코드):

public void transfer(Long fromId, Long toId, int amount) {
    TransactionStatus status = txManager.getTransaction(...);
    try {
        targetService.transfer(fromId, toId, amount);  // 실제 메서드
        txManager.commit(status);
    } catch (Exception e) {
        txManager.rollback(status);
        throw e;
    }
}

핵심 통찰:

  • 5주차의 OOP 원칙들(템플릿 메소드, 전략 패턴, OCP, DI) +
  • 6주차의 PlatformTransactionManager 추상화 +
  • 프록시 패턴 (AOP)
  • 하나의 어노테이션 으로 묶임

자기 점검

  • @Transactional이 붙은 빈을 직접 인스턴스화하면 트랜잭션이 동작하는가? (힌트: NO, 프록시 필요)
  • private 메서드에 @Transactional을 붙이면? (힌트: 동작 안 함)

Unit 7.3 — @Transactional 사용 시 주의사항

선수 지식: Unit 7.2

핵심 함정 5가지:

① private 메서드에는 동작 안 함

@Service
public class MyService {
    @Transactional
    private void internal() { }  // ❌ 프록시가 못 가로챔
}

② Self-invocation (자기 호출) 문제

@Service
public class MyService {
    public void outer() {
        this.inner();  // ❌ 프록시 거치지 않음 → 트랜잭션 안 걸림
    }

    @Transactional
    public void inner() { ... }
}

③ 기본 롤백은 RuntimeException만

@Transactional
public void method() throws Exception {  // 체크 예외
    throw new IOException();  // ❌ 롤백 안 됨!
}

// 해결
@Transactional(rollbackFor = Exception.class)

④ 트랜잭션 전파 옵션 이해

  • REQUIRED (기본): 기존 트랜잭션 있으면 참여, 없으면 새로 시작
  • REQUIRES_NEW: 항상 새 트랜잭션
  • NESTED: 중첩 트랜잭션 (Savepoint)

⑤ readOnly 활용

@Transactional(readOnly = true)
public List<Item> findAll() { ... }
  • 읽기 전용 → 영속성 컨텍스트 최적화 → 성능 ↑

자기 점검

  • self-invocation 문제를 어떻게 해결하는가? (힌트: 별도 빈 분리, AspectJ)
  • 체크 예외에서 롤백이 기본이 아닌 이유는? (힌트: 비즈니스 예외와 시스템 예외 구분)

🎓 종합 자기 점검 (7주차 졸업 시험)

SQL JOIN

  1. INNER JOIN, LEFT JOIN, RIGHT JOIN, FULL OUTER JOIN의 차이를 결과 행 관점에서?
  2. "부서 없는 직원" 만 찾는 쿼리는?
  3. MySQL에서 FULL OUTER JOIN을 어떻게 우회하는가?

ORM과 JPA

  1. 객체-관계 미스매치의 5가지 측면은?
  2. ORM의 정의와 ORM이 SQL을 완전히 대체하지 못하는 이유는?
  3. JPA와 Hibernate의 관계는?
  4. JPA가 JDBC를 대체하는가? (힌트: NO, 위에 얹힘)
  5. Spring Data JPA와 JPA의 차이는?
  6. Querydsl이 필요한 시나리오는?

JPA 매핑

  1. @Entity의 조건 3가지는?
  2. @GeneratedValue의 4가지 전략과 각각 적합한 DB는?
  3. IDENTITY 전략의 단점은?
  4. @Column이 생략 가능한 경우와 그 이유는?

수동 트랜잭션의 한계

  1. 비즈니스 로직에 Connection을 매개변수로 전달하는 게 왜 나쁜 설계인가?
  2. 수동 트랜잭션의 3가지 함정은?

PlatformTransactionManager

  1. PlatformTransactionManager의 핵심 메서드 3개는?
  2. 3가지 주요 구현체와 각각의 사용처는?
  3. JpaTransactionManager 하나로 JDBC 트랜잭션도 처리되는 이유는?

@Transactional

  1. @Transactional이 동작하기 위한 조건은? (힌트: 프록시)
  2. self-invocation 문제란?
  3. private 메서드에 @Transactional이 동작하지 않는 이유는?
  4. 기본 롤백 대상이 RuntimeException 뿐인 이유는?
  5. @Transactional(readOnly = true) 의 효과는?
  6. 트랜잭션 전파 속성 REQUIRED와 REQUIRES_NEW의 차이는?

📌 학습 운영 팁

5주차 복습 — 스프링 빈 다시 보기

이번 자료 도입부의 "스프링 빈이란?"은 5주차 Phase 8(ApplicationContext + DI)의 복습 포인트입니다. 다음 3가지를 다시 짚고 가시면 충분해요:

  1. 빈은 ApplicationContext가 관리하는 객체
  2. 기본은 싱글톤 — 4주차 동시성과 연결됨
  3. DI는 빈끼리의 관계 설정

9-섹션 마스터 프롬프트로 깊이 파야 할 Unit

반드시 깊이 파기:

  • Unit 1.5 — JOIN 선택 가이드 (실무 매일 사용)
  • Unit 3.2 — JPA의 등장 (SQL Mapper와의 본질적 차이)
  • Unit 3.4 — Spring Data JPA + Querydsl (실무 표준)
  • Unit 4.3 — @GeneratedValue 전략 (DB별 차이)
  • Unit 7.1 — 프록시 패턴 (AOP의 출발점, 면접 단골)
  • Unit 7.2 — @Transactional 동작 원리 (★★★ 면접 단골)
  • Unit 7.3 — @Transactional 5가지 함정 (실무 직결)

Phase별 진도 체크리스트

[ ] Phase 1 — SQL JOIN (Unit 1.1~1.5)
[ ] Phase 2 — ORM 패러다임 (Unit 2.1~2.2)
[ ] Phase 3 — JPA 입문 (Unit 3.1~3.4)
[ ] Phase 4 — JPA 엔티티 매핑 (Unit 4.1~4.5)
[ ] Phase 5 — 수동 트랜잭션의 한계 (Unit 5.1~5.2)
[ ] Phase 6 — PlatformTransactionManager (Unit 6.1~6.3)
[ ] Phase 7 — @Transactional (Unit 7.1~7.3)
[ ] 종합 자기 점검 24문항 통과

6주차 → 7주차 연결 포인트

6주차에서 본 추상화의 사상이 이번 주에 두 번 더 반복됩니다:

6주차7주차
DataSource (커넥션 추상화)PlatformTransactionManager (트랜잭션 추상화)
JdbcTemplate (반복 제거)@Transactional (보일러플레이트 제거)
SQL 매핑 RowMapperJPA 어노테이션 매핑

"Spring은 끊임없이 추상화한다" — 6, 7주차의 일관된 메시지.


1~7주차 통합 흐름

  • 1~3주차: 자바 언어 자체 (OOP·JVM·컬렉션·함수형)
  • 4주차: 멀티스레드와 동시성
  • 5주차: Spring IoC/DI 입문
  • 6주차: 테스트 + 웹 인프라 + DB 접근의 진화
  • 7주차 (지금): JPA(ORM) + 트랜잭션 추상화
profile
Software Developer

0개의 댓글