본문 바로가기
프로젝트

GiftFunding) Dirth Checking (feat.회원정보 입력된 부분 일부만 수정할 때)

by son_i 2023. 11. 30.
728x90

더티체킹(Dirth Checking)이란 ?

 Dirth ) 엔티티 데이터의 변경된 부분

 Dirth Checking) 변경된 부분을 감지한다.

 

JPA에서 영속성 컨테이너가 관리하는 엔티티의 상태를 감지해서 변경된 부분이 있다면 자동으로 트랜잭션이 끝나는 시점에 데이터베이스에 반영하는 기능.

 

개발자가 update 관련된 쿼리를 작성하지 않아도 되기 때문에, 코드의 복잡성을 줄일 수 있다는 특징이 있다.

 


더티 체킹 조건

  • 영속성 컨텍스트에서 관리되는 엔티티

영속성 컨텍스트는 엔티티를 처음 조회할 때 시작되며, 이후 변경을 감지한다.

준영속(Detach된 엔티티)/비영속(DB에 반영되기 전 처음 생성된 엔티티) 상태의 엔티티는 더티체킹의 대상이 되지 못 한다. == 값을 변경해도 DB에 반영되지 않는다.

 

영속성 컨텍스트 포스팅 참조 :   JPA 영속성 컨텍스트 (tistory.com)

 

  • Transcation이 커밋 되었을 때

트랜잭션이 커밋되기 전까지 영속성 컨텍스트는 변경사항을 추적하기만 하고, DB에 반영하지 않는다. 따라서 트랜잭션이 커밋될 때 영속성 컨텍스트는 엔티티의 변경된 상태를 DB에 반영한다.


더티 체킹 특징

- 별도로 데이터베이스에 save 작업을 하지 않아도 변경된 내용을 감지하여 자동으로 update 해줌.

 

- 더티체킹은 모든 필드 업데이트를 기본으로 한다. 

변경된 필드만 업데이트 하고자 할 때는 엔티티에 @DynamicUpdate 를 붙인다.

 

 ex) birthDay 필드만 변경했을 때

   @DynamicUpdate 없을 때 모든 필드가 업데이트

 

@DynamicUpdate 있을 때 birthDay 필드와 수정일시 필드 업데이트.


더티체킹 방법

Spring data JPA에서는 @Transactional을 사용해서 더티 체킹을 수행할 수 있음.

 

나는 회원정보 일부를 수정하는 서비스부분의 메소드에 사용하였다.

<UserServiceImpl>

@Override
    @Transactional
    public UpdateInfo.Response update(UpdateInfo.Request request, String token) {
        String mail = tokenProvider.getMail(
            this.resolveTokenFromRequest(token));
        User user = userRepository.findByEmail(mail)
            .orElseThrow(() -> new UserException(USER_NOT_FOUND));

        updateCheck(request, user);

        return user.toDto();
    }

    private void updateCheck(UpdateInfo.Request request, User user) {
        if (StringUtils.hasText(request.getName())) {
            user.setName(request.getName());
        }
        if (StringUtils.hasText(request.getPhone())) {
            user.setPhone(request.getPhone());
        }
        if (StringUtils.hasText(request.getAddress())) {
            user.setAddress(request.getAddress());
        }
        if (!ObjectUtils.isEmpty(request.getBirthDay())) {
            user.setBirthDay(request.getBirthDay());
        }
    }

이 메소드 안에서는 save 해주는 부분이 없음에도 더티체크를 통해 필드가 변경된 부분만 트랜잭션이 커밋될 때 DB에 반영된다. 

 

@Transactional이 뭐길래 ?!

스프링에서 트랜잭션 처리를 위해 사용하는 선언적 트랜잭션.

 

선언적 트랜잭션이란 설정파일 or 어노테이션 방식으로 간편하게 트랜잭션에 관한 행위를 정의하는 것

 

나는 어노테이션을 이용한 방법을 사용한 것이고 @Transactional은 클래스 단위, 메소드 단위에 선언해준다.

 

- 동작원리

트랜잭션은 Spring AOP를 통해 구현되어 있다.

더 정확히는 어노테이션 기반 AOP를 통해 구현되어있다. 따라서 아래와 같은 특징이 있다.

  

 1. 클래스, 메소드에 @Transactional이 선언되면 해당 클래스에 트랜잭션이 적용된 프록시 객체 생성

 2. 프록시 객체는 @Transactional이 포함된 메소드가 호출될 경우, 트랜잭션을 시작하고 Commit or Rollback을 수행

 3. CheckedException or 예외 없을 때 Commit

 4. UncheckedException이 발생하면 Rollback

 

- 주의할 점

  •  우선순위

@Transactional은 우선순위를 가지고 있다.

클래스 메소드에 선언된 트랜잭션 우선순위가 가장 높고 인터페이스에 선언된 트랜잭션 순위가 가장 낮음.

 

클래스 메소드 -> 클래스 ->  인터페이스 메소드 -> 인터페이스

따라서 공통적인 트랜잭션 규칙은 클래스에, 특별한 규칙은 메소드에 선언하는 식으로 구성할 수 있음.

 

인터페이스 보다는 클래스에 적용하는 것을 권장.

 인터페이스나 인터페이스의 메서드에 적용할 수 있음
 하지만 인터페이스 기반 프록시에서만 유효한 트랜잭션이 설정됨.
 자바 어노테이션은 인터페이스로부터 상속되지 않기 때문에 클래스 기반 프록시 or AspectJ 기반에서는 트랜잭션 설정을 인식할 수 없다.

 

  • 트랜잭션 모드

@Transactional은 Proxy Mode와 AspectJ Mode가 있는데 Proxy Mode가 Default로 설정돼있음.

Proxy Mode는 다음과 같은 경우 동작하지 않음.

 

- 반드시 public 메소드에 적용되어야 함.

  Protected, Private method에서는 선언되어도 에러는 없지만 동작도 안 함.

  Non-public 메서드에 적용하고 싶으면 AspectJ Mode 고려.

 

- Transactional이 적용되지 않은 Public method에서 @Transactional이 적용된 Public Method를 호출할 경우, 트랜잭션 동작하지 않음

 

참고 https://imiyoungman.tistory.com/9 

 


테스트 환경에서 @Transactional

테스트 메소드에 @Transactional 을 사용하면 트랜잭션으로 감싸지며 메소드가 종료될 때 자동으로 롤백된다.

단 , id는 롤백되지 않는다. Auto Increment옵션은 트랜잭션의 범위 밖에서 동작하기 때문이다.