1 분 소요

MyBatis로 쿼리를 작성한다면 repository는 interface에 @Mapper를 적용해서 아래처럼 정의한뒤 사용할 수 있다.

@Mapper
interface UserRepository {
  fun selectActiveUsers(): List<User>
  fun seleectInActiveUsers(): List<User>
}


좀 더 단순화해서 repository에 함수 A(), B()가 있고 이 함수들을 활용한 새로운 함수 C()를 만들고 싶다고 하자.
A(), B()는 호출 시 xml에 있는 쿼리를 날린다고 가정하고, C()는 default method로 구현해보자.
대충 이렇게 만들어볼 수 있을 것 같다.

@Mapper
interface Repository {
  fun A(): Int
  fun B(): Int
  fun C(): Int = A() + B()
}


그렇다면 C()를 호출했을 때 A(), B() 응답값을 더한걸 얻을 수 있을까?

@Service
class Service(private val repository: Repository) {
  fun foo() {
    println("C response=${repository.C()}")
  }
}


정답은 그렇지 않다.

A(), B()를 호출했을 때 xml에 매핑된 SQL을 실행할 수 있는 이유는 스프링에서 Repository의 구현체로 프록시 객체를 주입해주기 때문이다. 그래서 interface에 정의된 함수를 호출하면 xml에 매핑된 sql을 실행하는데, C()는 xml에 없으니까 BindingException 예외를 던지게 된다.

org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): ...
  • 만약에 BindingException이 안던져졌어도 this.A(), this.B()를 호출한거여서 쿼리를 정상적으로 실행하지 못했을 것이다. (self-invocation)

interface를 사용하는 편리함을 포기하자니 SqlSessionFactory를 사용해야하는 번거로움이 있다.
그렇다고 interface를 사용하자니 default method를 정의할 수가 없다.

이 경우에는 2가지 해결방법이 있다.

첫 번째는 해당 repository를 사용하는 곳이 제한적일 때(=관리 포인트가 적을 때) 유용하다.
사용하는 곳에서 함수를 별도로 정의해주는 방법이다. 사용하는 곳이 많으면 수정할 때 번거로워서 좋지 않은 방법이다.

@Service
class Service(private val repository: Repository) {
  fun foo() {
    println("C response=${C()}")
  }

  // 여기에 정의
  private fun C(): Int {
    return repository.A() + repository.B()
  }
}


두 번째는 클래스를 따로 만들어서 interface repository를 주입받아 사용하는 방법이다.
(자유지만) 관리 포인트를 최소한으로 줄이기 위해 interface는 다른 곳에서 참조하지 않는다.
interface repository를 사용하던 곳이 많았다면 일일이 C()를 구현해주기보다 이 방법이 보다 편할 것이다.

@Component
class CustomRepository(private val repository: Repository) {
  fun A(): Int = repository.A()
  fun B(): Int = repository.B()
  fun C(): Int = repository.A() + repository.B()
}


그러면 주입받던 클래스 타입만 바꿔주어도 쉽게 교체가 가능하다.

@Service
class Service(private val repository: CustomRepository) { // Repository -> CustomRepository
  fun foo() {
    println("C response=${repository.C()}")
  }
}


References

카테고리:

업데이트:

댓글남기기