[JPA] @CreatedBy, @LastModifiedBy 를 위한 AuditorAware 적용
spring-data-commons 에는 @CreatedBy, @LastModifiedBy 라는 어노테이션이 있습니다. 물론, @CreatedDate, @LastModifiedDate가 자매품(?)으로 있긴하지만, 생성시간과 업데이트시간은 해당 어노테이션을 지정하는 것만으로도 끝나지만, 생성자, 수정자는 별도의 셋팅 과정이 필요합니다. 이 과정에서 Spring Security 셋팅이 필수로 필요합니다. Spring Security 셋팅 없이도 할 수 있겠지만 요즘 백엔드와 프론트엔드도 분리되고 JWT 토큰으로 인증을 하기에 더더욱 필요한 것 같습니다.
아래는 JPA, Spring Secutiry 환경에서 작성된 코드입니다.
MemberEntity.java
@Getter
@ToString
@Builder
@Entity(name = "회원 테이블")
@Table(name = "tb_member")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PROTECTED)
@EntityListeners(AuditingEntityListener.class)
public class MemberEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "member_seq", nullable = false)
private Long memberSeq;
@Column(name = "member_id", nullable = false, length = 50)
private String memberId;
@Column(name = "co_name", nullable = false, length = 50)
private String coName;
@Column(name = "real_name")
private String realName;
@Column(name = "status", nullable = false, length = 4)
private String status;
@CreatedBy
@Column(name = "create_member_seq", nullable = false, updatable = false)
private Long regMemberSeq;
@LastModifiedBy
@Column(name = "mod_member_seq")
private Long modMemberSeq;
@CreatedDate
@Column(name = "create_date", nullable = false, updatable = false)
private LocalDateTime regDate;
@LastModifiedDate
@Column(name = "mod_date")
private LocalDateTime modDate;
public void updateRealName(String value) {
this.realName = value;
}
}
JPA를 다 알고 계시다는 가정하에 사용되는 어노테이션은 따로 설명하지 않겠습니다. @CreatedBy, @LastModifiedBy 어노테이션만 기억하시고 해당 필드에 지정해주시면 됩니다.
AuthenticationUserDto.java
@ToString
@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class AuthenticationUserDto {
private String memberId;
private Long memberSeq;
private String name;
}
Spring Secutiry Filter 에서 넘어온 JWT Token을 AuthenticationToken객체로 변환하고 Detail에 DTO를 만들어서 실어놨습니다.
AuditorAwareImpl.java
@Slf4j
public class AuditorAwareImpl implements AuditorAware<Long> {
@Override
public Optional<Long> getCurrentAuditor() {
AuthenticationToken authenticationToken = (AuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
if (authenticationToken == null || !authenticationToken.isAuthenticated()) {
log.debug("Not found AuthenticationToken");
return null;
}
AuthenticationUserDto authenticationUserDto = (AuthenticationUserDto) authenticationToken.getDetails();
log.debug("Found AuthenticationToken: {}", authenticationUserDto);
return Optional.of(authenticationUserDto.getMemberSeq());
}
}
SecurityContextHolder에서 객체가 있는지 확인을 하고 없으면 null을 있으면 Spring Secutiry Filter에서 인증된 객체에 넣어둔 DTO를 꺼내서 @CreatedBy, @LastModifiedBy 어노테이션이 붙은 필드에 넣을 값을 가지고 옵니다.
JpaAuditConfiguration.java
@Configuration
@EnableJpaAuditing(auditorAwareRef = "auditorProvider")
public class JpaAuditConfiguration {
@Bean
public AuditorAware<Long> auditorProvider() {
return new AuditorAwareImpl();
}
}
TestController.java
@Operation(summary = "Test 2", description = "테스트 투")
@GetMapping("/{memberSeq}")
public ResponseEntity<ResultEntity> test2(@PathVariable("memberSeq") Long memberSeq) throws Exception {
ResultEntity result = ResultEntity.builder()
.success(true)
.build();
MemberEntity member = memberRepository.findById(memberSeq).get();
member.updateRealName("정우성4");
memberRepository.save(member);
return new ResponseEntity<>(result, HttpStatus.OK);
}
제 환경에 맞는 컨트롤러라 크게 신경안쓰셔도 되고 해당 부분에서 insert, update를 하신 후 토큰에 들어 있는 값이 잘 들어가는지 DB에서 확인해보시면 됩니다.
Spring Secutiry Filter을 통해서 요청 헤더에서 토큰 꺼내서 유효성 체크하고 인증하고, 인증된 객체를 생성하는 부분은 생략하였으니 참고부탁드리겠습니다. 기회가 되면 해당 부분은 별도의 포스팅으로 작성하도록 하겠습니다.