같이 보면 좋은 글
🔗 JPQL과 n+1 문제
🧩 프로젝트 와이어프레임 링크
🐚 프로젝트 깃허브 주소
목차
1. softDelete + 쿼리문 활용
2. entity 연관관계
3. postman 환경변수 설정
4. 회고
Review, Store 같은 주요 엔티티에 소프트 삭제 기능을 도입했다.
기존에는 삭제 요청이 들어오면 DB에서 데이터를 완전히 삭제(하드 딜리트) 했는데, 이렇게 하면 나중에 복구하거나 기록을 추적할 수가 없었다...ㅜㅜ
그래서 방향을 바꿨다.
삭제 요청이 오면 DB에서 지우지 않고
deleted = true로 상태만 바꾸도록 했다.
즉, 눈에만 안 보이게 만들고 데이터는 살려두는 방식.
이러면 복구도 가능하고, 추후 로그로 활용하거나 감사용으로도 쓸 수 있다!
@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseEntity {
...생략
public void softDelete() {
this.deleted = true;
this.deletedAt = LocalDateTime.now();
}
public boolean isDeleted() {
return deleted;
}
}
기능은 넣었는데… 문제가 생겼다.
삭제 표시된 데이터도 API에서 계속 보이는 것,,,,
deleted 플래그는 분명히 추가했지만, 조회할 때 그걸
걸러주는 조건을 안 넣었던 게 문제였다.
JPA는 기본적으로 모든 데이터를 다 가져오니까,
deleted = true인 데이터도 아무 조건 없이 다 포함돼서 내려왔다.
예를 들어 findAll()이나 findById()를 호출해도 삭제된 데이터가 쓱— 같이 따라오는 상황.....👻
JPA에게 이렇게 말해보자
"이 테이블에서 가져올 땐, deleted = false 조건을 항상 붙여줘!"
이걸 도와주는 게 바로 ✨ @Where 어노테이션!!!!
소프트딜리트를 구현할 엔티티에 아래의 어노테이션을 달아 구현하면 된다!
@Where(clause = "deleted = false")
public class Menu extends BaseEntity {
...
}
| 항목 | 결과 |
|---|---|
| API 조회 | 🔥 삭제된 데이터가 더 이상 내려오지 않음 |
| 프론트 UX | 👀 삭제된 가게/리뷰가 사용자 화면에 표시되지 않음 |
| 코드 수정 범위 | ✨ @Where 한 줄 추가로 간단하게 해결 |
| 구분 | 하드 삭제 | 소프트 삭제 |
|---|---|---|
| 방식 | DB에서 완전히 삭제 | 삭제 여부만 표시 |
| 복구 가능성 | ❌ 없음 | ✅ 있음 |
| 장점 | 저장 공간 절약, 단순 | 데이터 추적 가능, 감사 로그에 유리 |
| 단점 | 기록이 남지 않음 | 조회 시 필터링 필요 |
중요한 사용자 데이터나 기록 또는 감사가 필요한 데이터는 소프트 삭제로,
테스트 데이터나 로그성 데이터는 하드 삭제로 처리하는 게 적당하다!
: 객체 지향적으로 데이터의 관계를 표현하기 위한 필수적 요소
1. OneToOne (1:1)
한 엔티티가 다른 하나의 엔티티와만 연결되는 관계
→ 한 사람은 하나의 여권만 가진다.
2. OneToMany (1:N)
한 엔티티가 여러 엔티티를 가질 수 있는 관계
→ 한 가게는 여러 메뉴를 가진다.
3. ManyToOne (N:1)
여러 엔티티가 하나의 엔티티에 속하는 관계
→ 여러 메뉴가 한 가게에 속한다. (OneToMany의 반대 방향)
4. ManyToMany (N:N)
여러 엔티티가 서로 여러 엔티티와 연결되는 관계
→ 한 학생은 여러 수업을 듣고, 한 수업에 여러 학생이 있다
처음에 이 관계를 설계할 때 진짜 너무 헷갈렸다...
Store랑 Menu는 어떤 관계지?
장바구니(Cart)는 사용자(User)랑 어떻게 연결하지?
무조건 @OneToMany만 쓰면 되는 거 아니야? (아님)
그래서 예시로 생각해봤다
가게Store가 여러 개의 메뉴Menu를 팔잖아?
버거킹(가게) → 와퍼, 콰트로치즈, 감자튀김 등등
🔗 Store → Menu ➡️ @OneToMany !
: 가게 입장에서 메뉴가 여러 개니까
(하나의 가게는 많은 메뉴를 가진다)
🔗 Menu → Store ➡️ @ManyToOne!!
(여러 메뉴가 하나의 가게에 속함)
: 메뉴 하나는 어디 가게 메뉴인지 알아야 하니까
그럼 메뉴 입장에서는 가게가 하나니까
🔗 Menu → Store ➡️ @ManyToOne
:메뉴 입장에서는 가게가 하나니까
(여러 메뉴가 하나의 가게에 속함)
🔗 User → Cart ➡️ @OneToMany !
: OneToOne도 맞지만, 향후 유저가 여러개의 장바구니를 가질 수 있도록 리팩토링될 가능성이 있기때문에 원투매니로 설정하는게 좋다!
(유저는 각각 고유의 장바구니를 가진다)
🔗 Cart → CartItem ➡️ @OneToMany !
: 장바구니는 여러개의 아이템을 가질 수 있다
🔗 Cart → Store ➡️ @ManyToOne !
: 하나의 가게에 A유저가 장바구니를 생성하고, B유저도 장바구니를 생성할 수 있기때문에
(유저는 해당 가게 이외의 가게에서 품목을 담을 시 초기화된다)
Redis 사용해보기
→ 검색어 필터링하기, refresh 토큰 저장 등등..
프론트와 연결하는 방법 공부해보기
→ Figma + Al 로 프론트 생성해보기 https://theinnovators.zone/archives/4185
외부 API 연동
OAuth 구현해보기