얼마전 이상한 현상을 겪었는데,

* AMapper.doMethod(Integer)
* BMapper.doMethod(Integer)

와 같이 서로 다른 맵퍼 인터페이스가 우연히 동일한 메소드 이름과 시그니쳐를 갖을 경우에 에상치 못한 결과가 발생한다.

물론, AMapper의 쿼리를 기술한 xml 파일 안에 다음과 같이 오타가 있을 경우에 그렇다.

<mapper namespace="<package_list>.AMapper">
    <select id="doMethods" parameterType="integer" resultType="AVO"> ..... </select>
</mapper>

이 현상은, 결론부터 말하면 AMapper.doMethod를 실행했으나 실제로 실행된 쿼리는 BMapper.doMethod의 것이다. 즉,전혀 엉뚱한 쿼리를 불러다가 실행하는 것이다.

Mapper 인터페이스를 통해 doMethod를 호출하면 다음과 같은 키값으로 이용해서 대응하는 쿼리를 추출하려고 시도한다.

String key = "<package_list>.AMapper.doMethod"
MappedStatement stmt = StrictMap.get(Key); // 여기서 참조를 얻어낸다.
상식대로라면 키값이 "<packaget_list>.AMapper.doMethod" 이므로 여기에 대응하는 MapperMethod 는 찾지 못한다. 왜냐하면 오타때문에 원하는 MapperMethod 는  "<package_list>.AMapper.doMethods" 의 키값으로 등록되어 있기 때문이다.

실제로 위 코드에서 value 는 null 인데,  getShortName(..)으로 한 번 더 검색을 시도한다.
주어진 키값인 "<package_list>.AMapper.doMethod" 로 대응하는 MappedStatement를 못찾자, 메소드 이름인 "doMethod"만 가지고 다시 시도한다.

그런데 처음 iBatis 가 로드되면 XML 설정 파일들을 해석해서 MappedStatement 를 생성하고 strictMap 에 넣어두는데, 아래와 같이 하나의 mapped statement 인스턴스를 다음과 같이 두 번 씩 넣어준다.

"<package_list>.AMapper.doMethods" = mapped_statementA
"doMethods" = mapped_statementA
"<package_list>.BMapper.doMethod" = mapped_statementB
"doMethod" = mapped_statementB

AMapper 의 메소드 이름인 "doMethod"로 조회했지만 BMapper의 "doMethod"가 걸리는 바람에 완전히 다른 쿼리가 실행된 것이다.

왜 구현을 이렇게 했을까? 좀 의외인데...

다른 부분에서 full name 이 아니라 메소드 이름만으로 접근해야하는 부분이 있는 것인지, 아니면 처음 구현할 때 메소드 이름만으로 주어진 MappedStatement 를 찾도록 했다가 중간에 구현이나 설계가 바뀌면서 호환성 문제때문에 저렇게 남겨둔 것인지는... 확실치 않다.

다만 한가지 중요한 점은 Mapper interface 에 메소드를 선언할 때 가급적 중복이 없게끔 해야, 저런 "덫"을 피할 수 있다.

User.selectById 가 아니라 User.selectByUserId 라든가 User.selectUserById 와 같이 메소드 이름이 장황해지더라도 이름이 중복될 여지를 남기지 않아야 할 듯 하다.

왜 이런 짓을....


Posted by yeori
,