해결된 질문
작성
·
255
0
학습 관련 질문을 최대한 상세히 남겨주세요!
고민 과정도 같이 나열해주셔도 좋습니다.
먼저 유사한 질문이 있었는지 검색해보세요.
인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요.
좋아요 수 구현 부분 강의를 듣고 따라하던 도중 문제가 생겼습니다.
ArticleLikeCount의 엔티티에서 version에 @Version을 붙이고 난 후, 테이블의 값을 모두 삭제하고 나서 테스트를 돌리면 에러가 발생합니다. 계속해서..
Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect):
위 에러가 계속 발생하네요.
테스트는 like 메서드를 실행했습니다.
이상하게도 강사님 코드로는 정상 작동이 되어서, 그대로 복사해서 붙여넣기로 가져와서 돌려보면 안되네요.
테이블에 데이터가 이미 존재하면 그때부터는 정상적으로 되는 것 같은데, 테이블이 비어있으면 에러가 발생합니다 ㅜㅜ
혹시라도 확인하실 수 있도록 제 프로젝트 파일을 압축해서 올려놓은 링크 공유하겠습니다. ㅜㅜ
https://drive.google.com/file/d/1H9UR9UXZhgmBH-XrXoXPc2-dgrixF7b-/view?usp=drive_link
답변 3
4
혹시 저처럼 최신 버전으로 해당 강의를 보며 따라가시는 분들이 있으실 수도 있어서 도움이 되도록 본 질문과 관련된 문제와 해결 방법 공유하겠습니다.
Version 초기화 수정
일단 스프링부트 3.4.x 를 사용하시는 분들은 강사님 말씀대로 낙관적락 적용 시 init() 메서드에서 version = 0으로 직접 초기화 하는 부분을 빼시면 됩니다. 버전이 업데이트 되며 자동으로 해당 부분을 업데이트 해주는데, 직접 초기화 하는 과정에서 에러가 발생합니다.
RestClient 호출 방법 변경
강의에서 restClient호출 시 메서드 체이닝을 restClient.post().uri().retrieve() 로 하고 있으나 버전 업데이트 이후에는 uri().retrieve().toBodilessEntity()까지 해주어야 합니다. 실제 공식 문서 확인 결과 retrieve() 까지만 호출했을 경우 서버에 호출이 되지 않는다라고 적혀있네요.
onStatus 메서드 체이닝 추가
위에까지 했을 때도 테스트가 정상 작동하지 않습니다. 이유는 서버에서 낙관적락으로 데이터 업데이트 충돌 시 에러가 발생하는데 이 에러를 restClient에서 변환해서 처리할 수 없기 때문입니다. 그 결과 restClient 호출 시 에러가 발생하여 해당 작업을 처리하고 있는 스레드가 죽어버리는 문제가 발생합니다.
따라서 .onStatus 메서드 체이닝을 추가하여 해당 statusCode를 처리할 수 있는 handler를 추가해줘야 합니다.
최종적으로 다음과 같은 방식으로 코드를 수정하면 강의 내용을 따라가는데 문제가 없습니다.
ArticleLikeCount
public static ArticleLikeCount init(Long articleId, Long likeCount) {
ArticleLikeCount articleLikeCount = new ArticleLikeCount();
articleLikeCount.articleId = articleId;
articleLikeCount.likeCount = likeCount;
return articleLikeCount;
}
test - like 메서드
void like(Long articleId, Long userId, String lockType) {
restClient.post()
.uri("/v1/article-likes/articles/{articleId}/users/{userId}/" + lockType, articleId, userId)
.retrieve()
.onStatus(HttpStatusCode::is5xxServerError, (request, response) -> {
return;
})
.toBodilessEntity();
}
1
상훈님, 안녕하세요!
일단 스프링부트 버전을 강의와 동일하게 맞추시는걸 권장드립니다!
버전업이 되면서 동작이 변한 부분들이 종종 보이는 것 같네요.
코드 상에서 version 컬럼을 직접 0으로 초기화하면서,
insert를 해야 하는데 내부적으로 update or delete 작업으로 판단하는 것 같습니다.
version = 0L 초기화 코드를 지우면 정상 동작됩니다!
version == null인 경우에 insert로 판단되는 것 같네요!
다만, 스프링부트 버전 차이에 대한 이슈는 제가 모두 확인해드리기에는 어려움이 있네요..!
크게 주요한 부분이 아니라면 강의와 동일한 버전으로 맞추는 것을 권장드립니다!
0
안녕하세요, 인프런 AI 인턴입니다. 박상훈님.
테스트에서 발생하는 “Row was updated or deleted by another transaction” 오류는 일반적으로 JPA 엔티티에 대한 병행 제어 문제와 관련이 있습니다. @Version
어노테이션을 사용하는 경우, JPA는 낙관적 잠금을 사용하여 동시성 문제를 해결합니다. 이 오류는 트랜잭션 중 특정 엔티티의 버전이 기대한 값과 다를 때 발생할 수 있습니다.
초기 데이터 생성: 엔티티를 테스트하기 전에 초기 데이터를 생성하여 데이터베이스가 비어있지 않은 상태에서 테스트를 시작해보세요. 초기 데이터 삽입은 테스트의 신뢰성을 높일 수 있습니다.
트랜잭션 관리: 서비스에서 트랜잭션 관리를 직접 제어하여, 트랜잭션의 시작과 종료를 명확하게 관리해보세요. 테스트에서 사용하는 메서드가 트랜잭션 내에서 실행되는지 확인해야 합니다.
엔티티 상태 확인: 오류 발생 시, 해당 엔티티가 정확히 어떤 상태였는지 로그를 통해 확인할 수 있는 코드를 추가해보세요. 그렇게 하면 문제의 원인을 정확히 파악하는 데 도움이 됩니다.
또한, 비슷한 문제를 다루고 있는 인프런의 다른 게시물을 참조하는 것도 도움이 될 수 있습니다. 아래는 참고할 만한 자료입니다:
위 방법을 시도해보시고, 추가적인 도움이 필요하다면 프로젝트 환경과 테이블 구조 등 추가 정보를 함께 제공해주시면 좋을 것 같습니다.
저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다. 현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏 추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.
유익한 공유 감사합니다!!