7주차 Unit 4.4 — @Column 과 컬럼 매핑

Psj·2026년 6월 1일

F-lab

목록 보기
227/240

Unit 4.4 — @Column 과 컬럼 매핑

F-LAB JAVA · 7주차 · Phase 4 · JPA 엔티티 매핑


📌 학습 목표

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

  • @Column 어노테이션의 정의는?
  • name 옵션 의 동작은?
  • length 옵션 의 의미는?
  • nullable / unique 제약은?
  • columnDefinition 의 사용은?
  • precision / scale (BigDecimal) 은?
  • @Enumerated 는?
  • @Temporal / @Lob 은?
  • @Column 생략 가능 한 경우는?

🎯 핵심 한 문장

@Column 은 엔티티 필드를 DB 컬럼에 매핑하는 어노테이션으로 name (컬럼명) · length (길이) · nullable (NULL 허용) · unique (UNIQUE 제약) · columnDefinition (DDL 직접) · precision/scale (BigDecimal 정밀도) 등의 옵션을 제공하며 ddl-auto 가 켜져있으면 DDL 에 반영되고, Enum 은 @Enumerated(STRING), 날짜/시간은 java.time 이면 자동 매핑되며, 모든 옵션이 기본값으로 충분하면 @Column 자체를 생략 가능하다 (Spring Boot 의 명명 전략이 camelCase → snake_case 자동 변환).
@Column 은 엔티티의 일반 필드 (PK 외) 를 DB 컬럼에 매핑하는 어노테이션이다.
주요 옵션 — (1) name (DB 컬럼명, 생략 시 필드명 그대로 또는 명명 전략에 따라 자동 변환), (2) length (문자열 최대 길이, 기본 255), (3) nullable (NULL 허용 여부, 기본 true), (4) unique (UNIQUE 제약, 기본 false), (5) columnDefinition (DDL 직접 작성, 위 옵션을 덮어씀), (6) precision / scale (BigDecimal 의 정밀도).
이 옵션들은 ddl-auto 가 켜져있을 때 (create/update) DDL 생성에 반영되며, 운영에선 Flyway/Liquibase 마이그레이션이 표준 이므로 옵션은 보통 검증 (validate) 또는 문서적 의미 로 사용된다.
Enum 은 @Enumerated(EnumType.STRING) (반드시 STRING — 기본 ORDINAL 은 순서 변경 시 데이터 오염), 날짜/시간은 java.time (LocalDateTime/LocalDate) 이면 자동 매핑되어 @Temporal 불필요 (옛 Date/Calendar 만 @Temporal 필요), 대용량은 @Lob (CLOB/BLOB).
모든 옵션이 기본값으로 충분하면 @Column 자체를 생략 가능 하며, Spring Boot 의 SpringPhysicalNamingStrategy 가 camelCase → snake_case 자동 변환해주므로 (blNo → bl_no) 보통 옵션 명시는 길이 제한·NOT NULL·UNIQUE 같은 명시적 제약이 필요할 때만 한다.

비유 — 옷장 칸 (각 컬럼) 의 사양

@Column = 옷장 칸의 사양 카드:

상황:
  - 옷장 (테이블) 의 각 칸 (컬럼) 마다
  - 사양 카드 (옵션) 부착

옵션 6가지:

[1] name: 칸의 이름표
  - "겨울옷"
  - 자바 필드명 → DB 컬럼명

[2] length: 칸의 크기
  - "최대 10벌"
  - VARCHAR(N)

[3] nullable: 비울 수 있나?
  - false → 반드시 채워야 (NOT NULL)
  - true → 비워도 OK

[4] unique: 유일한가?
  - true → 같은 옷 X
  - 회원의 이메일 같은 곳

[5] columnDefinition: DDL 직접
  - "JSON 컬럼 직접 정의"
  - 위 옵션 무시

[6] precision / scale: 정확도
  - 돈 금액 (100,000.50)
  - BigDecimal

자동 변환 (Spring Boot):
  - 자바 blNo → DB bl_no (snake)
  - 자동 → 옵션 생략 가능

생략 가능:
  - 기본값으로 충분하면 @Column X
  - 필요 시만 명시

DDL 생성:
  - ddl-auto: create / update → 반영
  - validate → 검증
  - 운영 → Flyway 표준

Enum:
  - @Enumerated(STRING) ★ 필수
  - ORDINAL 은 위험!

날짜:
  - java.time → 자동
  - Date/Calendar 만 @Temporal

ILIC:
  - 명시 패턴 (length / unique / precision)
  - 자동 변환은 활용

→ @Column = 컬럼 사양, 6가지 옵션, 생략 가능, Enum 은 STRING 필수.


🧭 9개 섹션 로드맵

1. @Column 정의
2. name 옵션
3. length 옵션
4. nullable / unique
5. columnDefinition
6. precision / scale
7. @Enumerated
8. @Temporal / @Lob
9. 생략 가능 + ILIC 표준

1️⃣ @Column 정의

1.1 정의

@Column:

  엔티티 필드를 DB 컬럼에 매핑:
    - PK 외 일반 필드
    - 옵션 6가지
    - DDL 생성 시 반영

1.2 기본 사용

import jakarta.persistence.*;

@Entity
public class Shipment {
    @Id
    private Long id;
    
    @Column(name = "bl_no", length = 50, nullable = false, unique = true)
    private String blNo;
    
    @Column(precision = 10, scale = 2)
    private BigDecimal weight;
    
    @Column(length = 20)
    private String status;
}

1.3 생략 가능

// @Column 생략 (기본값으로 충분 시)
@Entity
public class Shipment {
    @Id
    private Long id;
    
    private String blNo;      // 자동 매핑: bl_no, VARCHAR(255), NULL OK
    private String status;    // 자동 매핑: status, VARCHAR(255)
    private BigDecimal weight; // DECIMAL(19, 2) (Hibernate 기본)
}

// 명시 권장 케이스:
// - length 제한 필요
// - NOT NULL
// - UNIQUE
// - 컬럼명 다름

1.4 ddl-auto 와 함께

# application.yml
spring:
  jpa:
    hibernate:
      ddl-auto: validate   # 또는 create / update

# ddl-auto:
# - none: 아무것도 X (운영)
# - validate: 매핑 검증 (운영 권장)
# - update: 변경 사항 반영 (개발)
# - create: 매번 새로 (테스트)
# - create-drop: 시작 시 create, 종료 시 drop

1.5 ILIC 의 맥락

// ILIC 의 @Column 패턴
@Entity
@Table(name = "shipments")
public class Shipment {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(name = "bl_no", length = 50, nullable = false, unique = true)
    private String blNo;
    
    @Column(name = "status", length = 20, nullable = false)
    private String status;
    
    @Column(name = "weight", precision = 10, scale = 2)
    private BigDecimal weight;
    
    @Column(name = "created_at", nullable = false, updatable = false)
    private LocalDateTime createdAt;
    
    @Column(name = "updated_at")
    private LocalDateTime updatedAt;
    
    @Column(name = "memo", columnDefinition = "TEXT")
    private String memo;
}

// ILIC 표준:
// - 명시적 제약 시 @Column
// - 자동 변환 가능한 건 생략 가능 (선택)

1.6 자기 점검 답변

@Column 어노테이션의 정의는?

:
1. @Column:

  • 컬럼 매핑
  1. 위치:

    • 필드 위
  2. 옵션 6가지:

    • name / length / nullable / unique / 등
  3. 생략:

    • 기본값 OK 시

2️⃣ name 옵션

2.1 name 의 의미

name 옵션:

  DB 컬럼명 명시:
    @Column(name = "bl_no")
    private String blNo;

  생략 시:
    - 자동 변환 (Spring Boot 명명 전략)
    - blNo → bl_no

2.2 자동 변환 (Spring Boot)

자동 변환:

  SpringPhysicalNamingStrategy:
    - camelCase → snake_case
    - 자바: blNo
    - DB: bl_no

  shipperName → shipper_name
  createdAt → created_at
  isActive → is_active

2.3 명시가 필요한 경우

명시 필요 케이스:

  1. 자바 필드명과 DB 컬럼명 다름:
     - 자바: blNumber
     - DB: bl_no
     → @Column(name = "bl_no")

  2. 기존 레거시 DB:
     - 표준 안 따름
     - 명시 필수

  3. 명확성:
     - 코드 리뷰 친화
     - 의도 명확

2.4 명시 vs 자동

// 자동 변환 활용
@Entity
public class Shipment {
    private String blNo;          // → bl_no 자동
    private LocalDateTime createdAt;   // → created_at 자동
}

// 명시 (권장은 케이스 바이 케이스)
@Entity
public class Shipment {
    @Column(name = "bl_no")
    private String blNo;
}

// 다른 이름 (명시 필수)
@Entity
public class Shipment {
    @Column(name = "bill_of_lading_number")
    private String blNo;
}

2.5 ILIC 의 맥락

// ILIC 의 명명 전략

// Spring Boot 자동 활용:
@Entity
@Table(name = "shipments")
public class Shipment {
    private String blNo;             // → bl_no (자동)
    private BigDecimal weight;       // → weight
    private LocalDateTime createdAt; // → created_at (자동)
}

// 또는 명시 (혼합):
@Entity
@Table(name = "shipments")
public class Shipment {
    @Column(name = "bl_no", length = 50)
    private String blNo;
    
    @Column(precision = 10, scale = 2)
    private BigDecimal weight;
    
    @Column(name = "created_at", updatable = false)
    private LocalDateTime createdAt;
}

// ILIC 표준:
// - 자동 변환 활용 (단순 케이스)
// - 명시 (제약 필요 시)

2.6 자기 점검 답변

name 옵션의 동작은?

:
1. name:

  • 컬럼명 명시
  1. 자동:

    • camelCase → snake_case
  2. 명시 필요:

    • 다른 이름 / 레거시
  3. 권장:

    • 케이스 따라

3️⃣ length 옵션

3.1 length

length:

  문자열 최대 길이:
    - VARCHAR(N) 의 N
    - 기본값: 255
    - DDL 생성 시 반영

  적용 대상:
    - String 필드만
    - 숫자 / 날짜 X

3.2 기본값 (255)

// length 생략
@Column
private String status;   // VARCHAR(255)

// 또는
private String status;   // VARCHAR(255)

3.3 명시

// 짧은 컬럼 (저장 공간 ↓)
@Column(length = 20)
private String status;   // VARCHAR(20)

@Column(length = 50)
private String blNo;     // VARCHAR(50)

@Column(length = 100)
private String name;     // VARCHAR(100)

3.4 length 선택 가이드

length 선택:

  20:
    - 상태 코드 (status)
    - ENUM 같은 고정 짧은 값
    - PHONE / 우편번호

  50:
    - 짧은 이름
    - bl_no, 사번
    - 일반 식별자

  100:
    - 이름 (사람/회사)
    - 이메일
    - 짧은 제목

  255 (기본):
    - 일반 텍스트
    - 주소

  TEXT (length 안 됨):
    - 긴 텍스트
    - columnDefinition = "TEXT"

3.5 length 의 의미

length 의 진짜 의미:

  DDL 생성 시:
    @Column(length = 50)
    → VARCHAR(50)
    → DB 가 50자 초과 시 에러

  하지만:
    - 운영은 Flyway (DDL 별도)
    - 코드의 @Column 은 문서적 의미
    - validate 모드면 매핑 검증만

→ "이 컬럼 의도"

3.6 ILIC 의 맥락

// ILIC 의 length 선택
@Entity
@Table(name = "shipments")
public class Shipment {
    @Column(name = "bl_no", length = 50, unique = true)
    private String blNo;
    
    @Column(name = "status", length = 20)
    private String status;       // BOOKED / SHIPPED 등
    
    @Column(name = "memo", columnDefinition = "TEXT")
    private String memo;          // 긴 메모
}

@Entity
@Table(name = "customers")
public class Customer {
    @Column(name = "name", length = 100)
    private String name;
    
    @Column(name = "email", length = 100, unique = true)
    private String email;
    
    @Column(name = "phone", length = 20)
    private String phone;
    
    @Column(name = "address", length = 200)
    private String address;
}

// 의미:
// - 의도된 최대 길이
// - 운영 DB DDL 과 일치 (Flyway 와 동기)
// - 코드 리뷰 시 의도 명확

3.7 자기 점검 답변

length 옵션의 의미는?

:
1. length:

  • VARCHAR(N)
  1. 기본:

    • 255
  2. 선택:

    • 의도된 최대
  3. TEXT:

    • columnDefinition

4️⃣ nullable / unique

4.1 nullable

nullable:

  NULL 허용 여부:
    - true (기본): NULL OK
    - false: NOT NULL

  명시 권장:
    - 데이터 무결성
    - 의도 명확

4.2 unique

unique:

  단일 컬럼 UNIQUE 제약:
    - true: UNIQUE
    - false (기본): 중복 OK

  복합 UNIQUE 는 @Table 의 uniqueConstraints

4.3 명시 예시

@Entity
public class Customer {
    @Id
    private Long id;
    
    @Column(nullable = false, length = 100)
    private String name;     // NOT NULL
    
    @Column(unique = true, length = 100, nullable = false)
    private String email;    // UNIQUE, NOT NULL
    
    @Column(length = 20)
    private String phone;    // NULL OK (기본)
}

// DDL:
// CREATE TABLE customers (
//     id BIGINT PRIMARY KEY,
//     name VARCHAR(100) NOT NULL,
//     email VARCHAR(100) NOT NULL UNIQUE,
//     phone VARCHAR(20)
// );

4.4 복합 UNIQUE

// 복합 UNIQUE 는 @Table
@Entity
@Table(name = "shipment_items",
    uniqueConstraints = {
        @UniqueConstraint(
            name = "uk_shipment_seq",
            columnNames = {"shipment_id", "item_sequence"}
        )
    }
)
public class ShipmentItem {
    @Id
    private Long id;
    
    @Column(name = "shipment_id")
    private Long shipmentId;
    
    @Column(name = "item_sequence")
    private int itemSequence;
    // 둘이 합쳐 UNIQUE
}

4.5 nullable 의 트레이드오프

nullable 의 트레이드오프:

  nullable = false (NOT NULL):
    - 데이터 무결성 ↑
    - 검색 효율 ↑
    - 변경 어려움 (기존 NULL 데이터)

  nullable = true (NULL):
    - 유연
    - 의미 모호 (NULL = 미정? 없음? 0?)
    - 보통 피함

→ 가능하면 NOT NULL

4.6 ILIC 의 맥락

// ILIC 의 nullable / unique 활용
@Entity
@Table(name = "shipments")
public class Shipment {
    @Id
    @GeneratedValue
    private Long id;
    
    // 필수 + UNIQUE
    @Column(name = "bl_no", length = 50, unique = true, nullable = false)
    private String blNo;
    
    // 필수
    @Column(name = "status", length = 20, nullable = false)
    private String status;
    
    // 선택 (NULL OK)
    @Column(name = "weight", precision = 10, scale = 2)
    private BigDecimal weight;
    
    // 필수
    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "customer_id", nullable = false)
    private Customer customer;
}

// ILIC 표준:
// - 필수 필드: nullable = false 명시
// - UNIQUE: 자연 키 (bl_no 등)
// - 명확한 의도
class Customer {}

4.7 자기 점검 답변

nullable / unique 제약은?

:
1. nullable:

  • NULL 허용 여부
  1. unique:

    • 유일 제약
  2. 복합 UNIQUE:

    • @Table
  3. 권장:

    • NOT NULL 명시

5️⃣ columnDefinition

5.1 columnDefinition

columnDefinition:

  DDL 직접 작성:
    - 다른 옵션 (length 등) 무시
    - DB 별 컬럼 정의 가능
    - 완전한 제어

5.2 사용 케이스

// TEXT 타입
@Column(columnDefinition = "TEXT")
private String description;
// DDL: description TEXT
// length 옵션 무시

// LONGTEXT
@Column(columnDefinition = "LONGTEXT")
private String content;

// JSON (MySQL 5.7+)
@Column(columnDefinition = "JSON")
private String extraData;

// 기본값 명시
@Column(columnDefinition = "VARCHAR(20) DEFAULT 'BOOKED'")
private String status;

// TIMESTAMP DEFAULT
@Column(columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
private LocalDateTime createdAt;

5.3 다른 옵션 무시

// columnDefinition 우선
@Column(
    columnDefinition = "VARCHAR(50)",
    length = 100        // ← 무시됨
)
private String blNo;
// 실제: VARCHAR(50) (columnDefinition 우선)

5.4 DB 종속

DB 종속:

  columnDefinition 의 단점:
    - DB 별 SQL 다름
    - MySQL: TEXT
    - Oracle: CLOB
    - 마이그레이션 시 변경 필요

  → DB 무관성 ↓
  → 보통 피함 (특수 케이스만)

5.5 권장 사용

권장 사용:

  - TEXT 타입 (긴 텍스트)
  - JSON (DB 별 다름)
  - 특수 컬럼

  피해야:
    - 단순 length / nullable 등 (그냥 옵션 쓰기)
    - DB 종속 코드 증가

5.6 ILIC 의 맥락

// ILIC 의 columnDefinition 활용

@Entity
@Table(name = "shipments")
public class Shipment {
    @Id
    private Long id;
    
    // 일반 (length 옵션)
    @Column(length = 50)
    private String blNo;
    
    // TEXT (긴 메모)
    @Column(columnDefinition = "TEXT")
    private String memo;
    
    // JSON (MySQL 의 동적 데이터)
    @Column(name = "extra_data", columnDefinition = "JSON")
    private String extraData;
    
    // 기본값 명시
    @Column(name = "status", columnDefinition = "VARCHAR(20) DEFAULT 'BOOKED'")
    private String status;
}

// ILIC 의 정책:
// - 일반 컬럼: 옵션 (length 등)
// - 특수 (TEXT/JSON): columnDefinition
// - DB 종속 최소화

5.7 자기 점검 답변

columnDefinition 의 사용은?

:
1. columnDefinition:

  • DDL 직접
  1. 케이스:

    • TEXT / JSON / DEFAULT
  2. 다른 옵션:

    • 무시
  3. DB 종속:

    • 단점

6️⃣ precision / scale

6.1 precision / scale

precision / scale:

  BigDecimal (DECIMAL) 의 정밀도:
    - precision: 전체 자릿수
    - scale: 소수점 이하 자릿수

  예:
    precision = 10, scale = 2
    → DECIMAL(10, 2)
    → 12345678.90 (정수 8 + 소수 2)

6.2 사용

@Column(precision = 10, scale = 2)
private BigDecimal weight;
// DECIMAL(10, 2)
// 최대 99,999,999.99

@Column(precision = 15, scale = 2)
private BigDecimal amount;
// DECIMAL(15, 2)
// 최대 9,999,999,999,999.99 (수백조)

@Column(precision = 20, scale = 8)
private BigDecimal rate;
// DECIMAL(20, 8)
// 환율 / 코인 (정밀도 ↑)

6.3 BigDecimal 사용 이유

BigDecimal 사용 이유:

  돈 / 금액 / 정확한 소수:
    - double: 부동소수점 오차
      (0.1 + 0.2 = 0.30000000000000004)
    - BigDecimal: 정확
      (0.1 + 0.2 = 0.3)

  필수:
    - 금액 (운임, 가격)
    - 환율
    - 무게 (정확)
    - 비율

6.4 적절한 precision / scale

적절한 precision / scale:

  금액 (KRW):
    - precision = 15, scale = 2
    - 최대 9조 .99

  금액 (USD):
    - precision = 12, scale = 2
    - 최대 99억 .99

  비율 (%):
    - precision = 5, scale = 2
    - 0.00 ~ 999.99

  환율:
    - precision = 20, scale = 8
    - 정밀

  무게 (kg):
    - precision = 10, scale = 2
    - 최대 99,999,999.99 kg

6.5 default

default (precision / scale):

  Hibernate 기본:
    - precision = 19, scale = 2 (DECIMAL(19, 2))
    - 큰 정밀도

  하지만 명시 권장:
    - 의도 명확
    - DB 저장 공간 ↑ (불필요한 정밀도)

6.6 ILIC 의 맥락

// ILIC 의 precision / scale 활용

@Entity
@Table(name = "shipments")
public class Shipment {
    @Column(name = "weight", precision = 10, scale = 2)
    private BigDecimal weight;   // 무게 (kg)
    
    @Column(name = "volume", precision = 10, scale = 3)
    private BigDecimal volume;   // 부피 (CBM)
}

@Entity
@Table(name = "freights")
public class Freight {
    @Column(name = "amount", precision = 15, scale = 2)
    private BigDecimal amount;   // 운임 (큰 금액 가능)
    
    @Column(name = "currency", length = 3)
    private String currency;     // USD, KRW
    
    @Column(name = "exchange_rate", precision = 20, scale = 8)
    private BigDecimal exchangeRate;   // 환율 (정밀)
}

@Entity
@Table(name = "shipment_items")
public class ShipmentItem {
    @Column(name = "unit_price", precision = 12, scale = 2)
    private BigDecimal unitPrice;   // 단가
    
    @Column(name = "quantity", precision = 10, scale = 2)
    private BigDecimal quantity;
}

// ILIC 표준:
// - 금액: precision = 15, scale = 2 (KRW 충분)
// - 환율: precision = 20, scale = 8 (정밀)
// - 무게: precision = 10, scale = 2

6.7 자기 점검 답변

precision / scale (BigDecimal) 은?

:
1. precision:

  • 전체 자릿수
  1. scale:

    • 소수점 이하
  2. BigDecimal:

    • 정확한 소수 (금액)
  3. 권장:

    • 명시

7️⃣ @Enumerated

7.1 @Enumerated

@Enumerated:

  Enum 필드를 DB 컬럼에 매핑:
    - EnumType.ORDINAL (기본): 순서 (0, 1, 2)
    - EnumType.STRING: 이름 ("BOOKED", "SHIPPED")

  ★ STRING 권장 (강력)

7.2 ORDINAL 의 위험

ORDINAL 의 위험:

  enum ShipmentStatus { BOOKED, CONFIRMED, SHIPPED }
  
  DB 저장:
    - BOOKED → 0
    - CONFIRMED → 1
    - SHIPPED → 2

  위험: enum 순서 변경 시
    enum ShipmentStatus { BOOKED, PENDING, CONFIRMED, SHIPPED }
    - BOOKED → 0 (그대로)
    - PENDING → 1 ← 새로 추가
    - CONFIRMED → 2 ← 자리 이동
    - SHIPPED → 3
  
  기존 DB:
    - 0 = BOOKED (OK)
    - 1 = CONFIRMED (×) → 이제 PENDING 으로!
    - 2 = SHIPPED (×) → 이제 CONFIRMED 로!
  
  → 데이터 오염!

7.3 STRING 의 안전

// STRING (권장)
@Enumerated(EnumType.STRING)
@Column(name = "status", length = 20)
private ShipmentStatus status;

// DB 저장:
// - BOOKED → "BOOKED"
// - SHIPPED → "SHIPPED"

// enum 순서 변경 / 추가 / 삭제:
// - "BOOKED" 는 그대로 BOOKED
// - 의미 변경 X
// → 안전!
enum ShipmentStatus { BOOKED, CONFIRMED, SHIPPED }

7.4 STRING 의 단점 (사소)

STRING 의 단점:

  - 저장 공간 ↑ (정수 1 vs 문자열 20)
  - 정렬은 알파벳 (의미 X)

  하지만:
    - 안전 >>> 공간
    - STRING 압도적 권장

7.5 코드 표 사용 (대안)

// 코드 표 (별도 클래스)
public enum ShipmentStatus {
    BOOKED("01", "예약"),
    CONFIRMED("02", "확인"),
    SHIPPED("03", "선적");
    
    private final String code;
    private final String label;
    
    // ... constructor, getter
}

// @Convert 로 코드 매핑
@Convert(converter = ShipmentStatusConverter.class)
@Column(length = 2)
private ShipmentStatus status;

// 컨버터
@Converter
public class ShipmentStatusConverter 
    implements AttributeConverter<ShipmentStatus, String> {
    
    @Override
    public String convertToDatabaseColumn(ShipmentStatus s) {
        return s != null ? s.getCode() : null;
    }
    
    @Override
    public ShipmentStatus convertToEntityAttribute(String code) {
        return Arrays.stream(ShipmentStatus.values())
            .filter(s -> s.getCode().equals(code))
            .findFirst().orElse(null);
    }
}
// → 코드 (01) 로 DB 저장, 이름은 자바에서
class ShipmentStatusConverter {}
@interface Converter {}
interface AttributeConverter<X, Y> {
    Y convertToDatabaseColumn(X x);
    X convertToEntityAttribute(Y y);
}

7.6 ILIC 의 맥락

// ILIC 의 Enum 매핑

public enum ShipmentStatus {
    BOOKED, CONFIRMED, SHIPPED, DELIVERED, CANCELLED
}

@Entity
@Table(name = "shipments")
public class Shipment {
    @Id
    private Long id;
    
    @Enumerated(EnumType.STRING)
    @Column(name = "status", length = 20, nullable = false)
    private ShipmentStatus status;
    
    // 비즈니스 메서드
    public void markAsShipped() {
        if (status != ShipmentStatus.CONFIRMED) {
            throw new IllegalStateException();
        }
        this.status = ShipmentStatus.SHIPPED;
    }
}

// DB:
// status VARCHAR(20) → "BOOKED", "SHIPPED", ...

// 효과:
// - 타입 안전 (enum)
// - DB 가독성 (이름)
// - 순서 변경 안전

7.7 자기 점검 답변

@Enumerated 는?

:
1. @Enumerated:

  • Enum 매핑
  1. ORDINAL:

    • 위험 (순서 0/1/2)
  2. STRING:

    • 안전 (권장)
  3. 대안:

    • Converter (코드)

8️⃣ @Temporal / @Lob

8.1 @Temporal

@Temporal:

  날짜/시간 매핑:
    - 옛 Date / Calendar 만
    - java.time 은 자동 매핑

  옵션:
    - TemporalType.DATE: 날짜만
    - TemporalType.TIME: 시간만
    - TemporalType.TIMESTAMP: 날짜+시간

8.2 옛 Date (사용 X)

// 옛 Date (사용 권장 X)
@Temporal(TemporalType.TIMESTAMP)
private java.util.Date createdAt;

@Temporal(TemporalType.DATE)
private java.util.Date birthDate;

// 문제:
// - 가변 (변경 가능)
// - 시간대 처리 어려움
// - 비추천

8.3 java.time (자동, 권장)

// java.time (권장, Java 8+)
private LocalDateTime createdAt;   // 자동 TIMESTAMP
private LocalDate birthDate;        // 자동 DATE
private LocalTime openingTime;      // 자동 TIME
private Instant timestamp;          // 자동 TIMESTAMP (UTC)
private ZonedDateTime zonedDate;    // 자동 TIMESTAMP WITH ZONE

// @Temporal 불필요!

8.4 @Lob

@Lob:

  Large Object:
    - String → CLOB (Character Large Object)
    - byte[] → BLOB (Binary Large Object)
    - 매우 큰 데이터

8.5 @Lob 사용

// 텍스트 (CLOB)
@Lob
@Column(name = "content")
private String content;
// → DB: CLOB / TEXT / LONGTEXT (DB 따라)

// 바이너리 (BLOB)
@Lob
@Column(name = "image_data")
private byte[] imageData;
// → DB: BLOB / LONGBLOB

8.6 @Lob vs columnDefinition

@Lob vs columnDefinition:

  @Lob:
    - 표준 JPA
    - DB 가 자동 (CLOB/BLOB)

  columnDefinition = "TEXT":
    - DB 종속 (MySQL TEXT)
    - 명시적

  실무:
    - 작은 (TEXT, 65535 byte): columnDefinition = "TEXT"
    - 큰 (LONGTEXT, 4GB): @Lob 또는 columnDefinition
    - 보통 columnDefinition 명시

8.7 ILIC 의 맥락

// ILIC 의 날짜 / Lob 매핑

@Entity
@Table(name = "shipments")
public class Shipment {
    @Id
    private Long id;
    
    // 날짜 (java.time, 자동)
    @Column(name = "created_at", nullable = false, updatable = false)
    private LocalDateTime createdAt;
    
    @Column(name = "updated_at")
    private LocalDateTime updatedAt;
    
    @Column(name = "shipping_date")
    private LocalDate shippingDate;
    
    // 긴 텍스트
    @Column(name = "memo", columnDefinition = "TEXT")
    private String memo;          // 일반 (TEXT)
    
    @Lob
    @Column(name = "long_description")
    private String longDescription;   // 매우 긴 (LONGTEXT)
    
    @PrePersist
    void onCreate() {
        this.createdAt = LocalDateTime.now();
        this.updatedAt = LocalDateTime.now();
    }
    
    @PreUpdate
    void onUpdate() {
        this.updatedAt = LocalDateTime.now();
    }
}

// ILIC 표준:
// - java.time 사용 (LocalDateTime/LocalDate)
// - @Temporal 거의 안 씀
// - 긴 텍스트: columnDefinition = "TEXT" 또는 @Lob

8.8 자기 점검 답변

@Temporal / @Lob 은?

:
1. @Temporal:

  • 옛 Date/Calendar 만
  1. java.time:

    • 자동 매핑
  2. @Lob:

    • 큰 데이터 (CLOB/BLOB)
  3. 권장:

    • java.time + columnDefinition

9️⃣ 생략 가능 + ILIC 표준

9.1 생략 가능 조건

생략 가능 조건:

  @Column 기본값으로 충분:
    - name 자동 변환 (snake_case)
    - length = 255 OK
    - nullable = true OK
    - unique = false OK

  → @Column 생략

  명시 필요 시만 @Column

9.2 자동 매핑 예시

// 자동 매핑 (생략 OK)
@Entity
public class Shipment {
    @Id
    @GeneratedValue
    private Long id;
    
    private String blNo;             // bl_no, VARCHAR(255)
    private String status;            // status, VARCHAR(255)
    private BigDecimal weight;        // weight, DECIMAL(19,2)
    private LocalDateTime createdAt;  // created_at, TIMESTAMP
    
    @Enumerated(EnumType.STRING)
    private ShipmentStatus statusEnum; // status_enum, VARCHAR(255)
}
enum ShipmentStatus {}

9.3 명시 권장 케이스

명시 권장 케이스:

  1. length 제한 필요:
     - @Column(length = 50)

  2. NOT NULL:
     - @Column(nullable = false)

  3. UNIQUE:
     - @Column(unique = true)

  4. 이름 다름:
     - @Column(name = "different_name")

  5. precision / scale (BigDecimal):
     - @Column(precision = 15, scale = 2)

  6. updatable = false (불변):
     - @Column(updatable = false)

9.4 ILIC 표준 (균형)

// ILIC 표준 (균형)
@Entity
@Table(name = "shipments")
public class Shipment {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    // 명시 (제약)
    @Column(name = "bl_no", length = 50, nullable = false, unique = true)
    private String blNo;
    
    // 명시 (NOT NULL)
    @Enumerated(EnumType.STRING)
    @Column(length = 20, nullable = false)
    private ShipmentStatus status;
    
    // 명시 (정밀도)
    @Column(precision = 10, scale = 2)
    private BigDecimal weight;
    
    // 자동 (생략)
    private String memo;
    
    // 명시 (updatable false)
    @Column(name = "created_at", nullable = false, updatable = false)
    private LocalDateTime createdAt;
    
    // 명시 (NULL OK)
    @Column(name = "updated_at")
    private LocalDateTime updatedAt;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "customer_id", nullable = false)
    private Customer customer;
}
enum ShipmentStatus {}
class Customer {}

9.5 운영 / 마이그레이션

운영 / 마이그레이션:

  코드의 @Column:
    - 의도 / 문서
    - ddl-auto: validate (운영)
    - 매핑 검증

  실제 DDL:
    - Flyway / Liquibase
    - 마이그레이션 스크립트
    - 운영 안정

  두 곳 동기화 중요:
    - 코드 @Column 변경
    - + 마이그레이션 스크립트

9.6 ILIC 의 운영

ILIC 의 운영

코드 (JPA 엔티티):
  - @Column 으로 의도 표현
  - validate 모드로 매핑 검증

DDL (Flyway):
  - V1__init_tables.sql
  - V2__add_column.sql
  - 운영 / 개발 동일

새 컬럼 추가 시:
  1. Flyway 스크립트 추가 (V3__add_xxx.sql)
  2. 엔티티에 @Column 추가
  3. 자동 매핑 검증 (validate)
  4. 배포

→ 코드와 DDL 동기화

9.7 면접 단골 질문 매핑

Q핵심 답변
@Column?컬럼 매핑
옵션 6가지?name/length/nullable/unique/columnDefinition/precision
name 생략?snake_case 자동
length 기본?255
nullable 기본?true
precision / scale?BigDecimal
@Enumerated?STRING 권장
ORDINAL 위험?순서 변경
java.time?자동 매핑
생략 가능?기본값 OK 시

9.8 자기 점검 체크리스트

@Column

  • 정의

name

  • 자동/명시

length

  • 의미

nullable / unique

  • 제약

columnDefinition

  • DDL 직접

precision / scale

  • BigDecimal

@Enumerated

  • STRING

@Temporal / @Lob

  • 사용

생략

  • 가능

9.9 추가 심화 질문

Q1: @Column(insertable = false / updatable = false)?

답:

  • insertable = false: INSERT 시 제외
  • updatable = false: UPDATE 시 제외 (생성 후 불변)
  • 트리거나 자동 컬럼 (DB DEFAULT) 시
  • created_at 같은 곳

Q2: @Generated?

답:

  • Hibernate 고유 (JPA X)
  • DB 가 생성한 값 자동 반영
  • @Generated(GenerationTime.INSERT) 등

Q3: @Formula?

답:

  • 계산 컬럼 (Hibernate)
  • SQL 표현식 매핑
  • @Formula("price * quantity")

Q4: @Convert (AttributeConverter)?

답:

  • 사용자 정의 변환
  • 객체 ↔ DB 컬럼
  • 예: Address 객체 → JSON

Q5: BooleanConverter?

답:

  • Y/N ↔ Boolean
  • 1/0 ↔ Boolean
  • 레거시 DB 호환

🎯 핵심 요약 — 3줄 정리

1. @Column = 컬럼 매핑

  • 6가지 옵션: name / length / nullable / unique / columnDefinition / precision-scale
  • 모두 기본값으로 충분하면 @Column 자체 생략 가능 (자동 매핑)
  • Spring Boot 의 camelCase → snake_case 자동 변환 활용

2. 주요 옵션의 의미

  • name: 컬럼명 (생략 시 자동), length: VARCHAR 길이 (기본 255), nullable: NULL 허용 (기본 true), unique: UNIQUE 제약
  • columnDefinition: DDL 직접 (TEXT/JSON 등 특수)
  • precision/scale: BigDecimal 정밀도 (금액 = 15,2 일반)

3. 특수 어노테이션

  • @Enumerated(STRING) 필수 (ORDINAL 은 순서 변경 시 데이터 오염)
  • java.time (LocalDateTime/LocalDate) 자동 매핑 → @Temporal 불필요
  • @Lob (CLOB/BLOB) 또는 columnDefinition = "TEXT" 큰 데이터

📚 다음으로...

Unit 4.5 — 자동 매핑 규칙 (Phase 4 완주)

이번 Unit에서 @Column 을 봤다면, 다음은 자동 매핑 (Phase 4 마지막).

  • Spring Boot 의 명명 전략
  • camelCase ↔ snake_case 자동
  • 이 자동 변환 끄기
  • SpringPhysicalNamingStrategy

Phase 4 진행 상황

🏷️ Phase 4 — JPA 엔티티 매핑
  ✅ Unit 4.1 @Entity 와 엔티티 개념
  ✅ Unit 4.2 @Id 와 PK 매핑
  ✅ Unit 4.3 @GeneratedValue 전략 ★깊이
  ✅ Unit 4.4 @Column 과 컬럼 매핑 ← 여기
  ⏭ Unit 4.5 자동 매핑 규칙 — Phase 4 완주 + Part A 완주

7주차 누적 진행

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

총: 15/24 Unit (63%)

profile
Software Developer

0개의 댓글