'분류 전체보기'에 해당되는 글 140건

  1. 2009.07.26 AL Client (XUL Sample)
  2. 2009.07.24 AL Ver 0.1
  3. 2009.07.24 일곱번째 계 - 호출의 빈도(1)
  4. 2009.07.17 Second Program Effect
  5. 2009.07.15 XUL 단상
  6. 2009.07.14 여섯번째 계
  7. 2009.07.08 호출의 빈도
  8. 2009.07.06 한줄짜리 함수
  9. 2009.07.05 레이어의 속도
  10. 2009.06.25 read & write
pds2009. 7. 26. 13:27


AL을 사용하는 Web이 아닌 다른 예제를 만들어 볼려고 했던 샘플이다. 

XUL을 사용했는데 처음 사용할때 View 목적을 위해서는 장점이 많아서 AL 관리프로그램으로 만들려고 했지만. 
Action이 들어가는 순간 XUL의 목적에도 맞지 않고 실제로 사용하기에도 무척 까다롭다는걸 한달 반 헤딩하고 깨달았다-ㅅ-

그래서 일명 시망-ㅅ-프로그램으로 
Edit Action와 관련한 기능은 작동하지 않고 View 기능만 제대로 동작한다. 
현재로서는 XUL의 한계를 느껴서 더이상 이프로그램을 패키징을 할 생각이 없지만..
나름 Lucene의 Side OpenSource인 LukeAll을 흉내내서 PlugIn 방식이라든가 신경을 많이 쓴게 아까워서 일단 올려봄. 
(각 화면은 Let 형태로 별도로 동작하고 뭉쳐서도 동작하는 독특(?)한 아키텍쳐를 가지고 있다. 이 방식을 사용한 이유는 테스트의 용이성때문이다. )

이전의 WebSample은 AL의 아주 일부분의 기능만 사용했지만. 
XUL Sample은 AL에서 지원하는 기능의 50%정도를 사용한다. 


실행방법은 이전 pds의 framework_core.jar와 anotherLore.jar가 있는 디렉토리에  해당 디렉토리 프롬프트 창에서 java -jar anotherLore_client.jar를 실행하면 된다.(jar 확장자가 인식되면 dblClick로도 실행)


Reset AnotherLore : 샘플노드와 관련 타입등을 지운다. 
Load SampleData : 샘플 노드와 관련 타입등을 만든다. 
을 선택하고 OK 클릭. 


최근 어느책에서 본건데 24시간 무정지 시스템은 달리는 자동차에서 엔진을 교체하는것과 같다고 한다. 그러나 그보다 최악은 달리는 자동차의 엔진을 만드는 일이다. 나는 이전에 그런 경험을 하면서 다시는 그와 비슷한 일이 발생하지 않도록 엔진을 만들때 여러개의 MockUp자동차를 만들기로 했고 WebSample의 AL의 찰흙로 만든 자동차라면 XULExample는 나무로 만든 자동차 같다. 


'pds' 카테고리의 다른 글

그들이 위임했을뿐  (0) 2012.12.19
AL Ver 0.1  (0) 2009.07.24
AL의 첫 테스트 버전  (0) 2009.04.12
Framework - Source  (0) 2009.02.19
Bleujin Framework Jar  (0) 2009.01.15
Posted by bleujin
pds2009. 7. 24. 09:37

아마도 3달전에 늦어도 2달전에는 Open 할수 있었지만..
다른걸 좀 만드느라 패키징을 미루다 보니 이제서야 오픈. 

4개의 파일(10M이내로 등록하기 위해 Layer 별로 분리)

framework_core.jar : 기본적인 system framework...(db, configuration, log 등등)
anotherLore.jar : AL core
anotherLore_web.jar : AL을 사용한 Web Example(struts)
webapps.zip : jsp, js

3개의 jar를 같은 디렉토리에 놓고 webapps.zip을 그 디렉토리에 압축을 푼후
(다운로드 받을때 티스토리에서 보안때문인지 확장자를 바꿔버리는것 주의.)

해당 디렉토리 도스 프롬프트에서 java -jar anotherLore_web.jar 라고 실행하면 WebServer가 기동
http://localhost:8080/board/ 에서 확인 가능.


이전과 다른점은 Oracle을 사용하지 않아도 샘플의 실행과 테스트의 용이성을 위해서  File에 write를 한다. 십만건 이상에 Session이 많을 경우 DB를 써야 하겠지만 그 이하라면 파일로도 처리가 가능. 
다만 File 저장소는 테스트용으로 만들어서 write가 많이 일어나는 경우 동시성이 취약함. 테스트 용이성을 위해 만들었지만 데이타 캐쉬나 분산연산등의 역할을 담당할 수도 있는 추가적인 장점이 많이 있어서 보완중.

config file : default-config.xml, anotherlore-config.xml

정확한 테스트는 아니지만 
5개 Active Thread로 대충 초당 10건 write, 초당 300건 read 정도 ?

5WriterThread * 10회 반복 + 50ReaderThread * 10회 반복 = 7.5초.

anotherLore java soruce.... 













'pds' 카테고리의 다른 글

그들이 위임했을뿐  (0) 2012.12.19
AL Client (XUL Sample)  (0) 2009.07.26
AL의 첫 테스트 버전  (0) 2009.04.12
Framework - Source  (0) 2009.02.19
Bleujin Framework Jar  (0) 2009.01.15
Posted by bleujin


대부분의 책에서 인터페이스와 추상 클래스에 기반한 프로그래밍이란 얘기가 너무 많이 나와서 흔히 인터페이스를 사용하는게 목적처럼 되어 버리는 경우가 있다. 

인터페이스를 사용하는 것은 수단일뿐 그 자체로 목적이 아니다. 그럼 인터페이스를 사용하는 목적은 무엇일까에 대해 의문을 가져야 한다. 그 답을 알기전에 먼저 인터페이스는 절대선이 아니라는 것을 이해해야 한다. 오히려 인터페이스는 프로그램의 문서화처럼 존재 자체의 손해 보다 더 많은 이득을 얻기 위한 필요악같은 존재이다. 

아이러니하게도 인터페이스는 복잡함을 감소시키기 위한 추가적인 복잡함의 장치이다. 인터페이스를 사용한다는 것은 관리해야 할 파일 하나가 늘어다는 것이므로 그 자체로 복잡함이 증가한다. 다만 인터페이스를 사용함으로서 구현자는 "인터페이스의 책임"의 구현에만 집중할 수 있고 호출자는 별로 관심없는 상세 구현보다 책임 그 자체에 집중할 수 있게 함으로서 "구현자과 호출자 사이의 복잡함"을 감소시킨다. 

여기서 ~사이의 복잡함이란 시간과 공간이라는 차원을 포함하고 있다. 애초에 구현자와 호출자가 같은 시간과 공간에 존재한다면 얻는 복잡함의 감소보다 잃는 복잡함의 증가가 더 크다. 


일단 "공간"에 대해서 말하자면 서울과 LA의 거리라는 물리적 감각보다는 나와 나 이외의 생각의 거리라는 의미가 강하다. 만약 혼자 하는 프로젝트라면 혹은 아주 마음이 잘 맞는 팀이라면 공간 사이의 거리가 크지 않을 수도 있다. 

그러나 "시간"의 거리는 시간이 갈수록 엔트로피처럼 항상 늘어나고 혼자하는 프로젝트라도 시간의 거리는 생긴다. 그래서 나는 인터페이스의 가장 큰 목적은 이 시간의 거리 복잡함을 줄이는 것이라고 생각한다. 다시 말해서 복잡함이란 현재의 특정 상황 혹은 시점에서의 복잡함만을 뜻하는 것이 아니라 지속적으로 변함으로써 생기는 복잡함이 더욱 많다는 말이다. 



시간의 거리로 생기는 복잡함이 보다 복잡한 이유는 이렇게 생긴 복잡함은 지속적인 파동의 다른 변화를 일으키고 이로인해 더욱더 복잡해지기 때문이다.. 게다가 앞날이라는 것은 본질적으로 예측하기 어렵기 때문에 미래를 대비하는 것 작체가 불필요한 복잡합이 야기될수 있다. 


특정 객체가 변해야 할 이유는 그 자체가 아니라 그가 사용하는 객체가 변해야 할때도 일어나고 이때문에 객체는 그 자신보다 보다 덜 변하는 것에 의존하여야 한다. (보다 덜 이라는 의미는 <=(작거나 같다) 의미인데 당연히 < (작다) 로 할수있으면 하는게 더 낫다. ) 

쉬워보이지만 이 원칙은 의외로 지키기 어렵다. 

이에 관해서 첫번째 알려진 것은 벽돌형 layer 아키텍쳐에서 상위 레이어는 하위 레이어에 의존해야 하고 하위 레이어는 상위 레이어에 의존해서는 안된다 라는 것인데 종종 이를 위반하는 것은 상위 레이어와 하위 레이어를 같은 시간에 혹은 같은 사람이 만들기 때문인데 주의하지 않으면 처음 생각보다 쉽지 않다. 

보통의 프로그램에서 하나의 새로운 레이어를 만드는 일은 그리 많지 않지만 하나의 레이어에서도 "보다 덜 변하는 것에 의존해야 한다는 원칙"은 중요한 것은 이것이 단순히 레이어간의 이야기만이 아니기 때문이다.


'Framework > 아키텍쳐 일반' 카테고리의 다른 글

Second Program Effect  (0) 2009.07.17
여섯번째 계  (0) 2009.07.14
호출의 빈도  (0) 2009.07.08
한줄짜리 함수  (0) 2009.07.06
예언?  (0) 2009.06.12
Posted by bleujin

" 설계자의 첫번째 작업은 절제되고 깔끔한 편이 많다. 그는 자신이 하는 일을 아직 잘 모르는 상태라는 것을 인식한다. 그래서 매우 조심스럽고 절제하는 태도를 가진다. 

그가 첫번째 작업을 설계하는 과정에서 다양한 꾸밈과 장식이 떠오르지만 이것들은 "다음번"에 사용할 것으로 일단 옆으로 제쳐놓는다. 머지않아 첫번째 시스템은 끝이 나고 일정한 수준의 시스템을 달성했다는 것을 입증한 설계자는 자신감이 충만한 상태로 두번째 시스템을 구축할 준비에 나서게 된다. 

이 두번째 시스템이 누구에게나 가장 위험한 시스템이다. ~ 두번째 시스템에서는 첫번째 시스템에서 옆으로 제쳐두었던 모든 아이디어와 치장들을 사용하여 과도하게 설계하는 경향이 나타나게 된다.  "


브룩스는 두번째 시스템 효과에 대해 말하면서 성공한 제품의 두번째 제품은 과도한 설계로 과도하게 복잡한 시스템이 될 가능성이 많음을 경고하였다. 여기서 과도하게 복잡하다는 것은 시스템을 세련되게 만들기 위한 장치가 시스템의 기본 장치보다 더 앞서가게 되는 것을 말한다. 

보통의 경우 두번째 시스템은 첫번째의 그것보다 기능이 더 많다. 그리고 이것들은 적절히 분류 관리되거나 절제되어 있지 않음으로서 UI가 복잡해지고 옵셔널한 기능이 많아지면서 설정이 많아지는 등이다. 

솔류션 시스템의 경우 첫번째 시스템에서는 직접적으로 고객과 맞닿는 일이 많지 않다. 대부분 실험실에서 소수 인원의 설계로 인해 통일되고 간결한 형태를 지닌다. 첫번째 버전이 고객에게 팔리게 된 이후에 많은 추가 요구사항이 생기게 되고 그것들의 일부는 서로 이율배반적이다. 

설계자는 그것들을 잘 조절하려고 노력하겠지만 그러함에도 많은 요구사항을 어쩔수없이 받아들임으로서 두번째 시스템은 500라인짜리 XML 설정 파일을 가지게 되는 경우는 흔하다. 아마도 그 설정파일의 대부분 사용되지 않을 옵션 기능을 설정하고 활성화 시키는 역할들을 담당하지만  너무도 길고 복잡해서 그에 관심을 쏟는 사용자는 거의 없다. 

이후 이것들은 문제가 생겼을 경우에 문제의 해결에 장애가 되고 덧데기와 IF 코드로 빠르게 부패되는 시스템이 된다. 

이러한 것이 근본적으로 피할수 없는 문제는 아니다. 브룩스는 그 해결방법의 하나로 두개 이상의 시스템 작업을 한, 노련한 설계자를 선택하는 것을 들었지만 사실 최근 대부분의 프로젝트는 과거와 같이 오랜 시간동안 초기 설계자가 이후에도 꾸준히 참여하는 경우는 극히 드물다. 프로젝트는 대부분 1년 이내이며 그동안 인원이 바뀐다. 첫번째 시스템이 성공하든 실패하든 말이다. 

개인적으로 시스템의 발전 방향은 기능의 개수를 늘리는데 집착할 것이 아니라 좀더 똑똑한 시스템이 되어야 한다고 생각한다. 기존의 기능과 상충되는 요구사항이 들어왔을 경우 혹은 목적은 같지만 처리가 달라질 경우에 이후에 개발자도 까먹을 유연성(?)을 이유로 XML 설정 파일을 늘리는 것은 해결책이 될 수 없다. 

똑똑한 시스템이란 사용자의 행동에 주목하는 시스템을 말한다. 사용자가 말하는 것이 아니라 원하는 것을 하는 시스템을 만들어라 라는 말이 있는데 아웃룩의 메일관리와 지메일의 메일관리의 차이이다. 아웃룩은 메일을 분류하기 위해 4단계 이상의 설정을 거치지만 지메일은 달랑 제목을 파싱해서 링크해주는 것 하나이지만 앞의 관점에서 지메일의 관리방식이 좀더 똑똑하다. 


'Framework > 아키텍쳐 일반' 카테고리의 다른 글

일곱번째 계 - 호출의 빈도(1)  (0) 2009.07.24
여섯번째 계  (0) 2009.07.14
호출의 빈도  (0) 2009.07.08
한줄짜리 함수  (0) 2009.07.06
예언?  (0) 2009.06.12
Posted by bleujin
Framework/Another Lore2009. 7. 15. 02:26

AL Client를 XUL(ThinLet)를 사용해서 약 한달간 만들었는데. 브라우저가 아닌 Java에서의 XUL은 2% 부족하다는 느낌이다. 

XUL(XML User-Interface Language)은 단순히 IDL 이므로 다양한 Client에서(물론 이전에 클라이언트가 XUL을 인지해야 한다. Firefox와는 달리 대표적인 브라우저인 ie는 XUL을 지원하지 않는다. ) 별도의 UI 로직를 고심할 필요가 없다는 것은 크나큰 장점이다. (https://developer.mozilla.org/Ko/The_Joy_of_XUL 모질라의 XUL Page)

그러나 XUL이 세상에 나온지 5년이 넘게 지났지만 생각보다 그 영향력은 매우 미비하다. 개인적으로는 XUL을 써보면서 lightweight Framework는 lightweight 하다라는 사실을 새삼스럽게 느꼈다. 

XUL의 Java OpenSource인 ThinLet(http://thinlet.sourceforge.net/component.html)는 매우 훌륭한 프레임워크였다. 무엇보다 놀란것은 그 간결한 구조였다. ThinLet은 XML을 Object Array Tree 형태로 관리하여 개별적인 컴포넌트의 paint를 구현한다는 것은 복잡한 머리속을 순식간에 관통하게 만드는 Simple 미학을 보여준다. Java의 Swing에 의존하지 않기 때문에 오히려 역으로 확장이 쉬운 아키텍쳐이다. 

XUL의 장점은 대표적으로 
    Based on existing standards, 
    Platform portability, 
    Separation of presentation from application logic, 
    Easy customization, localization, or branding 
등을 들 수 있다. 특히나  HTML 처럼 XUL은 플랫폼 중립적(platform-neutral)하기 때문에 비교적 단순하게 portable하고 cross platform application UI를 만들수 있다는 점에 매료되었다. 

문제가 됐던것은 바로 Lightweight 하다는 점이다. 어느정도 익숙해지고 난후 ThinLet은 Draggable한 Panel을 지원하지 않았기 때문에 ThinLet의 단순한 아키텍쳐에 감탄하면서 ThinBean 형태로 확장해서 만들었다. 얼마 지나지 않아서 다시 Draggable한 Table이 필요했고 역시 또 만들었다. 이 과정에서 ThinLet의 이벤트 처리 과정을 대폭 수정해야 했다. 왜냐하면 기본적인 Action Handler로는 너무 부족함이 많았고 결정적으로 Drag할수 있도록 Table의 Cell을 프로그램이 인지해야 했다. ThinLet에서 모든 객체는 paint된 pixel에 불과했기 때문에 Mouse Event가 발생한 곳의 객체를 인지하게 만드는 과정은 매우 복잡했고(zindex가 있음을 생각해보자) 어려웠다. 

나를 결국 손을 들게 만든건 Draggabl한 TreeTable이다. AL은 저장 구조상 TreeTable UI가 꼭 필요했다. 1주일이 넘게 삽질하면서 XUL에서 독립적인 Draggable한 TreeTable UI를 만들기는 아주아주 어렵다는 사실을 알았고 더 큰 문제는 설사 만든다고 하더라도 더 이상 그것은 XUL이 아니라는 사실을 깨닫게 됐다는 점이다. 

이른바 추상화의 함정에 빠진것이다. 

XUL은 일종의 새로운 추상층(Abstraction Layer)이다. 소프트웨어는 마치 양파와도 같이 겹겹이 층을 이루고 있는데, 각층은 그 아래충 위에 조심스럽지만 다소 불안정하게 쌓여있다. 새로운 층이 쌓일때마다 뭔가 복잡하고 지엽적인 것이 좀더 직관적이고 보편적인 것으로 대체되기 때문에 컴퓨터 공학에서는 추상을 여러개의 조그만 프로세스를 묶어 하나의 큰 프로세스로 만드는 과정이라고 정의한다. 

조엘의 말에 따르면 모든 추상화는 완벽하지 않고 함정이 있다고 하는데 예컨데 우리가 Java나 C#을 쓰지만 여전히 point에 대해서 알아야 한다는 것이다. VC++의 MFC 혹은 Delphi의 VCL만 쓰면서 Win32 API에 대해 전혀 알 필요가 없다면 정말 좋겠지만 조금만 더 깊이 들어가려 하면 바로 포인터와 관련된 지식을 만나게 되는 것과 같다. 사실 나는 기꺼이 Open된 ThinLet 코드를 볼수 있고, 가지고 있었기 때문에 사다리를 오르락 내리락 하며 문제를 수정했고 이러한 구멍에 대해서 충분히 이해를 하고 있었다.

그러나 결정적으로 수정에 수정을 거듭하다보니 수정된 프로그램은 XUL이라고 부르기에는 부끄러울 정도로 너덜너덜 해졌다. ThinLet의 마지막 버전은 2005년이고 코드를 작성하는 내내 왜 아무도 Draggabe한 Panel이나 Table을 만들지 않았을까가 궁금했었다. 그 의문은 그동안 겪었던 많은 문제들처럼 오랜동안 헤딩을 하고서야 깨달았는데, 쉽게 말하면 HTML에서 Draggable한 Table을 만들지 않는 이유와 같다. HTML처럼 XUL은 View를 하기 위한 Markup Language인데 Drag Action은 View가 아닌 영역에도 발을 걸칠수 밖에 없다. 즉 이전처럼 Model를 단지 View하기만 했던 구조와 달리 Drag등의 Action이 들어가는 순간 VIew는 Model을 인지하지 않으면 View를 할 수 없게 되었다. 그리고 Model과 Controller가 혼합되면서 Draggable한 TreeTable을 만든다고 하더라도 이게 그건 VIew역할을 하는 XUL이 아니고 오히려 더 복잡해진 정체를 알수 없는 혼합물이 되어버린 것이다. 

사실 프로그래밍 언어에 한해서는 이제 거의 이런 추상화의 문제로부터 벗어나 보인다. 우리는 수십년전의 어셈블리 개발자의 포트란 개발자에 대한 비난을 이젠 역사서에서나 읽을 수 있으니 말이다. 그러나 아직 개별적인 프레임워크에 있어서 그러한 함정은 너무나 많다. 

물론 이것이 XUL은 단지 VIew이기 때문에 XUL의 잘못이라고는 할 수 없다. 그러나 Helloworld를 만드는 것은 버튼 하나로 만들수 있지만 10만줄 짜리 프로그램을 만드는 데는 2배의 시간이 걸린다면 아마도 내가 잘못 선택을 한것이다. 


'Framework > Another Lore' 카테고리의 다른 글

언젠가...  (0) 2009.08.14
레이어의 속도  (0) 2009.07.05
read & write  (0) 2009.06.25
최근에 책을 읽다가..  (0) 2009.06.11
AL : Permission  (1) 2009.04.30
Posted by bleujin

여섯번째는 인터페이스에 대한 이야기이다. 

나는 오래전부터 추상클래스와 인터페이스의 문법적인 차이 이상의 문제에 대해 궁금해 왔다.

기본적으로 인터페이스는 다중 상속이 가능하고 추상클래스는 속성을 가질수 있다는 차이점 말고도 무언가 더 많은 차이가 있을것만 같았다. 

앞서 말한바와 같이 일단 속성을 가진다는 특성때문에 명사형 객체는 추상클래스가, 동사나 형용사는 인터페이스로 만들 확률이 높다. 물론 Thread와 Runnable처럼 두가지 형태를 모두 가질수도 있다. 보통 이정도 기준으로도 70%정도는 맞을 수 있다. 

다만 명사형임에도 인터페이스를 할 때가 있는데 그건 레이어의 바깥에 노출되는 객체 즉 구현에 있어서 매우 많은 유연성을 필요로 하기 때문에 - 여기서 더 유연이라는 의미는 인터페이스의 다중상속에 의지하는 바가 크다. - 명사형임에도 인터페이스를 고려해야 할 때가 있다. 

여기서 상속의 의미를 구분해야 할 것다. 인터페이스의 implements와 추상 클래스의 extends 모두 보통 상속이라고 부르고. 보통 부모 자식 관계라고 하는데 이 말은 명사형 추상클래스에 적합하고 인터페이스는 미묘하게 다른 의미를 갖는다. (영어 서적을 읽으면서 느껴지는 이 미묘한 뉘앙스의 차이가 번역을 거치면 일괄적인 부모-자식으로 변역되어 버린다.)

concrete 클래스에 있어서 인터페이스는 부모라기 보다는 일종의 이름이다. 흔한 수수께끼처럼 자기꺼지만 자기가 사용하지 않는걸 이름이라고 하는데 인터페이스는 이 수수께끼처럼 자기것이지만 자기가 사용하지 않는 것이고 우리가 사용하는 부모라는 의미로 보기 힘들다. (이게 영어와 한국어의 뉘앙스 차이일수도 있다.)

인터페이스와의 관계를 부모-자식 관계로  보지않고 이름 혹은 별명으로 보면 인터페이스의 의미에 대해 더 이해하기 쉬워진다. 인터페이스라는 의미 그 자체로 보면 접점-통로의 의미이므로 이름처럼 자기것이지만 불려지는 것이 되고 따라서 존재적 의미보다는 보여지는 의미가 더 중요해진다. (별로 상관없는 이야기지만 김춘수의 꽃이라는 시를 보면 존재적 의미보다 선언적 의미가 더 앞선다.)

추상클래스는 앞서 말한대로 명사형에 치우쳐 있고 속성을 가지기 때문에 상속이라는 용어와 부모자식관계라는 번역에 위화감은 없지만 인터페이스와 구현체는 좀 달리 이해할필요가 있다. 하지만 이러한 관계에 새로운 용어를 여기서 다시 만든다는 것은 더 많은 혼란을 야기할수 있기 때문에 그냥 외부에 불려지는 이름정도라고 넘어가자. 

따라서 인터페이스는 추상클래스보다 외부에 보여지는 모습이 중요하다. 아니 그게 전부라고 해도 좋을 정도이다. 이제 여섯번째 계를 말할때가 왔다. 잘 알려진 구현은 숨기고 대신 의도는 드러내라가 여섯번째 계이다. 본질과 작동원리에 대한 이해보다 인터페이스는 표층이 더 중요하고  특히나 다른 객체에게 더 중요하다. 자신의 것이지만 남에게 불리는 이름처럼 인터페이스는 객체가 다른 객체에게 불리어지는 이름이기 때문에 자신의 구현과는 상관이 없다. 다만 구현 객체들은 이런 이름으로 불리어야 한다는 걸 제시할 뿐이다. 

6계는 이미 다른 객체지향 책에서 많이 언급하고 있으므로 좀더 자세한 예제가 필요하다. 



'Framework > 아키텍쳐 일반' 카테고리의 다른 글

일곱번째 계 - 호출의 빈도(1)  (0) 2009.07.24
Second Program Effect  (0) 2009.07.17
호출의 빈도  (0) 2009.07.08
한줄짜리 함수  (0) 2009.07.06
예언?  (0) 2009.06.12
Posted by bleujin

이전글대로 변화의 속도에 다르기 때문에 소스포지나 코드서치를 통해서 얻은 Lib를 직접 사용하는 것보다 중개자 클래스(이것이 좀더 확장되면 새로운 Layer가 된다.)를 도입하는 것은 현명하다. 그런데 변화의 속도만으로 판단한다면 아마도 모든 Java Core Lib에 대해 중개자 클래스를 도입해야 할지도 모른다. 

그래서 변화의 속도 못지않게 중요한게 한가지 더 있다. 언젠가 다시 나올 7계인 호출의 빈도를 말하는 - 객체는 객체자체로서의 아닌 다른 객체에서 어떻게 그리고 얼마나 자주 보이는가가 더 중요하다.이다. 이전에서 말한바와 같이 양적인 차이는 질적인 차이를 유발한다. 만약에 단지 몇번 호출하고 말것이라면 굳이 번거로운 길을 갈 필요는 없다. 그러나 아주 많다면 그것이 new ArrayList() 라도 중개자 클래스(혹은 메소드)를 만들어서 하는게 도움이 된다. 


인간은 분류하는 동물

호출 빈도의 증가는 단순히 양적인 증가 이상의 의미가 있다. 서랍안에 물건이 단 6개만 있다면 책장에 책이 7권정도라면 그럭저럭 괜찮다. 하지만 하나의 서랍안에 물건이 37개라거나 한칸에 책이 76권이라면 서랍과 책장이 어질러져 있다고 느낀다. 그래서 Brian Buchanan은 Theory of library classification = 문헌 분류 이론의 한국어판 서문에서 “아마도 패턴과 순서에 대한 관심은 우리 인간들이 타고 나는 모양이다. 이러한 의미에서 인간을 분류하는 동물 (classifying animal) 이라고 정의할 수 있을 것이다. 이것은 카오스(혼돈) 에 대한 두려움을 바탕으로 하고있고, 또한 통제할 수 없는 환경을 통제함으로서 불안정한 세계에서 안전하다는 망상을 갖고자 하는 욕망에서 생겨나는 듯 하다.”라고 그의 분류관을 개관해 주고 있다. 


양이 많아지면 우리는 분리하고 싶어한다. 사실 System.out.println의 중개 클래스를 만드는 게 좋은 이유는 첫째의 변화의 속도 보다 우리가 그 메소드를 너무나 자주 쓰기 때문에 의존하는 바가 더 크다. 인터페이스를 만드는 이유를 생각해봐도 이는 당연한 말이다. 주로 인터페이스가 되는 객체는 인터페이스라는 말 그대로 Front Object 즉 바깥에서 가장 많이 노출되는 객체일 경우가 많고 핵심 알고리즘이 담겨서 있어서나 단지 양이 길어서가 아닌 가장 많이 불리는 객체에 대해 객체이름과 인터페이스 설계를 가장 고민해야 한다. 

사실 객체는 어떻게 보면 보여지는 모습 (인터페이스)이 바로 그것의 본질이다. 현상을 이해하는데 본질과 작동원리 (구현)에 대한 이해보다 표면의 관찰, 즉 통신수단으로서의 인터페이스가 중요하다.

'Framework > 아키텍쳐 일반' 카테고리의 다른 글

Second Program Effect  (0) 2009.07.17
여섯번째 계  (0) 2009.07.14
한줄짜리 함수  (0) 2009.07.06
예언?  (0) 2009.06.12
패키지의 설계 원칙  (0) 2009.06.08
Posted by bleujin
함수의 적정 최대 라인수가 얼마냐 하는 것은 아직까지 논의가 되고 있는 문제인데 보통의 경우 모니터의 화면을 넘기지 않을 정도라는 대체적인 합의는 있다. (대략 20라인 이내) 

Max는 그렇다 치고 Minimum은 어떨까? 
한줄짜리 함수는 가치가 있을까? 이전의 강의에서 나는 종종 그건 지나친것 같다라는 질문을 받곤 했다. 



한줄짜리 함수의 첫번째 유용성은 리팩토링 책에서 소개하듯이 가독성때문이다. 

1. if (date.before(SUMMER_START) || date.after(SUMMER_END))를

2. if (notSummer(date)) 와 같이 바꾼다면 (Decompose Conditional)

Reading하기가 좀더 쉽다. 아마도 notSummer는 private 함수일것이고 함수명으로 좀더 의미를 명확하게 전달하기 때문에 굳이 함수를 들여다봐야 해서 복잡해지진 않는다.(이런 가독성에 대해서는 이전글에서 언급했듯이 동전의 양면이다. 만약 함수명이 적절하게 지어지지 않는다면 좀더 복잡해질 것이다. Framework에서 구조의 문제도 마찬가지이다. )

그럼에도 한줄짜리 함수를 싫어하는 사람들은 꽤 있었지만 현재는 그 수가 많이 줄었다. 이는 툴의 발전에도 많이 영향을 받았다. 이전에 text editor에서 코드를 작성하던 쥬라기 시대에는 툴에 함수 바로가기 등의 assist 기능이 없어서 text search로 함수 내용을 확인해야 하던때라 가능한 하나의 함수에서 해주는게 미덕인 시절이 있었다. 

한줄 함수를 싫어하는 다른 이유로는 C 시절에는 Function Call Stack이 쌓이게 됨으로써 발생하게 되는 비효율을 이유로 들었으나 자바에서는 C와 달리 이러한 비효율이 성능상에서 차지하는 부분이 아주 작다. 이미 자바 라이브러리 자체가 수십개의 Function Stack Call을 하고 있으므로 거기에 몇개쯤 더한다고 거의 영향이 없다. (그러나 C나 C++은 영향이 있다. 그 영향 자체는 상대적이기에 많다 적다고 할수 없지만 확실히 자바보다는 영향이 많다.)




잘 알려지지 않는 한줄 함수의 두번째 유용성은 일종의 중개자로서의 역할인데
예컨데 System.out.println()을 예로 들어보자. 

최근에는 Unit시리즈의 유닛테스트를 많이 사용하고 있긴 하지만 그럼에도 유닛테스트하기 어려운 Swing이나 Persitence가 있고 또 가끔은 System.out.print으로 Debugging을 할때가 있다. 문제는 코드 중간중간에 써 놓은 System.out.println은 확인이 끝나고 처치가 곤란할때가 종종 있다. 

그렇다고 Logger를 쓰자니 잠깐 확인을 위해서 jar도 path에 걸어야 하고 설정도 해야하니 웬지 배꼽이 더 커 보인다. 

그럴때 Debug.debug() 클래스를 선언해서 쓰면 유용하다. 

예컨데 
public class Debug extends OutputStream{

private static void out(String message) {
System.out.println("   : " + message) ;
}

public static void error(Class cls, Object...objs) {
debug(cls, objs) ;
}

public static void debug(Object... objs) {
out(Arrays.deepToString(objs)) ;
}
public static void debug(Class cls, Object... objs) {
debug(Arrays.deepToString(objs) + " at " + cls.getName()) ;
}

public void write(int b) throws IOException {
System.out.print(b) ;
}
public static void happy(Object... objs) {
// TODO Auto-generated method stub
}
public static void happy(Class cls, Object... objs) {
// TODO Auto-generated method stub
}

}}
와 같이 별거 없는 클래스이다.(함수도 하나였는데 자꾸 쓰다보니 늘어났다.)

System.out.println() 대신 Debug.debug() 한줄함수로 바꾸면 생기는 이득은 멀까?

첫번째로 Reading이 쉬워지는 것외에 툴의 Call Hierarchy 기능을 통해 사용되고 있는 곳을 알수 있다. (System.out.println의 Call Hierachy를 찾으면 지나치게 많은 것을 찾는다.) 또는 잠깐의 조작을 통해 특정 메시지만 다른 outted message와 다르게 보이게 만들수도 있다. (System.out.println 함수를 바꿀수는 없지만 debug함수는 수정이 가능하다.)

둘째로 언제든 Logger 클래스를 사용하는 걸로 변신할수 있다. 이를테면 error() Method만 Logger 클래스를 사용하도록 이후에 수정에 가능하다. 시점이 나중이라는 것이 중요하다. 처음에는 그냥 쉽게 화면에 print 하는 방식으로 사용하다가 필요가 생기면 해당 함수를 수정하면 그만이다. 

한줄짜리 함수에 이런 장점이 생기는 근본 이유를 들여다 보면 좀더 흥미롭다. 왜 Debug.debug() 함수가 유용성을 가지는가? 그것은 우리가 System.out.print를 수정할 수 없기 때문이다. 만약 우리가 System.out.print를 컨트롤 할 수 있었다면 debug함수를 작성해서 수정할 필요없이 out class의 println 함수를 직접 수정하면 그만이다. 

앞서(4달전에:) 변화의 속도에 대해서 말한적이 있는데 현재 작성하고 있는 코드와 자바의 java.lang 패키지는 변화의 속도가 다르다. 그리고 그 변화의 속도가 다를때 부동산 중개인의 존재이유와 마찬가지로 우리는 중개자 클래스 혹은 함수가 필요하다. 다시 말해서 단 한줄짜리 함수라고 하더라도 해당 함수의 변화의 속도가 다르다면 그 함수는 유용성을 가질때가 많다. 

사실 대부분의 프레임워크와 마찬가지로 마치 빅뱅이론처럼 Logger Framework는 이러한 한줄짜리 함수에서 출발했다. 누군가 System.out.println으로 화면에 메시지를 보여주는게 관리상 효율이 좋지 못하다는 걸 깨달았고 한줄짜리 함수를 만들었는데 거기에 기능을 덧붙이고 유연성을 더하고 예외도 고려하고 등등을 하다보니 이게 Logger Framework가 되었다. 

그러나 그렇다고 해서 우리는 처음부터 System.out.println과, 보다 설정하기도 귀찮고 사용하기 어려운 Logger Framework 두가지중에서 꼭 하나를 결정해야 할 필요는 없다. 그런 선택은 미루다가 나중에 고를수 있거나 혹은 제 3의 길의 선택할 수 있는 방향을 열어두는게 좋다.

즉 한줄짜리 함수는 변화의 속도가 다른 Layer사이에서 그 속도의 조정과 완충을 담당하는 중재자 역할을 할때 새로운 효용성을 발견할 수 있다. 자신이 컨트롤 할수 없는 함수(System.out, Logger 등등)을 5번 이상 호출해야 한다면 이런 한줄짜리 중개자 함수를 고려해 보자. 



'Framework > 아키텍쳐 일반' 카테고리의 다른 글

여섯번째 계  (0) 2009.07.14
호출의 빈도  (0) 2009.07.08
예언?  (0) 2009.06.12
패키지의 설계 원칙  (0) 2009.06.08
양화 구축  (2) 2009.06.08
Posted by bleujin
Framework/Another Lore2009. 7. 5. 20:31
앞서 소개한적이 있지만 

"전산학의 모든 문제는 또 다른 수준의 간접층으로 해결할 수 있다" 
라고 말한 휠러는 

"그러나 그러면 또 다른 문제가 생기는 것이 일반적이다" 라고 덧붙인 적이 있다. 

"또다른 문제"의 대표적인 예로는 공간과 시간적 부담과 코드의 가독성 등을 들 수 있다. 

이 포스팅에서는 이중에서 시간적 부담 즉 속도에 관해서이다. 


일반적으로 대부분의 프레임워크는 하나의 간접층(Layer)이며 위의 명제를 피할 수는 없다. 그러나 프레임워크를 쓴다고 항상 느려진다고 할 수 없는 이유가 있다. 

만약 A -> B 과정이 1초가 걸렸다면
A -> A' -> B 과정은 1초보다 작게 걸린다는 것은 논리적으로 불가능하다. 

그러나 시간은 연속성의 특징을 가지고 있다. 무슨말이냐면 프로그램에서의 시간 혹은 속도의 개념은 하나의 event개념이 아니다. 프로그램을 작성하면 무수히 많은 A -> B 과정을 작성하게 된다. 

이를테면 A가 Client Call이라고 하고 B를 Engine라고 가정했을때 (Engine에는 DB나 NS등의 여러가지를 대입할 수 있다.)

1. Client Call(Engine Library) -> Engine과
2. Client Call(Framework Call) -> Framework(Inner Use Engine Library) -> Engine 라는 경우를 상상해 보자. 

하나의 원자 동작을 보았을때 1은 2보다 항상 속도가 빠르다. 그러나 실제 프로그래밍에서는 2가 종종 더 빠른 이유는(잘 만들었을 경우를 가정했을때...) 연속성의 이유때문이다. 함수 하나 혹은 command 하나는 말 그대로의 원자 event로 이루어지지 않는다. 

그리고 이것때문에 이를테면 1의 동작에 0.1초가 걸린다고 하고 10번 반복한다고 했을때 1은 1초가 걸리지만 2는 1초 이하로 떨어뜨릴수 있다. 

좀더 IT적인 용어로 말하자면 Context Switching 비용 때문이다. 

예를 들어
Row row = Engine.executeCommand() ;
if(row.exist()) Engine.updateCommand() ;
else Engine.insertCommand();

와 같은 코드의 경우 Engine의 row의 exist 여부를 체크하여 2번의 Engine Call을 실행한다. 이 과정에서의 속도는 (0.1 + a(context switching cost)) * 2 + b(Caller cost) 가 소모하게 된다. 

만약 위의 과정을 
Framework.mergeCommand() 라고 바꿀수 있다면 0.1 * 2 + a(context switching cost) + b'(Maybe Lower Caller Cost) + c (framework inner handling cost) 로 수식이 바뀐다. 만약 여기서 a + b> b' + c라면 프레임워크를 사용함으로서 속도가 향상되었다고 말할 수 있다. 추가적으로 좀더 간결하게 프로그램을 작성할 수 있다. 여기서 간결함이란 문제는 이전글을 참조하자. 

3번의 동작이 연속으로 이루어진다면 2a + b> b' + c 정도만 해도 향상될 수 있다. 


두번째 속도가 빨라질 수 있는 이유로 
앞서 원자 event라고 가정했던 1의 과정조차 실제로는 바깥에서는 보이지 않는 내부적으로는 연계된 다른 과정을 거치게 된다.

앞서 원자 action event라고 했던 1이 사실은 
Client Call -> Inner Action 1 -> Inner Action 2 -> Engine Inner으로 이루어져 있다가 가정하자. 
각각의 비용은 a + b + c + d  라고 가정하자. 

만약 
Client Call -> Inner Action 1 -> NewFramework -> Inner Action 2 -> Engine Inner로 작성하였다면..
a + b + @(framework cost) + c + d 의 비용이기 때문에 역시 단순 논리로 봤을때는 빨라지지 않는다. 

그러나 프로그래밍이 다중 사용자 환경(One User 프로그램의 경우에도 Multi Thread로 작성된다면 마찬가지이다. )이고 Action 2가 공유될수 있는 Action이라고 한다면 얘기가 달라진다

Inner Action2을 사용자 혹은 여러 Thread가 공유할 수 있다면

N번의 Call이 발생했을때. 
N(a + b + c + d)의 비용이 N(a + b + @ + d) + c의 비용 수식으로 바꿀수 있다. 
이는 c의 cosr가 total cost에서 얼마의 비중인가에 따라 그 가치가 달라진다. 잘 알려진 예제로 Connection Pooling 같은 과정을 생각한다면 이해하기 쉽다. 

# 컨텍스트 스위치 context switch
멀티테스킹 OS는 서로 다른 처리를 수행하는 처리 단위로 프로세스/쓰레드를 짧은 시간에 전환함으로써 병렬처리를 실현하고 있다. 이때의 프로세스/쓰레드 전환처리를 컨텍스트 스위치라고 한다. 이렇게 컨텍스트 스위치할때 멀티쓰레드는 메모리 공간을 공유하기 때문에 메모리 공간의 전환 처리는 생략할 수 있다. 

메모리 공간을 전환하지 않게 되면 CPU 상의 메모리 캐시(정확히는 TLB Translation Lookaside Buffer : 메모리의 가상 주소를 물리 주소로 변환하는 처리를 고속화하기 위한 캐시로 CPU 내부의 구조다. 컨텍스트 스위치가 일어나면 TLB가 초기화되는데 이 영향에 의한 TBL 캐시 미스는 상대적으로 비용이 많이 든다.)를 그대로 유지할 수 있는 등의 큰 이점이 있으므로 멀티 프로세스에 비해 성능에 미치는 영향은 현저하다. 

이러한 이유로 위의 경우 자바와 예로 든 엔진(예컨데 DB)의 변환은 멀티 프로세스 관계일때 더 차이가 두드러진다. 





원래 주제와 좀 떨어지지만 휠러 얘기가 나왔으니 "발생하는 또다른 문제"중 가독성의 얘기를 해보자. 

여기서의 가독성은 동전의 양면과 같다. 사실 맨 처음 웹 프로그래밍을 한다고 했을때 스프링을 사용하는 것은 그다지 좋지 못하다.(라고 생각한다.) 스프링의 스펙은 그리 만만치 않다. 오히려 기본적인 서블릿 프로그래밍보다 더 많은 걸 알아야 한다. 스프링 프레임아웤의 유용성을 떠나 아이러니한것은 중급개발자의 반복작업을 줄여주기 위한 프레임워크가 오히려 초보개발자용 프레임워크로 둔갑한다는 것이다. 

어쨌거나 휠러가 지적한 것은 프레임워크의 겉핧기 사용등의 프레임워크의 내부에 대한 이해 없이 접근하면 작성하는 프로그래머도 또 그걸 읽는 프로그래머(그 사람이 해당 프레임워크에 대해 알고 있는지와 상관없이)도 고통이 된다는 사실을 말한 것이다. 

앞서 동전의 양면이라고 한것은 프레임워크에 대한 많은 학습없이 직관적으로 사용이 가능하고 기대대로 프레임워크가 작동한다면 오히려 가독성에 도움이 될 수 있다는 의미이다. 

그런면에서 프레임워크의 직관적 사용은 좋은 프레임워크를 결정하는데 핵심이 된다. 스프링 프레임워크가 직관적이지 못한것은 어찌보면 프레임워크가 커버하는 범위와 관계가 있을지 모른다. 스프링 프레임워크는 너무 많은 일을 하는 프레임워크이고 또 유연성을 위해 XML 형식의 configuration이 지나치게 많고 복잡해서 휠러의 또다른 문제를 벗어나지 못했다. 











'Framework > Another Lore' 카테고리의 다른 글

언젠가...  (0) 2009.08.14
XUL 단상  (0) 2009.07.15
read & write  (0) 2009.06.25
최근에 책을 읽다가..  (0) 2009.06.11
AL : Permission  (1) 2009.04.30
Posted by bleujin
Framework/Another Lore2009. 6. 25. 10:47

습관은 관습을 낳고 관습은 편견을 낳는다. 

우리는 bean 객체를 많이 봐왔기 때문에 getMethod가 있으면 setMethod도 있어야 할 것이라고 생각한다. 물론 이것이 틀렸다고는 할수 없지만 가끔은 생각을 바꿔보는게 도움이 될 수 있다. 

예컨데 다음 상황을 보자. 

Node rootNode = session.getRootNode() ;
NodeType empType = getNodeType("employee") ;

Node newNode = rootNode.createChild(empType, "abcd") ;


이 상태에서 newNode를 repository에 저장하는게 좋을까? 아니다. 그러기엔 너무 일러보인다. 왜냐하면 아직 실제 값을 설정하지 않았기 때문이다. 

그 보다는 
newNode.setProperty("ename", "bleujin") ;
newNode.setProperty("empNo", 20) ;
와 같이 값을 설정하고 

newNode.save() 를 호출했을때 repository에 저장하는게 보다 효율적일 것이다. Transaction Handling이 가능하고 setProperty를 호출할때마다 DBCall을 하는것보단 훨씬 효율적이다. 


다만 위의 경우 문제가 있다. 

다음 상황을 상상해 보자. 

Node parent = rootNode.createChild(empType, "parent") ;
Node child = parent.createChild(empType, "bleujin") ;
child.setProperty("ename", "bleujin") ;
child.setProperty("empNo", 20) ;
child.save() ;

별문제가 없어보인다. 
그러나 위와 같이 현재 Session의 모든 PendingEvent를 저장하는 session.save() 를 호출하지 않고 child.save() ;를 호출했을 경우에 (물론 API 상으로는 아무런 문제가 없다.) parent가 저장되지 않은 상태에서 child가 먼저 저장될 수 있는가? 에 대한 의문이 든다. 더군다다 child만 save()하고 parent는 일부러 혹은 실수로 save를 하지 않거나 혹은 parent.save()를 호출했을 경우에 에러가 발생했을 경우를 상정해 보면 문제가 더욱 복잡해진다. 

API상으로는 문제가 없지만 child만 save가 되고 parent는 save 되지 않았을 경우는 정상적인 상태가 아니다. 그렇다고 맨 처음의 방식으로 돌아가는 것도 Transaction이 보장되는 것은 아니다. 각각의 호출마다 repository Call을 했을 경우 첫번째와 두번째 사이에 에러가 발생할 수 있다는 것은 마찬가지 이다. 

물론 사용자 실수야 라고 탓이라고 문제를 회피할 수도 있겠지만 이는 올바른 방법이 아니다. 순서에 의존하는 API는 (예컨데 a를 호출하고 난 다음에 b를 호출하고 그 다음에 c를 호출하고..) 사용하기 복잡하고 실수를 가져오는게 당연하다. 

이 문제를 해결하기 위해 AL은 존재와 무존재를 구분한다. 
예컨데 


interface INode {
    .......
    getterMethod() ;
    setterMethod() ;
    Node retrieve(...) ;
    Node save() ;
}

interface NodeTemplate extends INode {
     ....
}

interface Node extends INode {
    NodeTempalte createChild(NodeType nodeType, String name) ;
}

와 같이 인터페이스를 분리하면 문제는 명확해진다. 
ISP(Interface Segregation Principle)의 의미는 어렵지 않지만 책임의 한정이 어렵다. 여기서는 책임을 read책임과 write책임으로 분리한 것이다. 


NodeTemplate parent = rootNode.createChild(empType, "parent") ;
위 상태에서 NodeTemplate는 createChild Method를 호출할 수 없기때문에 

Node savedParent = parent.save() ; 를 호출한 이후 child를 만들거나 아니면 session.save()를 호출하여 pendingEvent를 같이 처리해야 한다. 


위 예제에서 좀더 나아가서 getMothod용 Node와 setMethod용 Node를 구분한다면 개선의 소지가 더 있다. 이를테면 Setter가 없는 Node를 통해 더 가벼운 Node를 만들 수 있게 된다. 그리고 Getter가 필요없는 Node를 통해 더 빨리 Node를 얻어올 수 있다. 




'Framework > Another Lore' 카테고리의 다른 글

XUL 단상  (0) 2009.07.15
레이어의 속도  (0) 2009.07.05
최근에 책을 읽다가..  (0) 2009.06.11
AL : Permission  (1) 2009.04.30
AL : 현재의 난제들  (0) 2009.04.30
Posted by bleujin