Cheese Fondue

점심은 간단히 fondue를 만들어 먹었다.

따듯하게 덥힌 화이트 와인에 스위스 치즈와 콜비 치즈를 녹이고 이탈리안 빵을 살짝 구운 것을 푹 담갔다가 먹으면 된다. 스위스 치즈의 강한 향과 와인의 쌉쌀한 맛이 잘 어울린다. 준비도 간단하고 먹기도 편해서 좋다. 치즈가 제법 들어가서 그런지 먹고 나면 든든하다. 근데 와인의 알콜이 다 날라가지 않아서 그런가 살짝 어질..

Spring 3.0 (41) @Deprecated SpringMVC

스프링이 3.0까지 오면서 @Deprecated 된 것들은 얼마나될까?

아예 삭제가 된 것이 있을지는 모르겠지만, 최신 소스를 기준으로 하면 @Deprecated는 총 98개가 있다. 일부는 클래스에 일부는 메소드에 달려있기 때문에 정확한 양을 가늠하기는 그렇지만, 아무튼 그정도 된다는 얘기다.

그 중 상당 수는 스프링이 내부적으로 사용하는 인터널 클래스나 메소드를 보다 나은 것으로 개선하면서 사용하지 않게 된 것 들이다.

 

하지만 스프링 개발자들이 직접 사용하는 것들 중에도 기능이나 사용방법이 확장/개선되거나 JDK버전이 올라가면서 자연스럽게 @Deprecated되는 것들도 있다.

 

예를 들어보자.

2.0에서 도입된 scope 개념은 빈의 라이프싸이클을 매우 유연하게 확장할 수 있도록 해주었다. 대신 기존에 singleton과 prototype 두가지 밖에 없을 때 사용했던 singleton 항목은 그에 따라 사용 scope가 매우 제한적이고 오해의 소지가 있으므로 @Deprecated되었다.

또 JDK1.5이하 버전과 이상버전이 혼재해 있던 시절에 등장했던 JdbcTemplate의 ParameterizedRowMapper는 이제 JDK5+로 기준이 바뀌면서 아예 기존의 RowMapper에 Generics를 적용한 RowMapper<T>로 통합되었다. 그러면서 역시 @Deprecated.

 

그 외에 대폭적으로 @Deprecated된 것들은 주로 템플릿 메소드 패턴을 기반으로 만들어진 기능들이다. 대표적으로 TestCase를 상속해서 만든 Spring Test들이나, AbstractController 클래스의 하위 클래스로 이루어진 SpringMVC들이다.

템플릿 메소드 패턴을 기가막히게 사용해서 상당히 매력적이고 편리한 사용방법을 제공했던 이 두가지는, 그러나 구현상속을 기반으로 한 패턴이 가지는 한계에 도달할 수 밖에 없었고, 결국 SpringTest는 2.5에서 등장한 JUnit 리쓰너 기반의 TestContext Framework에게, SpringMVC도 2.5에서 등장한 애노테이션+POJO 클래스 기반 컨트롤러방식인 @MVC에게 자리를 내줄 수 밖에 없었다.

 

테스트는 그렇다 치고, 지난 5년간 지지고 볶고 하면서 함께 해온 내 사랑 SpringMVC 심플폼컨트롤러까지 사라지는 운명에 처한다는 것이 조금은 서운하기는 하다.

내가 처음 스프링을 기반으로 프로젝트를 시작했던  2004년 초에 스프링과 하이버네이트까지는 일단 어렵지 않게 결정할 수 있었다. 문제는 웹MVC 부분을 무엇을 적용할까 하는 것이었다. 스트럿츠1이 당시엔 대세였지만, 이미 그 한심한 구조에 많이 실망했던 터라 패스. 그리고 나서 고민을 했던 것은 당시 2.0이 막 나왔던 WebWork과 최신 SpringMVC였다. 그땐 WebWork의 간결한 구조도 맘에 들었고, 당시에 막 등장해서 관심을 끌었던 Confluence도 그것을 쓴다고 해서 WebWork을 선택하기로 마음이 기울어졌다. 그러던 중에  SpringMVC를 본격적으로 살펴보게 되었다.

처음엔 뭐가 이리 복잡한가 하면서 싶었다. 레퍼런스 문서에는 Controller 인터페이스 설명이 달랑있을 뿐이었고, 책도 물론 없었고, 결국 API문서와 소스를 가지고 씨름을 하면서 이해를 하려고 노력을 했다. 그러다가 처음 사용해본 SimpleFormController에 완전히 반해버렸다. 이런 식으로 폼을 사용하는 컨트롤러 코드를 멋지게 만들어내다니 하는 감동을 온몸으로 느끼고 나서, 바로 WebWork을 취소하고 SpringMVC로 개발하는 것으로 변경했다. 하이버네이트를 쓰기로 한 것과 더불어 가장 잘한 선택이었다고 생각한다.

근데 그 SimpleFormController를 비롯한 주요한 SpringMVC 컨트롤러들이 이제 용도폐기되게 되어버렸다. 쩝~

정확히 말하자면 BaseCommandController와 그 하위 5개 컨트롤러들이 @Deprecated된 것이다. 구체으로 Command(요즘은 모델이라고 하는..) 기반의 컨트롤러들이 모두 사라진 것이다. 당연히 SimpleFormController도.

이제는 CoC 스타일의 메소드 패턴 분석과 @ModelAttribute 설정을 이용해서 기존의 command object를 처리하던 것이 모두 대치되버렸다.

그렇다고 Controller인터페이스 기반의 컨트롤러구조가 모두 사라진 것은 아니다. Controller 인터페이스와 가장 기본적인 구현인 AbstractController는 살아남았다. 또한 애노테이션 없는 CoC 방식의 MultiActionController도 아직 @Deprecated되지 않았다. @MVC가 있는데 이건 왜 그대로 두엇나 모르겠다. 정식버전 나오기전에 사라질지도..

 

여전히 @MVC에서도 Controller 인터페이스를 구현한 클래스나, 같은 방식으로 만들어진 컨트롤러(1 URL : 1 Controller 매핑을 가지는)를 사용할 수 있다. 원한다면 Controller인터페이스를 구현해서 확장한 새로운 컨트롤러 구조를 만들어도 된다. Controller인터페이스를 가지는 컨트롤러(스프링용어로는 핸들러)를 지원하는 SimpleControllerHandlerAdapter는 여전히 존재하고 있으니까.

3.0에서는 @Deprecated로만 된 것들 중 상당 수는 다음 버전에서 아예 제거될 것이라고 한다. JDK 라이브러리도 아니고 구지 용량을 차지하면서 계속 끌고갈 필요는 없으니까 말이다.

버티컬 마우스

몇달 전부터 오른쪽 손목이 아프기 시작했다. 손목통증이야 PC앞에서 일을 하는 사람에게 흔히 있는 있는 것이긴 하지만, 이번 것은 좀 심하게 아파서 물건을 들때마다 통증이 심하게 느껴지고, 컨디션이 안좋은 날은 타이핑을 하거나 그냥 가만히 있어도 손목이 저리기도 하다. 그렇다고 손목을 쓰는 일을 피할 수는 없는 노릇이고, 가능한 자세를 바르게 하고 작업을 하고, 무거운 물건을 들거나 손목에 과도한 힘이 들어가는 일은 피하려고 노력하고 있다.

그러는 중에 특히 마우스를 사용하면 손목에 부담이 가는 것을 발견했다. 보통 대부분의 작업은 마우스 없이 키보드만으로 하는 스타일이긴한데, 최근에 웹 사이트 디자인을 해본다고 디자인툴을 많이 사용하고, 또 책 원고를 작성하면서 필요한 다이어그램을 무식하게도 워드의 도형기능을 이용해서 일일히 그리다 보니 마우스를 이용한 세밀한 움직임이 필요에 손목에 부담이 많이 가고 있다.

그래서 언젠가 한번 들어본 적이 있는 버티컬 마우스를 써볼까 생각이 들었다.

그래서 구입한 것이 Anapa 마우스.

호주 ebay에 주문을 했는데, 발송지가 한국이다. 한국 제품인가보다.

마우스를 잡았을 때 손목이 아래를 향했을 때와 달리 오른쪽으로 60-70도 정도 비틀어진다. 차렷자세에서 그대로 손을 올려서 책상에 두면 딱 마우스를 잡는 모양이 된다. 그래서 손목에 부담이 안간다는 게 원리인 듯.

버튼도 큼직하고 클릭감도 좋다. 휠버튼에 파란불이 들어오는 것이 이뻐서 평화가 종종 달려들어 마구 만지는 것이 흠이긴 하다.

손목부분이 확실히 편하다. 또 손목이 바닥에 밀착되어있지 않으니 마우스를 움직일 때 손목을 비틀지 않고 팔 전체를 움직일 수 있다는 것도 장점인 것 같다. 팔걸이가 높은 의자를 사용한 덕이기도 하지만.

생각했던 것보다 마우스를 정밀하게 움직이는 것은 어렵지 않다. 이제 사용한지 5일 정도 되었는데, 가끔 힘조절이 안되서 마우스가 밀려서 잘못 클릭을 하는 경우가 있기는 하지만 대체로 사용하는데 불편은 없는 듯 하다.

한가지 고민은 엄지손가락을 정확히 어떤 위치에 둘 것인가와 휠 버튼을 어느 손가락으로 클릭하거나 돌릴 것인가 이다. 엄지가 놓이는 장소는 넓은 편인데, 자연스럽게 놓으면 아래로 내려가지만 그러면 클릭할 때 불편하다. 아무래도 단단히 고정된 바닥을 향해서 누르는 것에 비해서, 좌측으로 클릭을 한다는 것이 조금 불편하기 때문이다. 그래서 엄지에 힘을 살짝 줘서 마우스를 고정해야 하는데, 그러려면 마우스의 윗부분에 엄지를 대는게 편하다. 마치 손가락으로 물건을 집는 듯한 방식으로 클릭이 일어난다. 물론 손바닥으로 마우스 아래부분을 잘 고정하면 엄지의 도움이 없이도 클릭이 가능하긴 하지만, 손목에 힘이 들어가서 별로 좋은 자세는 아닌 듯.

보통 마우스에선 오른쪽 버튼은 주로 중지로 눌렀지만 이건 약지로 누르는 것이 오히려 편한 것 같다. 물론 별로 사용하지 않고, 힘이 약한 손가락이라 좀 연습이 필요하겠지만 넓은 우측면에 손이 놓이면 그 정도가 적당한 듯.

제일 고민은 휠 버튼인데 버튼을 돌리는 것은 중지가 편하긴 한데, 클릭할 때는 좀 불안한 편이다. 브라우저의 새 탭으로 클릭 기능을 많이 사용하기 때문에 휠버튼을 자주 누르는데, 중지로 하면 살짝 손이 떨리기도 한다. 그래서 검지로 클릭을 자주 하게 된다. 전체적으로 손가락을 내리면 이게 편하긴 한데, 자세히 보면 그 과정에서 손목이 좀 더 비틀어지는 것을 볼 수 있다. 그래서 버티컬 마우스의 효과가 반감되는 것이 아닌가 하는 불안감이 살짝있다. 중지를 이용해서 휠 버튼을 클릭하는 것을 좀 더 연습하면 잘 할 수 있을 것 같기도 하고…

아무튼 신기한 마우스에 적응하는 중인데.. 대체로 만족스럽다.

 

남들은 허접 싸구려 기계식 키보드라고 하지만, 지금 쓰는 아론 내추럴 기계식 키보드도 만족스럽다. 좀 더 벌어지고 가운데가 올라왔으면 좋겠지만, 가벼운 터치감은 이만하면 만족스럽다. 요즘 ALT키가 자꾸 눌리는 현상이 있는게 좀 문제긴 하지만.

우측에 키패드가 놓이는 것 때문에 마우스를 잡기 위해서 손을 많이 이동하는 것도 조금 불만이긴 하다. 어떤 키보드는 그래서 키패드가 좌측에 있는 것도 있다.

한국에서 가져온 책상은 높이가 좀 높은게 흠이다. 의자를 최대한 높이고 발받침도 사용하기 하지만 조금만 더 낮았으면 손목이 더 편하지 않을까 싶다. 높이 조절 책상을 찾아보니 거의 500불쯤 한다. 꺅… 몸에 좋다는 어떤 의자는 1000불도 넘는 듯.

 

오늘의 결론은 건강에 돈을 투자하자. 그런데 그 투자할 돈을 벌려면 건강을 해칠 수도.

Spring 3.0 (40) Spring ASM 모듈의 소스는 어디에?

오랜만에 쓰는 스프링 3.0 얘기. RC1의 출시예정일을 20일을 훌쩍 넘겨버렸지만, 아직 RC1의 릴리스는 언제쯤 될지 예측하기도 쉽지 않다. 모든 개발할 내역이 이슈트래커에 올라오는 것은 아니지만, 어쨌든 이슈트래커의 RC1 버전을 기준으로 보자면 남은 이슈는 130여개. 날이 갈 수록 줄기는 커녕, 오히려 늘어나고 있다. 계속 새로운 이슈가 등장하기 때문이겠지. 물론 이미 클리어된 것도 90쯤 된다.

아마 이러다가 더 이상 시간을 끌기가 마케팅적으로 좋지 않다고 판단될 즈음에 상당수 이슈가 final로 넘어가면서 RC1이 갑자기 나올 수도 있다. M2에서 그랬던 것처럼 말이다. 또는 RC2가 하나 더 나올지도 모르겠는데, 글세.

어쨌든 최근에는 changelog도 업데이트 잘 안되고 있다. SVN로그를 보면 주로 웹 관련된 사항이 많이 수정되고 있는 듯. 코드를 다듬고 테스트를 추가하는 등도 많이 있는 것으로 봐서 버그 수정도 많은 것 같고.

과연 3.0 정식버전은 언제 출시될 것인가. 이렇게 질질 끌다가 어느 순간 후다닥 마무리져서 나올지도 모른다. 아니면 더욱 질질 끌 수도. 2.1을 2.5로 바꿔가면서 거의 7-8개월이나 일정을 연기한 것처럼.

 

오늘 얘기는 org.springframework.asm 모듈.

3.0 초기에 아직 annotated factory method(@Configuration)가 나오기 전에 JavaConfig 프로젝트 3.0에서 돌려보려고 했더니 asm 관련 클래스를 찾을 수 없다고 오류가 났다. 결국 JavaConfig은 2.5랑 돌릴 수 밖에 없었다. 왜 그랬을까? 구버전 호환성을 자랑하는 스프링에 무슨 일이 있었을까.

3.0은 모듈을 기존보다 더욱 상세하게 무려 19개로 쪼갰다.

그 중에는 org.springframework.asm이라는 이름의 모듈이 있다. 그런데 정작 최신 스프링 소스를 받아서 이 모듈의 프로젝트를 열어보면 소스와 테스트 폴더가 모두 텅 비어있다. 그런데 다른 모듈(context 등등)에 보면 이 org.springframework.asm 모듈을 사용하는 곳이 있다. 그 프로젝트에 라이브러리 항목에서 org.springframework.asm.jar 파일을 열어보면 그 안에 클래스가 들어 있다. 이게 어찌된 일인가? 모듈 프로젝트에는 소스가 없는데, 그 빌드된 바이너리 파일에는 클래스가 들어있다. 물론 패키지가 org.springframework.asm으로 당당히 잡혀있는 클래스들이다. 이는 어찌된 일인가?

 

ObjectWeb의 ASM 라이브러리는 클래스 바이트코드 조작과 분석을 위한 라이브러리이다. 스프링은 빈 스캐너에서나 메소드의 변수 이름을 읽는 등의 용도로 이를 사용한다. ASM의 장점은 클래스를 로딩하지 않고도 클래스의 메타 정보를 읽을 수 있다는 점이라고 한다. 그래서 여기 저기서 제법 많이 사용된다.

문제는 CGLib과 하이버네이트와 사이에서 발생했다. 기존에도 스프링과 하이버네이트를 같이 사용하다보면 서로 비슷한 오픈소스 라이브러리를 사용하는 것을 자주 발견할 수 있었다. 그 중 자주 눈에 띄는 것이 바로 Enhancer를 이용해서 클래스에 다이나믹프록시를 쉽게 만들게 해주는 CGLib이다. CGLib가 이름(code generation lib.) 때문에 이를 무슨 ROO처럼 코드를 생성해주는 코드생성기 쯤으로 착각하고 있는 사람들도 많은 것 같은데, 그런 코드생성기와는 전혀 다른 개념이다.

아무튼 스프링은 클래스프록시 때문에 이 CGLib을 사용하고 하이버네이트도 역시 다양한 프록시를 쓰기 때문에 CGLib을 필요로 한다. 문제는 이런 식으로 같은 라이브러리를 사용하는 두개 이상의 다른 프레임워크를 쓸 때 서로 사용하는 라이브러리의 버전이 다른 경우가 발생할 수 있다는 점이다.

기존에 제기됐던 이슈는 스프링과 히이버네이트가 서로 다른 ASM라이브러리 버전을 사용하기 때문에 발생한 것이다. 스프링은 2.x, 하이버네이트는 1.x대를 사용하고 있던 때가 있었는데(지금은 모르겠다..) 문제는 ASM이 1과 2가 서로 클래스 이름은 같지만 바이너리 버전에서 호환이 안된다는 점이다. 같은 라이브러리를 사용하는데 마이너 버전만 좀 다르면, 보다 최신버전을 사용하는 쪽에 버전을 맡추면 대부분 문제가 해결된다. 문제는 ASM처럼 버전에 따라서 아예 기능이 서로 호환이 안되는 경우가 있다는 것이다.

결국 그 당시 스프링과 특정 버전의 하이버네이트를 같이 사용하면 어느 한쪽의 기능이 정상적으로 동작하지 않거나, 클래스를 찾을 수 없어서 오류가 나는 상황이 발생한다.

 

같은 클래스로더에 로딩되야 하는 프레임워크들 사이에 이렇게 바이너리 레벨의 호환이 안되는 라이브러리의 버전충돌 문제가 생기는 것은 참 난처한 일이다. OSGi처럼 수평적인 네트워크 구조의 클래스로딩 방식을 가지는 경우에는 라이브러리의 버전 충돌 문제를 근본적으로 회피할 수 있지만, 계층적인 종속 구조를 가지는 기존 자바 플랫폼에서는 일단 불가능하다.

그래서 이런 문제를 해결하기 위해서 사용하는 방법이 repacking이다. 뭐냐면 아예 버전이 다른 한쪽을 자바패키지를 통채로 바꾸는 것이다. 클래스의 이름과 내용은 같더라도 패키지가 다르면 다른 클래스로 인식하고 함께 사용할 수 있기 때문이다.

그래서 종종 충돌이 발생하는 라이브러리의 경우 이렇게 재패키지화 하는 경우가 많이 있다.

ASM버전 문제에 잘 낑겨들어가는 CGLib의 경우는 그래서 cglib_nodeps.jar라고 재패키징한 라이브러리도 종종 찾아볼 수 있다. ASM라이브러리를 cglib의 패키지 아래로 모두 옮기고, cglib에서 ASM의 기능을 사용할 때는 그렇게 패키지를 바꾼 라이브러리를 사용하게 하는 것이다. 결국 cglib.jar –> asm.jar 해야 할 것을 asm.jar가 필요없이 그냥 cglib_nodeps.jar를 사용하면 되게 하는 것이다.

그래서 스프링이나 하이버네이트의 과거 라이브러리 사용흔적을 잘 살펴보면 이 cglib_nodeps가 종종 등장했다.

문제는 cglib이 사용하는 ASM 에서 충돌이 생긴거라면 상관없지만, 스프링이나 하이버네이트가 직접 사용하는 ASM이 있을 경우가 있어서 문제가 생기는 경우가 있다는 점이다. 이때문에 서로 개발자들 간에 라이브러리 문제를 해결하기 위해서 잘 조율을 했으면 좋겠지만, 뭐 니가 고쳐라 이런 식으로 가면 또 쉽지 않다.

결국 스프링 팀은 자주 문제가 될 것으로 판단되는 이 ASM라이브러리를 스프링에서 본격적으로 사용하기 시작하면서, 아예 이를 재패키징 하기로 결정했다. 문제는 재패키징 방법이 편하기는 하지만 라이브러리 사이즈를 불필요하게 증가시킨다는 점이다. 특히 특정 기능에서 사용하지만, Core모듈에 추가되야 하는 이 ASM이 문제다. 그래서 결국 이 재패키징 된 ASM 만 별도의 모듈로 제공하기로 했다. 그래서 필요하면 이를 사용하고 아니면 뺄 수 있도록 한 것이다. 물론 3.0 기능을 본격적으로 쓴다면 대부분 사용할 테지만.

그래서 org.springframework.asm모듈에는 소스가 없다. 대신 objectweb의 asm과 asm.common 두개의 라이브러리를 리패키징해서 클래스 패키지를 모두 org.springframework.asm으로 변경하고 이를 재포장해서 모듈 라이브러리로 만드는 것이다. 그러면 ASM의 클래스들이 필요한 경우 모두 이 org.springframework.asm 밑의 클래스를 사용하기만 하면 된다. 이렇게 해서 충돌 문제는 모두 해결.

 

이 재패키징은 그럼 어떻게 할까?

그건 Jar Jar Links라는 Jar 재포장 라이브러리를 이용한다.

asm모듈의 ivy 빌드파일에 보면 다음과 같은 부분이 나온다.

<dependencies>
        <dependency org="org.objectweb.asm" name="com.springsource.org.objectweb.asm" rev="2.2.3" conf="jarjar->runtime"/>
        <dependency org="org.objectweb.asm" name="com.springsource.org.objectweb.asm.commons" rev="2.2.3" conf="jarjar->runtime"/>
</dependencies>

 

이 두개의 deps는 jarjar를 통해서 런타임 스코프를 가지는 라이브러리로 사용한다는 내용이다.

그리고 ANT빌드파일을 보면

<jarjar destfile="${jar.output.file}" index="true" filesetmanifest="merge">
            <manifest>
                <attribute name="Bundle-ManifestVersion" value="2"/>
                <attribute name="Bundle-Version" value="${bundle.version}"/>
                <attribute name="Implementation-Title" value="${implementation.title}"/>
                <attribute name="Implementation-Version" value="${implementation.version}"/>
            </manifest>
            <zipfileset src="${target.dir}/jarjar-staging/com.springsource.org.objectweb.asm.jar"/>
            <zipfileset src="${target.dir}/jarjar-staging/com.springsource.org.objectweb.asm.commons.jar"/>
            <rule pattern="org.objectweb.asm.**" result="org.springframework.asm.@1"/>
</jarjar>

이 jarjar를 사용한 곳이 나오는데 rule pattern을 보면 org.objectweb.asm의 클래스들을 org.springframework.asm으로 변경할 수 있도록 설정되어있다.

 

JavaConfig은 2.5 방식대로 내부적으로 ASM을 사용하고 있고, 아직 재포장된 org.springframework.asm에 호환되게 바뀌지 않았기 때문에 3.0에서 사용할 수 없었던 것이다.

 

최신 하이버네이트 라이브러리를 보면 문제가 자주 되는 cglib과 asm등을 아예 몽땅 하이버네이트 패키지로 리패키징해버렸다. hibernate-cglib-repack-2.1_3.jar 을 보면 알 수 있다. ASM은 물론이고 cglib까지 org.hibernate 밑으로 넣어버렸으니 이제 충돌날 걱정은 없다. 클래스가 좀 많아지고 전체 바이너리 사이즈가 조금 커졌을 뿐.

 

오늘의 결론.

1. 스프링의 asm 모듈에 소스가 안보인다고 SVN checkout을 잘못받았나 당황하지 말자.

2. 라이브러리 버전 충돌나면  jarjar의 도움으로 리패키징을 해보자.

3. 그것도 귀찮으면 OSGi를 써서 버전문제를 해결보자.

4. 제일 중요한 결론. 버전 올라가면서 바이너리 레벨에서 호환안될 거면 클래스 이름이나 패키지좀 바꿔라!

마커 애노테이션과 디자인을 위한 리플렉션

지난 번에 한 생각을 좀 더 정리해보려고 자바의 까칠대마왕 조쉬 블록이 뭐라고 했는지 좀 찾아봤다.

 

조쉬는 자바JDK의 핵심 개발자이고 Java5+의 개발에도 깊이 관여한 만큼 애노테이션에 관해선 전반적으로 긍정적인 입장이다. CoC스타일의 이름을 이용한 패턴보다는 애노테이션을 쓰는 게 낫다고 한다. 예를 test로 시작하는 메소드 이름을 찾아내는(JUnit2.0에 등장한 기법이다) 방식보다는 @Test를 붙이는게 낫다는 것을 예로 들기도 한다. 사실 그동안은 없으니까 못썼지.

그러나 마커 애노테이션의 타입이 ElementType.TYPE이라면 대신 마커 인터테이스가 낫지 않은지 곰곰히 따져보라고 한다. Marker Interface는 타입을 정의해주지만 Marker Annotation은 그렇지 않기 때문이다. 물론 리플렉션 API로 확인은 가능하지만, 사용편의성에서 보자면 차이는 크다. 인터페이스 vs 읽기편한 XML 수준이다. @List list = new ArrayList() 이런건 안되니깐.

타입(Class, Interface)이 아닌 메소드, 필드에는 애노테이션을 어쩔 수 없이 사용해야겠지만 말이다.

일단 여기서 인터페이스 +1.

그러나 부가정보가 들어가면 애노테이션 승리.

만약 스프링의 @Component에 bean name을 지정하는 것만 없었다면 Component인터페이스를 마커로 달게 하고 이를 가지고 컴포넌트 스캔을 하도록 했을지도 모르겠다.

타입에 붙는 마커 애노테이션이 어짜피 마커 인터페이스처럼 메소드 추가도 안하는데, 그냥 타입으로 사용할 수 있게 해주면 안되나하는 아쉬움도 있지만, 어짜피 태생이 메타 정보니 따로 놀아야겠지. 이쯤에서 아주 아주 오래전에 아드리안 콜리어가 말한 APOJO(Annotated POJO)도 POJO냐라는 얘기가 생각난다. 나는 A를 떼도 문제없이 동작하면 POJO라고 생각한다. A가 없으면 뭔가 기능에 절름발이가 된다면 POJO가 아닌 뭔가 다른 것이다. 왜냐면 그 A를 처리해주는 툴(프레임워크)의 도움이 필요할테니, 그게 독립된 오브젝트라고 말하기는 좀 이상하니깐.

어쨌든 내 불만이라면 애노테이션이 붙어있다고 마치 타입이 선언된 것처럼 취급을 해버리는 방식이다. 애노테이션 기반의 절름발이 다형성?

 

다음은 리플렉션. 당연한 얘기지만 리플렉션의 과용은 CoC, 메타프로그래밍를 위해서는 필수이지만, 그만큼 단점이 있다.

조쉬는 리플렉션에 대해서는 이렇게 단언하고 있다. "리플렉션 보다는 인터페이스를 선호해라" IoR이라고 해야 하나. 그 이유로

- 컴파일 타임의 타입 체킹 불가능 (뭘 언제 어떻게 호출할지 모르니깐)

- 리플렉션을 사용하는 코드의 지저분해짐 (한번만 개발해보면 온몸으로 느낄 수 있다)

- 성능 (이건 뭐 많이 개선되었다고 하긴 하는데.. 그래도 많이 쌓이면 차이가 크겠지)

그러다 이런 얘기가 나온다.

"코드 리플렉션 기능은 원래 컴포넌트베이스 애플리케이션 개발 툴을 위해서 만들어진 것이다."

그렇구나. 리플렉션은 원래 CoC적용하고 메타 프로그래밍 하라고 만든 것은 아니고, 개발이나 설계도구에서 클래스 정보를 읽어오거나 setter를 호출해서 프로퍼티 값을 넣는 등등에서 사용하기 위해서 만들어진 것이다.

따라서 당연하게도 리플렉션을 이용한 방법보다는 정상적인 코드에 의해서 호출되는 방법을 선호해야 한다. 아무리 루비 사용자들이 자바의 스태틱 타이핑은 꾸졌고, 메타 프로그래밍도 안되는 저급 OOP 바보언어라고 놀려도 "우리도 리플렉션API도 있고 할 거 다 한다"라고 비굴하게 굴 건 없다.

 

물론 다이나믹프록시라든가 RPC를 위한 스텁기능등을 지원하기 위해서 꼭 필요할 때가 있다. 프레임워크 또한 필요하다. 리플렉션 API가 없었다면 JUnit의 플러거블 셀렉터는 어찌 만들었을까. 그럼 JUnit 0.9쯤에서 끝나고, 아마 매 테스트마다 클래스 하나씩 새로 정의해서 써야 했을 거다. 템플릿 메소드 패턴이 있으니까 setUp, tearDown쯤은 공유했겠지만.

결론은 적절한 사용이 어디까지냐인데.. 한참 더 생각해볼 일이고. 분명한 것은 폼나는 CoC등에 너무 욕심내지 말고, 과도한 사용은 득보다 실이 클 수 있다는 상식을 가지면 좋다는 것. 계속 생각해보자.

 

참, 우리의 최고수는 연락이 없다. 잠적했나? 삐졌나? 내일이 세미나 날인데…

←Older