๐Ÿซง @SQLDelete

JPA์—์„œ ๊ธฐ๋ณธ์ ์ธ ์‚ญ์ œ๋Š” Hard Delete๋กœ ๋˜์–ด ์žˆ๋‹ค.

์ด ์‚ญ์ œ ๋ฐฉ์‹์„ @SQLDelete๋ฅผ ํ†ตํ•ด ์ฟผ๋ฆฌ๋ฅผ ์ž‘์„ฑํ•ด๋‘๋ฉด, delete๊ฐ€ ํ˜ธ์ถœ๋  ๋•Œ ๋ฐ์ดํ„ฐ๋ฅผ ์‹ค์ œ๋กœ ์‚ญ์ œํ•˜์ง€ ์•Š๊ณ  ๋ฏธ๋ฆฌ ์ •์˜ํ•ด๋‘” ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•˜๋„๋ก ๋ฐฉ์‹ ๋ณ€๊ฒฝ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

 

Hard Delete

๋ฌผ๋ฆฌ ์‚ญ์ œ๋ผ๊ณ ๋„ ํ•˜๋ฉฐ, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— Delete Query๋ฅผ ๋‚ ๋ ค ํ•ด๋‹น ๋ฐ์ดํ„ฐ๋ฅผ ์‹ค์ œ๋กœ ์‚ญ์ œํ•œ๋‹ค.

Soft Delete

๋…ผ๋ฆฌ ์‚ญ์ œ๋ผ๊ณ ๋„ ํ•˜๋ฉฐ, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— Update Query๋ฅผ ๋‚ ๋ ค ํ•ด๋‹น ๋ฐ์ดํ„ฐ๊ฐ€ ์‚ญ์ œ๋œ ๊ฒƒ์„ ํ•˜๋‚˜์˜ ์ปฌ๋Ÿผ์„ ์ด์šฉํ•˜์—ฌ ๊ตฌ๋ถ„ํ•œ๋‹ค.

@SQLDelete๋Š” ๋…ผ๋ฆฌ ์‚ญ์ œ๋ฅผ ์œ„ํ•œ ๊ธฐ๋Šฅ์ธ ๊ฒƒ์ด๋‹ค.

 

๐Ÿซง ๋ฌธ์ œ ๋ฐœ๊ฒฌ

ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋‹ค๊ฐ€ ๋ฐœ๊ฒฌํ•˜๊ฒŒ ๋œ ๋ฌธ์ œ..

@Test
@DisplayName("ํšŒ์›์„ ๋…ผ๋ฆฌ ์‚ญ์ œํ•œ๋‹ค")
void succeed_to_soft_delete_user() {
    // given
    User foundUser = userRepository.findById(user.getId()).get();

    // when
    userRepository.delete(foundUser);

    // then
    System.out.println(foundUser.getIsDeleted());
}

์œ„์˜ ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•ด ๋ณด๋ฉด UPDATE ์ฟผ๋ฆฌ๋Š” ์‹คํ–‰๋˜์ง€ ์•Š๊ณ  INSERT ์ฟผ๋ฆฌ๋งŒ ์‹คํ–‰๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

๋˜ํ•œ isDeleted๊ฐ€ ์—ฌ์ „ํžˆ false์ธ๋ฐ, ์ด๋Š” SQLDelete๋Š” ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์—์„œ ๊ด€๋ฆฌ๋˜๋‹ค๊ฐ€ ํŠธ๋žœ์žญ์…˜์ด ๋๋‚˜๊ณ  ์‹ค์ œ DB์— ์ฟผ๋ฆฌ๋ฅผ ๋ณด๋‚ผ๋•Œ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. (์ง€์—ฐ ์“ฐ๊ธฐ, flush๊ฐ€ ๋ฐœ์ƒํ•ด์•ผ ๋จ)

 

๊ทผ๋ฐ!!! ๋‚ด๊ฐ€ ํ•œ์ฐธ ํ—ค๋งธ๋˜ ๊ฒƒ์€ SELECT ์ฟผ๋ฆฌ๋„ ์‹คํ–‰์ด ์•ˆ๋œ๋‹ค๋Š” ๊ฑฐ์˜€๋‹ค.

@Test
@DisplayName("ํšŒ์›์„ ๋…ผ๋ฆฌ ์‚ญ์ œํ•œ๋‹ค")
void succeed_to_soft_delete_user() {
    // given
    User foundUser = userRepository.findById(user.getId()).get();

    // when
    userRepository.delete(foundUser);
    User deletedUser = userRepository.findById(foundUser.getId()).orElse(null);

    // then
    System.out.println("isDeleted After delete: " + deletedUser);
}

๋ถ„๋ช… findById๋กœ foundUser๋Š” ์˜์†ํ™”๊ฐ€ ๋˜์–ด์žˆ์„ ํ…๋ฐ userRepository.findById()์˜ ๊ฒฐ๊ณผ๊ฐ€ null์ด ๋‚˜์˜ค๋Š” ๊ฒƒ์ด ๋„์ €ํžˆ ์ดํ•ด๊ฐ€ ๊ฐ€์งˆ์•Š์•˜๋‹ค.

 

 

findById ์กฐํšŒ๊ฐ€ ์•ˆ๋˜๋‹ˆ๊นŒ flush ํ•ด ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์˜ ๋ณ€๊ฒฝ ๋‚ด์šฉ์„ DB์— ๋ฐ˜์˜ํ•œ ํ›„ ์ดˆ๊ธฐํ™” ํ•ด๋ณด์•˜๋‹ค.

๊ทธ ํ›„ ๋‹ค์‹œ findById ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด deletedUser๋ฅผ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์˜ฌ๋ ค ๋ณด์•˜๋‹ค.

@Test
@DisplayName("ํšŒ์›์„ ๋…ผ๋ฆฌ ์‚ญ์ œํ•œ๋‹ค")
void succeed_to_soft_delete_user() {
    // given
    User foundUser = userRepository.findById(user.getId()).get();

    // when
    userRepository.delete(foundUser);
    em.flush();
    em.clear();
    
    User deletedUser = userRepository.findById(user.getId()).get();

    // then
    assertThat(deletedUser).isNotNull();
    System.out.println("๋™์ผ์„ฑ ๊ฒ€์ฆ: " + (foundUser == deletedUser));
    System.out.println("foundUser is_deleted: " + foundUser.getIsDeleted());
    System.out.println("deletedUser is_deleted: " + deletedUser.getIsDeleted());
}

๊ทธ๋Ÿฐ๋ฐ ๊ฐ‘์ž๊ธฐ UPDATE ๋ฌธ์ด ์‹คํ–‰๋˜๋Š”๋ฐ, foundUser๋Š” deletedUser์™€๋Š” ๋‹ฌ๋ฆฌ deleted_at ํ•„๋“œ๊ฐ€ ๊ฐฑ์‹ ๋˜์ง€ ์•Š์•„ ์ด ๋‘˜์˜ ๋™์ผ์„ฑ์ด ๊นจ์ง€๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

์ด๋Š” ๊ณง foundUser๋Š” ์–ด๋Š ์ˆœ๊ฐ„๋ถ€ํ„ฐ ๋น„์˜์†ํ™”๋œ ์ƒํƒœ์ž„์„ ์˜๋ฏธํ•œ๋‹ค.

 

๐Ÿซง ์™œ ๋น„์˜์†ํ™” ๋๋Š”๋ฐ?!?

์˜์†ํ™”๋œ foundUser๋ฅผ softDete ์ฒ˜๋ฆฌํ•˜๊ณ , findById()๋ฅผ ํ†ตํ•ด User ์—”ํ‹ฐํ‹ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋ฉด ์™œ ๋ฐ˜ํ™˜๊ฐ’์ด null์ด์—ˆ์„๊นŒ?

๋‚˜๋Š” ๋‹จ์ˆœํžˆ deleted_at ํ•„๋“œ๊ฐ€ ์ˆ˜์ •๋˜๋Š” ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ–ˆ์ง€๋งŒ SQLDelete๋ฅผ ํ†ตํ•ด ์ˆ˜ํ–‰๋˜๋Š” UPDATE ๋ฌธ์€ ๋‹จ์ˆœํ•œ ์ˆ˜์ •์ด ์•„๋‹Œ ์‚ญ์ œ์˜ ๋ชฉ์ ์„ ๊ฐ–๋Š” ์ˆ˜์ •์ด๋‹ค. ๋”ฐ๋ผ์„œ soft delete๋ฅผ ํ•˜๋Š” ์‹œ์ ์— foundUser๋ฅผ ํŠน์ˆ˜ํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•˜๊ณ  UPDATE ์ฟผ๋ฆฌ๋ฅผ ์ง€์—ฐ ์“ฐ๊ธฐ ๋“ฑ๋กํ•ด ๋†“์€ ๊ฒƒ์œผ๋กœ ์ถ”์ •๋œ๋‹ค. (๋ฐ”๋กœ ๋น„์˜์†ํ™”์‹œ์ผฐ๋‹ค๋ฉด SELECT ์ฟผ๋ฆฌ๊ฐ€ ๋‚˜๊ฐ”์„ ๊ฒƒ..)

 

nativeQuery๋กœ ๊ฐ€์ ธ์˜จ deletedUser๋Š” ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ๊ด€๋ฆฌ๋˜๋Š” ์ƒํƒœ๊ฐ€ ๋˜์—ˆ๊ณ , savedUser๋Š” ๋น„์˜์†ํ™”๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด ๋‘˜์˜ ๋™์ผ์„ฑ์ด ๊นจ์ง„ ๊ฒƒ ๊ฐ™๋‹ค.

 

 

 

 

 

Soft Delete์™€ Hard Delete์˜ ์ฐจ์ด์™€ @SQLDelete

DB์˜ ์‚ญ์ œ ๊ตฌํ˜„ ๋ฐฉ๋ฒ•์€ ๋‘๊ฐ€์ง€๊ฐ€ ์žˆ๋‹ค. Soft Delete ๋ฐ์ดํ„ฐ๋ฅผ ์‹ค์ œ๋กœ ์‚ญ์ œํ•˜์ง€ ์•Š๊ณ , ์‚ญ์ œ flag๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค. Hard Delete ์‹ค์ œ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ญ์ œํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค. ์‹ค์ œ๋กœ ์„œ๋น„์Šค๋ฅผ ๊ตฌํ˜„ํ•  ๋•Œ์—๋Š” So

hello-backend.tistory.com

 

JPA, H2 ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค ๋Œ€์†Œ๋ฌธ์ž ๊ตฌ๋ถ„์—๋Ÿฌ

๊ฒฐ๋ก  spring์—์„œ ๋Œ€์†Œ๋ฌธ์ž ๊ตฌ๋ถ„์„ ํ•˜์ง€ ์•Š๋„๋ก ํ•˜์—ฌ ํ†ต๊ณผํ•˜์˜€์Šต๋‹ˆ๋‹ค. ๊ณต์‹๋ฌธ์„œ ๋‚ด์šฉ์ค‘ ""(๋”ฐ์˜ดํ‘œ๋กœ ์ด์Šค์ผ€์ดํ”„)๋˜์ง€ ์•Š์€๊ฒƒ์€ ๋Œ€๋ฌธ์ž๋กœ ๋ณ€๊ฒฝํ•˜์—ฌ ์‹คํ–‰ํ•œ๋‹ค๋Š”๊ฒƒ์„ ๋ณด๊ณ  ์ด๋กœ์ธํ•˜์—ฌ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ

lahezy.tistory.com

 

[Spring Boot] Soft Delete ์ •์ฑ…์„ ์œ„ํ•œ @SQLDelete์™€ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ ๊ฐ„์˜ ์žฌ๋ฐŒ๋Š” ์ 

๐Ÿ“• ๋ชฉ์ฐจ 1. Soft Delete ๋ฐ˜์˜ 2. ๋™์ผ์„ฑ์ด ๊นจ์ง„๋‹ค? 3. ๊ณ ์ฐฐ 1. Soft Delete ๋ฐ˜์˜ ๐Ÿ“Œ Annotation @Entity @Getter @Table(name = "user") @NoArgsConstructor(access = AccessLevel.PROTECTED) @DynamicInsert @SQLRestriction("deleted_at IS NULL") @SQLD

jaeseo0519.tistory.com