인프런 커뮤니티 질문&답변
@Value 추가 시 에러 발생
해결된 질문
작성
·
331
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
답변 4
5

혹시 저처럼 최신 버전으로 해당 강의를 보며 따라가시는 분들이 있으실 수도 있어서 도움이 되도록 본 질문과 관련된 문제와 해결 방법 공유하겠습니다.
- 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 초기화 0L 해주는거 때문에 낙관적 락 예외가 터졌었는데, 지우니깐 null 들어가고 이후에 버전업하면 0이 되더라구요.
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는 낙관적 잠금을 사용하여 동시성 문제를 해결합니다. 이 오류는 트랜잭션 중 특정 엔티티의 버전이 기대한 값과 다를 때 발생할 수 있습니다.
해결 방법 제안:
- 초기 데이터 생성: 엔티티를 테스트하기 전에 초기 데이터를 생성하여 데이터베이스가 비어있지 않은 상태에서 테스트를 시작해보세요. 초기 데이터 삽입은 테스트의 신뢰성을 높일 수 있습니다. 
- 트랜잭션 관리: 서비스에서 트랜잭션 관리를 직접 제어하여, 트랜잭션의 시작과 종료를 명확하게 관리해보세요. 테스트에서 사용하는 메서드가 트랜잭션 내에서 실행되는지 확인해야 합니다. 
- 엔티티 상태 확인: 오류 발생 시, 해당 엔티티가 정확히 어떤 상태였는지 로그를 통해 확인할 수 있는 코드를 추가해보세요. 그렇게 하면 문제의 원인을 정확히 파악하는 데 도움이 됩니다. 
또한, 비슷한 문제를 다루고 있는 인프런의 다른 게시물을 참조하는 것도 도움이 될 수 있습니다. 아래는 참고할 만한 자료입니다:
위 방법을 시도해보시고, 추가적인 도움이 필요하다면 프로젝트 환경과 테이블 구조 등 추가 정보를 함께 제공해주시면 좋을 것 같습니다.
저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다. 현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏 추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.







유익한 공유 감사합니다!!