semtax의 개발 일지

스프링부트로 게시판 만들기 7 : 데이터 Auditing 본문

개발/Java

스프링부트로 게시판 만들기 7 : 데이터 Auditing

semtax 2020. 5. 1. 17:23
반응형

개요

이번 포스팅에서는 Data Auditing(데이터 이력 관리) 에 대해서 다루어 보도록 하겠다.

Data Auditing?

상황을 1가지 가정해보자, 만약 당신이 게시판 관리자고, 관리자 페이지를 통해서 게시글 댓글목록을 관리 하고 있다고 가정을 해보자.

그렇다면 언제 이 글이나 댓글을 작성했는지 라던가, 누가 작성했는지를 알아야, 나중에 차단을 시키던지 다른걸 하던지 관리를 할 수가 있다.

또한, 사용자에 따라서 글 쓰는 기능을 제한 하는 기능도 위의 Data Auditing이 있어야 가능하다.

바로 위와 같은 이유들 때문에, 이러한 데이터 이력관리가 중요하다는 것을 알 수 있다.

하지만 이러한 데이터 이력관리를 처음부터 만들기에는 뭔가 귀찮다. 뭔가 누가 만들어 놓은게 있을것같다.

다행히도, JPA에서는 이러한 기능을 이미 제공해주고 있다.

이제, 어떻게 사용하는지 알아보도록 하겠다.




구현


먼저 Auditing 정보를 저장하는 Entitiy를 생성해주자.

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

    @CreatedDate
    @Column(updatable = false)
    private LocalDateTime createdTime;

    @LastModifiedDate
    private LocalDateTime lastModifiedDate;

    @CreatedBy
    @Column(updatable = false)
    private String createdBy;

    @LastModifiedBy
    private String lastModifiedBy;

    public LocalDateTime getCreatedTime() {
        return createdTime;
    }

    public LocalDateTime getLastModifiedDate() {
        return lastModifiedDate;
    }

    public String getCreatedBy() {
        return createdBy;
    }

    public String getLastModifiedBy() {
        return lastModifiedBy;
    }
}

위에서, @EntityListeners를 통해서 엔티티의 영속성 컨텍스트 라이프 사이클에 따른 Auditing 정보를 세팅하는 콜백함수를 등록 한다.

그리고, @CreateBy, @ModifiedBy, @CreateByDate, @ModifiedByDate 어노테이션을 통해서 Auditing 정보를 추가 해주는걸 알 수 있다.

이때, @CreateBy, @ModifiedBy를 붙여준 필드 같은 경우에는, 스프링에서 "AuditorAware" 객체를 구현해주고, 빈으로 등록해주는 방식으로 필드에 값을 주입하게 된다.

마지막으로, @MappedSuperclass 를 이용해서 다른 Entity에서 선언한 Auditing Entity를 상속을 통해 포함할 수 있게 해준다.

이렇게 하면 엔티티의 영속성 컨텍스트 라이프 사이클에 맞춰서 Auditing 정보가 등록되게 된다.

이제 게시글 Entitiy에 아까전에 만들어준 객체를 아래와 같이 상속받으면 된다.

@Entity
public class Post extends BaseEntity{

    @Id @GeneratedValue
    @Column(name = "post_id")
    private Long Id;

    private String title;
    private String content;


    public Long getId() {
        return Id;
    }

    public void setId(Long id) {
        Id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Post post = (Post) o;
        return Objects.equals(Id, post.Id) &&
                Objects.equals(title, post.title) &&
                Objects.equals(content, post.content);
    }

    @Override
    public int hashCode() {
        return Objects.hash(Id, title, content);
    }
}


댓글 Entity도 아래와 같이 추가해준다.

@Entity
public class Comment extends BaseEntity{

    @Id @GeneratedValue
    @Column(name = "comment_id")
    private Long Id;

    private String title;
    private String content;

    @ManyToOne
    @JoinColumn(name = "post_id")
    private Post post;

    public Long getId() {
        return Id;
    }

    public void setId(Long id) {
        Id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public Post getPost() {
        return post;
    }

    public void setPost(Post post) {
        this.post = post;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Comment comment = (Comment) o;
        return Objects.equals(Id, comment.Id) &&
                Objects.equals(title, comment.title) &&
                Objects.equals(content, comment.content);
    }

    @Override
    public int hashCode() {
        return Objects.hash(Id, title, content);
    }
}

다음으로, 아래 코드처럼 어노테이션을 이용해서 JPA Auditing 기능을 활성화 하겠다고 설정한다.

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

    @Bean
    public AuditorAware<String> auditorProvider() {
        return () -> {
            ServletRequestAttributes attr
                = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();

            String currentUser = (String)attr.getRequest().getSession().getAttribute(Sessions.SESSION_ID);

            if(currentUser != null)
                return Optional.of(currentUser);
            else
                return Optional.of("Anonymous");
        };
    };
}

위 코드에서, RequestContextHolder 라는 클래스를 사용했는데, 이를 이용해서 Servlet 요청 객체를 가져올수 있다. 이걸 이용해서 서블릿 요청 내에 있는 session 값을 가져 올 수 있다.

(스프링 시큐리티를 쓰는 경우에는 SecurityContext를 통해 세션 정보들을 가져와서 넣어주면 된다.)

테스트

Postman을 켜서 아래와 같이 테스트를 해보자

추가한 Auditing 정보들(글쓴이, 수정한이, 수정날짜, 생성날짜)이 정상적으로 표시되는것을 알 수 있다.

반응형
Comments