Spring/문제 해결 (Troubleshooting)

JPA 변경사항이 즉시 반영되지 않을 때 - 영속성 컨텍스트와 DB 값 불일치 문제

가지코딩 2025. 5. 23. 12:17

1. 문제 상황

  • update API 에서,
  • 데이터베이스에는 이름 변경이 정상적으로 반영됨
  • 하지만 클라이언트에 반환되는 응답 DTO의 updatedAt 값은 변경 전 값이 계속 보여지는 문제 발생
@Transactional
@Override
public UserResponseDTO updateName(Long id, UserRequestDTO requestDTO) {
    User user = userRepository.findById(id)
        .orElseThrow(() -> UserExceptionFactory.notFoundById(id));

    ...

    user.setName(requestDTO.getName());

    return new UserResponseDTO(user);
}

 

 

DB 반영 잘됨

 

 

응답의 updatedAt 은 변경되지 않음


2. 원인 분석

왜 updatedAt 값 변경이 안될까?

  • JPA는 트랜잭션 종료 시점에 데이터베이스에 변경사항을 반영(flush 및 commit)한다.
    • 종료 시점: UserResponseDTO updateName(Long id, UserRequestDTO requestDTO){} 가 종료될 때 !
  • 변경 사항 반영 전에 응답을 반환하기 때문에, 응답 데이터의 updatedAt 에는 수정 시간이 반영되지 않음.

 

* 흐름도


해결 방법 1

flush() + refresh() 호출

@Service
public class UserServiceImpl implements UserService {

    private final UserRepository userRepository;
    private final PasswordEncoder passwordEncoder;
    private final EntityManager entityManager; // 영속성 컨텍스트 접근


    @Transactional
    @Override
    public UserResponseDTO updateName(Long id, UserRequestDTO requestDTO) {
        User user = userRepository.findById(id)
            .orElseThrow(() -> UserExceptionFactory.notFoundById(id)); // 유저 체크

        ...

        // 이름 변경
        user.setName(requestDTO.getName());

        // DB에 즉시 반영하고, 영속성 컨텍스트의 엔티티 갱신
        userRepository.flush();         // DB에 SQL 실행
        entityManager.refresh(user);   	// DB로부터 다시 조회하여 updatedAt 포함 동기화

        return new UserResponseDTO(user);
    }
}

해결 방법 2

DB가 아닌 애플리케이션에서 updatedAt 갱신 처리

  • @LastModifiedDate 같은 Spring Data JPA Auditing 기능을 활용해, 애플리케이션 레벨에서 updatedAt 필드를 즉시 갱신하게 한다.
  • 트랜잭션이 끝나면서 커밋이 발생할 때 내부적으로 flush가 호출되므로, 별도 flush() 호출 없이도 updatedAt이 반영된다.
    • 하지만 !!! 이런 경우에는 flush 고려해야 한다
      • 트랜잭션이 아직 커밋되지 않은 상태에서, 변경된 updatedAt 값을 즉시 읽어야 할 때
@Transactional
@Override
public UserResponseDTO updateName(Long id, UserRequestDTO requestDTO) {
    User user = userRepository.findById(id).orElseThrow(() -> UserExceptionFactory.notFoundById(id)); // 유저 체크

    // 이름 변경
    user.setName(requestDTO.getName());

    // DB 에 변경 사항 즉시 반영
    userRepository.flush();

    return new UserResponseDTO(user);
}

 

 

 

* @LastModifiedDate 와 DB의 ON UPDATE CURRENT_TIMESTAMP 동시 사용 문제 주의

https://gajicoding.tistory.com/353

 

@LastModifiedDate 와 DB의 ON UPDATE CURRENT_TIMESTAMP 동시 사용 문제

updatedAt 필드를 자동으로 관리하는 방법 2가지JPA Auditing의 @LastModifiedDate: 엔티티가 변경될 때 JPA가 Java 객체의 필드를 수정DB의 ON UPDATE CURRENT_TIMESTAMP: DB에서 직접 updated_at 값을 자동 변경 이 둘을

gajicoding.tistory.com