🎯 JPA @Entity 완벽 가이드
JPA의 시작점이자 핵심! @Entity를 정확하게 이해하고 실무에 바로 적용해보세요.
📋 목차
- @Entity가 뭔가요?
- @Entity의 핵심 역할
- 언제 사용해야 할까?
- 반드시 지켜야 할 4가지 규칙
- 절대 사용하면 안 되는 경우
- Entity vs DTO vs VO 비교
- 실무 표준 템플릿
- 핵심 요약
1️⃣ @Entity가 뭔가요?
@Entity는 JPA에서 클래스에게 특별한 신분을 부여하는 어노테이션입니다.
@Entity
public class Member {
// 이 클래스는 이제 특별합니다! 🌟
}
🎭 이 한 줄의 의미
“이 클래스의 인스턴스는 데이터베이스 테이블의 한 행(row)과 1:1로 매핑된다”
🔄 객체와 DB의 관계
| 자바 세계 🖥️ | 데이터베이스 세계 💾 |
|---|---|
| 클래스 (Class) | 테이블 (Table) |
| 객체 (Object) | 행 (Row) |
| 필드 (Field) | 컬럼 (Column) |
실제 변환 예시
// Java 코드
@Entity
public class Member {
private Long id;
private String name;
}
⬇️ JPA가 자동으로 변환 ⬇️
-- Database Table
CREATE TABLE member (
id BIGINT,
name VARCHAR(255)
);
2️⃣ @Entity의 핵심 역할
@Entity가 붙으면 JPA에게 다음과 같은 슈퍼파워를 위임합니다:
| 기능 | 설명 |
|---|---|
| 🗂️ 테이블 매핑 | 클래스를 DB 테이블과 연결 |
| 🔍 조회 (SELECT) | 데이터 읽기 |
| 💾 저장 (INSERT) | 데이터 생성 |
| ✏️ 수정 (UPDATE) | 데이터 변경 |
| 🗑️ 삭제 (DELETE) | 데이터 제거 |
| 🎪 영속성 관리 | 객체 생명주기 추적 |
💡 핵심: JPA가 당신의 객체 생명주기를 완전히 관리합니다!
3️⃣ 언제 사용해야 할까?
✅ Case 1: DB에 저장해야 하는 도메인 객체
@Entity
public class Order {
@Id
private Long id;
private String productName;
private int quantity;
}
적용 대상:
- 👤 회원 (Member)
- 📦 주문 (Order)
- 🛍️ 상품 (Product)
- 📝 게시글 (Post)
판단 기준: “이 데이터가 DB에 영구적으로 저장되어야 하나?” → YES면 @Entity
✅ Case 2: JPA Repository로 관리하고 싶을 때
public interface MemberRepository extends JpaRepository<Member, Long> {
// Member는 반드시 @Entity여야 합니다!
}
✅ Case 3: 변경 감지가 필요할 때 (Dirty Checking)
// 트랜잭션 안에서
Member member = memberRepository.findById(1L).orElseThrow();
member.changeName("Kobe"); // 이름만 변경
// save() 호출 없이도 자동으로 UPDATE 쿼리 발생! 🎉
⚡ Entity만의 특권: 변경 감지 자동화
4️⃣ 반드시 지켜야 할 4가지 규칙
🔹 규칙 1: 기본 생성자 필수
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Member {
// JPA가 리플렉션으로 객체를 생성하기 위해 필요
}
| 접근 제어자 | 사용 가능? | 추천 |
|---|---|---|
private |
❌ | - |
protected |
✅ | ⭐⭐⭐ |
public |
✅ | ⭐ |
💡 실무 표준:
protected사용 (무분별한 객체 생성 방지)
🔹 규칙 2: @Id 필수
@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; // 기본 키 없으면 Entity가 아님!
}
🔹 규칙 3: final 클래스 금지
public final class Member { } // ❌ 프록시 생성 불가!
이유: JPA는 지연 로딩을 위해 프록시 객체를 생성하는데, final 클래스는 상속 불가
🔹 규칙 4: 필드 접근 방식 일관성
@Entity
public class Member {
@Id // 필드에 @Id → 모든 필드 접근
private Long id;
private String name; // getter 없어도 OK
}
@Entity
public class Member {
private Long id;
@Id // getter에 @Id → 모든 getter 접근
public Long getId() {
return id;
}
}
⚠️ 주의: 필드 접근과 getter 접근을 섞지 마세요!
5️⃣ 절대 사용하면 안 되는 경우
❌ Case 1: DTO (Data Transfer Object)
// ❌ 잘못된 사용
@Entity
public class MemberRequest {
private String name;
private String email;
}
// ✅ 올바른 사용
public class MemberRequest {
private String name;
private String email;
}
이유:
- DB 저장 대상 아님
- 단순 데이터 전달용
- 상태 관리 불필요
❌ Case 2: VO (Value Object)
// ❌ 잘못된 사용
@Entity
public class Money {
private int value;
}
// ✅ 올바른 사용
@Embeddable // or 일반 클래스
public class Money {
private final int value; // 불변
}
❌ Case 3: 유틸리티/계산 클래스
// ❌ @Entity 불필요
public class PriceCalculator {
public int calculate(int price, int discount) {
return price - discount;
}
}
6️⃣ Entity vs DTO vs VO 비교
| 특성 | Entity 🏛️ | DTO 📮 | VO 💎 |
|---|---|---|---|
| DB 매핑 | ✅ | ❌ | ❌ |
| 상태 변경 | ✅ | ✅ | ❌ (불변) |
| 식별자 (@Id) | ✅ | ❌ | ❌ |
| JPA 관리 | ✅ | ❌ | ❌ |
| 목적 | 도메인 모델 | 데이터 전달 | 값 표현 |
| 생명주기 | 영속성 컨텍스트 | 요청/응답 범위 | 사용 시점 |
🎯 언제 무엇을 사용할까?
클라이언트 → [DTO] → Controller → Service → [Entity] ↔ Repository ↔ DB
↓
[VO] (도메인 내부 값)
7️⃣ 실무 표준 템플릿
🌟 완벽한 Entity 클래스 예제
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, length = 50)
private String name;
@Column(unique = true, nullable = false)
private String email;
@Enumerated(EnumType.STRING)
private MemberStatus status;
private LocalDateTime createdAt;
// 정적 팩토리 메서드 (권장)
public static Member create(String name, String email) {
Member member = new Member();
member.name = name;
member.email = email;
member.status = MemberStatus.ACTIVE;
member.createdAt = LocalDateTime.now();
return member;
}
// 도메인 로직
public void changeName(String name) {
if (name == null || name.isBlank()) {
throw new IllegalArgumentException("이름은 필수입니다.");
}
this.name = name;
}
public void deactivate() {
this.status = MemberStatus.INACTIVE;
}
}
✨ 이 템플릿의 장점
| 요소 | 장점 |
|---|---|
@NoArgsConstructor(PROTECTED) |
무분별한 생성 방지 |
@Getter |
불필요한 Setter 제거 |
정적 팩토리 메서드 |
명확한 생성 의도 |
도메인 로직 메서드 |
비즈니스 로직 캡슐화 |
@Column 옵션 |
DB 제약조건 명시 |
8️⃣ 핵심 요약
💡 한 문장 정리
@Entity는 “이 클래스는 DB에 저장되고, JPA가 생명주기를 관리한다”는 선언이다.
📌 기억해야 할 공식
✅ DB 테이블과 1:1 매핑 = @Entity
✅ 조회/저장/수정 대상 = @Entity
❌ 단순 전달용 객체 = @Entity 붙이지 마세요!
🎓 실무 체크리스트
-
기본 생성자 있나요? (
protected권장) -
@Id필드 있나요? -
final클래스 아니죠? - DTO/VO와 혼동하지 않았나요?
- 도메인 로직이 Entity에 있나요?
🚀 다음 학습 주제
-
@Table,@Column상세 옵션 - 연관관계 매핑 (
@OneToMany,@ManyToOne) - 영속성 컨텍스트와 1차 캐시
- 변경 감지 (Dirty Checking) 원리