(loop (print (eval (read))))

;;닭집을 차리기 위한 여정

OO에 대한 느낌적 느낌

자바

나는 주로 먹고살기위해 자바를 써왔다. 현재도 많은 사람들이 바로 이 이유로 자바를 선택한다.
OO는 멋지다. 솔직히 간지폭발이다. 하지만 자바를 위시한 현실에서는 별로 안 멋지고 안쓰느니만 못한 부분도 간간히 보인다.

간단한 아이디어의 구현

예를들어 이런 수식이 있다고 하자. 흔히 예제로 자주드는 시그마 표현식이다.

$\sum\limits_{n=a}^b f(x) = f(a) + f(a+1) + … + f(b-1) + f(b)$

이것을 다른 평범한 언어 - 예를들어 스킴이나 자바스크립트, 좀더 내려가서 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)) 1 2)
; => 5
자바스크립트
1
2
3
4
5
6
7
8
9
10
function sigma(f,a,b){
if(a > b)
return 0;
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>
int sigma(void*, int, int);
int term(int);
int main(int argc, char *argv[])
{
printf("%d" ,sigma(&term, 1, 2));
// => 5
return 0;
}
int sigma(void* f, int a, int b){
if(a > b)
return 0;
else
return ((int(*)(int))f)(a) + sigma(f, a + 1, b);
}
int term(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;
public class Main {
public static void main(String[] args) {
System.out.println(sigma(new Term() {
@Override
public int term(int x) {
return x * x;
}
}, 1, 2));
}
// => 5
public static interface Term {
public int term(int x);
}
public static int sigma(Term f, int a, int b) {
if (a > b)
return 0;
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 파일에다가 해당 빈의 스펙을 기술해야만 했다.

현실에서 가장 만나기 좋은 예시는 @Transactional 이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public abstract class PaymentService<REQ extends PayRequestable, RESP extends PayResponseable, PAY extends Payment> {
@Transactional
public abstract PAY draftPayment(long paymentId, REQ payRequest, Order order) throws SystemException;
@Transactional
public abstract RESP requestPayment(REQ payRequest, Order order) throws SystemException;
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public abstract void completePayment(PAY payment, RESP payResponse) throws SystemException;
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public abstract void logPayment(User user, PAY payment, RESP response);
@Transactional
public PAY process(Order order, long paymentId, REQ payRequest) throws SystemException {
// .... 결제처리를 한다.
return payment;
}
@Transactional(propagation = Propagation.NEVER)
public void checkExistingOrder(User user) {
// .....
}
@Transactional(propagation = Propagation.NESTED)
public void saveOrderItemsAppliedPromotion(Order order) {
// .....
}
}

위의 시그마 표현식과 다를바 없는 간단한 아이디어지만 이 코드들이 제대로 동작하게 하기위해서 어노테이션의 특성을 또 파악하고 스프링의 설정상 함정을 회피하고 또한 하나의 스타일을 강요해야한다.

  • 별로 멋있지도 않고
  • 불안하고 무섭고
  • 힘들다

결론은 많은 사람들이 간단한 아이디어를 포기하고 괴랄한 구현을 선호하게 된다. 서비스에서 내부 메서드를 호출하지 못하므로 그냥 서비스 인터페이스를 하나 더 만들고 서비스 구현체 클래스를 하나 더 만드세요 ~ 물론 다른 방법도 있지만 그건 표준적이지 못하고 팀워-크에 민폐를 끼치는 일이에요~ 무엇보다 무슨일이 생길지 몰라요~ 라고 하는 일들이 벌어진단 얘기.

수백줄에 이르는 xml이 왜 아직 남아있겠는가요?

결론

스킴이나 이쪽 동네에는 좀 다른 클래스와 제네릭 메서드를 구현하는 방법이 있긴 하지만…

  • 아직 늦지 않았다. 공무원을 하는게 좋다. 당장 공무원 공부를 시작하면 좋다.
  • 왜 이런 자바쟁이 짓을 하고 있는지 모르겠다. 이런거 한다고 누가 알아주지도 않는다.
  • 결론따위 없다. 내가 뭐 아는게 있어야지. 감히 신성한 패턴에 대해 논하다니 잠깐 미쳤었나 보다.