2025. 7. 27. 02:24ㆍDB/JPA
개요
현재 진행 중인 프로젝트에서 유저 회원 탈퇴를 소프트딜리트로 구현을 하기로 하여 만들어보았다.
여러 생각을 하면서 만들다보니 소스코드가 깔끔하지는 않은데 글을 적으면서 정리를 해보기로 하였다.
소프트딜리트의 원리
소프트 딜리트는 데이터를 실제로 삭제하지 않고, deleted 같은 플래그 값을 true로 바꿔서 논리적으로 삭제를 하는 방식이다.
조회시 과거에는 @Where 어노테이션을 사용하였지만, 현재는 @SQLRestriction 어노테이션을 사용하여 구현한다.
JPA에서는 @SQLDelete, @Where 어노테이션 등을 사용해 자동화할 수 있었지만, 최신 버전에서는 필터링 로직 구현하는 방식이 권장된다.
이번에는 간단하게 보여주기 위해 프로젝트 일부를 잘라오면서, 단순하게 구현을 하도록 하겠다.
소프트 딜리트 코드
User.java
@Getter
@NoArgsConstructor
@Entity
@Table(name = "USERS")
@SQLRestriction("user_status = 'ACTIVE'")
public class User extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique = true, nullable = false, length = 30)
private String email;
@Column(nullable = false)
private String password;
@Enumerated(EnumType.STRING)
@Column(name = "user_status",nullable = false)
private UserStatus status;
public User(String email, String password) {
this.email = email;
this.password = password;
this.status = UserStatus.ACTIVE;
}
@Override
public void softDelete() {
super.softDelete();
this.status = UserStatus.DELETE;
}
}
유저 클래스에는 softDelete를 @Override를 사용해서 BaseEntity.java 의 softDelete를 User 클래스에서도 가지고 있는 플래그 역할인 UserStatus와 deletedAt을 같이 변경을 하였다.
BaseEntity.java
@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseEntity {
@CreatedDate
@Column(updatable = false)
private LocalDateTime createdAt;
@LastModifiedDate
private LocalDateTime updatedAt;
private LocalDateTime deletedAt;
public void softDelete() {
this.deletedAt = LocalDateTime.now();
}
public boolean isDeleted() {
return this.deletedAt != null;
}
}
UserRepository.java
public interface UserRepository extends JpaRepository<User, Long> {
default void delete(User user) {
throw new UnsupportedOperationException("plz use User.class to softDelete method");
}
}
UserRepository 에서 delete 메소드를 사용하려고 하면 해당 메소드를 사용하지 말고 User 클래스에 있는 소프트 딜리트를 사용하도록 권장을 하려고 사용하였다.
UserService.java
@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
// 생략
@Transactional
public void deleteUser(Long userId, String password) {
User user = userRepository.findById(userId).orElseThrow(
() -> new RuntimeException("User not found"));
user.softDelete();
}
}
결과물
mysql> select * from users;
+--------------+---------------+---+----------------+------------------------------+-------------+-------------+
| created_at | deleted_at | id | updated_at | email | password | user_status |
+--------------+---------------+---+----------------+------------------------------+-------------+-------------+
| NULL | NULL | 1 | NULL | Email123@naver.com | $2a$10$Zme5TDMZ.tdDGzslUZmyGeNBAjlqPWef/uAYRjuMrv2xLdTxU0I1G | ACTIVE |
+--------------+---------------+---+----------------+------------------------------+-------------+-------------+
1 row in set (0.01 sec)
지금 확인한 건데 created_at과 updated_at에 버그가 있다 추후 수정하도록 하고
user_status값을 확인하면 ACTIVE 라고 적혀있다.
mysql> select * from users;
+--------------+---------------+---+----------------+------------------------------+-------------+-------------+
| created_at | deleted_at | id | updated_at | email | password | user_status |
+--------------+---------------+---+----------------+------------------------------+-------------+-------------+
| NULL | 2025-07-27 02:19:55.547515 | 1 | NULL | Email123@naver.com | $2a$10$Zme5TDMZ.tdDGzslUZmyGeNBAjlqPWef/uAYRjuMrv2xLdTxU0I1G | DELETE |
+--------------+---------------+---+----------------+------------------------------+-------------+-------------+
1 row in set (0.01 sec)
delete_at과 user_status 에 플래그 값이 들어가고 시간이 적힌 것을 확인 할 수 있었다.
마무리
created_at과 update_at이 버그가 날것을 블로그 적다가 확인한 것이 정말로 다행이라고 생각이 든다.
빨리 수정해야겠다.
'DB > JPA' 카테고리의 다른 글
[JPA] cascade 에 cascadeType 은 어떤 것이 있을까? (0) | 2025.03.13 |
---|