πŸ“š[Spring] Dirty Checking

텁텁·2025λ…„ 5μ›” 28일

κ²Œμ‹œκΈ€ μ—…λ°μ΄νŠΈ κΈ°λŠ₯을 κ΅¬ν˜„ν•˜λ˜ 쀑 μš”μ²­ 받은 ν•„λ“œλ§Œ μ„ λ³„μ μœΌλ‘œ λ³€κ²½λ˜λ„λ‘ μ²˜λ¦¬ν•˜κ³  μ‹Άμ—ˆλ‹€.
예λ₯Ό λ“€μ–΄ μ‚¬μš©μžκ°€ title 만 μˆ˜μ •ν•˜κ³  content λŠ” κ·ΈλŒ€λ‘œ 두고 싢을 수 있기 λ•Œλ¬Έμ— μš”μ²­ 객체에 ν¬ν•¨λœ ν•„λ“œλ“€ 쀑 null 이 μ•„λ‹Œ κ²ƒλ§Œ κ³¨λΌμ„œ μ μš©λ˜λ„λ‘ κ΅¬ν˜„ν–ˆλ‹€.


patchCheck λ©”μ„œλ“œ

이λ₯Ό μœ„ν•΄ μ—”ν‹°ν‹° 내뢀에 μ•„λž˜μ™€ 같은 λ©”μ„œλ“œλ₯Ό λ§Œλ“€μ—ˆλ‹€.

public void patchCheck(UpdatePostRequestDto uDto) {
    if(uDto.getTitle() != null) {
        this.title = uDto.getTitle();
    }

    if(uDto.getContent() != null) {
        this.content = uDto.getContent();
    }
}

ν•΄λ‹Ή λ©”μ„œλ“œλŠ” setter 와 μœ μ‚¬ν•˜μ§€λ§Œ νŠΉμ •ν•œ 의미λ₯Ό κ°€μ§€λŠ” μ „μš© λ©”μ„œλ“œλ‘œ λ”°λ‘œ λΆ„λ¦¬ν–ˆλ‹€.
null 이 μ•„λ‹Œ ν•„λ“œλ§Œ μ—”ν‹°ν‹° 값에 반영 ν•˜λŠ” μ—…λ°μ΄νŠΈμš© μˆ˜μ • λ©”μ„œλ“œλ‘œ 이름을 λͺ…ν™•ν•˜κ²Œ ν‘œν˜„ν•¨μœΌλ‘œμ¨ μ˜λ„λ₯Ό λ“œλŸ¬λƒˆλ‹€.


μš”μ²­ DTO

μš”μ²­μ— μ‚¬μš©λ˜λŠ” DTO λŠ” λ‹€μŒκ³Ό κ°™λ‹€.

@Getter
public class UpdatePostRequestDto {

    @Size(max = 20, message = "제λͺ©μ€ 20자 μ΄λ‚΄λ‘œ μž‘μ„±κ°€λŠ₯ν•©λ‹ˆλ‹€.")
    private String title;

    @Size(min = 5, max = 200, message = "μž‘μ„±κΈ€μ€ 5~200자 μ΄λ‚΄λ‘œ μž‘μ„±κ°€λŠ₯ν•©λ‹ˆλ‹€.")
    private String content;

}

ν•„λ“œλ“€μ€ null 을 ν—ˆμš©ν•˜λ©° 검증 λ‘œμ§μ€ μ‘΄μž¬ν•˜μ§€λ§Œ κ°’ μžμ²΄λŠ” optional ν•˜λ‹€.
μ‹€μ œλ‘œ μ–΄λ–€ ν•„λ“œκ°€ μˆ˜μ •λ μ§€λŠ” μš”μ²­μ— 따라 λ‹¬λΌμ§ˆ 수 μžˆλ‹€.


μ„œλΉ„μŠ€ 둜직

    @Transactional
    public PostResponseDto update(Long id, UserDetails userDetails, UpdatePostRequestDto uDto) {
        Post foundPost = validateUserAccessToPost(id, userDetails);

        //μˆ˜μ • λ©”μ„œλ“œ( == setter) -> λ”ν‹°μ²΄ν‚ΉμœΌλ‘œ save() κΉŒμ§€ μ§„ν–‰
        foundPost.patchCheck(uDto);

        return PostResponseDto.from(foundPost);
    }

@Transactional μ•ˆμ—μ„œ μ—”ν‹°ν‹°μ˜ ν•„λ“œλ₯Ό λ³€κ²½ν•˜κ³  save() 같은 λͺ…μ‹œμ μΈ λ©”μ„œλ“œ 호좜 없이도
JPA 의 더티 체킹(Dirty Checking) κΈ°λŠ₯에 μ˜ν•΄ μžλ™μœΌλ‘œ μ—…λ°μ΄νŠΈ 쿼리가 μ‹€ν–‰λœλ‹€.


Dirty Checking μ΄λž€?

JPA λŠ” νŠΈλžœμž­μ…˜ λ‚΄μ—μ„œ κ΄€λ¦¬λ˜κ³  μžˆλŠ” μ˜μ† μƒνƒœμ˜ 엔티티에 λŒ€ν•΄ λ³€κ²½ 사항이 λ°œμƒν•˜λ©΄ μžλ™μœΌλ‘œ 감지(dirty check) ν•΄μ„œ SQL update 쿼리λ₯Ό 생성해쀀닀.
즉 νŠΈλžœμž­μ…˜μ΄ μ’…λ£Œλ  λ•Œ μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ— μ €μž₯된 초기 μƒνƒœμ™€ ν˜„μž¬ μ—”ν‹°ν‹° μƒνƒœλ₯Ό λΉ„κ΅ν•΄μ„œ 바뀐 λΆ€λΆ„λ§Œ DB에 λ°˜μ˜ν•œλ‹€.
이λ₯Ό 톡해 save() 같은 λͺ…μ‹œμ  호좜 없이도 μžλ™ 반영이 κ°€λŠ₯ν•΄μ§„λ‹€.

profile
μ°¨κ·Όμ°¨κ·Ό

2개의 λŒ“κΈ€

comment-user-thumbnail
2025λ…„ 5μ›” 30일
public void patchCheck(UpdatePostRequestDto uDto) {
    if(uDto.getTitle() != null) {
        this.title = uDto.getTitle();
    }

    if(uDto.getContent() != null) {
        this.content = uDto.getContent();
    }
}

이 μ½”λ“œμ—μ„œ UpdatePostRequestDtoλ₯Ό μ—”ν‹°ν‹°κ°€ μ•Œκ³  μžˆμ–΄λ„ λ˜λŠ”κ°€? 둜 νŠœν„°λ‹˜μ„ 방문해보셔도 쒋을 것 κ°™μŠ΅λ‹ˆλ‹€.

그외에 λ³€μˆ˜λͺ…이 patchCheck인데, setFieldsIfNotNull 같은 ν˜•μ‹μœΌλ‘œ λ©”μ†Œλ“œλͺ…μ—μ„œ μ˜λ―Έκ°€ μ’€ 더 λͺ…ν™•νžˆ λ“œλŸ¬λ‚˜λ©΄ 쒋을 것 κ°™λ‹€λŠ” 생각이 λ“­λ‹ˆλ‹€.

1개의 λ‹΅κΈ€