Home > Backend Development > πŸ“š[Backend Development] πŸš€ @EntityListeners

πŸ“š[Backend Development] πŸš€ @EntityListeners
Backend Development Server Java Lombok

🎧 JPA @EntityListeners μ™„λ²½ κ°€μ΄λ“œ

Entity 생λͺ…μ£ΌκΈ°λ₯Ό κ°μ‹œν•˜λŠ” λ˜‘λ˜‘ν•œ λ¦¬μŠ€λ„ˆ! 데이터 μ €μž₯ 도ꡬλ₯Ό 도메인 이벀트 μ‹œμŠ€ν…œμœΌλ‘œ μ—…κ·Έλ ˆμ΄λ“œν•˜μ„Έμš”.


πŸ“‹ λͺ©μ°¨

  1. @EntityListenersκ°€ λ­”κ°€μš”?
  2. 감지 κ°€λŠ₯ν•œ 7κ°€μ§€ 생λͺ…μ£ΌκΈ° 이벀트
  3. μ–Έμ œ μ‚¬μš©ν•΄μ•Ό ν• κΉŒ?
  4. Entity λ‚΄λΆ€ vs μ™ΈλΆ€ Listener
  5. 싀무 ν‘œμ€€ νŒ¨ν„΄
  6. λ°˜λ“œμ‹œ μ•Œμ•„μ•Ό ν•  μ£Όμ˜μ‚¬ν•­
  7. @EntityListeners vs Spring Event
  8. 핡심 μš”μ•½

1️⃣ @EntityListenersκ°€ λ­”κ°€μš”?

@EntityListenersλŠ” JPA Entity의 생λͺ…μ£ΌκΈ° 이벀트λ₯Ό κ°€λ‘œμ±„μ„œ νŠΉμ • λ‘œμ§μ„ μžλ™μœΌλ‘œ μ‹€ν–‰ν•˜κ²Œ ν•΄μ£ΌλŠ” κ°•λ ₯ν•œ μ–΄λ…Έν…Œμ΄μ…˜μž…λ‹ˆλ‹€.

🎯 ν•œ λ¬Έμž₯ μ •μ˜

β€œμ΄ Entityκ°€ μ €μž₯Β·μˆ˜μ •Β·μ‚­μ œλ  λ•Œ, 이 클래슀의 λ©”μ„œλ“œλ₯Ό μžλ™μœΌλ‘œ ν˜ΈμΆœν•΄μ€˜!”

πŸ’‘ κΈ°λ³Έ ν˜•νƒœ

@Entity
@EntityListeners(AuditingEntityListener.class)
public class Member {
    // 이제 이 EntityλŠ” κ°μ‹œ λŒ€μƒ! πŸ‘€
}

πŸ”„ λ™μž‘ 원리

Entity λ³€κ²½ β†’ JPAκ°€ 감지 β†’ Listener 호좜 β†’ μžλ™ μ‹€ν–‰

μ˜ˆμ‹œ:

  1. member.save() 호좜
  2. JPA: β€œμž κΉ! Memberκ°€ μ €μž₯되렀고 ν•΄!”
  3. Listener: β€œμ €μž₯ μ „ μž‘μ—… μ‹€ν–‰!”
  4. μ‹€μ œ INSERT 쿼리 μ‹€ν–‰

2️⃣ 감지 κ°€λŠ₯ν•œ 7κ°€μ§€ 생λͺ…μ£ΌκΈ° 이벀트

JPAλŠ” Entity의 전체 생λͺ…μ£ΌκΈ°λ₯Ό 7개 μ‹œμ μœΌλ‘œ λ‚˜λˆ μ„œ κ°μ‹œν•©λ‹ˆλ‹€.

이벀트 μ–΄λ…Έν…Œμ΄μ…˜ μ‹€ν–‰ μ‹œμ  μ£Όμš” μš©λ„
πŸ’Ύ μ €μž₯ μ „ @PrePersist INSERT μ „ 생성일 μ„€μ •, μ΄ˆκΈ°κ°’ μ„ΈνŒ…
βœ… μ €μž₯ ν›„ @PostPersist INSERT ν›„ 둜그 기둝, 이벀트 λ°œν–‰
✏️ μˆ˜μ • μ „ @PreUpdate UPDATE μ „ μˆ˜μ •μΌ κ°±μ‹ , 검증
βœ… μˆ˜μ • ν›„ @PostUpdate UPDATE ν›„ λ³€κ²½ 이λ ₯ μ €μž₯
πŸ—‘οΈ μ‚­μ œ μ „ @PreRemove DELETE μ „ μ—°κ΄€ 데이터 정리
βœ… μ‚­μ œ ν›„ @PostRemove DELETE ν›„ μΊμ‹œ μ‚­μ œ, 둜그
πŸ“– λ‘œλ”© ν›„ @PostLoad SELECT ν›„ 데이터 ν›„μ²˜λ¦¬

πŸ“Š 생λͺ…μ£ΌκΈ° νƒ€μž„λΌμΈ

CREATE: @PrePersist β†’ [INSERT] β†’ @PostPersist
UPDATE: @PreUpdate β†’ [UPDATE] β†’ @PostUpdate  
DELETE: @PreRemove β†’ [DELETE] β†’ @PostRemove
READ:   [SELECT] β†’ @PostLoad

3️⃣ μ–Έμ œ μ‚¬μš©ν•΄μ•Ό ν• κΉŒ?

βœ… Case 1: 생성일/μˆ˜μ •μΌ μžλ™ 관리 ⭐ (κ°€μž₯ λŒ€ν‘œμ !)

@Entity
@EntityListeners(AuditingEntityListener.class)
public class Post {

    @CreatedDate
    @Column(updatable = false)
    private LocalDateTime createdAt;

    @LastModifiedDate
    private LocalDateTime updatedAt;
    
    @CreatedBy
    @Column(updatable = false)
    private String createdBy;
    
    @LastModifiedBy
    private String lastModifiedBy;
}

μž₯점:

  • βœ… μžλ™μœΌλ‘œ μ‹œκ°„ 기둝
  • βœ… κ°œλ°œμžκ°€ μ‹ κ²½ μ“Έ ν•„μš” μ—†μŒ
  • βœ… 휴먼 μ—λŸ¬ λ°©μ§€

πŸ’‘ ν•„μˆ˜ μ„€μ •: @EnableJpaAuditing을 μ„€μ • ν΄λž˜μŠ€μ— μΆ”κ°€ν•΄μ•Ό μž‘λ™!


βœ… Case 2: 곡톡 둜직 뢄리

public class ValidationListener {
    
    @PrePersist
    @PreUpdate
    public void validate(Object entity) {
        if (entity instanceof Validatable) {
            ((Validatable) entity).validate();
        }
    }
}

적용 λŒ€μƒ:

  • πŸ“ 둜그 기둝
  • πŸ” 감사(Audit) 좔적
  • βœ… μƒνƒœ 검증
  • πŸ“Š 톡계 μˆ˜μ§‘

βœ… Case 3: 데이터 보정/μ •κ·œν™”

public class DataNormalizationListener {
    
    @PrePersist
    @PreUpdate
    public void normalize(Object entity) {
        if (entity instanceof Member) {
            Member member = (Member) entity;
            // 이메일 μ†Œλ¬Έμž λ³€ν™˜
            member.normalizeEmail();
            // μ „ν™”λ²ˆν˜Έ ν˜•μ‹ 톡일
            member.normalizePhoneNumber();
        }
    }
}

ν™œμš© μ˜ˆμ‹œ:

  • πŸ“§ 이메일 μ†Œλ¬Έμž λ³€ν™˜
  • πŸ“± μ „ν™”λ²ˆν˜Έ ν¬λ§·νŒ…
  • βœ‚οΈ λ¬Έμžμ—΄ trim
  • πŸ”€ λŒ€μ†Œλ¬Έμž μ •κ·œν™”

βœ… Case 4: Entity μ±…μž„ 뢄리

❌ Before: Entity에 λͺ¨λ“  둜직

@Entity
public class Member {
    private LocalDateTime updatedAt;
    
    public void update(String name) {
        this.name = name;
        this.updatedAt = LocalDateTime.now();  // 😰 맀번 직접 μ„€μ •
    }
}

βœ… After: Listener둜 뢄리

@Entity
@EntityListeners(AuditingEntityListener.class)
public class Member {
    @LastModifiedDate
    private LocalDateTime updatedAt;  // πŸŽ‰ μžλ™μœΌλ‘œ 섀정됨!
    
    public void update(String name) {
        this.name = name;  // λΉ„μ¦ˆλ‹ˆμŠ€ λ‘œμ§μ—λ§Œ 집쀑
    }
}

이점:

  • 🎯 관심사 뢄리 (Separation of Concerns)
  • πŸ§ͺ ν…ŒμŠ€νŠΈ μš©μ΄μ„± ν–₯상
  • πŸ“ μ½”λ“œ 가독성 κ°œμ„ 

4️⃣ Entity λ‚΄λΆ€ vs μ™ΈλΆ€ Listener

πŸ”Ή 방식 1: Entity 내뢀에 직접 μž‘μ„±

@Entity
public class Member {
    
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;

    @PrePersist
    public void onCreate() {
        this.createdAt = LocalDateTime.now();
        this.updatedAt = LocalDateTime.now();
    }
    
    @PreUpdate
    public void onUpdate() {
        this.updatedAt = LocalDateTime.now();
    }
}
μž₯점 βœ… 단점 ❌
κ°„λ‹¨ν•˜κ³  직관적 Entity μ±…μž„ 증가
별도 클래슀 λΆˆν•„μš” μž¬μ‚¬μš© λΆˆκ°€λŠ₯
λΉ λ₯Έ κ΅¬ν˜„ ν…ŒμŠ€νŠΈ 볡작

πŸ”Ή 방식 2: μ™ΈλΆ€ Listener 클래슀 (✨ ꢌμž₯)

// Listener 클래슀
public class AuditListener {
    
    @PrePersist
    public void prePersist(Object entity) {
        System.out.println("μ €μž₯ 직전: " + entity);
    }

    @PreUpdate
    public void preUpdate(Object entity) {
        System.out.println("μˆ˜μ • 직전: " + entity);
    }
}

// Entity
@Entity
@EntityListeners(AuditListener.class)
public class Member {
    // 깔끔! 🎨
}
μž₯점 βœ… 단점 ❌
관심사 μ™„λ²½ 뢄리 클래슀 μΆ”κ°€ ν•„μš”
μ—¬λŸ¬ Entityμ—μ„œ μž¬μ‚¬μš© 초기 μ„€μ • ν•„μš”
ν…ŒμŠ€νŠΈ 독립적 -
μœ μ§€λ³΄μˆ˜ 쉬움 -

πŸ† 선택 κ°€μ΄λ“œ

κ°„λ‹¨ν•œ 둜직 (1-2쀄) β†’ Entity λ‚΄λΆ€ λ©”μ„œλ“œ
λ³΅μž‘ν•œ 둜직 / μž¬μ‚¬μš© β†’ μ™ΈλΆ€ Listener 클래슀 ⭐

5️⃣ 싀무 ν‘œμ€€ νŒ¨ν„΄

🌟 BaseTimeEntity νŒ¨ν„΄ (좔상 클래슀 ν™œμš©)

@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseTimeEntity {

    @CreatedDate
    @Column(updatable = false)
    private LocalDateTime createdAt;

    @LastModifiedDate
    private LocalDateTime updatedAt;
}

🎯 μ‹€μ œ Entityμ—μ„œ 상속

@Entity
public class Post extends BaseTimeEntity {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String title;
    private String content;
    
    // createdAt, updatedAt은 μžλ™ 관리됨! πŸŽ‰
}

@Entity
public class Comment extends BaseTimeEntity {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String content;
    
    // createdAt, updatedAt은 μžλ™ 관리됨! πŸŽ‰
}

✨ κ³ κΈ‰ νŒ¨ν„΄: μƒμ„±μž/μˆ˜μ •μž 좔적

@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseEntity {

    @CreatedDate
    @Column(updatable = false)
    private LocalDateTime createdAt;

    @LastModifiedDate
    private LocalDateTime updatedAt;

    @CreatedBy
    @Column(updatable = false)
    private String createdBy;

    @LastModifiedBy
    private String lastModifiedBy;
}

πŸ”§ AuditorAware μ„€μ • (μƒμ„±μž/μˆ˜μ •μž μžλ™ μ£Όμž…)

@Configuration
@EnableJpaAuditing
public class JpaAuditingConfig {

    @Bean
    public AuditorAware<String> auditorProvider() {
        return () -> {
            // SecurityContextμ—μ„œ ν˜„μž¬ μ‚¬μš©μž κ°€μ Έμ˜€κΈ°
            Authentication authentication = 
                SecurityContextHolder.getContext().getAuthentication();
            
            if (authentication == null || !authentication.isAuthenticated()) {
                return Optional.of("system");
            }
            
            return Optional.of(authentication.getName());
        };
    }
}

6️⃣ λ°˜λ“œμ‹œ μ•Œμ•„μ•Ό ν•  μ£Όμ˜μ‚¬ν•­

⚠️ 주의 1: λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 λ„£μ§€ 말 것! (맀우 μ€‘μš”)

❌ 잘λͺ»λœ μ‚¬μš©

public class PaymentListener {
    
    @PostPersist
    public void processPayment(Order order) {
        // 결제 처리 ❌
        paymentService.charge(order);
        
        // μ•Œλ¦Ό λ°œμ†‘ ❌
        notificationService.sendEmail(order);
        
        // μ™ΈλΆ€ API 호좜 ❌
        externalApi.syncOrder(order);
    }
}

문제점:

  • πŸ”₯ νŠΈλžœμž­μ…˜ 경계가 뢈λͺ…ν™•
  • πŸ”₯ μ‹€νŒ¨ μ‹œ λ‘€λ°± 처리 볡작
  • πŸ”₯ μˆœμ„œ 예츑 λΆˆκ°€λŠ₯

βœ… μ˜¬λ°”λ₯Έ μ‚¬μš©

public class AuditListener {
    
    @PrePersist
    public void setCreatedDate(Object entity) {
        // λ‹¨μˆœν•œ κ°’ μ„€μ •λ§Œ βœ…
        if (entity instanceof BaseTimeEntity) {
            ((BaseTimeEntity) entity).setCreatedAt(LocalDateTime.now());
        }
    }
}

⚠️ 주의 2: Repository/Service μ£Όμž… λΆˆκ°€ (κΈ°λ³Έ)

❌ μž‘λ™ν•˜μ§€ μ•ŠμŒ

public class BadListener {
    
    @Autowired  // ❌ μ£Όμž… μ•ˆ 됨!
    private MemberRepository memberRepository;
    
    @PrePersist
    public void doSomething(Object entity) {
        memberRepository.save(...);  // NullPointerException!
    }
}

이유: ListenerλŠ” 기본적으둜 Spring Bean이 μ•„λ‹˜!

βœ… ν•΄κ²° 방법 1: Spring Bean으둜 등둝

@Component
public class SpringBeanListener {
    
    @Autowired
    private MemberRepository memberRepository;
    
    @PrePersist
    public void doSomething(Object entity) {
        // 이제 μž‘λ™ν•¨!
    }
}

βœ… ν•΄κ²° 방법 2: ApplicationContext ν™œμš©

public class SmartListener {
    
    @PrePersist
    public void doSomething(Object entity) {
        ApplicationContext context = 
            BeanUtil.getApplicationContext();
        MemberRepository repo = 
            context.getBean(MemberRepository.class);
        // μ‚¬μš©
    }
}

🎯 더 쒋은 방법: Spring Event μ‚¬μš©

// ListenerλŠ” λ‹¨μˆœν•˜κ²Œ
@PostPersist
public void onCreated(Member member) {
    ApplicationEventPublisher publisher = 
        BeanUtil.getBean(ApplicationEventPublisher.class);
    publisher.publishEvent(new MemberCreatedEvent(member));
}

// 별도 EventHandlerμ—μ„œ 처리
@EventListener
public void handleMemberCreated(MemberCreatedEvent event) {
    // μ—¬κΈ°μ„œ Repository, Service 자유둭게 μ‚¬μš©
    notificationService.sendWelcomeEmail(event.getMember());
}

⚠️ 주의 3: μ˜ˆμ™Έ λ°œμƒ μ‹œ 전체 νŠΈλžœμž­μ…˜ μ‹€νŒ¨

@PrePersist
public void validate(Member member) {
    if (member.getAge() < 0) {
        throw new IllegalArgumentException("λ‚˜μ΄λŠ” 음수일 수 μ—†μŠ΅λ‹ˆλ‹€");
        // β†’ 전체 INSERT μ·¨μ†Œ! 🚨
    }
}

κ²°κ³Ό:

  • μ˜ˆμ™Έ λ°œμƒ β†’ DB μ €μž₯ μ‹€νŒ¨
  • νŠΈλžœμž­μ…˜ λ‘€λ°±
  • 데이터 일관성 μœ μ§€

πŸ’‘ Tip: 검증은 가급적 Service λ ˆμ΄μ–΄μ—μ„œ!


⚠️ 주의 4: Cascade μ—°μ‚°κ³Όμ˜ μƒν˜Έμž‘μš©

@Entity
@EntityListeners(LoggingListener.class)
public class Order {
    
    @OneToMany(cascade = CascadeType.ALL)
    private List<OrderItem> items;
}

@Entity
@EntityListeners(LoggingListener.class)
public class OrderItem {
}

주의: Order μ €μž₯ μ‹œ OrderItem의 Listener도 λͺ¨λ‘ 싀행됨!


7️⃣ @EntityListeners vs Spring Event

μ–Έμ œ 무엇을 μ‚¬μš©ν•΄μ•Ό ν• κΉŒμš”?

비ꡐ ν•­λͺ© @EntityListeners 🎧 Spring Event πŸ“’
호좜 μ‹œμ  DB νŠΈλžœμž­μ…˜ μ „/ν›„ λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 흐름
μ£Όμš” μš©λ„ 감사, 데이터 보정 μ•Œλ¦Ό, μ™ΈλΆ€ 연동
μ˜μ‘΄μ„± μ£Όμž… λ³΅μž‘ν•¨ (κΈ°λ³Έ λΆˆκ°€) μžμœ λ‘œμ›€
μ™ΈλΆ€ μ‹œμŠ€ν…œ ❌ λΉ„μΆ”μ²œ βœ… μΆ”μ²œ
νŠΈλžœμž­μ…˜ DB νŠΈλžœμž­μ…˜ λ‚΄λΆ€ 별도 μ œμ–΄ κ°€λŠ₯
μ‹€ν–‰ μˆœμ„œ JPAκ°€ κ²°μ • λͺ…μ‹œμ  μ œμ–΄
μ„±λŠ₯ 빠름 μƒλŒ€μ μœΌλ‘œ 느림

🎯 선택 κ°€μ΄λ“œ

// EntityListeners μ‚¬μš© βœ…
- 생성일/μˆ˜μ •μΌ μžλ™ 기둝
- 데이터 μ •κ·œν™” (trim, μ†Œλ¬Έμž λ“±)
- κ°„λ‹¨ν•œ 감사 둜그

// Spring Event μ‚¬μš© βœ…
- 이메일/ν‘Έμ‹œ μ•Œλ¦Ό λ°œμ†‘
- μ™ΈλΆ€ API 연동
- λ³΅μž‘ν•œ λΉ„μ¦ˆλ‹ˆμŠ€ 둜직
- μ—¬λŸ¬ μ„œλΉ„μŠ€ 호좜

πŸ’‘ ν•¨κ»˜ μ‚¬μš©ν•˜κΈ°

// 1단계: EntityListenerμ—μ„œ 이벀트 λ°œν–‰
@Component
public class MemberEventListener {
    
    @Autowired
    private ApplicationEventPublisher eventPublisher;
    
    @PostPersist
    public void onMemberCreated(Member member) {
        eventPublisher.publishEvent(new MemberCreatedEvent(member));
    }
}

// 2단계: EventHandlerμ—μ„œ λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 처리
@Component
public class MemberEventHandler {
    
    @Autowired
    private EmailService emailService;
    
    @EventListener
    @Async
    public void handleMemberCreated(MemberCreatedEvent event) {
        emailService.sendWelcomeEmail(event.getMember());
    }
}

8️⃣ 핡심 μš”μ•½

πŸ’‘ ν•œ λ¬Έμž₯ 정리

@EntityListenersλŠ” Entity 생λͺ…주기에 μžλ™μœΌλ‘œ λ°˜μ‘ν•˜λŠ” 후크(Hook)λ‹€.

πŸ“Œ κΈ°μ–΅ 곡식

βœ… 생성일/μˆ˜μ •μΌ μžλ™ 기둝 = @EntityListeners + Auditing
βœ… 데이터 보정/검증 = @EntityListeners
❌ λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 = Spring Event μ‚¬μš©
❌ μ™ΈλΆ€ API 호좜 = Spring Event μ‚¬μš©

πŸŽ“ 싀무 체크리슀트

κΈ°λ³Έ μ„€μ •

  • @EnableJpaAuditing μ„€μ • μ™„λ£Œ
  • BaseTimeEntity 좔상 클래슀 생성
  • λͺ¨λ“  Entityκ°€ BaseTimeEntity 상속

κ³ κΈ‰ μ„€μ •

  • AuditorAware κ΅¬ν˜„ (μƒμ„±μž/μˆ˜μ •μž 좔적)
  • μ»€μŠ€ν…€ Listener 뢄리
  • Spring Event와 μ—­ν•  λΆ„λ‹΄

μ£Όμ˜μ‚¬ν•­

  • Listener에 λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 μ—†μŒ
  • μ™ΈλΆ€ API 호좜 μ—†μŒ
  • DIκ°€ ν•„μš”ν•˜λ©΄ Spring Bean으둜 등둝
  • μ˜ˆμ™Έ 처리 μ „λž΅ 수립

πŸš€ μ‹€μ „ 예제: μ™„λ²½ν•œ ꡬ성

1️⃣ κΈ°λ³Έ Entity

@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseEntity {

    @CreatedDate
    @Column(updatable = false)
    private LocalDateTime createdAt;

    @LastModifiedDate
    private LocalDateTime updatedAt;

    @CreatedBy
    @Column(updatable = false)
    private String createdBy;

    @LastModifiedBy
    private String lastModifiedBy;
}

2️⃣ μ„€μ • 클래슀

@Configuration
@EnableJpaAuditing
public class JpaConfig {

    @Bean
    public AuditorAware<String> auditorProvider() {
        return () -> Optional.ofNullable(
            SecurityContextHolder.getContext()
                .getAuthentication()
                .getName()
        );
    }
}

3️⃣ μ‹€μ œ Entity

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Post extends BaseEntity {

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

    @Column(nullable = false)
    private String title;

    @Column(nullable = false, columnDefinition = "TEXT")
    private String content;

    @Enumerated(EnumType.STRING)
    private PostStatus status;

    public static Post create(String title, String content) {
        Post post = new Post();
        post.title = title;
        post.content = content;
        post.status = PostStatus.DRAFT;
        return post;
        // createdAt, updatedAt, createdBy, lastModifiedByλŠ” μžλ™ μ„€μ •! πŸŽ‰
    }

    public void publish() {
        this.status = PostStatus.PUBLISHED;
        // updatedAt, lastModifiedByλŠ” μžλ™ κ°±μ‹ ! πŸŽ‰
    }
}

🎯 λ‹€μŒ ν•™μŠ΅ 주제

  • Spring Event 심화 (@EventListener, @Async)
  • JPA Auditing κ³ κΈ‰ μ„€μ •
  • @Embedded와 @Embeddable
  • Soft Delete νŒ¨ν„΄ κ΅¬ν˜„
  • λ³€κ²½ 이λ ₯ 좔적 μ‹œμŠ€ν…œ ꡬ좕

πŸ“š μ°Έκ³  자료

곡식 λ¬Έμ„œ

κ΄€λ ¨ μ–΄λ…Έν…Œμ΄μ…˜

  • @MappedSuperclass - 곡톡 λ§€ν•‘ 정보 상속
  • @EnableJpaAuditing - Auditing κΈ°λŠ₯ ν™œμ„±ν™”
  • @CreatedDate, @LastModifiedDate - μ‹œκ°„ μžλ™ 관리
  • @CreatedBy, @LastModifiedBy - μ‚¬μš©μž μžλ™ 관리

πŸ’¬ FAQ

Q1. @EntityListenersλ₯Ό μ—¬λŸ¬ 개 등둝할 수 μžˆλ‚˜μš”? λ„€! λ°°μ—΄λ‘œ μ—¬λŸ¬ Listenerλ₯Ό 등둝할 수 μžˆμŠ΅λ‹ˆλ‹€. ```java @Entity @EntityListeners({ AuditingEntityListener.class, ValidationListener.class, LoggingListener.class }) public class Member { } ``` μ‹€ν–‰ μˆœμ„œλŠ” λ°°μ—΄ μˆœμ„œλŒ€λ‘œμž…λ‹ˆλ‹€.
Q2. Listenerμ—μ„œ λ‹€λ₯Έ Entityλ₯Ό μ‘°νšŒν•  수 μžˆλ‚˜μš”? κ°€λŠ₯ν•˜μ§€λ§Œ 신쀑해야 ν•©λ‹ˆλ‹€. ```java @Component public class RelationListener { @Autowired private EntityManager em; @PrePersist public void loadRelation(Order order) { // κ°€λŠ₯ν•˜μ§€λ§Œ N+1 문제 주의! Member member = em.find(Member.class, order.getMemberId()); } } ``` μ„±λŠ₯ μ΄μŠˆκ°€ λ°œμƒν•  수 μžˆμœΌλ―€λ‘œ κΌ­ ν•„μš”ν•œ 경우만 μ‚¬μš©ν•˜μ„Έμš”.
Q3. @PostLoadλŠ” μ–Έμ œ μ‚¬μš©ν•˜λ‚˜μš”? 주둜 데이터 ν›„μ²˜λ¦¬λ‚˜ 캐싱에 μ‚¬μš©ν•©λ‹ˆλ‹€. ```java @PostLoad public void decrypt(Member member) { // μ•”ν˜Έν™”λœ 데이터 λ³΅ν˜Έν™” member.decryptSensitiveData(); } ``` ν•˜μ§€λ§Œ μ„±λŠ₯에 영ν–₯을 쀄 수 μžˆμœΌλ―€λ‘œ μ‹ μ€‘ν•˜κ²Œ μ‚¬μš©ν•˜μ„Έμš”.