프로젝트/원티드 BE 챌린지

JPA 환경에서 메시지 체인 리팩토링 시 주의점

haema_ 2024. 6. 10. 10:39
728x90

메시지 체인?

레퍼런스를 따라 계속해서 메서드 호출이 이어지는 형태를 말한다.

 

코드로 보자면

product.getUser().getId()

 

 

와 같은 형태이다.

 

흔히 '냄새나는 코드' 라는 이름으로 리팩토링 대상이 되는 구문이다.

 

리팩토링 방법

주로 '캡슐화'를 통해 해결한다.

 

클래스 내부에 해당 메서드 체인을 숨겨서, 외부에서는 product 클래스만으로 해당 상품의 userId를 알 수 있도록 하는 것이다.

 

public class Product{
	private User user;
	//etc
    
	public Long getUserId(){
		return this.user.getId();
        }
}

 

위와 같이 Product 내부에 user.getId()를 숨겨서, 외부에서는 

product.getUserId() 라는 메서드로 product의 userId를 한번에 찾아올 수 있다.

 

JPA에서의 오류

해당 메서드 체인을 리팩토링 하는 과정에서,

getUserId 메서드를 작성한 이후 컴파일 에러가 발생했다.

Caused by: org.springframework.data.repository.query.QueryCreationException: Could not create query for public abstract java.util.List wanted.market.api.domain.orders.repository.OrderRepository.findAllByProductUserIdAndStatus(java.lang.Long,wanted.market.api.domain.orders.enums.OrderStatus); Reason: Failed to create query for method public abstract java.util.List wanted.market.api.domain.orders.repository.OrderRepository.findAllByProductUserIdAndStatus(java.lang.Long,wanted.market.api.domain.orders.enums.OrderStatus); Could not resolve attribute 'userId' of 'wanted.market.api.domain.product.entity.Product'
	at org.springframework.data.repository.query.QueryCreationException.create(QueryCreationException.java:101) ~[spring-data-commons-3.3.0.jar:3.3.0]
	at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.lookupQuery(QueryExecutorMethodInterceptor.java:115) ~[spring-data-commons-3.3.0.jar:3.3.0]
	at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.mapMethodsToQuery(QueryExecutorMethodInterceptor.java:99) ~[spring-data-commons-3.3.0.jar:3.3.0]
	at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.lambda$new$0(QueryExecutorMethodInterceptor.java:88) ~[spring-data-commons-3.3.0.jar:3.3.0]
	at java.base/java.util.Optional.map(Optional.java:260) ~[na:na]
	at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.<init>(QueryExecutorMethodInterceptor.java:88) ~[spring-data-commons-3.3.0.jar:3.3.0]
	at org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:357) ~[spring-data-commons-3.3.0.jar:3.3.0]
	at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.lambda$afterPropertiesSet$5(RepositoryFactoryBeanSupport.java:286) ~[spring-data-commons-3.3.0.jar:3.3.0]
	at org.springframework.data.util.Lazy.getNullable(Lazy.java:135) ~[spring-data-commons-3.3.0.jar:3.3.0]
	at org.springframework.data.util.Lazy.get(Lazy.java:113) ~[spring-data-commons-3.3.0.jar:3.3.0]
	at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.afterPropertiesSet(RepositoryFactoryBeanSupport.java:292) ~[spring-data-commons-3.3.0.jar:3.3.0]
	at org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean.afterPropertiesSet(JpaRepositoryFactoryBean.java:132) ~[spring-data-jpa-3.3.0.jar:3.3.0]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1835) ~[spring-beans-6.1.8.jar:6.1.8]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1784) ~[spring-beans-6.1.8.jar:6.1.8]
	... 44 common frames omitted

 

 

원인은 OrderRepository의 JPA구문 중 findBy~ProductUserId~ 에서 Product의 userId를 찾을 수 없기 때문이라고 한다.

JPA 구문을 내부적으로 변환하는 과정에서 기본 getter가 쓰이는데, getUserId를 custom으로 정의해서 발생하는 오류인 것으로 확인했다.

 

해결 방법

해당 캡슐화 메서드명을 get~이 아닌 다른 이름으로 설정하여 해결할 수 있다.

ex)

public class Product{
	private User user;
	//etc
    
	public Long findUserId(){
		return this.user.getId();
        }
}

 

혹은 getUserId가 아닌

getProductUserId 등 내부적으로 사용하는 이름을 피해서 메서드명을 작성해주면 정상적으로 작동한다!

반응형