[JPA] Audit ์‚ฌ์šฉํ•˜๊ธฐ

์˜ค๋Š˜์€ Spring Data JPA๊ฐ€ ์ œ๊ณตํ•˜๋Š” Audit ๊ธฐ๋Šฅ์„ ์•Œ์•„๋ณด์ž. Audit์€ ์ฃผ๋กœ DB๊ฐ’์ด ๋ณ€๊ฒฝํ–ˆ์„ ๋•Œ ๋ˆ„๊ฐ€ ๊ฐ’์„ ๋ณ€๊ฒฝํ–ˆ๊ณ , ์–ธ์ œ ๋ณ€๊ฒฝํ–ˆ๋Š”์ง€ Audit(๊ฐ์‚ฌ)ํ•˜๋Š” ์šฉ๋„๋กœ ์‚ฌ์šฉํ•œ๋‹ค. Spring Data JPA๋Š” @CreatedDate, @LastModifiedDate, @CreatedBy, @LastModifiedBy ์™€ ๊ฐ™์€ ์–ด๋…ธํ…Œ์ด์…˜์„ ์ œ๊ณตํ•œ๋‹ค.

1. @EnableJpaAuditing ์ถ”๊ฐ€

@EnableJpaAuditing ์–ด๋…ธํ…Œ์ด์…˜์„ RootClass์˜ ์ตœ ์ƒ๋‹จ์— ์ถ”๊ฐ€ํ•œ๋‹ค.

@SpringBootApplication
@EnableJpaAuditing
public class Application {
    // ์ƒ๋žต
}

2. ํ•ด๋‹น Entity์— AuditingEntityListenerย ๋ฅผ ๋“ฑ๋ก

ํ•ด๋‹น ์—”ํ‹ฐํ‹ฐ์— ํด๋ž˜์Šค ๋ ˆ๋ฒจ์— @EntityListeners๋ฅผ ์„ ์–ธํ•˜๊ณ ,AuditingEntityListner๋ฅผ ๋“ฑ๋กํ•œ๋‹ค.

@Data
@Entity
// ์—”ํ‹ฐํ‹ฐ ๋ฆฌ์Šค๋„ˆ ๋“ฑ๋ก
@EntityListeners(AuditingEntityListener.class)
public class Comment {

    @Id
    @GeneratedValue
    private Long id;
    private String comment;

3. Auditํ•  ๋‚ ์งœ ํ•„๋“œ๋ฅผ ์ •์˜ํ•œ๋‹ค.

ํ•ด๋‹น ์—”ํ‹ฐํ‹ฐ์— Audit์„ ํ•  ํ•„๋“œ๋ฅผ ์ •์˜ํ•œ๋‹ค. (์ƒ์„ฑ์ผ,์ˆ˜์ •์ผ )

    // Audit ๋‚ ์งœ field๋ฅผ ์ •์˜ํ•œ๋‹ค.
    @CreatedDate
    private Date createdDate;

    @LastModifiedDate
    private Date lastModifiedDate;

4. Auditํ•˜๋Š” ์ž‘์„ฑ์ž ํ•„๋“œ๋ฅผ ์ •์˜ํ•œ๋‹ค.

    // Audit ์ž‘์„ฑ์ž field๋ฅผ ์ •์˜ํ•œ๋‹ค.
    @CreatedBy
    @ManyToOne
    private Account createdBy;

    @LastModifiedBy
    @ManyToOne
    private Account updatedBy;

DB์˜ ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ์ผ์œผํ‚ค๋Š” Author๋ฅผ Accountํด๋ž˜์Šค์˜ ๊ฐ์ฒด์™€ ๋งคํ•‘ํ–ˆ๋‹ค.
Account ํด๋ž˜์Šค๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

@Entity
@Data
public class Account {

    @Id
    @GeneratedValue
    private Long id;

    private String username;
    private String password;
}

@CreatedBy์™€ @LastModifiedBy ๋Š” Spring Security์˜ ContextHolder์•ˆ์— ๋“ค์–ด์žˆ๋Š” Principal(์‹ ์›์ •๋ณด)์˜ name๊ฐ’์œผ๋กœ ๋งคํ•‘์„ ํ•ด์ค€๋‹ค. ๋งŒ์•ฝ์— Customํ•˜๊ฒŒ ๊ตฌํ˜„ํ• ๋ ค๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด AuditorAware ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค. Spring Security์˜ ContextHolder์— ์กด์žฌํ•˜๋Š” Authentication ์ •๋ณด๋ฅผ ํ†ตํ•ด์„œ ๋งคํ•‘ํ•œ๋‹ค.

@Service
public class AccountAwareAudit implements AuditorAware<Comment> {

    @Override
    public Optional<Comment> getCurrentAuditor() {
        // Spring Security๋ฅผ ํ†ตํ•œ Auditor ๋งคํ•‘
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null || !authentication.isAuthenticated()) {
            return null;
        }

        return ((MyUserDetails) authentication.getPrincipal()).getUser();
    }

์ด๋ ‡๊ฒŒ ๋งŒ๋“  ๋นˆ(AccountAwareAudit)์„ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ๋„๋ก @EnableJpaAuditing์˜ auditorAwareRef ์†์„ฑ๊ฐ’์œผ๋กœ ๋“ฑ๋ก ํ•ด์ค€๋‹ค.

@SpringBootApplication
@EnableJpaAuditing(auditorAwareRef = "accountAwareAudit")
public class Application {

    // ์ƒ๋žต
}

์ •๋ฆฌ

Spring Data Jpa๊ฐ€ ์ œ๊ณตํ•˜๋Š” ์—”ํ‹ฐํ‹ฐ์˜ ๋ณ€ํ™”๋ฅผ ์ถ”์ ํ•˜๋Š” Audit ๊ธฐ๋Šฅ์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด์•˜๋‹ค.
๋‚ ์งœ Audit์€ ์ œ๊ณตํ•ด์ค€ ์–ด๋…ธํ…Œ์ด์…˜ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ตฌํ˜„ํ•  ๋ถ€๋ถ„์ด ์—†์—ˆ์ง€๋งŒ, ์ž‘์„ฑ์ž์— ๊ด€ํ•œ ๋ถ€๋ถ„์€ ๋Œ€๋ถ€๋ถ„ Spring Security๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ContextHolder๋ฅผ ํ†ตํ•ด์„œ ์‹ ์›์ •๋ณด๋ฅผ ๊ฐ€์ ธ์™€์„œ ๋งคํ•‘ํ•ด ์ฃผ์—ˆ๋‹ค.

์ฐธ๊ณ ์ž๋ฃŒ

์—ฐ๊ด€ ํฌ์ŠคํŠธ