2주차 미션 - JPA, AuditorAware 구현해 보기
우아한 테크 캠프 프로 2기

2주차 미션 - JPA, AuditorAware 구현해 보기

모든 소스 코드는 https://github.com/lkimilhol/tistoryblog 에서 확인 가능합니다!

 

안녕하세요.

이번에는 2주차 미션의 리뷰에서 알게 된 AuditorAware 구현을 해보려고 합니다.

 

피드백을 받았었습니다!

JpaAuditing, AuditorAware이라는 것이 무엇인지 알아보고, 구현을 해보도록 하겠습니다.

 

1. 중복되는 컬럼을 BaseEntity에 구현


BaseEntity를 abstract class로 구현하여 이를 상속받아 컬럼을 재사용 하는 방법은 잘 아실거라 생각됩니다.

 

전의 포스트에서 사용하였던 코드를 이용하여 데이터의 등록시간과 수정시간을 넣어 볼 예정입니다!

내용을 모르시는 분들을 위해 간단히 설명하면, User 클래스는 다수의 Computer 클래스를 갖도록 하는 1:N 관계의 간단한 구조입니다!

 

그럼 바로 BaseEntity를 구현해 보도록 하겠습니다!

 

BaseEntity.java

@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseEntity {
    @CreatedDate
    @Column(name = "insert_time", columnDefinition = "DATETIME", nullable = false)
    private LocalDateTime insertTime;

    @LastModifiedDate
    @Column(name = "update_time", columnDefinition = "DATETIME")
    private LocalDateTime updateTime;
}

코드가 매우 간단한데요. 천천히 한변 살펴볼까요?

 

일단 @MappedSuperclass 애노테이션을 살펴보겠습니다.

이 애노테이션은 중복 되는 경우에 공통 되는 컬럼을 만들기 위해 사용하는 애노테이션입니다.

매핑 정보만 제공할 뿐 따로 구현하는 내용이 없기 때문에 보통 abstract class로 제공을 합니다.

 

@EntityListeners 애노테이션은 JPA entity에 대해 CRUD 이벤트의 전과 후를 콜백 받을 수 있는 애노테이션입니다.

AuditingEntityListener.class를 확인 해보면 다음과 같은 메서드를 확인 할 수 있는데요.

즉 create와 update시 Entity의 감지가 가능하다 정도로 이해 할 수 있을거 같습니다.

 

그렇다면 BaseEntity를 상속받아 테스트를 진행해 보겠습니다!

 

시작하기 앞서 메인 메서드가 있는 클래스에 다음과 같은 애노테이션을 추가해주세요!

@EnableJpaAuditing 

 

테스트를 실행시키면 다음과 같은 create table을 확인 할 수 있습니다.

기존에 insert_time과 update_time 컬럼은 존재하지 않았는데, 데이터가 생긴 것을 확인 해 볼 수 있는데요!

 

그렇다면 insert_time, update_time에 값이 잘 채워져 있을까요?

값을 출력해 보았는데요. insert timed, update_time이 제대로 들어가 있는 것을 확인 할 수 있습니다.

update_time 값은 동일한데요. 이 값을 수정이 잘 되는지 보겠습니다.

 

2. 값을 수정하는 경우


이제 값을 수정하여 update_time 값이 제대로 들어가는지도 확인해 보겠습니다.

 

UserService.java

@Transactional
public void updateName(Long userId, String name) {
	User user = userRepository.findById(userId).orElseThrow(RuntimeException::new);
	user.updateName(name);
}

유저의 이름을 변경하는 로직입니다. update가 된다면 update_time이 바뀔 것을 예상하고 있습니다.

테스트 케이스를 작성했습니다.

 @DisplayName("insert_time 확인")
 @Test
 void insertTime() {
   // given
   List<User> users = userRepository.findAll();
   User user = users.get(0);
   userService.updateName(user.getId(), "김메이어");

	// when
	User findUser = userRepository.findById(user.getId()).orElseThrow(RuntimeException::new);

	// then
	assertThat(findUser.getInsertTime()).isNotNull();
	assertThat(findUser.getUpdateTime()).isNotNull();
}

그리고 테스트 케이스를 확인해봅시다.

업데이트가 잘 이루어 졌습니다.

값이 잘 수정이 되었네요! 그렇다면 우리가 궁금했던 createdBy, LastModifiedBy는 어떤 기능일까요?

3. AuditorAware 구현


먼저 AuditorAware란 무엇일까요?

만약 Entity의 값이 생성 되고 변경 될 때 누가 만들었는지, 누가 수정했는지 까지 자동으로 값을 업데이트 해주는 기능이라고 합니다.

 

게시판 도메인의 경우 글을 작성한 유저를 @CreatedBy 애노테이션을 통해서 자동으로 누가 작성했는지(만들었는지) 알 수 있습니다.

이는 Security 구현이 필요하다고 하는데요. 저는 User에 관한 내용을 직접 넘어줌으로써 기능만 확인 해 보도록 하겠습니다.

 

우리는 유저, 컴퓨터에 관한 도메인이기 때문에 주문이라는 도메인을 작성 해 보도록 하겠습니다.

 

Order.java

@Entity
@Table(name = "orders")
public class Order extends BaseEntity {

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

    @CreatedBy
    private String userName;

    public Order() {}

    public Long getId() {
        return id;
    }
}

 

UserName에 @CreatedBy 애노테이션이 붙은 것을 확인 할 수 있습니다.

 

그리고 AuditorAware를 구현할 클래스를 만듭니다.

 

UserAuditorAware.java

@Service
public class UserAuditorAware implements AuditorAware<String> {
    @Override
    public Optional<String> getCurrentAuditor() {
        return Optional.of("주문자");
    }
}

원래 getCurrentAuditor의 리턴으로 실제 주문을 하는 유저의 name을 리턴해야 합니다. 하지만 현재는 예제를 보여주기 위함이므로 간단하게 "주문자" 라는 String 값을 리턴 하도록 할게요.

 

그리고 main 메서드가 있는 클래스에 다음과 같이 애노테이션을 수정해야합니다.

테스트 케이스를 작성해 보겠습니다!

@SpringBootTest
class OrderTest {

    @Autowired
    private OrderRepository orderRepository;

    @DisplayName("주문자 테스트")
    @Test
    void create() {
        // given
        Order order = new Order();
        // when
        orderRepository.save(order);
        // then
        assertThat(order).isNotNull();
        assertThat(order.getUserName()).isEqualTo("주문자");
    }
}

테스트가 성공하였습니다!

 

우리는 @PrePersist 애노테이션을 통해서 주문자의 값을 수정 할 수 있습니다. 이는 persist가 일어나기 전에 사용되는 애노테이션 입니다. 코드를 조금 수정하겠습니다. github에는 주석처리 할 예정입니다!

 

Order.java

@Entity
@Table(name = "orders")
public class Order extends BaseEntity {

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

    @CreatedBy
    private String userName;

    public Order() {}

    public Long getId() {
        return id;
    }

    public String getUserName() {
        return userName;
    }

    @PrePersist
    public void setup() {
        this.userName = "test";
    }
}

테스트를 "주문자"에서 "test"로 변경하여 실행시켜 보겠습니다.

@DisplayName("주문자 테스트")
@Test
void create() {
  // given
  Order order = new Order();
  // when
  orderRepository.save(order);
  // then
  assertThat(order).isNotNull();
  assertThat(order.getUserName()).isEqualTo("test");
}

마찬가지로 성공했습니다!

 

4. 마치며


Security를 이용하면 손 쉽게 글 작성자와 수정자를 넣어주는 강력한 기능이 될 거 같습니다.

현재는 Security에 관한 지식이 전무 하므로 ^^;; 실습을 조금 변경해서 해보았습니다.

 

감사합니다!