[Spring Boot] Jpa Auditing (+ BaseEntity) 로 엔티티 관련 이벤트 자동 기록하기

2024. 9. 7. 18:46· Spring Boot
목차
  1. 1. Jpa Auditing 이란?
  2. 2. Jpa Auditing 주요 기능
  3. 3. Base Entity
  4. 4. Jpa Auditing + BaseEntity 사용 방법 및 예제
  5. 4-1. 의존 라이브러리
  6. 4-2. SpringBootApplication 수정
  7. 4-3. @CreatedBy, @LastModifiedBy 관련 사용자 정보 설정
  8. 4-4. domain
  9. 4-5. repository
  10. 4-6. service
  11. 4-7. controller
  12. 5. 실행 결과
  13. 5-1. Post 엔티티 생성 (POST localhost:8080/api/posts)
  14. 5-2. 생성된 Post 엔티티 MySQL로 확인
  15. 5-3. Post 엔티티 수정 (PUT localhost:8080/api/posts/1)
  16. 5-4. 수정한 Post 엔티티 MySQL로 확인

1. Jpa Auditing 이란?

  • 엔티티에서 발생한 이벤트를 추적하고 기록함
    • 이벤트 : 생성(create), 수정(update), 삭제(delete) 등
  • Spring Data JPA 에서 기본적으로 Auditing 정보를 추가하는 기능 제공함

 

2. Jpa Auditing 주요 기능

  • @CreatedDate
    • 엔티티 생성 시간을 자동으로 기록함
@CreatedDate
private LocalDateTime createdAt;
  • @LastModifiedDate
    • 엔티티 최종 수정 시간을 자동으로 기록함
    • 엔티티 생성 시 생성 시간이 저장됨
@LastModifiedDate
private LocalDateTime editedAt;
  • @CreatedBy
    • 엔티티를 생성한 사용자 정보를 자동으로 기록함
@CreatedBy
private User createdBy;
  • @LastModifiedBy
    • 엔티티를 최종 수정한 사용자 정보를 자동으로 기록함
    • 엔티티 생성 시 생성한 사용자 정보가 저장됨
@LastModifiedBy
private User editedBy;

 

3. Base Entity

  • 만약 모든 엔티티에 생성 시간, 수정 시간 등을 기록해야 한다면?
    • 모든 엔티티에 Jpa Auditing의 기능에 해당하는 어노테이션을 붙여야 함
    ⇒ 중복 코드 발생!
  • 따라서 Base Entity 도입
    • Auditing 관련 엔티티 생성
    • Auditing이 필요한 실제 엔티티에서는 Base Entity를 상속받으면 중복 코드 줄일 수 있음!
  • 예시 코드
    • BaseEntity
@MappedSuperClass // Super 클래스로 지정하는 어노테이션
@EntityListners(AuditingEntityListener.class)
public abstract class BaseEntity {
		
		@CreatedDate
		private LocalDateTime createdAt;
		
		@LastModifiedDate
		private LocalDateTime editedAt;
}
  • 상속받는 엔티티 (Post)
@Entity
public class Post extends BaseEntity {

	@Id
	private Long id;
	
	// BaseEntity의 필드들을 상속받음!
}

⇒ 엔티티 내부의 코드 복잡성이 줄어들고 중복 코드 감소!

 

4. Jpa Auditing + BaseEntity 사용 방법 및 예제

📌 사전 설명
- 스프링 시큐리티 사용 시 @CreatedBy, @LastModifiedBy 에 대해서 아래와 같이 작성 가능       
    1. SecurityContextHolder.getContext().getAuthentication()을 통해 현재 인증된 사용자의 Authentication 객체를 가져와서 사용자의 id, 이름 등을 가져옴
    2. Post 객체에 User에 대한 참조 추가 가능
@CreatedBy
private User createdBy; // User를 직접 참조

// 혹은

@CreatedBy
private Long createdBy; // User.getId()등 통한 id만 참조

- 이 예제는 간단한 예제로 스프링 시큐리티 사용 x
- 여기서는 Post 생성, 수정 시 Header에 “loginUserId” (Long 타입) 을 담아서 넘길 것
   ⇒ createdBy, lastModifiedBy에는 Header에 담긴 userId 를 담음!

 

4-1. 의존 라이브러리

  • build.gradle
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    compileOnly 'org.projectlombok:lombok'
    developmentOnly 'org.springframework.boot:spring-boot-devtools'
    runtimeOnly 'com.mysql:mysql-connector-j'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

 

4-2. SpringBootApplication 수정

  • @SpringBootApplication 에 해당하는 클래스 (여기서는 JpaAuditingExApplication.java) 에 @EnableJpaAuditing 어노테이션 추가
@EnableJpaAuditing
@SpringBootApplication
public class JpaAuditingExApplication {

    public static void main(String[] args) {
        SpringApplication.run(JpaAuditingExApplication.class, args);
    }

}

 

4-3. @CreatedBy, @LastModifiedBy 관련 사용자 정보 설정

  • AuditorAwareImpl
@Component
@RequiredArgsConstructor
public class AuditorAwareImpl implements AuditorAware<Long> {

    private final HttpServletRequest request;

    @Override
    public Optional<Long> getCurrentAuditor() {
        Long userId = Long.valueOf(request.getHeader("loginUserId"));
        return Optional.ofNullable(userId);
    }
}

⇒ 위에 작성했듯이 JPA Auditing 에 사용할 사용자 정보를 Header에 포함시켜 요청 전송할 것

이 loginUserId 가 엔티티 생성자(createdBy), 엔티티 수정자(lastModifiedBy)가 됨!

 

4-4. domain

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

    @CreatedDate
    private LocalDateTime createdAt;

    @CreatedBy
    private Long createdBy;

    @LastModifiedDate
    private LocalDateTime editedAt;

    @LastModifiedBy
    private Long editedBy;
}

 

  • Post
@Entity
@Getter @Setter
@NoArgsConstructor
@AllArgsConstructor
public class Post extends BaseEntity{
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "post_id")
    private Long id;

    private String title;
    private String content;

    public Post(String title, String content) {
        this.title = title;
        this.content = content;
    }
}

 

  • DTO > PostDto
@Data
public class PostDto {

    private String title;
    private String content;
}

 

4-5. repository

  • PostRepository
public interface PostRepository extends JpaRepository<Post, Long> {
}

 

4-6. service

  • PostService
    • 간단한 조회, 생성, 수정 로직을 포함
@Service
@RequiredArgsConstructor
public class PostService {

    private final PostRepository postRepository;

    public List<Post> findAll() {
        return postRepository.findAll();
    }

    public Post findOne(Long postId) {
        return postRepository.findById(postId).orElse(null);
    }

    @Transactional
    public Post save(PostDto postDto) {
        Post post = new Post(postDto.getTitle(), postDto.getContent());
        return postRepository.save(post);
    }

    @Transactional
    public Post update(Long postId, PostDto postDto) {
        Post post = postRepository.findById(postId).orElse(null);

        if(post == null) return null;

        post.setTitle(postDto.getTitle());
        post.setContent(postDto.getContent());

        return postRepository.save(post);
    }
}

 

4-7. controller

  • PostApiController
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/posts")
public class PostApiController {

    private final PostService postService;

    @GetMapping
    public List<Post> findAll() {
        return postService.findAll();
    }

    @GetMapping("/{postId}")
    public Post findOne(@PathVariable("postId") Long postId) {
        return postService.findOne(postId);
    }

    @PostMapping
    public Post create(@RequestBody PostDto postDto) {
        return postService.save(postDto);
    }

    @PutMapping("/{postId}")
    public Post update(@PathVariable("postId") Long postId, @RequestBody PostDto postDto) {
        return postService.update(postId, postDto);
    }
}

 

5. 실행 결과

5-1. Post 엔티티 생성 (POST localhost:8080/api/posts)

  • Header에 “loginUserId” : 1 추가

요청에 포함한 헤더
요청 body
실행결과 반환된 Post 객체

 

5-2. 생성된 Post 엔티티 MySQL로 확인

  • created_at : 생성 시간 자동 저장됨을 확인
  • edited_at : 최종 수정 시간 자동 저장됨을 확인
  • created_by : 생성한 사용자 정보 (header에 담긴 id) 자동 저장됨을 확인
  • edited_by : 수정한 사용자 정보 (header에 담긴 id) 자동 저장됨을 확인

 

5-3. Post 엔티티 수정 (PUT localhost:8080/api/posts/1)

  • 이번에는 Header에 "loginUserId" : 2 추가

요청에 포함한 헤더
요청 body
실행결과 반환된 Post 객체

 

5-4. 수정한 Post 엔티티 MySQL로 확인

  • created_at : 생성 시간이 저장됨을 확인 (변화 x)
  • edited_at : 최종 수정 시간 자동 저장됨을 확인
  • created_by : 생성한 사용자 정보가 저장됨을 확인 (변화 x)
  • edited_by : 최종 수정한 사용자 정보 자동 저장됨을 확
저작자표시 변경금지 (새창열림)

'Spring Boot' 카테고리의 다른 글

[Spring Boot] TDD와 given-when-then 패턴으로 테스트 코드 작성 (JUnit, AssertJ, 단위 테스트, 통합 테스트, Mock 등)  (0) 2024.09.11
[Spring Boot] 주소 입력으로 날씨 정보 출력 예제 프로젝트 (Geocoder API 2.0, open weather map API)  (0) 2024.09.08
[Spring Boot] JpaRepository 개념, 사용 방법 (쿼리 메서드, @Query, JPQL, Native Query)  (0) 2024.09.05
[Spring Boot] ResponseEntity 사용 이유, 사용 방법  (1) 2024.09.05
[Spring Boot] 스프링 트랜잭션 (@Transactional)  (0) 2024.09.05
  1. 1. Jpa Auditing 이란?
  2. 2. Jpa Auditing 주요 기능
  3. 3. Base Entity
  4. 4. Jpa Auditing + BaseEntity 사용 방법 및 예제
  5. 4-1. 의존 라이브러리
  6. 4-2. SpringBootApplication 수정
  7. 4-3. @CreatedBy, @LastModifiedBy 관련 사용자 정보 설정
  8. 4-4. domain
  9. 4-5. repository
  10. 4-6. service
  11. 4-7. controller
  12. 5. 실행 결과
  13. 5-1. Post 엔티티 생성 (POST localhost:8080/api/posts)
  14. 5-2. 생성된 Post 엔티티 MySQL로 확인
  15. 5-3. Post 엔티티 수정 (PUT localhost:8080/api/posts/1)
  16. 5-4. 수정한 Post 엔티티 MySQL로 확인
'Spring Boot' 카테고리의 다른 글
  • [Spring Boot] TDD와 given-when-then 패턴으로 테스트 코드 작성 (JUnit, AssertJ, 단위 테스트, 통합 테스트, Mock 등)
  • [Spring Boot] 주소 입력으로 날씨 정보 출력 예제 프로젝트 (Geocoder API 2.0, open weather map API)
  • [Spring Boot] JpaRepository 개념, 사용 방법 (쿼리 메서드, @Query, JPQL, Native Query)
  • [Spring Boot] ResponseEntity 사용 이유, 사용 방법
공대생안씨
공대생안씨
전자공학과 학부생의 코딩 일기
공대생의 코딩 일기전자공학과 학부생의 코딩 일기
티스토리
|
로그인
공대생안씨
공대생의 코딩 일기
공대생안씨
글쓰기
|
관리
전체
오늘
어제
  • All Categories (153)
    • Spring Boot (46)
      • JPA (7)
      • Lombok (2)
    • Java (21)
    • DevOps (3)
      • CI,CD (8)
      • Monitoring (2)
    • Database (7)
      • MySQL (5)
      • MongoDB (1)
      • H2 (1)
    • Trouble Shooting (5)
    • FE (4)
    • IntelliJ (3)
    • Git (3)
    • Algorithm (41)

블로그 메뉴

  • 홈
  • 태그
  • Github

공지사항

인기 글

hELLO · Designed By 정상우.v4.2.2
공대생안씨
[Spring Boot] Jpa Auditing (+ BaseEntity) 로 엔티티 관련 이벤트 자동 기록하기
상단으로

티스토리툴바

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.