be_better

[Springboot] Spring Data JPA 활용 - JPA Auditing 본문

Springboot/이론

[Springboot] Spring Data JPA 활용 - JPA Auditing

NiceTry 2023. 11. 12. 21:44

목차

    JPA Auditing 적용

    JPA에서 'Audit'이란 '감시하다'라는 뜻으로, 각 데이터마다 '누가', '언제' 데이터를 생성했고 변경했는지 감시한다는 의미로 사용된다. 앞에서 작성한 코드를 보면 알 수 있듯 엔티티 클래스에는 공통적으로 '생성 일자'와 '변경 일자' 필드가 들어간다.

     

    대표적으로 많이 사용되는 필드는 다음과 같다.

    • 생성 주체
    • 생성 일자
    • 변경 주체
    • 변경 일자

    이러한 필드들은 매번 엔티티를 생성하거나 변경할 때마다 값을 주입해야 하는 번거로움이 있는데 이 같은 번거로움해소하기 위해 Spring Data JPA에서는 이러한 값을 자동으로 넣어주는 기능을 제공한다.

     

    JPA Auditing 기능 활성화

    먼저 스프링 부트 애플리케이션에 Auditing 기능을 활성화해야 한다.

     

    main() 메서드가 있는 클래스에 @EnableJpaAuditing 어노테이션을 추가하는 방법이 있지만 테스트 코드를 작성할 때 @WebMvcTest 어노테이션을 지정해서 테스트를 수행하는 코드를 작성하면 애플리케이션 클래스를 호출하는 과정에서 예외가 발생할 수 있다.

     

    이 같은 문제를 해결하기 위해서는 별도의 Configuration 클래스를 생성해서 애플리케이션 클래스의 기능과 분리해서 활성화 해야 한다.

    @Configuration
    @EnableJpaAuditing
    public class JpaAuditingConfiguration {
        
    }

     

    BaseEntity 만들기

    코드의 중복을 없애기 위해서는 각 엔티티에 공통으로 들어가게 되는 칼럼을 하나의 클래스로 빼는 작업을 수행해야 한다. 이 작업은 반드시 그래야 하는 것은 아니지만 자주 활용되는 기법이므로 참고해둘 필요가 있다.

     

    생성 일자와 변경 일자만 추가한 BaseEntity 생성
    @Getter
    @Setter
    @ToString
    @MappedSuperclass
    @EntityListeners(AuditingEntityListener.class)
    public class BaseEntity {
        
        @CreatedDate
        @Column(updatable = false)
        private LocalDateTime createdAt;
        
        @LastModifiedDate
        private LocalDateTime updatedAt;
    }

    여기서 사용한 주요 어노테이션은 다음과 같다.

    • @MappedSuperclass : JPA의 엔티티 클래스가 상속받을 경우 자식 클래스에게 매핑 정보를 전달
    • @EntityListeners : 엔티티를 데이터베이스에 적용하기 전후로 콜백을 요청할 수 있게 하는 어노테이션
    • AuditingEntityListener : 엔티티의 Auditing 정보를 주입하는 JPA 엔티티 리스너 클래스
    • @CreatedDate : 데이터 생성 날짜를 자동으로 주입하는 어노테이션
    • @LastModifiedDate : 데이터 수정 날짜를 자동으로 주입하는 어노테이션

    위와 같이 BaseEntity를 생성하고 Product 엔티티 클래스에서 공통 칼럼을 제거해서 다음과 같이 코드를 수정한다.

    @Entity
    @Getter
    @Setter
    @NoArgsConstructor
    @ToString(callSuper = true)
    @EqualsAndHashCode(callSuper = true)
    @Table(name = "product")
    public class Product extends BaseEntity {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long number;
    
        @Column(nullable = false)
        private String name;
    
        @Column(nullable = false)
        private Integer price;
    
        @Column(nullable = false)
        private Integer stock;
        
    }

    이렇게 설정하면 기존에 테스트했던 것처럼 매번 LocalDateTime.now() 메서드를 사용해 시간을 주입하지 않아도 자동으로 값이 생성되는 것을 볼 수 있다.

    @SpringBootTest
    public class JpaAuditingTest {
    
        @Autowired
        ProductRepository productRepository;
    
        @Test
        void test() {
            Product product = new Product();
            product.setName("펜");
            product.setPrice(1000);
            product.setStock(100);
    
            Product savedProduct = productRepository.save(product);
    
            System.out.println("productName : " + savedProduct.getName());
            System.out.println("createdAt : " + savedProduct.getCreatedAt());
        }
    }

    출력 결과는 다음과 같다.