JPA 이벤트 리스너(Entity Listener)

젼이·2024년 10월 23일

JPA 이벤트 리스너(Entity Listener)는 JPA 엔티티(Entity) 객체의 상태 변화와 관련된 특정 이벤트에 대해 미리 정의된 콜백 메서드를 실행할 수 있게 해주는 기능이다.
이 기능을 통해 엔티티의 생성(Insert), 수정(Update), 삭제(Delete) 등의 라이프사이클 이벤트에 맞춰 추가 로직을 실행하거나 데이터 무결성을 유지하는 데 활용할 수 있다.

콜백 메서드란?

콜백 메서드(Callback Method)는 특정 이벤트가 발생했을 때 자동으로 호출되는 메서드를 의미한다.
즉, 개발자가 직접 호출하지 않아도 시스템이나 프레임워크가 내부적으로 해당 메서드를 실행한다.
콜백 메서드는 주로 이벤트 처리, 라이프사이클 관리, 비동기 작업 등에 사용된다.
JPA에서는 엔티티 객체의 상태 변화에 따라 콜백 메서드가 호출되며, 그 외에도 GUI 프로그래밍, 네트워크 통신, 웹 프레임워크 등 다양한 분야에서 사용된다.

콜백 메서드의 동작 원리

콜백 메서드는 다음과 같은 흐름으로 동작한다.

  1. 시스템(또는 프레임워크)에서 특정 이벤트가 발생한다.
  2. 해당 이벤트에 미리 정의된 콜백 메서드가 자동으로 실행된다.
  3. 콜백 메서드에서는 필요한 작업(예: 로그 남기기, 데이터 수정 등)을 수행한다.

JPA 이벤트 리스너란?

JPA 이벤트 리스너는 엔티티가 특정한 상태 변화 이벤트를 겪을 때 실행되는 콜백 메서드이다. 이 기능은 엔티티에 대한 상태 변화를 감지하고, 그에 따라 부가적인 처리가 필요할 때 유용하다.

주요 이벤트에는 생성(Insert), 수정(Update), 삭제(Delete)와 같은 데이터베이스 조작 뿐만 아니라 영속성 컨텍스트에서의 상태 변화가 포함된다.

영속성 컨텍스트의 상태 변화란?

영속성 컨텍스트(Persistence Context)는 JPA에서 엔티티 객체를 관리하는 일종의 캐시 공간이다. 쉽게 말해, 엔티티 객체와 데이터베이스 사이의 중간 저장소 역할을 한다. 영속성 컨텍스트는 엔티티의 상태 변화를 추적하며, 적절한 시점에 데이터베이스와 동기화한다.

JPA에서 엔티티의 4가지 상태

1. 비영속(Transient)

  • 영속성 컨텍스트에 등록되지 않은 상태
  • 단순히 자바 객체일 뿐, 아직 데이터베이스와 아무런 연관이 없다.
  • 예: new 키워드로 생성한 객체
User user = new User(); // 비영속 상태

2. 영속(Managed)

  • 영속성 컨텍스트에 등록된 상태
  • JPA가 이 객체를 관리하며, 변경된 사항을 감지해 적절한 시점에 데이터베이스에 반영한다.
  • EntityManager.persist()를 호출하면 영속 상태가 된다.
entityManager.persist(user); // 영속 상태로 변경

3. 준영속(Detached)

  • 한때 영속 상태였지만, 영속성 컨텍스트에서 분리된 상태
  • 이 객체는 더 이상 JPA에서 관리하지 않으므로 변경 사항이 데이터베이스에 반영되지 않는다.
  • EntityManager.detach()를 호출하거나, 영속성 컨텍스트가 종료되면 준영속 상태가 된다.
entityManager.detach(user); // 준영속 상태로 변경

4.삭제(Removed)

  • 영속성 컨텍스트에서 삭제된 상태
  • EntityManager.remove()를 호출하면 엔티티가 삭제된다. 데이터베이스에서도 해당 레코드가 삭제될 예정이다.
entityManager.remove(user); // 삭제 상태로 변경




JPA에서 지원하는 주요 콜백 이벤트

JPA에서 정의된 이벤트 콜백 메서드는 다음과 같다.

  • @PrePersist: 엔티티가 저장되기 전 호출 (INSERT 이전)
  • @PostPersist: 엔티티가 저장된 후 호출 (INSERT 이후)
  • @PreUpdate: 엔티티가 업데이트되기 전 호출 (UPDATE 이전)
  • @PostUpdate: 엔티티가 업데이트된 후 호출 (UPDATE 이후)
  • @PreRemove: 엔티티가 삭제되기 전 호출 (DELETE 이전)
  • @PostRemove: 엔티티가 삭제된 후 호출 (DELETE 이후)
  • @PostLoad: 엔티티가 로딩된 후 호출 (SELECT 이후)




이벤트 리스너 구현 방법

이벤트 리스너는 두 가지 방식으로 구현할 수 있다.

  1. 엔티티 내에서 직접 콜백 메서드를 정의하는 방법
  2. 별도의 리스너 클래스를 정의하는 방법

1. 엔티티 내부에서 콜백 메서드 정의

import jakarta.persistence.*;

@Entity
public class User {

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

    private String username;

    private boolean active;

    // 엔티티가 저장되기 전에 실행되는 메서드
    @PrePersist
    public void prePersist() {
        this.active = true;
        System.out.println("PrePersist: 엔티티 저장 전 수행");
    }

    // 엔티티가 저장된 후 실행되는 메서드
    @PostPersist
    public void postPersist() {
        System.out.println("PostPersist: 엔티티 저장 후 수행");
    }

    // 엔티티가 삭제되기 전 실행되는 메서드
    @PreRemove
    public void preRemove() {
        System.out.println("PreRemove: 엔티티 삭제 전 수행");
    }

    // Getter, Setter 생략
}

2. 별도의 리스너 클래스를 정의하는 방법

별도의 리스너 클래스를 정의하여 여러 엔티티에서 공통적으로 사용할 수 있다.

import jakarta.persistence.*;

public class UserEntityListener {

    @PrePersist
    public void prePersist(Object entity) {
        System.out.println("UserEntityListener - PrePersist: " + entity);
    }

    @PostPersist
    public void postPersist(Object entity) {
        System.out.println("UserEntityListener - PostPersist: " + entity);
    }

    @PreRemove
    public void preRemove(Object entity) {
        System.out.println("UserEntityListener - PreRemove: " + entity);
    }
}

엔티티에서 리스너 등록

@Entity
@EntityListeners(UserEntityListener.class) // 리스너 클래스 등록
public class User {

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

    private String username;

    // Getter, Setter 생략
}




이벤트 리스너의 활용 예시

  1. 생성 시간 및 수정 시간 자동 관리
    @PrePersist@PreUpdate를 이용하여 엔티티의 생성 및 수정 시간을 자동으로 기록할 수 있다.
@Entity
public class AuditEntity {

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

    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;

    @PrePersist
    public void prePersist() {
        this.createdAt = LocalDateTime.now();
        this.updatedAt = LocalDateTime.now();
    }

    @PreUpdate
    public void preUpdate() {
        this.updatedAt = LocalDateTime.now();
    }
}
  1. 로그 기록
  2. 데이터 무결성 보장
profile
신입 개발자 임니당 : > (2025.02.05~)

0개의 댓글