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

Psj·2026년 6월 1일

F-lab

목록 보기
219/240

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

F-LAB JAVA · 7주차 · Phase 2 · ORM 패러다임
🔄 Phase 2 시작 — "왜 ORM 이 필요한가" 의 근본 답


📌 학습 목표

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

  • 객체-관계 미스매치 의 정의는?
  • 모델링 차이 (상태+행동 vs 행과 열) 는?
  • 상속 차이 (있음 vs 없음) 는?
  • 연관 관계 차이 (참조 vs FK) 는?
  • 식별 차이 (객체 식별자 vs PK) 는?
  • 데이터 타입 차이 (풍부 vs 제한) 는?
  • OrderList<OrderItem> 을 RDB 로? 표현은?
  • 객체 상속을 RDB 로 표현하는 3가지 는?
  • 이 차이를 메우는 코드 = ORM 등장 전 고통 의 의미는?

🎯 핵심 한 문장

객체-관계 미스매치 (Object-Relational Impedance Mismatch) 는 객체 (OOP) 와 관계형 DB 가 모델링·상속·연관관계·식별·데이터 타입 5가지 측면에서 본질적으로 다른 패러다임이라 매번 손으로 변환 코드를 쓰는 고통이며, 이 차이를 자동으로 메우는 게 ORM 등장의 이유다.
객체-관계 미스매치 (Impedance Mismatch) 는 객체 (OOP) 와 관계형 DB 가 근본적으로 다른 패러다임 이라 생기는 불일치다.
5가지 측면에서 다르다 — (1) 모델링 (객체는 상태+행동, RDB 는 행과 열), (2) 상속 (객체에 있음, RDB 에 없음), (3) 연관 관계 (객체는 참조 order.member, RDB 는 외래 키), (4) 식별 (객체 식별자 vs PK), (5) 데이터 타입 (List·Map 같은 풍부 컬렉션 vs 제한적 SQL 타입).
이 차이를 매번 손으로 메우는 코드 (객체 → SQL, ResultSet → 객체, List → 별도 테이블 등) 가 ORM 등장 전의 고통이며, 자동으로 메워주는 게 ORM 의 핵심이다.
특히 — OrderList<OrderItem> 을 갖는 구조는 RDB 에서 orders + order_items + FK 로 표현하고, 객체 상속은 SINGLE_TABLE / JOINED / TABLE_PER_CLASS 3가지 전략 중 하나로 표현해야 하며, 이런 변환을 ORM 이 자동화한다.

비유 — 영어 ↔ 한국어 번역의 어려움

객체 ↔ RDB = 영어 ↔ 한국어:

영어 (객체):
  - 동사/목적어/주어 분명
  - 풍부한 시제
  - 단/복수 명확

한국어 (RDB):
  - 다른 어순
  - 다른 표현 방식
  - 다른 문법

5가지 차이:
  1. 모델링: 문장 구조 (vs 단어)
  2. 상속: 관계 표현 방식
  3. 연관: 참조 (vs 외래 키)
  4. 식별: 이름 (vs 번호)
  5. 타입: 풍부 (vs 제한)

번역의 고통:
  - 매번 손으로 문장 변환
  - 단어 1:1 매핑 X
  - 의미 보존 어려움
  - 시간 낭비

자동 번역기 (ORM):
  - 영어 → 한국어 자동
  - 객체 → SQL 자동
  - 사용자는 모국어로
  - 변환 코드 X

ILIC:
  - Shipment (객체) ↔ shipments (테이블)
  - Customer.shipments (List) ↔ FK
  - JPA 가 자동 매핑

→ 객체-관계 미스매치 = 5가지 본질적 차이, ORM = 자동 번역기.


🧭 9개 섹션 로드맵

1. 미스매치 정의
2. 모델링 차이
3. 상속 차이
4. 연관 관계 차이
5. 식별 차이
6. 데이터 타입 차이
7. Order → List<OrderItem> 표현
8. 객체 상속 RDB 표현 3가지
9. ORM 등장 전 고통

1️⃣ 미스매치 정의

1.1 정의

객체-관계 미스매치
(Object-Relational Impedance Mismatch):

  객체 (OOP) 와 관계형 DB (RDB) 가
  본질적으로 다른 패러다임:
    - 모델링 방식 다름
    - 표현 방식 다름
    - 5가지 측면 차이

1.2 "Impedance Mismatch"

"Impedance Mismatch":

  전기 공학 용어 차용:
    - 두 회로의 임피던스 다름
    - 전류 전달 비효율
    - 비유: 두 시스템의 부조화

1.3 5가지 측면

5가지 측면:

  1. 모델링 (상태+행동 vs 행과 열)
  2. 상속 (있음 vs 없음)
  3. 연관 관계 (참조 vs FK)
  4. 식별 (== vs PK)
  5. 데이터 타입 (풍부 vs 제한)

→ ORM 의 도전 과제

1.4 ILIC 의 맥락

객체 vs RDB (ILIC)

ILIC 의 객체 (Java):
  class Shipment {
    Long id;
    String blNo;
    Customer customer;          // 참조
    List<ShipmentItem> items;   // 컬렉션
    BigDecimal weight;
    void calculateFreight() {}  // 행동
  }

ILIC 의 RDB (MySQL):
  shipments
    - id, bl_no, customer_id, weight
  customers
    - id, name
  shipment_items
    - id, shipment_id, item_name

→ 매핑이 필요:
  - 참조 customer ↔ customer_id (FK)
  - List items ↔ 별도 테이블 + FK
  - 행동 X (RDB)

1.5 자기 점검 답변

객체-관계 미스매치의 정의는?

:
1. 정의:

  • 다른 패러다임
  1. 용어:

    • Impedance Mismatch
  2. 5가지:

    • 모델링/상속/연관/식별/타입
  3. 결과:

    • 변환 코드 필요

2️⃣ 모델링 차이

2.1 객체 모델링

객체 (OOP):

  상태 + 행동:
    - 필드 (상태)
    - 메서드 (행동)
    - 캡슐화 (private + 메서드)

  예:
    class Account {
      private BigDecimal balance;   // 상태
      void deposit(BigDecimal amount) {  // 행동
        this.balance = balance.add(amount);
      }
    }

2.2 RDB 모델링

RDB:

  행과 열:
    - 행 (Record): 한 단위 데이터
    - 열 (Column): 속성
    - 행동 X (데이터만)

  예:
    accounts 테이블:
      id | balance
      ───┼────────
      1  | 1000.00
      2  | 5000.00

2.3 차이

측면객체RDB
단위객체 (Instance)행 (Row)
속성필드컬럼
행동메서드없음 (SP 별개)
캡슐화있음없음
상태 변경메서드 통해UPDATE 직접

2.4 매핑

매핑:

  필드 ↔ 컬럼:
    - 객체의 필드 → DB 의 컬럼
    - 자동 가능 (이름 같으면)
    - 타입 변환 필요

  메서드:
    - DB 엔 없음
    - 객체에서만 동작
    - DB 는 그저 데이터 저장소

2.5 ILIC 의 맥락

// 객체 (ILIC)
public class Shipment {
    private Long id;
    private String blNo;
    private BigDecimal weight;
    private String status;
    
    // 행동
    public void markAsShipped() {
        if (this.status != "BOOKED") {
            throw new IllegalStateException();
        }
        this.status = "SHIPPED";
    }
    
    public BigDecimal calculateFreight(BigDecimal rate) {
        return this.weight.multiply(rate);
    }
}
-- RDB (ILIC)
CREATE TABLE shipments (
    id BIGINT PRIMARY KEY,
    bl_no VARCHAR(50),
    weight DECIMAL(10,2),
    status VARCHAR(20)
);
-- 행동 X (단지 데이터)
-- 메서드는 객체에서만
-- DB 는 데이터 저장소

2.6 자기 점검 답변

모델링 차이 (상태+행동 vs 행과 열) 는?

:
1. 객체:

  • 상태 + 행동
  1. RDB:

    • 행과 열
  2. 차이:

    • 행동 / 캡슐화
  3. 매핑:

    • 필드 ↔ 컬럼

3️⃣ 상속 차이

3.1 객체의 상속

객체의 상속:

  - 부모 클래스 + 자식 클래스
  - 자식은 부모 속성 + 메서드 상속
  - 다형성 (Polymorphism)

  예:
    class Vehicle { }
    class Car extends Vehicle { }
    class Truck extends Vehicle { }

3.2 RDB 의 상속

RDB 의 상속:

  - 직접 표현 X
  - 모든 테이블은 평면 (flat)
  - 우회 표현 필요

→ 객체 상속을 RDB 로 매핑 어려움

3.3 매핑 전략 3가지 (간단)

매핑 전략 3가지:

1. SINGLE_TABLE:
   - 한 테이블 + 구분 컬럼

2. JOINED:
   - 부모/자식 테이블 분리

3. TABLE_PER_CLASS:
   - 자식마다 별도 테이블

→ 상세는 섹션 8

3.4 실무 시나리오

실무 시나리오:

  배송 종류:
    abstract class Shipment { }
    class SeaShipment extends Shipment { }
    class AirShipment extends Shipment { }
    class LandShipment extends Shipment { }

  RDB 표현?
    - 한 테이블 (구분 컬럼)?
    - 여러 테이블?
    - 자식마다 따로?

→ 전략 선택 필요

3.5 ILIC 의 맥락

// 객체 상속 (ILIC, 가상 예시)
public abstract class Shipment {
    protected Long id;
    protected String blNo;
    protected BigDecimal weight;
}

public class SeaShipment extends Shipment {
    private String vesselName;
    private String containerNo;
}

public class AirShipment extends Shipment {
    private String airwayBillNo;
    private String flightNo;
}

public class LandShipment extends Shipment {
    private String truckPlate;
}

// RDB 표현?
// → 3가지 전략 (SINGLE_TABLE / JOINED / TABLE_PER_CLASS)
// → 섹션 8 에서 상세

3.6 자기 점검 답변

상속 차이 (있음 vs 없음) 는?

:
1. 객체:

  • 상속 + 다형성
  1. RDB:

    • 직접 표현 X
  2. 매핑:

    • 3가지 전략
  3. 시나리오:

    • 배송 종류 등

4️⃣ 연관 관계 차이

4.1 객체의 연관

객체의 연관:

  참조 (Reference):
    - 한 객체가 다른 객체 가리킴
    - .필드 접근

  예:
    class Order {
      Member member;   // 참조
    }
    
    Order order = ...;
    Member m = order.member;   // 직접 접근

4.2 RDB 의 연관

RDB 의 연관:

  외래 키 (Foreign Key):
    - 다른 테이블의 PK 저장
    - JOIN 으로 접근

  예:
    orders 테이블:
      | id | member_id |
    
    -- 접근
    SELECT m.* 
    FROM orders o
    JOIN members m ON o.member_id = m.id;

4.3 단방향 vs 양방향

단방향 vs 양방향:

객체:
  - 단방향: order → member
  - 양방향: order ↔ member (서로 참조)
  - 양쪽 필드 필요

RDB:
  - 양방향이 본질 (FK 로 양쪽 알 수 있음)
  - 단방향 개념 약함
  - JOIN 으로 어느 쪽이든

4.4 매핑

매핑:

  객체 참조 → FK 컬럼:
    Member member;  // 객체에서
    →
    member_id BIGINT;  // DB 에서
    + FOREIGN KEY (member_id) REFERENCES members(id)

  ORM 이 자동:
    - @ManyToOne
    - @OneToMany
    - @OneToOne

4.5 ILIC 의 맥락

// 객체 (ILIC)
public class Shipment {
    Long id;
    String blNo;
    Customer customer;    // 참조 (객체)
}

// RDB
// CREATE TABLE shipments (
//     id BIGINT,
//     bl_no VARCHAR(50),
//     customer_id BIGINT,   // FK
//     FOREIGN KEY (customer_id) REFERENCES customers(id)
// );

// 접근 방식:
// 객체:
shipment.getCustomer().getName();   // 자연스러움

// RDB:
SELECT c.name 
FROM shipments s
JOIN customers c ON s.customer_id = c.id
WHERE s.id = ?;
// JOIN 명시적
class Customer { String getName() { return null; } }
class Shipment { Customer getCustomer() { return null; } }

4.6 자기 점검 답변

연관 관계 차이 (참조 vs FK) 는?

:
1. 객체:

  • 참조 (.필드)
  1. RDB:

    • 외래 키
  2. 방향:

    • 양방향 차이
  3. 매핑:

    • ORM 자동

5️⃣ 식별 차이

5.1 객체 식별

객체 식별:

  - 객체 동일성 (==): 메모리 주소
  - 객체 동등성 (equals): 내용 비교
  - 자동 (JVM)

  예:
    Member m1 = new Member();
    Member m2 = new Member();
    m1 == m2  // false (다른 주소)
    m1.equals(m2)  // 구현 따라

5.2 RDB 식별

RDB 식별:

  - Primary Key (PK)
  - 유일한 식별자
  - 명시적 지정

  예:
    CREATE TABLE members (
      id BIGINT PRIMARY KEY,
      name VARCHAR(100)
    );

5.3 차이

차이:

객체:
  - 메모리 주소 (==)
  - JVM 의 객체 식별
  - 같은 내용도 다른 객체

RDB:
  - PK 값
  - 명시적 (id 컬럼)
  - 같은 PK = 같은 데이터

5.4 매핑 — @Id

매핑 — @Id:

  객체의 PK 필드 명시:
    @Id
    private Long id;

  객체 ↔ DB 행 매핑:
    - id 가 같으면 같은 데이터
    - 영속성 컨텍스트 (다음 주차)

5.5 영속성 컨텍스트

영속성 컨텍스트 (간단):

  JPA 의 1차 캐시:
    - 같은 트랜잭션 안에서
    - 같은 id 의 엔티티는 같은 객체
    - 메모리 주소 동일 (==)

  → 객체 식별 + DB 식별 일치

5.6 ILIC 의 맥락

// 식별 (ILIC)

// 객체 식별
Shipment s1 = new Shipment();
Shipment s2 = new Shipment();
s1 == s2;          // false (다른 객체)
s1.equals(s2);     // false (내용 다름)

// DB 식별 (PK)
INSERT INTO shipments (id, bl_no) VALUES (1, 'BL001');
INSERT INTO shipments (id, bl_no) VALUES (2, 'BL002');
-- id 가 다르면 다른 데이터

// JPA 매핑
@Entity
public class Shipment {
    @Id
    private Long id;   // DB PK 와 매핑
    private String blNo;
}

// JPA 영속성 컨텍스트
Shipment s1 = em.find(Shipment.class, 1L);
Shipment s2 = em.find(Shipment.class, 1L);
s1 == s2;   // true (같은 1차 캐시)
class Shipment {}

5.7 자기 점검 답변

식별 차이 (== vs PK) 는?

:
1. 객체:

  • 메모리 주소 (==)
  1. RDB:

    • Primary Key
  2. 매핑:

    • @Id
  3. 영속성:

    • 1차 캐시 동일

6️⃣ 데이터 타입 차이

6.1 객체의 풍부 타입

객체의 타입:

  기본 타입 + 컬렉션 + 사용자 정의:
    - int, long, String, BigDecimal
    - List, Set, Map
    - 사용자 정의 클래스
    - 중첩 객체

  매우 풍부

6.2 RDB 의 제한 타입

RDB 의 타입:

  기본 SQL 타입:
    - INTEGER, BIGINT
    - VARCHAR, TEXT
    - DECIMAL, FLOAT
    - DATE, DATETIME, TIMESTAMP
    - BOOLEAN

  - 컬렉션 X (List/Map 직접 저장 불가)
  - 사용자 정의 타입 제한적

6.3 컬렉션 매핑 문제

컬렉션 매핑 문제:

  객체:
    List<Order> orders;   // 자연스러움

  RDB:
    - 컬렉션 컬럼 X
    - 별도 테이블 필요
    - FK 로 연결

  → ORM 이 자동 매핑

6.4 사용자 정의 타입

사용자 정의 타입:

  객체:
    class Address {
      String city;
      String street;
    }
    
    class Member {
      Address address;   // 사용자 정의
    }

  RDB:
    - Address 타입 X
    - 분해 필요 (city, street 컬럼)
    - 또는 별도 테이블

6.5 ILIC 의 맥락

// 풍부한 타입 (ILIC)
public class Shipment {
    Long id;
    String blNo;
    BigDecimal weight;
    LocalDateTime createdAt;
    
    // 컬렉션
    List<ShipmentItem> items;
    Set<String> tags;
    
    // 중첩 객체
    Address loadingAddress;
    Address dischargeAddress;
    
    // Enum
    ShipmentStatus status;
}

class Address {
    String country;
    String city;
    String street;
}

enum ShipmentStatus { BOOKED, CONFIRMED, SHIPPED }
-- RDB 매핑 (분해)
CREATE TABLE shipments (
    id BIGINT,
    bl_no VARCHAR(50),
    weight DECIMAL(10,2),
    created_at DATETIME,
    -- Address 분해
    loading_country VARCHAR(50),
    loading_city VARCHAR(100),
    loading_street VARCHAR(200),
    discharge_country VARCHAR(50),
    discharge_city VARCHAR(100),
    discharge_street VARCHAR(200),
    -- Enum → VARCHAR
    status VARCHAR(20)
);

-- List items → 별도 테이블
CREATE TABLE shipment_items (
    id BIGINT,
    shipment_id BIGINT,
    item_name VARCHAR(200),
    FOREIGN KEY (shipment_id) REFERENCES shipments(id)
);

-- Set tags → 별도 테이블
CREATE TABLE shipment_tags (
    shipment_id BIGINT,
    tag VARCHAR(50),
    FOREIGN KEY (shipment_id) REFERENCES shipments(id)
);

6.6 자기 점검 답변

데이터 타입 차이 (풍부 vs 제한) 는?

:
1. 객체:

  • 풍부 (List/Map/사용자 정의)
  1. RDB:

    • 제한 (기본 SQL)
  2. 컬렉션:

    • 별도 테이블
  3. 사용자 정의:

    • 분해 또는 별도 테이블

7️⃣ Order → List 표현

7.1 객체 구조

// 객체 구조
public class Order {
    Long id;
    Member member;
    LocalDateTime orderDate;
    List<OrderItem> items;   // ★ 컬렉션
}

public class OrderItem {
    Long id;
    Product product;
    int quantity;
}

7.2 RDB 표현

-- 1. orders 테이블
CREATE TABLE orders (
    id BIGINT PRIMARY KEY,
    member_id BIGINT,
    order_date DATETIME,
    FOREIGN KEY (member_id) REFERENCES members(id)
);

-- 2. order_items 테이블 (별도!)
CREATE TABLE order_items (
    id BIGINT PRIMARY KEY,
    order_id BIGINT,             -- FK
    product_id BIGINT,
    quantity INT,
    FOREIGN KEY (order_id) REFERENCES orders(id),
    FOREIGN KEY (product_id) REFERENCES products(id)
);

7.3 1:N 관계

1:N 관계:

  Order 1 : N OrderItem
  
  객체에선:
    order.items  // List

  RDB 에선:
    order_items 의 order_id 가 FK
    
  → ORM 이 매핑

7.4 매핑 코드 (JPA 미리 보기)

// JPA 매핑 (Phase 3 에서)
@Entity
@Table(name = "orders")
public class Order {
    @Id
    private Long id;
    
    @ManyToOne
    @JoinColumn(name = "member_id")
    private Member member;
    
    @OneToMany(mappedBy = "order")
    private List<OrderItem> items;   // 자동 매핑
}

@Entity
@Table(name = "order_items")
public class OrderItem {
    @Id
    private Long id;
    
    @ManyToOne
    @JoinColumn(name = "order_id")
    private Order order;   // FK 반대편
}

// 사용
Order order = repository.findById(1L);
List<OrderItem> items = order.getItems();   // 객체 그래프 자동

7.5 SQL 직접 시 고통

SQL Mapper 시 고통:

  1. orders 조회
  2. order_items 별도 조회 (order_id 로)
  3. 자바에서 합치기 (Order.items 채우기)
  4. N+1 위험

  매번 이 코드:
    - 손으로 매핑
    - 누락 위험
    - 시간 ↓
    
→ ORM 등장 동기

7.6 ILIC 의 맥락

// ILIC 의 1:N (Shipment → ShipmentItem)
public class Shipment {
    Long id;
    String blNo;
    Customer customer;
    List<ShipmentItem> items;   // 1:N
}

public class ShipmentItem {
    Long id;
    String itemName;
    int quantity;
}
-- RDB
CREATE TABLE shipments (id, bl_no, customer_id);
CREATE TABLE shipment_items (id, shipment_id, item_name, quantity);
// JPA 매핑 (다음 Phase)
@Entity
class Shipment {
    @Id Long id;
    @OneToMany(mappedBy = "shipment")
    List<ShipmentItem> items;
}

@Entity  
class ShipmentItem {
    @Id Long id;
    @ManyToOne
    @JoinColumn(name = "shipment_id")
    Shipment shipment;
}

// 사용
Shipment s = repo.findById(1L);
List<ShipmentItem> items = s.getItems();  // 자동

7.7 자기 점검 답변

Order 가 List 갖는 구조 RDB 로?

:
1. 객체:

  • List
  1. RDB:

    • orders + order_items + FK
  2. 관계:

    • 1:N
  3. JPA:

    • @OneToMany 매핑

8️⃣ 객체 상속 RDB 표현 3가지

8.1 전략 1 — SINGLE_TABLE

SINGLE_TABLE (단일 테이블):

  한 테이블에 모든 자식:
    - 구분 컬럼 (dtype)
    - 공통 + 자식별 필드 모두

  장점:
    - JOIN X (빠름)
    - 단순

  단점:
    - 컬럼 NULL 多
    - 한 테이블 크기 ↑
-- SINGLE_TABLE
CREATE TABLE shipments (
    id BIGINT PRIMARY KEY,
    dtype VARCHAR(20),       -- 'SEA' / 'AIR' / 'LAND'
    bl_no VARCHAR(50),       -- 공통
    weight DECIMAL(10,2),    -- 공통
    -- SeaShipment
    vessel_name VARCHAR(100),
    container_no VARCHAR(50),
    -- AirShipment
    airway_bill_no VARCHAR(50),
    flight_no VARCHAR(20),
    -- LandShipment
    truck_plate VARCHAR(20)
);

-- 모든 데이터 한 테이블
-- 자식 안 쓰는 컬럼은 NULL

8.2 전략 2 — JOINED

JOINED (조인):

  부모 + 자식 테이블 분리:
    - 부모: 공통
    - 자식: 자식별 필드
    - JOIN 으로 합침

  장점:
    - 정규화
    - NULL X
    - 명확

  단점:
    - JOIN 필요 (느림)
    - 복잡
-- JOINED
-- 부모 (공통)
CREATE TABLE shipments (
    id BIGINT PRIMARY KEY,
    bl_no VARCHAR(50),
    weight DECIMAL(10,2)
);

-- 자식 (자식별)
CREATE TABLE sea_shipments (
    id BIGINT PRIMARY KEY,
    vessel_name VARCHAR(100),
    container_no VARCHAR(50),
    FOREIGN KEY (id) REFERENCES shipments(id)
);

CREATE TABLE air_shipments (
    id BIGINT PRIMARY KEY,
    airway_bill_no VARCHAR(50),
    flight_no VARCHAR(20),
    FOREIGN KEY (id) REFERENCES shipments(id)
);

CREATE TABLE land_shipments (
    id BIGINT PRIMARY KEY,
    truck_plate VARCHAR(20),
    FOREIGN KEY (id) REFERENCES shipments(id)
);

-- 조회
SELECT s.*, ss.vessel_name
FROM shipments s
JOIN sea_shipments ss ON s.id = ss.id;

8.3 전략 3 — TABLE_PER_CLASS

TABLE_PER_CLASS (구체 클래스별 테이블):

  자식마다 별도 테이블:
    - 공통 필드 중복 (각 자식 테이블에)
    - 부모 테이블 X

  장점:
    - JOIN X
    - 자식별 분리

  단점:
    - 공통 필드 중복
    - 부모 조회 시 UNION
-- TABLE_PER_CLASS
-- 자식별 (공통 + 자기)
CREATE TABLE sea_shipments (
    id BIGINT PRIMARY KEY,
    bl_no VARCHAR(50),         -- 공통 (중복)
    weight DECIMAL(10,2),       -- 공통 (중복)
    vessel_name VARCHAR(100),
    container_no VARCHAR(50)
);

CREATE TABLE air_shipments (
    id BIGINT PRIMARY KEY,
    bl_no VARCHAR(50),         -- 공통 (중복)
    weight DECIMAL(10,2),       -- 공통 (중복)
    airway_bill_no VARCHAR(50),
    flight_no VARCHAR(20)
);

CREATE TABLE land_shipments (
    id BIGINT PRIMARY KEY,
    bl_no VARCHAR(50),         -- 공통 (중복)
    weight DECIMAL(10,2),       -- 공통 (중복)
    truck_plate VARCHAR(20)
);

-- 부모 조회 (UNION)
SELECT id, bl_no, weight FROM sea_shipments
UNION ALL
SELECT id, bl_no, weight FROM air_shipments
UNION ALL
SELECT id, bl_no, weight FROM land_shipments;

8.4 비교 표

전략테이블 수JOIN정규화빠르기
SINGLE_TABLE1X빠름
JOINEDN+1O느림
TABLE_PER_CLASSNX (UNION)보통

8.5 선택 기준

선택 기준:

  SINGLE_TABLE:
    - 자식 클래스 적음
    - 공통 필드 많음
    - 성능 우선

  JOINED:
    - 정규화 중시
    - NULL 싫음
    - 객체 모델 충실

  TABLE_PER_CLASS:
    - 자식 자주 분리 조회
    - 거의 사용 X

→ JPA 의 @Inheritance 로 선택

8.6 ILIC 의 맥락

// ILIC 상속 매핑 가정 (실제는 X)
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "dtype")
public abstract class Shipment {
    @Id Long id;
    String blNo;
    BigDecimal weight;
}

@Entity
@DiscriminatorValue("SEA")
public class SeaShipment extends Shipment {
    String vesselName;
}

// → 부모 shipments + 자식 sea_shipments 테이블
// → JOIN 으로 조회
// → JPA 자동

// ILIC 실무는:
// - shipments + 구분 컬럼 (transport_mode)
// - 자식 클래스 X (상속 안 씀)
// - 또는 컴포지션 사용
class JoinedShipment {}

8.7 자기 점검 답변

객체 상속을 RDB 로 표현하는 3가지는?

:
1. SINGLE_TABLE:

  • 한 테이블 + 구분
  1. JOINED:

    • 부모/자식 분리
  2. TABLE_PER_CLASS:

    • 자식별 (UNION)
  3. 선택:

    • 성능/정규화

9️⃣ ORM 등장 전 고통

9.1 손으로 메우는 코드

손으로 메우는 코드:

  매번 작성:
    1. 객체 → SQL (INSERT/UPDATE)
    2. ResultSet → 객체 매핑
    3. List → 별도 테이블 + 합치기
    4. 객체 그래프 (1:N) 처리
    5. 상속 매핑 (SINGLE/JOINED/...)
    6. 캐시 (영속성 컨텍스트)
    7. 변경 감지 (Dirty Checking)
    8. 트랜잭션 동기화

→ 5가지 미스매치 × N 메서드

9.2 시간 낭비

시간 낭비:

  보일러플레이트:
    - 비즈니스 로직 < 매핑 코드
    - 6주차 Unit 7.1 과 비슷
    - 매번 똑같은 패턴

  유지보수:
    - 컬럼 추가 시 여러 곳 수정
    - 누락 위험
    - 디버깅 어려움

9.3 실수 위험

실수 위험:

  - 컬럼명 오타
  - 타입 불일치
  - 1:N 매핑 누락
  - 캐시 일관성

→ 버그의 온상

9.4 객체지향 깨짐

객체지향 깨짐:

  객체 ↔ DB 변환 코드:
    - 객체에 DB 코드 섞임
    - SoC 깨짐 (5주차)
    - 도메인 로직 흐려짐

→ 잘못된 OOP

9.5 ORM 의 약속

ORM 의 약속:

  자동 매핑:
    - 5가지 미스매치 모두
    - 어노테이션 기반
    - 객체로만 작업

  개발자:
    - 비즈니스 로직 집중
    - 객체로 사고
    - SQL 신경 ↓

→ 다음 Unit (2.2) 에서 정의

9.6 ILIC 의 맥락

ORM 없으면 (ILIC 가정)

ILIC 의 102 테이블:
  - 매 테이블마다 매핑 코드
  - 객체 → SQL
  - ResultSet → 객체
  - 1:N (Shipment ↔ ShipmentItem)
  - 5+ 테이블 JOIN 매핑

  코드 비율:
    - 비즈니스 로직 30%
    - 매핑 코드 70%
    - 매우 부담

ORM (JPA) 사용 시:
  - 매핑 코드 자동
  - 비즈니스 로직 집중
  - 102 테이블도 관리 가능
  - ILIC 의 현재

9.7 자기 점검 답변

이 차이를 메우는 코드 = ORM 등장 전 고통의 의미는?

:
1. 손으로:

  • 매번 매핑
  1. 고통:

    • 시간/실수
  2. 객체지향 깨짐:

    • SoC 위반
  3. ORM 의 약속:

    • 자동 + 집중

🎯 핵심 요약 — 3줄 정리

1. 5가지 미스매치

  • 모델링 (상태+행동 vs 행/열), 상속 (있음 vs 없음), 연관 (참조 vs FK), 식별 (== vs PK), 타입 (풍부 vs 제한)
  • 객체와 RDB 는 근본적으로 다른 패러다임

2. 표현의 어려움

  • OrderList<OrderItem>orders + order_items + FK
  • 객체 상속 → 3가지 전략 (SINGLE_TABLE / JOINED / TABLE_PER_CLASS)

3. ORM 등장의 이유

  • 미스매치 메우는 코드를 매번 손으로 = 고통 (보일러플레이트, 실수, OOP 깨짐)
  • ORM = 자동 매핑 + 객체로만 작업

📚 다음으로...

Unit 2.2 — ORM 의 정의와 효과 (Phase 2 완주)

이번 Unit에서 미스매치를 봤다면, 다음은 ORM 의 정의 (Phase 2 마지막).

  • ORM (Object-Relational Mapping) 정의
  • 효과 (자동 매핑, 개발 속도)
  • 대중적 ORM (Hibernate/SQLAlchemy/ActiveRecord)
  • ORM 의 단점 (학습곡선/N+1/튜닝)
  • ORM 이 SQL 완전 대체 X

Phase 2 진행 상황

🔄 Phase 2 — ORM 패러다임
  ✅ Unit 2.1 객체-관계 미스매치 ← 여기
  ⏭ Unit 2.2 ORM 의 정의와 효과 — Phase 2 완주

7주차 누적 진행

🗂️ Part A — 데이터 모델링과 ORM
  ✅ Phase 1 (5) ← 완주
  🔄 Phase 2 (1/2)

총: 6/24 Unit

🔄 Phase 2 시작 — 객체-관계 미스매치

profile
Software Developer

0개의 댓글