JpaSystemException이 뭐야..?

뭐가 문제지?

오류가 발생된 프로젝트 배경

걸음수에 따른 각 학교별 유저의 랭킹을 매번 조회하기에는 성능 저하가 크기 때문에 일정 간격으로 유저의 랭킹을 데이터베이스에 저장시키는 작업을 한다.

기본 키의 유일성을 주기 위해 유저 아이디와 생성된 날짜, 기간(WEEK / MONTH),범위(SCHOOL / CLASS)을 복합키로 구성했다. 그렇기 때문에 이를 자바 코드로 구현을 할 때 EmbeddId 또는 IdClass를 이용을 해야 한다. 그 중에서도 데이터베이스에 좀 더 초점을 맞췄기 때문에 IdClass를 이용해 복합키를 표현했다.

기존에 짜여진 코드

UserRank.java

import ...

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Entity
@Cacheable
@IdClass(UserRankId.class)
@Builder
public class UserRank {

    @Id
    private Long userId;

    @Id
    private LocalDate createdAt;

    @Id
    @Enumerated(EnumType.STRING)
    private DateType dateType;

    @Id
    @Enumerated(EnumType.STRING)
    private UserRankScope scopeType;

	...
}

UserRankId.java

import ...

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@EqualsAndHashCode
public class UserRankId implements Serializable {
    private String userId;

    private LocalDate createdAt;

    private String dateType;

    private String scopeType;
}

이런식으로 JPA에서 제공하는 메소드 쿼리를 이용해 유저 랭킹을 조회한다.
직접 postman을 이용해 조회를 해보면 다음과 같은 오류가 발생한다.

2022-04-20 10:19:39.018 ERROR 64492 --- [nio-8080-exec-3] c.w.w.global.error.ExceptionFilter       : org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.orm.jpa.JpaSystemException: Could not set field value [WEEK] value by reflection : [class com.walkhub.walkhub.domain.rank.domain.UserRankId.dateType] setter of com.walkhub.walkhub.domain.rank.domain.UserRankId.dateType; nested exception is org.hibernate.PropertyAccessException: Could not set field value [WEEK] value by reflection : [class com.walkhub.walkhub.domain.rank.domain.UserRankId.dateType] setter of com.walkhub.walkhub.domain.rank.domain.UserRankId.dateType

JpaSystemException? reflection? PropertyAccessException?
처음 보는 예외들이다.. 도대체 뭐가 문제지?

해결 과정

가장 먼저 "Could not set field value [WEEK] value by reflection" 이라는 말을 보고 잘은 모르겠지만 set field value 뭔가 UserRank 클래스 쪽에 문제가 있는 것 같다고 생각했다. 그래서 UserRank 클래스를 차근차근 뜯어보았다. 값을 설정해주는 부분에서 오류가 나와서 생성자를 봤지만 오류가 터질 수 없는 구조였다.

흠... 그러면 어디가 문제일까? 

그 다음으로 의심가는 부분은 바로 IdClass 부분이었다. 그러기 위해서는 위에 있던 UserRankId.java를 유심히 봤다.

어?? UserRank의 @Id에 매핑되어 있던 필드의 타입은 각각 Long, LocalDate, DateType, UserRankScope였지만 UserRankId에 적혀 있는 필드의 타입은 각각 String, LocalDate, String, String이었다...

혹시나 하는 마음에 복합키로 매핑된 필드와 IdClass 필드의 타입을 모두 맞추어주자 정상적으로 동작했다 🙀

결론

해결하고보니 단순한 실수로 인해 발생한 문제였다. 처음 보는 오류를 접하자 당황하고 겁부터 먹은 내 자신이 부끄러워졌다 🥲

오류는 사소한 것에서부터 시작된다는 것을 명심하자. 어떤 문제가 발생하더라도 가장 기본이 되는 부분부터 찾아보고 되돌아보는 것이 중요하다는 것을 깨달았다.

필드의 타입을 수정하면서 IdClass와의 타입 불일치가 일어나 발생한 오류였다. IdClass는 데이터베이스 중심적인 관점으로 복합키의 컬럼을 쉽게 구분할 수 있지만, 코딩을 함에 있어 실수를 유발하게 된다. 이번 기회를 통해 그동안 느끼지 못했던 장점과 단점을 채감할 수 있어서 좋은 경험이었던 것 같다.

앞으로 많은 컬럼을 복합키로 사용하게 될 경우에 IdClass 대신 EmbeddedId 사용을 고려해보면 좋을 것 같다.

처음 보는 오류를 마주친다면, 도전해보자!

좋은 웹페이지 즐겨찾기