Skip to content

[FEAT] BaseEntity 추가 - #6

Merged
ownue merged 2 commits into
developfrom
feat/#5-base-entity
Jul 8, 2026
Merged

[FEAT] BaseEntity 추가#6
ownue merged 2 commits into
developfrom
feat/#5-base-entity

Conversation

@ownue

@ownue ownue commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

📍 개요

BaseEntity 4종류를 추가했습니다.

⛓️‍💥 관련 이슈


🛠️ 작업 내용

  • BaseEntity를 추가했습니다.

🔥 리뷰 요청 사항

리뷰어가 중점적으로 확인해주었으면 하는 내용을 작성해주세요.

X


✅ 체크리스트

  • 코드 컨벤션을 준수했습니다.
  • 불필요한 코드 및 import를 제거했습니다.
  • 예외 처리를 적용했습니다.
  • 테스트를 완료했습니다.
  • 관련 Issue를 연결했습니다.

📎 참고 사항

X

Summary by CodeRabbit

  • New Features
    • 엔티티 생성/수정 시각이 자동으로 기록되도록 개선되었습니다(생성 시각 created_at, 수정 시각 updated_at).
    • 삭제 시각을 저장하는 소프트 삭제가 추가되어, 삭제 여부를 deleted_at 유무로 확인할 수 있습니다.
    • 전반적인 JPA 타임스탬프 감사(auditing) 기능이 활성화되어 일관된 시간 관리가 가능해졌습니다.

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Application에 JPA Auditing을 켜고, 생성·수정·소프트삭제 시각을 담는 4개의 추상 엔티티를 추가했습니다. BaseTimeEntityBaseCreatedEntity를 상속하도록 바뀌었고, 두 개의 소프트삭제 기반 클래스가 deletedAt, softDelete(), isDeleted()를 제공합니다.

Changes

Base Entity 및 Auditing 설정

Layer / File(s) Summary
Auditing 활성화
src/main/java/com/mr/Application.java
@EnableJpaAuditing이 추가되어 JPA Auditing이 활성화됨.
생성일자 기반 엔티티
src/main/java/com/mr/global/entity/BaseCreatedEntity.java
@MappedSuperclass 기반 BaseCreatedEntity가 추가되고 createdAt이 자동 기록됨.
생성/수정일자 기반 엔티티
src/main/java/com/mr/global/entity/BaseTimeEntity.java
BaseTimeEntityBaseCreatedEntity를 상속하고 updatedAt만 별도로 매핑하도록 변경됨.
생성/소프트삭제 엔티티
src/main/java/com/mr/global/entity/BaseCreatedDeletedEntity.java
deletedAtsoftDelete(), isDeleted()를 가진 소프트삭제용 기반 클래스가 추가됨.
생성/수정/소프트삭제 엔티티
src/main/java/com/mr/global/entity/BaseTimeDeletedEntity.java
BaseTimeEntity를 확장한 소프트삭제용 기반 클래스가 추가됨.

Estimated code review effort: 2 (Simple) | ~10 minutes

Poem

시계가 조용히 깨어나고 ⏱️
createdAt이 첫 숨을 적고
updatedAt은 다시 한 번 반짝
deletedAt은 필요할 때만 잠깐
엔티티들은 시간의 규칙을 배웠다 ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목이 BaseEntity 추가라는 핵심 변경을 간결하게 잘 요약합니다.
Linked Issues check ✅ Passed [#5] 요청된 4개 BaseEntity와 JPA Auditing 설정이 모두 반영되어 있습니다.
Out of Scope Changes check ✅ Passed 핵심 범위는 BaseEntity 구현이며, Auditing 활성화는 해당 엔티티 지원을 위한 필요한 변경으로 보입니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/#5-base-entity

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (4)
src/main/java/com/mr/global/entity/BaseCreatedEntity.java (1)

1-20: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

필드 중복, 상속으로 한 번에 정리해봐요! 👍

BaseCreatedEntitycreatedAt 선언이 BaseTimeEntity, BaseCreatedDeletedEntity, BaseTimeDeletedEntity에서도 그대로 반복되고 있습니다(제공된 컨텍스트 스니펫 기준). Java는 다중 상속이 불가능하니, BaseCreatedEntity를 최상위로 두고 BaseTimeEntity가 이를 상속해 updatedAt만 추가하고, BaseCreatedDeletedEntity도 이를 상속해 deletedAt만 추가하는 구조로 리팩터링하면 @CreatedDate 필드 선언 중복을 없앨 수 있습니다.

♻️ 제안: 상속 구조로 중복 제거
-public abstract class BaseTimeEntity {
+public abstract class BaseTimeEntity extends BaseCreatedEntity {

-    `@CreatedDate`
-    `@Column`(name = "created_at", nullable = false, updatable = false)
-    protected LocalDateTime createdAt;

     `@LastModifiedDate`
     `@Column`(name = "updated_at")
     protected LocalDateTime updatedAt;
 }

지금 시점(초기 도입 단계)에 정리해두면 나중에 4개 클래스를 따로 유지보수하는 수고를 덜 수 있어요.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/mr/global/entity/BaseCreatedEntity.java` around lines 1 -
20, `BaseCreatedEntity`의 `createdAt`이 다른 공통 엔티티들에서도 중복 선언되고 있으므로, 상속 구조를 정리해 중복을
제거하세요. `BaseCreatedEntity`를 공통 최상위로 유지하고 `BaseTimeEntity`는 이를 상속해 `updatedAt`만
추가, `BaseCreatedDeletedEntity`와 `BaseTimeDeletedEntity`도 각각 필요한 필드만 덧붙이도록 구조를
맞추세요. `BaseCreatedEntity`, `BaseTimeEntity`, `BaseCreatedDeletedEntity`,
`BaseTimeDeletedEntity`의 필드 선언과 상속 관계를 함께 수정해 `@CreatedDate` 중복을 없애면 됩니다.
src/main/java/com/mr/global/entity/BaseCreatedDeletedEntity.java (2)

21-22: 🧹 Nitpick | 🔵 Trivial

deleted_at 컬럼에 인덱스 추가를 고려해 보세요.

삭제되지 않은 레코드만 조회하는 쿼리(WHERE deleted_at IS NULL)가 많아질 경우, 인덱스가 없으면 풀 스캔이 발생할 수 있습니다. 마이그레이션 스크립트나 @Table(indexes = ...)로 인덱스를 추가하는 것을 권장드립니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/mr/global/entity/BaseCreatedDeletedEntity.java` around
lines 21 - 22, The BaseCreatedDeletedEntity.deletedAt mapping currently has no
index, so queries filtering on deleted_at IS NULL may scan the table. Add an
index for the deleted_at column by updating the entity’s `@Table`(indexes = ...)
on BaseCreatedDeletedEntity or by adding the index in the migration script, and
keep the existing `@Column`(name = "deleted_at") mapping unchanged.

24-26: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

softDelete() 재호출 시 멱등성 체크가 없어요.

이미 삭제된 엔티티에 softDelete()를 다시 호출하면 deletedAt이 최신 시각으로 덮어씌워집니다. 최초 삭제 시각을 감사 목적으로 보존하려면 가드 조건을 추가하는 게 좋습니다.

🛡️ 개선 제안
     public void softDelete() {
-        this.deletedAt = LocalDateTime.now();
+        if (this.deletedAt == null) {
+            this.deletedAt = LocalDateTime.now();
+        }
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/mr/global/entity/BaseCreatedDeletedEntity.java` around
lines 24 - 26, `BaseCreatedDeletedEntity.softDelete()` is not idempotent and
overwrites `deletedAt` on repeated calls. Update `softDelete()` to guard against
reassigning the timestamp when `deletedAt` is already set, so the first deletion
time is preserved for audit purposes. Use the `deletedAt` field in
`BaseCreatedDeletedEntity` as the check point and keep the existing method
behavior unchanged for not-yet-deleted entities.
src/main/java/com/mr/global/entity/BaseTimeDeletedEntity.java (1)

26-31: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

BaseCreatedDeletedEntitydeletedAt/softDelete() 로직이 완전히 중복되네요.

MappedSuperclass는 다중 상속이 불가능해 완전한 통합은 어렵지만, 같은 로직이 두 파일에 그대로 복사된 상태입니다. 인터페이스의 default 메서드로 softDelete()를 추출하는 방법도 검토해볼 수 있지만, 필드 접근을 위해 setter를 노출해야 하는 트레이드오프가 있어 지금 구조를 유지해도 무방합니다.

또한 이 파일의 softDelete()(Line 29-31)도 BaseCreatedDeletedEntity와 동일하게 재호출 시 deletedAt이 덮어써지는 멱등성 이슈가 있습니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/mr/global/entity/BaseTimeDeletedEntity.java` around lines
26 - 31, `BaseTimeDeletedEntity.softDelete()` duplicates the same
`deletedAt`/`softDelete()` logic found in `BaseCreatedDeletedEntity`, so keep
the current structure if needed but consider extracting shared behavior into a
common default method or helper where possible. More importantly, make
`softDelete()` idempotent by preserving an existing `deletedAt` value instead of
overwriting it on repeated calls, and update the `deletedAt` assignment logic in
this class accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/main/java/com/mr/global/entity/BaseCreatedDeletedEntity.java`:
- Around line 21-22: The BaseCreatedDeletedEntity.deletedAt mapping currently
has no index, so queries filtering on deleted_at IS NULL may scan the table. Add
an index for the deleted_at column by updating the entity’s `@Table`(indexes =
...) on BaseCreatedDeletedEntity or by adding the index in the migration script,
and keep the existing `@Column`(name = "deleted_at") mapping unchanged.
- Around line 24-26: `BaseCreatedDeletedEntity.softDelete()` is not idempotent
and overwrites `deletedAt` on repeated calls. Update `softDelete()` to guard
against reassigning the timestamp when `deletedAt` is already set, so the first
deletion time is preserved for audit purposes. Use the `deletedAt` field in
`BaseCreatedDeletedEntity` as the check point and keep the existing method
behavior unchanged for not-yet-deleted entities.

In `@src/main/java/com/mr/global/entity/BaseCreatedEntity.java`:
- Around line 1-20: `BaseCreatedEntity`의 `createdAt`이 다른 공통 엔티티들에서도 중복 선언되고
있으므로, 상속 구조를 정리해 중복을 제거하세요. `BaseCreatedEntity`를 공통 최상위로 유지하고 `BaseTimeEntity`는
이를 상속해 `updatedAt`만 추가, `BaseCreatedDeletedEntity`와 `BaseTimeDeletedEntity`도 각각
필요한 필드만 덧붙이도록 구조를 맞추세요. `BaseCreatedEntity`, `BaseTimeEntity`,
`BaseCreatedDeletedEntity`, `BaseTimeDeletedEntity`의 필드 선언과 상속 관계를 함께 수정해
`@CreatedDate` 중복을 없애면 됩니다.

In `@src/main/java/com/mr/global/entity/BaseTimeDeletedEntity.java`:
- Around line 26-31: `BaseTimeDeletedEntity.softDelete()` duplicates the same
`deletedAt`/`softDelete()` logic found in `BaseCreatedDeletedEntity`, so keep
the current structure if needed but consider extracting shared behavior into a
common default method or helper where possible. More importantly, make
`softDelete()` idempotent by preserving an existing `deletedAt` value instead of
overwriting it on repeated calls, and update the `deletedAt` assignment logic in
this class accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 2f14f485-ab62-4ff8-85d2-6e99f1d7b2d4

📥 Commits

Reviewing files that changed from the base of the PR and between 279b34a and 65e555e.

📒 Files selected for processing (5)
  • src/main/java/com/mr/Application.java
  • src/main/java/com/mr/global/entity/BaseCreatedDeletedEntity.java
  • src/main/java/com/mr/global/entity/BaseCreatedEntity.java
  • src/main/java/com/mr/global/entity/BaseTimeDeletedEntity.java
  • src/main/java/com/mr/global/entity/BaseTimeEntity.java

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
src/main/java/com/mr/global/entity/BaseCreatedDeletedEntity.java (2)

12-13: 🗄️ Data Integrity & Integration | 🔵 Trivial

소프트 삭제 필터링 전략도 함께 고려해보세요.

deletedAt 필드는 잘 추가되었지만, 이를 상속하는 엔티티의 리포지토리/쿼리에서 삭제된 레코드를 자동으로 걸러주는 장치(@Where(clause = "deleted_at is null") 또는 JPA @SQLRestriction 등)가 없습니다. 향후 각 리포지토리에서 매번 조건을 넣지 않으면 삭제된 데이터가 실수로 조회될 위험이 있습니다. 관련 공식 문서: Hibernate @SQLRestriction(구 @Where) 애노테이션을 참고하시면 도움이 될 것 같습니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/mr/global/entity/BaseCreatedDeletedEntity.java` around
lines 12 - 13, `BaseCreatedDeletedEntity`에 추가된 `deletedAt`만으로는 소프트 삭제가 자동 필터링되지
않으니, 이 베이스 엔티티를 상속하는 조회에서 삭제된 레코드가 기본 제외되도록 Hibernate 필터링을 적용하세요. `deletedAt`
필드가 선언된 `BaseCreatedDeletedEntity`에 `@Where(clause = "deleted_at is null")` 또는
동등한 `@SQLRestriction`를 추가해, 별도 리포지토리/쿼리마다 조건을 반복하지 않아도 되게 수정하면 됩니다.

8-24: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

소프트 삭제 로직 정확 👍, 다만 중복 코드가 눈에 띄네요.

softDelete()/isDeleted() 로직 자체는 정확하고 멱등성도 잘 지켜졌습니다. 다만 BaseTimeDeletedEntity에도 deletedAt, softDelete(), isDeleted()가 거의 동일하게 반복 구현되어 있습니다 (src/main/java/com/mr/global/entity/BaseTimeDeletedEntity.java:10-24 참고).

Java는 단일 상속만 지원하므로 필드 상속 구조상 완전한 중복 제거는 어렵지만, 공통 동작을 인터페이스의 default method로 추출하면 유지보수성을 높일 수 있습니다.

♻️ 인터페이스 추출 예시
public interface SoftDeletable {
    LocalDateTime getDeletedAt();
    void setDeletedAt(LocalDateTime deletedAt);

    default void softDelete() {
        if (getDeletedAt() == null) {
            setDeletedAt(LocalDateTime.now());
        }
    }

    default boolean isDeleted() {
        return getDeletedAt() != null;
    }
}

두 삭제 기반 클래스가 이 인터페이스를 구현하도록 하면 중복을 제거할 수 있습니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/mr/global/entity/BaseCreatedDeletedEntity.java` around
lines 8 - 24, `BaseCreatedDeletedEntity` duplicates the same soft-delete
behavior already present in `BaseTimeDeletedEntity`; extract the shared
`deletedAt`, `softDelete()`, and `isDeleted()` contract into a common
`SoftDeletable` interface with default methods, then have both entity base
classes implement it while keeping their own field mapping/getter-setter
accessors. Use the existing `deletedAt`, `softDelete()`, and `isDeleted()`
symbols to align the two classes around the shared behavior and remove the
repeated method bodies.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/main/java/com/mr/global/entity/BaseCreatedDeletedEntity.java`:
- Around line 12-13: `BaseCreatedDeletedEntity`에 추가된 `deletedAt`만으로는 소프트 삭제가 자동
필터링되지 않으니, 이 베이스 엔티티를 상속하는 조회에서 삭제된 레코드가 기본 제외되도록 Hibernate 필터링을 적용하세요.
`deletedAt` 필드가 선언된 `BaseCreatedDeletedEntity`에 `@Where(clause = "deleted_at is
null")` 또는 동등한 `@SQLRestriction`를 추가해, 별도 리포지토리/쿼리마다 조건을 반복하지 않아도 되게 수정하면 됩니다.
- Around line 8-24: `BaseCreatedDeletedEntity` duplicates the same soft-delete
behavior already present in `BaseTimeDeletedEntity`; extract the shared
`deletedAt`, `softDelete()`, and `isDeleted()` contract into a common
`SoftDeletable` interface with default methods, then have both entity base
classes implement it while keeping their own field mapping/getter-setter
accessors. Use the existing `deletedAt`, `softDelete()`, and `isDeleted()`
symbols to align the two classes around the shared behavior and remove the
repeated method bodies.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 27dc568d-fea3-4b23-b477-afbec564cb46

📥 Commits

Reviewing files that changed from the base of the PR and between 65e555e and cfda83b.

📒 Files selected for processing (3)
  • src/main/java/com/mr/global/entity/BaseCreatedDeletedEntity.java
  • src/main/java/com/mr/global/entity/BaseTimeDeletedEntity.java
  • src/main/java/com/mr/global/entity/BaseTimeEntity.java

@on1yoneprivate on1yoneprivate left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수고하셨습니다~

@rkdehdrbs7885-oss rkdehdrbs7885-oss left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그동안 BaseEntity는 전부 하나에 묶는 형식만 봤었는데, 이런 식으로도 할 수 있다는 걸 배웠습니다!

@ownue
ownue merged commit fc9b9c9 into develop Jul 8, 2026
1 check passed
@ownue
ownue deleted the feat/#5-base-entity branch July 8, 2026 14:01
@ownue ownue added the ✨feat label Aug 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

✨ Feature - BaseEntity 구현

3 participants