BE/스프링

JPA Auditing

sonoopy 2024. 11. 14. 17:35

 

JPA Auditing이란?

엔티티의 생성, 수정과 관련된 메타 데이터를 자동으로 관리하기 위한 기능
예를 들어, 엔티티가 생성된 시각, 마지막으로 수정된 시각, 생성한 사용자, 수정한 사용자 등의 정보를 데이터베이스에 자동으로 기록

 

  1. 데이터가 변경되는 시점 감지
  2. 데이터 변경 시 자동으로 이력 기록 (created_at, created_by...)
  3. 엔티티 클래스에 어노테이션 추가하여 사용
 

1. Gradle에 의존성 추가

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
}

 

 

2. Spring Boot 애플리케이션 @EnableJpaAuditing 추가

@SpringBootApplication
@EnableJpaAuditing
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

 

또는 별도의 설정 클래스를 생성

@Configuration
@EnableJpaAuditing
public class JpaAuditingConfig {
}

 

 

3. Base Entity 생성

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

    @CreatedDate
    private LocalDateTime createdAt;

    @LastModifiedDate
    private LocalDateTime updatedAt;

    @CreatedBy
    private String createdBy;

    @LastModifiedBy
    private String updatedBy;
}

 

 

4. AuditorAware 구현

: @CreatedBy @LastModifiedBy를 사용하려면 현재 사용자를 반환하는 AuditorAware 구현체를 생성

AuditorAware<T>는 JPA Auditing 기능에서 현재 감사 주체(Auditor)를 제공하기 위한 인터페이스

 

Spring Security 연동 : 인증된 사용자 이름 반환

@Component("auditorProvider")
public class SecurityAuditorAware implements AuditorAware<String> {

    @Override
    public Optional<String> getCurrentAuditor() {
        // Spring Security에서 현재 인증 정보 가져오기
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

        // 인증 정보가 없거나 인증되지 않은 경우
        if (authentication == null || !authentication.isAuthenticated()) {
            return Optional.empty();
        }

        // 인증된 사용자의 이름 반환
        return Optional.of(authentication.getName());
    }
}

 

 

추가 정보(예: 이메일, 사용자 ID)를 반환하려면,

Custom Principal 사용

Spring Security에서 사용자 정보를 UserDetails 구현체로 커스터마이징한 경우, Principal 객체에서 정보를 가져올 수 있음

@Override
public Optional<String> getCurrentAuditor() {
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

    if (authentication == null || !authentication.isAuthenticated()) {
        return Optional.empty();
    }

    Object principal = authentication.getPrincipal();
    if (principal instanceof CustomUserDetails) {
        CustomUserDetails userDetails = (CustomUserDetails) principal;
        return Optional.of(userDetails.getEmail()); // 사용자 이메일 반환
    }

    return Optional.empty(); // Principal이 CustomUserDetails가 아닌 경우
}

 

Custom Principal은 Spring Security의 Authentication 객체에 사용자 정의 정보 포함 가능

-> 인증 프로세스에서 Principal로 사용자 객체(UserDetails 구현체 등)를 설정하는 방식

 

 

5. 엔티티에서 BaseEntity를 상속받아 Auditing 필드 포함

@Getter
@NoArgsConstructor
@Entity
public class User extends BaseEntity {

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

    private String name;

    private String email;

    public User(String name, String email) {
        this.name = name;
        this.email = email;
    }
}