F-LAB JAVA · 7주차 · Phase 4 · JPA 엔티티 매핑
이 Unit을 끝내면 다음을 답할 수 있어야 한다.
@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 필수.
1. @Column 정의
2. name 옵션
3. length 옵션
4. nullable / unique
5. columnDefinition
6. precision / scale
7. @Enumerated
8. @Temporal / @Lob
9. 생략 가능 + ILIC 표준
@Column:
엔티티 필드를 DB 컬럼에 매핑:
- PK 외 일반 필드
- 옵션 6가지
- DDL 생성 시 반영
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;
}
// @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
// - 컬럼명 다름
# application.yml
spring:
jpa:
hibernate:
ddl-auto: validate # 또는 create / update
# ddl-auto:
# - none: 아무것도 X (운영)
# - validate: 매핑 검증 (운영 권장)
# - update: 변경 사항 반영 (개발)
# - create: 매번 새로 (테스트)
# - create-drop: 시작 시 create, 종료 시 drop
// 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
// - 자동 변환 가능한 건 생략 가능 (선택)
@Column 어노테이션의 정의는?
답:
1. @Column:
위치:
옵션 6가지:
생략:
name 옵션:
DB 컬럼명 명시:
@Column(name = "bl_no")
private String blNo;
생략 시:
- 자동 변환 (Spring Boot 명명 전략)
- blNo → bl_no
자동 변환:
SpringPhysicalNamingStrategy:
- camelCase → snake_case
- 자바: blNo
- DB: bl_no
shipperName → shipper_name
createdAt → created_at
isActive → is_active
명시 필요 케이스:
1. 자바 필드명과 DB 컬럼명 다름:
- 자바: blNumber
- DB: bl_no
→ @Column(name = "bl_no")
2. 기존 레거시 DB:
- 표준 안 따름
- 명시 필수
3. 명확성:
- 코드 리뷰 친화
- 의도 명확
// 자동 변환 활용
@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;
}
// 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 표준:
// - 자동 변환 활용 (단순 케이스)
// - 명시 (제약 필요 시)
name 옵션의 동작은?
답:
1. name:
자동:
명시 필요:
권장:
length:
문자열 최대 길이:
- VARCHAR(N) 의 N
- 기본값: 255
- DDL 생성 시 반영
적용 대상:
- String 필드만
- 숫자 / 날짜 X
// length 생략
@Column
private String status; // VARCHAR(255)
// 또는
private String status; // VARCHAR(255)
// 짧은 컬럼 (저장 공간 ↓)
@Column(length = 20)
private String status; // VARCHAR(20)
@Column(length = 50)
private String blNo; // VARCHAR(50)
@Column(length = 100)
private String name; // VARCHAR(100)
length 선택:
20:
- 상태 코드 (status)
- ENUM 같은 고정 짧은 값
- PHONE / 우편번호
50:
- 짧은 이름
- bl_no, 사번
- 일반 식별자
100:
- 이름 (사람/회사)
- 이메일
- 짧은 제목
255 (기본):
- 일반 텍스트
- 주소
TEXT (length 안 됨):
- 긴 텍스트
- columnDefinition = "TEXT"
length 의 진짜 의미:
DDL 생성 시:
@Column(length = 50)
→ VARCHAR(50)
→ DB 가 50자 초과 시 에러
하지만:
- 운영은 Flyway (DDL 별도)
- 코드의 @Column 은 문서적 의미
- validate 모드면 매핑 검증만
→ "이 컬럼 의도"
// 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 와 동기)
// - 코드 리뷰 시 의도 명확
length 옵션의 의미는?
답:
1. length:
기본:
선택:
TEXT:
nullable:
NULL 허용 여부:
- true (기본): NULL OK
- false: NOT NULL
명시 권장:
- 데이터 무결성
- 의도 명확
unique:
단일 컬럼 UNIQUE 제약:
- true: UNIQUE
- false (기본): 중복 OK
복합 UNIQUE 는 @Table 의 uniqueConstraints
@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)
// );
// 복합 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
}
nullable 의 트레이드오프:
nullable = false (NOT NULL):
- 데이터 무결성 ↑
- 검색 효율 ↑
- 변경 어려움 (기존 NULL 데이터)
nullable = true (NULL):
- 유연
- 의미 모호 (NULL = 미정? 없음? 0?)
- 보통 피함
→ 가능하면 NOT NULL
// 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 {}
nullable / unique 제약은?
답:
1. nullable:
unique:
복합 UNIQUE:
권장:
columnDefinition:
DDL 직접 작성:
- 다른 옵션 (length 등) 무시
- DB 별 컬럼 정의 가능
- 완전한 제어
// 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;
// columnDefinition 우선
@Column(
columnDefinition = "VARCHAR(50)",
length = 100 // ← 무시됨
)
private String blNo;
// 실제: VARCHAR(50) (columnDefinition 우선)
DB 종속:
columnDefinition 의 단점:
- DB 별 SQL 다름
- MySQL: TEXT
- Oracle: CLOB
- 마이그레이션 시 변경 필요
→ DB 무관성 ↓
→ 보통 피함 (특수 케이스만)
권장 사용:
- TEXT 타입 (긴 텍스트)
- JSON (DB 별 다름)
- 특수 컬럼
피해야:
- 단순 length / nullable 등 (그냥 옵션 쓰기)
- DB 종속 코드 증가
// 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 종속 최소화
columnDefinition 의 사용은?
답:
1. columnDefinition:
케이스:
다른 옵션:
DB 종속:
precision / scale:
BigDecimal (DECIMAL) 의 정밀도:
- precision: 전체 자릿수
- scale: 소수점 이하 자릿수
예:
precision = 10, scale = 2
→ DECIMAL(10, 2)
→ 12345678.90 (정수 8 + 소수 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)
// 환율 / 코인 (정밀도 ↑)
BigDecimal 사용 이유:
돈 / 금액 / 정확한 소수:
- double: 부동소수점 오차
(0.1 + 0.2 = 0.30000000000000004)
- BigDecimal: 정확
(0.1 + 0.2 = 0.3)
필수:
- 금액 (운임, 가격)
- 환율
- 무게 (정확)
- 비율
적절한 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
default (precision / scale):
Hibernate 기본:
- precision = 19, scale = 2 (DECIMAL(19, 2))
- 큰 정밀도
하지만 명시 권장:
- 의도 명확
- DB 저장 공간 ↑ (불필요한 정밀도)
// 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
precision / scale (BigDecimal) 은?
답:
1. precision:
scale:
BigDecimal:
권장:
@Enumerated:
Enum 필드를 DB 컬럼에 매핑:
- EnumType.ORDINAL (기본): 순서 (0, 1, 2)
- EnumType.STRING: 이름 ("BOOKED", "SHIPPED")
★ STRING 권장 (강력)
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 로!
→ 데이터 오염!
// 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 }
STRING 의 단점:
- 저장 공간 ↑ (정수 1 vs 문자열 20)
- 정렬은 알파벳 (의미 X)
하지만:
- 안전 >>> 공간
- STRING 압도적 권장
// 코드 표 (별도 클래스)
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);
}
// 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 가독성 (이름)
// - 순서 변경 안전
@Enumerated 는?
답:
1. @Enumerated:
ORDINAL:
STRING:
대안:
@Temporal:
날짜/시간 매핑:
- 옛 Date / Calendar 만
- java.time 은 자동 매핑
옵션:
- TemporalType.DATE: 날짜만
- TemporalType.TIME: 시간만
- TemporalType.TIMESTAMP: 날짜+시간
// 옛 Date (사용 권장 X)
@Temporal(TemporalType.TIMESTAMP)
private java.util.Date createdAt;
@Temporal(TemporalType.DATE)
private java.util.Date birthDate;
// 문제:
// - 가변 (변경 가능)
// - 시간대 처리 어려움
// - 비추천
// 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 불필요!
@Lob:
Large Object:
- String → CLOB (Character Large Object)
- byte[] → BLOB (Binary Large Object)
- 매우 큰 데이터
// 텍스트 (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
@Lob vs columnDefinition:
@Lob:
- 표준 JPA
- DB 가 자동 (CLOB/BLOB)
columnDefinition = "TEXT":
- DB 종속 (MySQL TEXT)
- 명시적
실무:
- 작은 (TEXT, 65535 byte): columnDefinition = "TEXT"
- 큰 (LONGTEXT, 4GB): @Lob 또는 columnDefinition
- 보통 columnDefinition 명시
// 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
@Temporal / @Lob 은?
답:
1. @Temporal:
java.time:
@Lob:
권장:
생략 가능 조건:
@Column 기본값으로 충분:
- name 자동 변환 (snake_case)
- length = 255 OK
- nullable = true OK
- unique = false OK
→ @Column 생략
명시 필요 시만 @Column
// 자동 매핑 (생략 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 {}
명시 권장 케이스:
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)
// 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 {}
운영 / 마이그레이션:
코드의 @Column:
- 의도 / 문서
- ddl-auto: validate (운영)
- 매핑 검증
실제 DDL:
- Flyway / Liquibase
- 마이그레이션 스크립트
- 운영 안정
두 곳 동기화 중요:
- 코드 @Column 변경
- + 마이그레이션 스크립트
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 동기화
| 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 시 |
답:
답:
답:
답:
답:
1. @Column = 컬럼 매핑
2. 주요 옵션의 의미
3. 특수 어노테이션
이번 Unit에서 @Column 을 봤다면, 다음은 자동 매핑 (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 완주
🗂️ Part A — 데이터 모델링과 ORM
✅ Phase 1 (5)
✅ Phase 2 (2)
✅ Phase 3 (4)
🏷️ Phase 4 (4/5)
총: 15/24 Unit (63%)