association 엘리먼트에 걸린 내용을 잘 보면 columnPrefix 어트리뷰트에 USR_ 이라고 명시하고 다른 결과맵으로 맵핑하도록 해놓았다. 그리고 discriminator 에 javaType 으로 enum 타입을 적시하고 어떤 타입으로 맵핑할 것인지 정한다. 물론 Post의 하위 타입이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<selectid="select"resultMap="Post">
<![CDATA[
SELECT
post.ID,
post.TYPE,
post.TITLE,
post.CONTENT,
usr.ID AS USR_ID,
usr.TYPE AS USR_TYPE,
usr.NAME AS USR_NAME,
usr.AGE AS USR_AGE
FROM
post
JOIN
USR ON post.USR_ID = USR.ID
]]>
</select>
columnPrefix
테이블명과 자바클래스명을 일치시킨다면, comlumnPrefix를 이용해서 모든 alias 를 멤버명과 동일하게 일치시킬 수 있다. 예를들어, Post 클래스에 comments 필드가 있다면 columnPrefix를 COMMENTS_ 로 주고 collection 으로 매핑하면 된다. (나는 이를 위해서라도 테이블명에 복수형을 붙이지 않는다. 테이블명을 복수형으로 붙이는건 (예를들어 users, posts..) 간지난다는거 빼고 좋은게 없는듯.
더 좋은것은 이 columnPrefix 가 선언된 하위 resultMap 은 재귀적으로 적용된다는 것. 예를들어 COMMENTS_AUTHOR_ID 같은 alias를 해석할수 있다는 것.
하지만 이 방법은 캐쉬를 적용하기에 좋지 않다.
discriminator
Post 클래스는 어쩔 수 없이 abstract 클래스여서는 안된다. 그런 제약이 있기는 해도, select 한 번으로 각 하위타입으로 매핑해서 (또한 재귀적으로) 조회할 수 있다는건 큰 매력이다. 참고로 이렇게 조회한 Post 의 리스트는 google-guava 라이브러리의 filter 메서드같은 함수형 접근과 아주 궁합이 잘 맞는다.
맞지 않나? 간단히 할 수 있는일을 왜 안 간단하게 하는지 모르겠다. 무슨 디커플링 어쩌구 하는데, 단 한번 쓰일 메서드 내지는 다음에 쓰일지 말지 애매한 메서드까지 왜 서비스 레이어 위에 올리는걸까? 왜 소스코드를 늘리고 더 복잡하게 만드는걸까? 이해가 안된다. 간단히 Authentication 만 봐도 그렇다. 인증 과정이 여러 포인트에서 지금 당장 일어나고 있다면 모를까, 최초 코드 작성시점에서 벌써 그런 사정을 고려할 필요는 없다. 이게 스프링 커뮤니티에서 추천하는 jdk프록시와 만나 impl 서비스 레이어를 만드는 것으로 더 복잡해진다.
예상과는 다르게 postMapper.insert(post) 마저 롤백되어 버린다. 실제로 브레이크 포인트를 걸어가며 디버깅해보면 프록시를 통하지 않고 다이렉트로 직접 호출하고 있다. substance인데 왜 ㅅㅂ 존나 노이해..
결국 유일하게 쓸만한 솔루션은 그냥 닥치고 AspectJ 라는 거다. AspectJ 를 쓰면 아무런 문제가 생기지 않는다. (혹은 cglib을 고쳐서 쓰거나…)
이렇게 동작하는것만 보장되면 구차하게 계속 서비스를 안 쪼개도 되고 템플릿 패턴을 쓸 수 있게된다. 생각해보면 컨트롤러와 서비스의 계층을 확실하게 나누는데도 도움이 된다. 예를들어 일반적인 처리는 컨트롤러에서, 특정 프로세스가 2회 이상 반복되는 경우에 템플릿 서비스로 계층화를 시키는게 가능하다. 이러면 소스를 줄이는데도 크게 도움이되고, 서비스== 템플릿 이므로 어떤 서비스가 어디서 호출되는지 고민할 필요도 없어진다.
이번에 이직준비를 하면서 한 회사로부터 면접 주제를 받았는데 바로 저 베스트 프랙티스에 대한 PT 였다. 주제도 주제이고, 정장입고 하래서 바로 포기했음. 여튼 이것에 대해서는 사실 기존에 하던 생각이 있어서 오랫만에 글을 적어봄.
무얼위한?
사전적 의미로는 모범사례. 하지만 이 IT하는 바닥에서는 여기에 여러 의미가 덧칠해져 마치 어떤 사상이나 종교 비슷한게 되버린다. 예를 들어 뭔가 할때 그것은 spring-way- 하지 안항요 라던가 엔터프라이즈 규격에 맞게 해주세요 라는 식. 좀 더 노골적으로 말하면 어떤 무리 가 있고 그들의 생각은 시간은 없고 일은 간지나게 해야겠으니 복사 붙여넣기 하여도 문제 없을만한 코드 가 바로 베스트 프랙티스 정도가 되는거시다.
도입부가 논리비약이 아주 심한데 뭐, 재밌으니까 계속 가봄.
Guice vs Spring
유명한 DI 프레임워크로 스프링과 구글쥬스가 있다. 자 어떤게 Best Practice 인가?
Autowire vs Inject
위에서 스프링을 선택했다면 또 선택지가 두 개 생긴다. 자 @Autowire 와 @Inject 어노테이션 중 무엇이 Best Practice 인가? 참고로 자바 표준은 @Inject 어노테이션이다.
XML vs Java Config
자 스프링을 선택했다면 또 선택지가 있다. xml로 applicationContext.xml 파일을 만들것인가? 하는것. 참고로 구글쥬스를 선택했다면 autowire 니 xml 이니 하는것을 고민할 필요가 없다. 없으니까. 10년도 더 전부터.
jdk proxy vs cglib proxy
jdk 프록시를 선택할 것이냐 아니면 cglib 프록시를 선택할 것이냐? 바꿔말해 interface 기반의 di를 할껀지? class 기반의 di를 할껀지? 또 바꿔말해 runtime 에 proxy를 생성할건지 아니면 거의- compile-time에 bytecode 조작을 통한 subclass를 만들건지? 다시말해 근본주의 노선인지? 아니면 실용주의 노선인지?
myBatis vs hibernate
단순하고 반복되는 코드를 패턴화해서, 유지보수까지는 바라지도 않고 개발중 정신이 혼미해지는거를 방지하는것은 좋다. 하지만 java -> orm 노선은 현실적인 문제 몇가지를 해결하지 못하고 있다.. 반면 myBatis는 존내 딱 자기 역할만하는데 그걸 참 잘함.
java vs clojure
그래서 말인데, orm 중 clojure 의 sqlKorma 같은게 정말 좋아보인다. 그냥 java 말고 clojure 만 쓰거나 두개를 같이 쓰는것은 어떤가?
그래서, 그래서…
적어도, IT하는 바닥에서 어떤 진짜 사상 (예를들어 OOP 같은거) 을 좀더 잘 드러내기 위한 Best Practice 같은건 있을 수 있다. 하지만 대부분의 사람들이 그걸 원하는지는 별개 문제임. 자신이 바라는게 복붙 가능한 코드라면 사실은 Best Practice 의 반대를 바라는게 아닐까?
예제에서 나오는 변수명은 사실 상당히 짧다. 본래의미를 축약하지 않고 다 써도 상당히 짧은 편일 것이다. 헌데 그 의미마저도 괴상한 접두어 때문에 가려져서 잘 보이지 않는다. 실제 업무용 어플리케이션은 이보다 훨씬 길고 다양한 의미를 가진 변수명을 쓰게 되는데 이럴때 이 의미가 가려지는 현상 은 훨씬 심해진다. 과연 저 괴상한 접두어들이 필요한가? 예를들어 불린타입의 숫자인지 아닌지에 대한 값이 들어있는 변수를 헝가리안 표기로 나타내면 bNumber 이다. 왜 안 isNumber ? 또 스킴이나 루비같은 언어에서는 가장 좋은 컨벤션을 쓴다( number? ). 게다가 축약된 접두어를 붙이느라 길어진 변수명을 짧게/접두어와 어울리게 만드느라 변수명을 줄여쓰는 사람들이 많은데 그러자면 bNo 가 된다. 여기에 포인터를 의미한다는 p를 붙여 pbNo. 값을 얻는답시고 *pbNo.
물론 이같은 것들은 이제 없으나 요런 사상은 아직도 여기저기에 남아있다. 주로 데이터베이스에 말이지.
물론 php, jsp 등 흔히 server page 라고 부르는 방식으로 만들수고 있고, html을 파싱해다가 원하는 부분만 코드가 개입하도록 할 수도 있다. 요건 개취인듯. 하지만 협업을 포기한다면 (다른데는 모르겠고, 우리나라에서는 반드시 포기해야한다) clojure의 저 접근이 괜찮은듯. jade는 몰라.
이것을 다른 평범한 언어 - 예를들어 스킴이나 자바스크립트, 좀더 내려가서 C에서는 어떻게 구현할까?
스킴
1
2
3
4
5
6
7
8
(define (sigma f a b)
(if (> a b)
0
(+ (f a)
(sigma f (add1 a) b))))
(sigma (λ(x) (* x x)) 12)
; => 5
자바스크립트
1
2
3
4
5
6
7
8
9
10
functionsigma(f,a,b){
if(a > b)
return0;
else
return f(a)
+ sigma(f, a+1, b);
}
sigma(function(x){return x*x}, 1, 2);
// => 5
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include<stdio.h>
intsigma(void*, int, int);
intterm(int);
intmain(int argc, char *argv[])
{
printf("%d" ,sigma(&term, 1, 2));
// => 5
return0;
}
intsigma(void* f, int a, int b){
if(a > b)
return0;
else
return ((int(*)(int))f)(a) + sigma(f, a + 1, b);
}
intterm(int i){
return i * i;
}
하지만 자바에서는 OO의 이름으로 널 용서하지 않을것이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package example;
publicclassMain{
publicstaticvoidmain(String[] args){
System.out.println(sigma(new Term() {
@Override
publicintterm(int x){
return x * x;
}
}, 1, 2));
}
// => 5
publicstaticinterfaceTerm{
publicintterm(int x);
}
publicstaticintsigma(Term f, int a, int b){
if (a > b)
return0;
else
return f.term(a) + sigma(f, a + 1, b);
}
}
하지만 자바를 오래해서 그런지, (또한 돈을 벌어다줘서 그런지) 저런 표현도 나쁘지 않다는 생각이 든다. 아직까진 참을 수 있다. 오히려 저렇게 뭔가 명시적이고 구조적이고 엔터프라이지-하고 꾸띄르적이고 뭔가 저렇게 하면 세련되고 아트적인 느낌을 줄 것만 같다.
복잡한 아이디어의 구현
현실세계로 돌아와서 자바로 허구헌날 만드는 웹 어플리케이션이나, 웹어플리케이션 혹은 웹 어플리케이션을 보자. 유행의 첨단을 주도하는 셀러브리티 트랜드 세터들이 매일 뭔가 프로퍼 웨이- 한 패턴이나 스타일을 들고 나온다. 그 중 하나가 aspect 다. respect 자바는 이 aspect oriented programming 을 위해서 굉장히 여러 시도를 하였으며 관련 프로젝트도 여럿 나오게 된다. 현실에서 우리가 자주 보는 spring, spring-aop 가 바로 현실적인 표준으로 자리잡은 프로젝트라 할 만하다. 전자정부에도 있다 이거. 그런데,
DI, aop 를 써서 무언가를 만드는것은 간지나고 좋다.
그런데 위에서 간단한 아이디어를 구현하는데에 이러한 복잡한 기능들이 방해가 되지는 않을까?
그러니까, 위에서 했던 간단히 익명 클래스 인스턴스를 넘겨받고 내부의 메서드를 호출하는등의 기능이 aop를 쓰면서도 잘 굴러갈까?
경험상, 위의 예시들은 될 수도 있고, 안될 수 도 있다. 보통 jdk proxy를 쓰느냐, cglib proxy를 쓰느냐에 따라서 되고 안되는 것들이 생겨난다. 게다가 간단히 익명 클래스 인스턴스를 넘겨받는 일들을 스프링 웨이- 하게 하려면 (불과 몇년 전까지만 해도) xml 파일에다가 해당 빈의 스펙을 기술해야만 했다.
위의 시그마 표현식과 다를바 없는 간단한 아이디어지만 이 코드들이 제대로 동작하게 하기위해서 어노테이션의 특성을 또 파악하고 스프링의 설정상 함정을 회피하고 또한 하나의 스타일을 강요해야한다.
별로 멋있지도 않고
불안하고 무섭고
힘들다
결론은 많은 사람들이 간단한 아이디어를 포기하고 괴랄한 구현을 선호하게 된다. 서비스에서 내부 메서드를 호출하지 못하므로 그냥 서비스 인터페이스를 하나 더 만들고 서비스 구현체 클래스를 하나 더 만드세요 ~ 물론 다른 방법도 있지만 그건 표준적이지 못하고 팀워-크에 민폐를 끼치는 일이에요~ 무엇보다 무슨일이 생길지 몰라요~ 라고 하는 일들이 벌어진단 얘기.
수백줄에 이르는 xml이 왜 아직 남아있겠는가요?
결론
스킴이나 이쪽 동네에는 좀 다른 클래스와 제네릭 메서드를 구현하는 방법이 있긴 하지만…
아직 늦지 않았다. 공무원을 하는게 좋다. 당장 공무원 공부를 시작하면 좋다.
왜 이런 자바쟁이 짓을 하고 있는지 모르겠다. 이런거 한다고 누가 알아주지도 않는다.
결론따위 없다. 내가 뭐 아는게 있어야지. 감히 신성한 패턴에 대해 논하다니 잠깐 미쳤었나 보다.