[Spring] spring-data-envers

한호성·2023년 3월 15일
1

Introduction

입사하고 맡은 프로젝트에서, 메인 서비스가 계약서에 히스토리를 남기는 것이였습니다. 이 때에는,
spring-data-enver라는 hibernate가 구현해주는 좋은 라이브러리를.. 알지 못해 조잡하게 DB Schema
를 관리하여서 작동하도록 만들었습니다. 개인 공부를 하면서, 여러 자료들을 읽다.. 이런 좋은것이
있는지 알게되어서 공부한 글입니다. 만약 db의 history를 저장해야하는 상황에 한번씩 생각해보면
좋을거 같습니다. 물론.. 데이터 traffic이 많은 곳에서는 RDB를 쓰는것이 불가능해서 다른방식으로
구현해야하겠지만, RDB를 사용하는 환경이라면 충분히 고려해볼만한 프로젝트라 생각됩니다.
[목차]

  • Spring Data Enver
  • Spring Data Enver 직접 사용해보기
  • 나의 생각

Spring Data Enver

Spring Data Enver 란?

Spring Framework에서 제공하는 확장 기는 중 하나로 Hibernate Envers 라이브러리를 사용하여, 데이터 변경 이력을 관리하는 기능을 제공합니다.

  • 엔티티의 변경이력 자동 관리
  • 이력조회, 버전별 조회 다양한 기능을 제공한다.
  • Spring Data JPA와 같이 사용할 수 있어서, JPA에서 제공하는 기능을 함께 사용할 수 있다.

우선 Gradle 기준

implementation 
'org.springframework.data:spring-data-envers'

해서 사용해주시면되겠습니다.

이렇게 정의해놓고 보면 정확하게 어떻게 쓰이는지 이해가 안될 수 있으니, Table을 보면서 이야기 해보겠습니다.

Spring Data Enver 사용해보기

계약이라는 Entity Class를 확인해보겠습니다. 아래의 사진에서 @Audited annotation을 추가해주시면,Entity가 어떻게 형성되는지 보겠습니다.

아래처럼 cotract_history라는 history table이 JPA 자동생성에 의해 생성됩니다.
audit 하고 싶지않은 field 의 경우 @Auadited를 통해 사용 가능합니다.
( History table의 이름은 설정을 통해 설정 가능합니다)

위의 사진을 보면 revision_id, revision_type이 존재하는 것을 알 수 있습니다. revision_id를 통해, 해당 contract의 hisotry version을 파악할 수 있고 revision_type 같은 경우에는 0(insert), 1(update), 1(delete)를 의미합니다.

파랑색 부분을 보시면 해당 필드가 변경되었는지 true or false로 나타낼수 있습니다. 이 때에는 entity에서 @Audited(withModifiedFlag = true) annotation을 붙여주시면 됩니다

#cf) 여기서 중요한점은 Transaction 단위로 revision을 관리한다는 것입니다. 아주 큰 장점으로 생각되는게, 한 Transaction에서 변경된 내용을 한번에 파악이 가능하기 때문입니다.

contract insert를 하게되면 자연스럽게 contract_hisotry에 insert 되는것을 test를 통해 확인해볼 수 있습니다.

이제부터는 실제 사용법에 대해 이야기해보겠습니다. 기존에 Spring Data JPA에서 사용하는 repository에 붙여서 사용할 수 있습니다. 보겠습니다.

RevisionRepository를 상속받아 사용하는것을 확인할 수 있습니다. RevisionRepository에는 어떤 함수들이 존재할까요? 알아보겠습니다.

4가지의 함수가 존재하는것을 확인할 수 있고, 각 함수의 함수명,Parameter를 보면 사용을 어떻게 하는지 알 수 있을 것입니다.
(공식문서를 참고해주세요.)

만약 검색조건을 만들고, 여러가지 동적쿼리를 만들고 싶다 하시면,org.hibernate.envers 에서 제공하는 AuditReader를 통해 동적쿼리를 만들어내실수 있습니다. 한번 AuditReader Bean 생성부터 사용까지 알아보겠습니다.

#cf) 여기서 초기에는 AuditReader Bean등록시, EntityManagerFactory가 아닌 entityManager를 DI받아서 사용하였습니다. 그렇게 만들어진 AuditReader를 사용할 때, EntityManager is closed 에러가 떳었고, 이는 EntityManager와 Factory의 관계를 이해하지 못해서 낸 오류라고 생각합니다. EntityManager는 Thread-safe가 존재하지 않고, 각각의 영속성 컨텍스트를 만들고, 하나의 Transcation을 관리합니다. 그에 반해 EntityManagerFactory는 Thread-safe하고, 하나의 어플리케이션에 하나만 존재해도 됩니다. 그렇기 때문에 EntityManagerFactory를 통해, entityManager를 만들어서 auditReader를 bean에 등록해야합니다. (사실 이 부분에 대해서 아직 모호한 부분이 있습니다.. 좀 더 알아보도록 하겠습니다.)

1.검색조건을 주어서, query 날리기

2. 해당 Entity에서 수정된 부분만 얻어오는 query 날리기

3. 해당 transaction에서 변경이력이 있는, 데이터 모두 가져오기

이 방식을 사용하기 위해선 .yml , .properties에서 변경이 필요합니다

  jpa:
    hibernate:
      ddl-auto: update
    generate-ddl: true
    show-sql: true
    properties:
      org:
        hibernate:
          envers:
            track_entities_changed_in_revision: true //해당 transaction 변경이력 있는 데이터 불러오기
            audit_table_suffix: _history //audit table 이름설정
            revision_field_name: revision_id // field명 정의
            revision_type_field_name: revision_type // field명 정의

3.기능을 사용하기 위해서


jpa 가 해당 table을 만들고, rev_id에 변경된 도메인값들을 저장합니다.

나의 생각

우선 비지니스관점에서 history를 관리하는 요구를 충분히 개발중에 받을 수 있을거라 생각합니다. 또한, JPA특성상, 데이터를 엎어치면서 이전기록을 갖을수 없고, 관리하기 위해서 같은 TABLE을 직접만드는 경우도 있을텐데, 이 때 사용하는것은 바람직하다고 생각합니다. 하지만.. 변경이력을 저장하는 과정, 불러오는 과정 모두 Resource가 많이 투입되는 것 같습니다. 만약 많은 데이터 transcation이 존재한다면, 서버에 무리가 갈것으로 생각되고.. (물론 RDB 만으로 안되는 경우엔, enver를 사용할 경우는 없을것이지만) 특히 위의 3번기능을 사용할 때는, 충분히 성능이슈가 있을것으로 생각합니다. 현재 비지니스 상황에 맞춰서, 적합하게 사용하는것이 좋을것으로 보입니다. rdb로 관리 +히스토리까지 관리 해야하는 경우 enver를 사용하면 좋을것으로 보입니다. 또, 이 방식을 봤으니, 스스로 만들어서 사용해볼수도 있겠지요..(있는걸 사용합시다..)

정리

정리하자면, 저의 생각은 RDB를 사용하는 경우 -> 이미 성능상 RDB를 사용하는것을 택했기 때문에, History를 관리한다면 enver를 사용하는 것이 생산성 측면에서 더 좋다고 생각합니다. but 3번기능의 성능이슈는 한번 체크를 해보고 사용하는것이 좋겠습니다. entity를 index없이 많이 돌아다니는것으로 알고 있어서 조회자체에 시간이 많이 걸립니다. (insert할 때도 추가적인 작업이 들어갑니다.. 조회는 말할것도없구요.)

유의사항

학습하는 과정에서 Referecne글 들을 참고하여 다시 정리하여 적은 글 입니다. 잘못된 부분이나 문제가 있는 부분은 피드백 주시면 감사하겠습니다 ^^*

Rerference

https://www.youtube.com/watch?v=fGPaj-rlN5w&t=1940s
https://docs.spring.io/spring-data/envers/docs/current/reference/html/#repositories.query-methods.details

profile
개발자 지망생입니다.

0개의 댓글