주석(코멘트)에 대한 대부분의 책은 단 한마디로 요약할 수 있다.
 "주식을 충실하게 달아라"

이는 다른말로 대부분의 프로젝트에는 주석이 충실하지 않다는 반증이고 주석 다는게 쉽지 않다는 말이기도 하다. 사실 좋은 코드와 나쁜 코드를 구분할 수 있는 것처럼 주석에도 좋은 주석과 나쁜 주석이 있다.

주석 안에는 무엇이 들어갈까 ? 주석을 달때 주의점은 아래와 같다 .

 - 어떻게가 아니라 왜를 기술하라 이를테면
    틀린예) GlbWLRegistry에 있는 WidgetList 구조를 업데이트 한다.
    라고 주석을 달아서는 안된다. 왜냐하면 그 내용은 코드안에 박혀 있기 때문이다. 따라서 "왜" 이렇게 코드를 작성했는지에 대해 주석을 달아야 한다. 
    바른예) 나중을 위해서 widget 정보를 캐시함 O


 - 코드를 묘사하지 마라
    틀린예) i++ ; // increment i
    DRY의 원칙은 단순히 코드끼리의 중복만을 의미하는 것은 아니다. 위 틀린예는 옆의 코드와 주석이 중복되기 때문에 DRY 원칙을 위배한 것이다. 코드와 중복되는 내용을 다시 코멘트로 쓰지 말아라. 


 - 코드를 대신하지 마라
    나쁜예) // 이 변수는 foo 클래스에 의해서만 접근해야 함. 
    코드의 주의사항을 달기보다는 코드를 다시 작성해라. 변수의 사용법을 설명하는 주석을 달기보다는 변수의 이름을 다시 지어라. 코드로 충분히 할 수있는 것을 주석으로 대체하지 말아라. 

    
 - 유용성을 유지하라
    주석은 당연한 것을 말하는 것이 아니라 당연하지 않다고 생각되는 의외의 것을 기록해야 한다. 주석으로 코드를 쉽게 설명할 생각은 하지 말고 그냥 코드를 쉽게 만들어라. 만약 더 이상 코드를 쉽고 명료하게 만들수가 없을 때에만 주석이 필요하다. 주석을 쓰레기 코드를 덮는 포장지로 사용해서는 안된다. 더이상 코드에 손댈게 없을때에야 진실되고 명료하고 알기 쉬운 주석을 달도록 노력해라. 


 - 주의를 흩트리지 마라
   코드가 주인이라는 사실을 잊지 말자. 따라서 주석을 달음으로서 불필요하게 코드 리딩과정에 주의를 흐트려서는 안된다. 쓸데 없는 아스키 기교로 주석의 네모 박스를 이쁘게 유지하는 등의 바보같은 노력을 하지 마라.
블록의 끝에  // end if ( a < 1 ) 같은 주석이 그럴듯 하게 보일지 모르지만 이 주석을 달 시간에 차라리 코드를 더 이쁘게 작성하고 지나친 중첩 block를 사용하지 말아라. 이런 주석은 코드가 수정될때 같이 수정이 되지 않아서 부패가 되고 썩어 버린다. 불필요한 코드만큼이나 불필요한 주석 그리고 공허한 주석을 달지 말아라.


주석에 대한 이런 관점은 주석은 필요악이라는 관점이다. - 비록 많은 사람들이 동의 하지 않을지라도 - More Comment, Better Code가 절대 아니다. 방법론에서 문서화들이 그러한 것처럼 Better Code를 추구하기 위한 과정일뿐 주석자체는 목적이 아니다. 그래서 좋은 코멘트를 작성하는 것보다는 좋은 코드를 작성하는것에 집중하는 것이 좋다.

Posted by bleujin
Framework/Database2009. 3. 12. 16:31

이전 글을 작년 7월에 써서 올해 1월 12일날 포스팅을 해서 기억도 잘 안난다 -ㅅ-; (심지어 거기에 나온 내용들은 이미 7년전쯤에 해본 내용이다.)

  public void testStatic() throws Exception {
    DBManager dbm = new MSSQLPoolDBManager("com.microsoft.jdbc.sqlserver.SQLServerDriver", 
         
"jdbc:microsoft:sqlserver://novision:1433;DatabaseName=test", "bleu", "bleu") ;
    Connection conn = null;
    ResultSet rs = null;
    PreparedStatement pstmt = null;

    try {
      conn = dbm.getConnection()
      pstmt = conn.prepareStatement(getProcSQL());
      rs = pstmt.executeQuery()
      populate(rs)
    catch (SQLException ex) {
      throw ex;
    finally {
      closeWithoutException(rs);
      closeWithoutException(pstmt);
      dbm.freeConnection(conn)
    }
  }

  private String getProcSQL() {
    return "select custNo, fixLength from customer_tblc";
  }

  private void populate(ResultSet rowsthrows SQLException {
    int i = 0;

    while (rows.next() && i++ < 10) {
      System.out.print(rows.getString(1));
    }
    System.out.println();

  }

"위 평범한 JDBC 코드는 customer_tblc에서 10건을 가져와서 10건을 화면에 찍는 프로그램이다"
는 사실이 아니라는 걸 얘기 했었다.

위 코드를 default cursor인 Forward Only cursor로 실행하면(빨간색 화살표는 여러번 반복된다는 뜻)


   1. WAS는 쿼리 실행을 요청한다. 
   2-1. DBServer에서는 cusotmer_tblc의 row를 읽는대로 Working Memory에 올린다.
   2-2  동시에 WorkingMemory에 어느정도의 데이타가 쌓이면(대략 2-8k) 바로 WAS에 전송한다.
   2-3  WAS는 패킷을 받아서 받았다는 confirm 메시지를 전송한다. 

   위 2-1 ~ 2-3 과정은 DB가 쿼리의 모든 row를 읽을때까지 반복된다. 
   WAS에서는 사용하는 10건의 row를 받았고 이후의 데이타는 쓸모 없지만 주구장창 모두 받아야 한다. 
   3. WAS는 나머지 데이타도 모두 받아서 버린후에 DB close를 요청한다.
   DB서버는 WorkingMemory를 정리하고 close 된다.

만약 테이블에 단지 20건 정도라면 괜찮지만 1000000건만 되도 99.9999%의 비용을 더 소모하였다.

단점은
  - WAS에서 필요한 양과 상관없이 해당 쿼리의 모든 데이타를 읽어야 한다.(컴퓨터에게 읽는다는 의미는 하드에 있는 바이트 배열을 메모리에 복제하였다는 얘기이다.)
  - WAS에서 필요한 양과 상관없이 모든 데이타를 네트워크에 전송함으로서 막대한 네트워크 부하가 발생한다. 
  - 모든 데이타를 받아야 종료(DB Close) 할수 있다.

장점
   - 읽는대로 바로 바로 전송해버리기 때문에 DB 메모리가 많이 소모되지 않는다.
   - 네트워크에 전송할때 여러 row를 묶어서 패킷으로 전송한다.
   - 커서는 비동기적으로 작동하기 때문에 최소한의 정보 - 현재 커서의 위치 - 만을 유지할 수 있다.

  public void testStatic() throws Exception {
    Connection conn = null;
    ResultSet rs = null;
    PreparedStatement pstmt = null;

    try {
      conn = dbm.getConnection()
      pstmt = conn.prepareStatement(getProcSQL(), ResultSet.TYPE_SCROLL_INSENSITIVE
                                                , ResultSet.CONCUR_READ_ONLY)
;
      rs = pstmt.executeQuery()
      populate(rs)//
    catch (SQLException ex) {
      throw ex;
    finally {
      closeWithoutException(rs);
      closeWithoutException(pstmt);
      dbm.freeConnection(conn);
    }
  }

위와같이 insensitive cursor로 실행하면


  1. WAS는 쿼리 실행을 요청한다.  
  2. DB는 쿼리의 "모든" 결과를 메모리에 snapshot 형태로 만든다. 모든 데이타의 load를 완료하면 Client의 신호를 기다린다
  3. Client(WAS)는 Fetch(rs.next())를 요청한다.
  4. DB는 cursor 정보를 유지하면서 currentRow의 정보를 Client에게 보낸다. 
  5. 패킷(1개의 row만 들어있음)을 받았다는 confirm 메시지를 보낸다.
  3,4,5의 동작을 9번 더 반복한다
  
  6. WAS는 DB close를 요청한다.
  DB는 결과셋과 cursor정보를 메모리에서 삭제하고 연결정보를 close한다.  


ForwadOnly와 달리 3번, 4번, 5번은 비동기적으로 작동한다. 즉 모든 결과셋을 Snapshot형태로 메모리에 모두 올리고 나서야 Client의 Fetch(res.next())를 실행할 수 있다.(이동안 Client은 Hang 상태이다. 역시 이전 글의 네트웨크 모니터링 캡쳐화면과 같이 보도록 하자.)


단점은
  - WAS에서 필요한 양과 상관없이 해당 쿼리의 모든 데이타를 읽어야 한다.(컴퓨터에게 읽는다는 의미는 하드에 있는 바이트 배열을 메모리에 복제하였다는 얘기이다.)
  - DB에서 읽은 모든 Row를 결과셋에 Snapshot 형태로 저장해야 하기 때문에 공유 자원인 DB 메모리가 많이 소모된다. (이 메모리는 재사용되지 않는다. 즉 같은 프로그램을 2번 실행한다는 것은 SnapShot을 2번 유지한다는 것을 뜻한다. )
   - 동기적으로 작동하기 때문에 Client의 fetch 요구시마다 row단위의 데이타 전송이 이루어진다.(패킷의 크기가 작다.)
   - cursor를 해당 프로그램이 종료될때까지(DB close 할때까지) 유지해야 하고 더 많은 정보를 관리해야 한다.
   - 첫번째 Row가 Client에 전송되는 시간이 느리다.


장점 
   - Client가 필요한 양만 네트워크에 전송한다. 


단점이 너무 심각해 보이는 위 두가지 경우는 황당해 보이겠지만 90% 이상의 사이트에서 발생하고 있는 현실이다. 말 그대로 불편한 진실인 것이다. (이 블로그에서 소개하는 DB Framework는 위 방식으로 작동하지 않는다.)


커서의 종류는 보통 ForwardOnly, Static, Keyset, Dynamic 네가지 종류가 있으며 JDBC 코드로는 작성하기는 어렵지만 다음에는 나머지 두개의 커서의 실행과정도 분석해 보자. (앞서 언급했듯이 JDBC에는 커서를 지정한다기 보다 다른 선택에 의해 자동 종속되고 Keyset과 Dynamic가 종속되는 경우는 극히 드물다. 반면에 C#등의 MS 계열은 커서 모드를 설정할 수 있도록 하고 있다. 다만 일부 조건을 만족시켜야 된다.)






'Framework > Database' 카테고리의 다른 글

Framework (Servant)  (0) 2009.03.12
Framework (DBController)  (0) 2009.03.12
Framework (Handler)  (0) 2009.03.08
Framework (Rows)  (0) 2009.03.07
Framework (IQueryable)  (0) 2009.03.06
Posted by bleujin
IT 이야기2009. 3. 12. 06:41



- 우리 프로젝트 계획은 평소처럼 아래의 일반적인 과정을 겪을 것입니다.

- 1단계는 능력의 과신으로 인한 근거없는 낙관주의 입니다.

- 2단계는 방해자들이 그들의 소굴에서 기어나와 우리의 꿈을 말살하려고 할겁니다.

- 3단계는 무지와 시기심에서 비롯된 루머들이 반복해서 양산되다가 급기야 일반적 사실로 둔갑합니다.

- 자원은 이런 그릇된 정보와 편협된 취사선택의 과정을 통해서 배분될 것입니다.

- 그리고 프로젝트가 엉망이되고 불가능해질때까지 요구사항들은 둥둥 떠따닐 것입니다.

- 그리고 나서 우리한테 2주가 남겠죠

- 저 그냥 근거없는 낙관론자로 돌아가고 싶은데요



여기서 웃음 포인트는 저기 "그리고 나서 우리한테 2주가 남겠죠"이다. 저 시기는 사용자, 관리자, 프로젝트 팀원, 그리고 프로젝트의 모든 다른 이해관계자들이 프로젝트를 납기안에 끝내지 못할 것이라는 것을 시인해야 하는 "진실의 순간"이다.  팀원들이 자기를 리더라고 생각해주길 바라는 관리자가 일정을 대폭 단축했기 때문에 프로젝트가 시작하고 바로 누군가 이건 불가능해 혹은 이게 성공할 확률은 로또보다 낮아라고 부르짖었을지라도 그건 이미 잊혀졌다.

개발자가 수일에 걸쳐 한 예측을 아무 근거없이 10-50%를 깍고 자신의 능력으로 이러한 단축을 이끌어 낼 수 있었다고 생각하는 관리자는 수없이 많다. 자신의 일은 이렇게 예산과 납기기한을 줄이는 것이라고 생각하는 듯하다. 물론 그렇다고 프로젝트 실행시간은 빨라지지 않는다. 프로젝트가 10%일찍 끝나지 않는다면 수십만달러의 손실이 발생할것이라고 프로그래머를 압박하는 대신에 애초에 왜 그만큼 일찍 시작하지 못했는지를 생각해야 한다. 

어쨌든 이 진실의 순간 프로젝트 관리자는 경질되고 후임이 온다. 만약 행운이 따르지 않는다면 다시 1단계로 올라가서 같은 과정을 반복하게 되고 다시 또 2주가 남게된다.


'IT 이야기' 카테고리의 다른 글

멘탈 2  (0) 2009.03.23
How vs Who  (0) 2009.03.16
Scott Adams  (0) 2009.03.11
사람이 읽는 코드  (0) 2009.03.11
멘탈 ?  (0) 2009.03.01
Posted by bleujin


프로그래밍 패러다임은 프로그래머에게 프로그래밍의 관점을 갖게 해 주고, 결정하는 역할을 한다. 예를 들어 객체지향 프로그래밍은 프로그래머들이 프로그램을 상호작용하는 객체들의 집합으로 볼 수 있게 하는 반면에, 함수형 프로그래밍은 상태값을 지니지 않는 함수값들의 연속으로 생각할 수 있게 해준다.

프로그래머가 어떤 언어를 사용하는 것과는 상관없이 프로그래밍 패러다임에 대해 알아두는 것은 새로운 관점의 세계관을 가진다는 것을 뜻한다. XP 켄트벡의 프로그래머들은 일년에 하나의 새로운 언어를 배워두는걸 권장하는 것은 아마도 이 때문이 아닌가 싶다. 예를 들어 객체지향 프로그래밍은 프로그래머들이 프로그램을 상호작용하는 객체들의 집합으로 볼 수 있게 하는 반면에, 함수형 프로그래밍은 상태값을 지니지 않는 함수값들의 연속으로 생각할 수 있게 해준다.

프로그래밍 패러다임과 프로그래밍 언어와의 관계는 프로그래밍 언어가 여러 프로그래밍 패러다임을 지원하기도 하기 때문에 복잡할 수도 있다. 어떤 언어들은 하나의 특정한 패러다임을 지원하기도 하는데, 스몰토크와 자바가 객체지향 프로그래밍을 지원하는 반면에, 헤스켈과 스킴은 함수형 프로그래밍을 지원한다.

여러가지 패러다임을 지원하는 언어들도 있는데, C++, 자바 스크립트, 커먼 리스프, 파이썬, 오즈가 이런 언어들이다. 예를 들어서 C++는 절차적 프로그래밍, 객체기반 프로그래밍, 객체지향 프로그래밍, 제네릭 프로그래밍의 요소들을 지원하도록 설계되었다. C++에서는 순수하게 절차적 프로그램을 작성할 수 있고, 순수하게 객체지향 프로그램을 작성할 수 있으며, 두 가지 패러다임 모두의 요소를 포함한 프로그램을 작성할 수도 있다. 자바 스크립트는 구조적 프로그래밍이고, 함수형 프로그래밍이고, 프로토타입 기빈 프로그래밍이며, 객체기반 프로그램밍 언어이지만 절차적 프로그래밍 방식도 사용한다.


구조적 프로그래밍과 비구조적 프로그래밍

구조적 프로그래밍(영어: structured programming)은 구조화 프로그래밍으로도 불리며 프로그래밍 패러다임의 일종인 절차적 프로그래밍의 하위 개념으로 볼 수 있다. GOTO문을 없애거나 GOTO문에 대한 의존성을 줄여주는 것으로 가장 유명하다.역사적으로 구조적 프로그램을 작성하기 위하여 몇가지 다른 구조화 기법과 방법론이 개발되어 왔다. 

데이크스트라의 구조적 프로그래밍은 프로그램의 논리 구조는 제한된 몇 가지 방법만을 이용하여 비슷한 서브 프로그램들로 구성된다. 프로그램에 있는 각각의 구조와 그 사이의 관계를 이해하면 프로그램 전체를 이해해야 하는 수고를 덜 수 있어, SoC에 유리하다. (데이크스트라의 관점에서 파생된 관점 : 하위 프로그램의 시작점은 한 군데이지만 끝점은 여러 개일 수 있다. )

저수준의 관점에서 구조적 프로그램은 간단하고, 계층적인 프로그램 제어 구조로 구성된다. 이 제어 구조들은 하나의 구문으로 간주되며, 동시에 더 간단한 구문들을 결합시키는 방법이다. 더 간단한 구문들은 또 다른 제어 구조일 수도 있고, 할당문이나 프로시저 호출과 같은 기본 구문일 수도 있다.(에츠허르 데이크스트라가 확인한 3가지 형태의 구조는 순차, 선택, 반복이다.)

설계에 있어서 구조적 프로그래밍이 항상 그런 것은 아니지만 하향식 설계와 관련이 있다. 하향식 설계를 할 때, 설계자는 큰 규모의 프로그램을 더 작은 공정으로 나누어 구현하고, 각각 검사한 다음에 전체 프로그램으로 합친다.

모든 절차적 프로그래밍 언어에서 구조적 프로그래밍을 할 수 있다. 1970년쯤부터 구조적 프로그래밍이 인기있는 기법이 되었기 때문에, 대부분의 새로 나온 절차적 프로그래밍 언어들이 구조적 프로그래밍을 고취시키기 위한 특징을 추가하였고 구조화되지 않은 프로그래밍을 쉽게 하기 위한 특징들은 남겨둔 것들도 있었다. 잘 알려진 구조적 프로그래밍 언어에는 파스칼(Pascal)과 에이다(Ada)가 있다.


비구조적 프로그래밍은 하나의 연속된 덩어리에 모든 코드를 넣는 프로그래밍 패러다임이다. 대비되는 개념으로는 구조적 프로그래밍이 있는데, 이는 프로그램의 작업이 (함수나 서브루틴으로 알려진) 더 작은 부분으로 나누어 필요할 때마다 호출하는 것이다. 비구조적 프로그래밍 언어는 코드의 특정부분으로 건너뛰는 GOTO문과 같은 흐름 제어문에 의존할 수 밖에 없다.

구조화되지 않은 원시 코드는 읽고 디버그하기가 매우 어렵고, 구조적인 작성을 지원하는 프로그래밍 언어에서는 추천하지 않는다. 그러나 프로그램 구조는 항상 조건문과 GOTO문을 조합하여 구현할 수 있기 때문에 구조가 모든 언어에서 필요한 것은 아니다. MS-DOS의 배치 파일과 같은 많은 스크립트 언어나 베이직이나 포트란 같이 오래된 언어에서는 여전히 사용되기도 한다. GOTO문을 쓰는 것에 수행 속도상의 이점은 없다. (실제로, 컴파일러가 최적화 할 수 있는 것들을 혼란시켜 오히려 불이익이 될 수도 있다.)

어셈블리어는 대체로 비구조적 언어인데, 기본이 되는 기계어 코드가 구조적이지 않기 때문이다. 어셈블리 언어에 있는 유일한 구조는 함수의 시작과 끝 같이 컴파일 도구에서 쓰는 것들이다.



명령행 프로그래밍과 선언형 프로그래밍

전산학에서 명령형 프로그래밍(Imperative programming)은 선언형 프로그래밍과 반대되는 개념으로, 프로그래밍의 상태와 상태를 변경시키는 구문의 관점에서 연산을 설명하는 프로그래밍 패러다임의 일종이다. 자연 언어에서의 명령법이 어떤 동작을 할 것인지를 명령으로 표현하듯이, 명령형 프로그램은 컴퓨터가 수행할 명령들을 순서대로 써 놓은 것이다.

명령형 프로그래밍 언어는 함수형 프로그래밍이나 논리형 프로그래밍언어와 같은 다른 형태의 언어와 다르다. 헤스켈 같은 함수형 프로그래밍 언어는 구문들을 순서대로 써 놓은 것이 아니며, 명령형 프로그래밍 언어와는 다르게 전역적인 상태가 없다. 프롤로그와 같은 논리 프로그래밍 언어는 "어떻게" 계산을 할지 보다는 "무엇"이 계산될 것인지를 정의한다는 생각으로 작성된다.

거의 대부분의 컴퓨터 하드웨어는 명령형으로 구현된다. 거의 모든 컴퓨터 하드웨어들이 컴퓨터의 고유 언어인 기계어를 실행하도록 설계되어 있는데, 이것이 명령형으로 씌어 있다. 낮은 수준의 관점에서 프로그램의 상태는 메모리의 내용으로 정의되고, 구문들은 기계어의 명령어로 정의된다. 높은 수준의 언어 구현은 변수와 더 복잡한 구문을 사용하지만, 여전히 같은 패러다임을 따른다. 요리법이나, 공정 점검표같은 것들은 컴퓨터 프로그램은 아니지만, 명령형 프로그래밍과 비슷한 형태의 이해하기 쉬운 개념이다. 각각의 단계의 지시 사항들이 있고, 상태라는 것은 현실 세계에 반영된다. 명령형 프로그래밍의 기본 생각이 개념적으로 친밀하고, 직접적으로 구체화되어 있어서, 대부분의 프로그래밍 언어들은 명령형이다.

보통 할당문은 메모리에 있는 정보에 연산을 수행하고, 결과값을 나중에 사용하기 위해 메모리에 저장한다. 추가로, 고급 명령형 언어는 산술 연산, 함수연산, 결과 값을 메모리에 할당하는 연산을 결합한 복잡한 수식을 계산한다. 반복문은 이런 연속된 구문을 여러번 실행하게 한다. 반복문은 미리 정의된 횟수만큼 반복하기도 하고, 어떤 조건이 바뀔때까지 반복하기도 한다. 조건 분기문은 구문의 덩어리를 어떤 조건이 만족하는 경우에만 실행하게 할 수 있다. 그렇지 않으면, 그 구문의 덩어리를 실행하지 않고 그 다음부터 실행한다. 비조건 분기문은 실행 순서를 프로그램의 다른 부분으로 옮기는 것이다. 여러 언어에서 제공하는 GOTO문, 서브프로그램, 프로시저, 호출문들이 비조건 분기문이다.

명령형 프로그래밍의 전형적인 예는 포트란과 알골이다. 파스칼, C, 에이다는 또 다른 예이다.


선언형 프로그래밍은 두 가지 구분되는 뜻이 있는데 두 가지 뜻 모두 통용되고 있다.

한 정의에 따르면, 프로그램이 어떤 방법으로 해야 하는지를 나타내기보다 무엇과 같은지를 설명하는 경우에 "선언형"이라고 한다. 예를 들어, 웹 페이지는 선언형인데 웹페이지는 제목, 글꼴, 본문, 그림과 같이 "무엇"이 나타나야하는지를 묘사하는 것이지 "어떤 방법으로" 컴퓨터 화면에 페이지를 나타내야 하는지를 묘사하는 것이 아니기 때문이다. 이것은 전통적인 포트란과 C, 자바와 같은 명령형 프로그래밍 언어와는 다른 접근방식인데, 명령형 프로그래밍 언어는 프로그래머가 실행될 알고리즘을 명시해주어야 하는 것이다. 간단히 말하여, 명령형 프로그램은 알고리즘을 명시하고 목표는 명시하지 않는데 반해 선언형 프로그램은 목표를 명시하고 알고리즘을 명시하지 않는 것이다.

또 다른 정의에 따르면, 프로그램이 함수형 프로그래밍 언어, 논리형 프로그래밍 언어, 혹은 제한형 프로그래밍 언어로 쓰여진 경우에 "선언형"이라고 한다. 여기서 "선언형 언어"라는 것은 명령형 언어와 대비되는 이런 프로그래밍 언어들을 통칭하는 것이다.

이 두가지 정의는 서로 겹치는 부분도 있다. 특히, 제한형 프로그래밍과 논리형 프로그래밍은 필요한 해의 특성을 설명하고(무엇) 그 해를 찾는데 사용하는 실제 알고리즘은 설명하지 않는다(어떤 방법). 그러나 대부분의 논리형과 제한형 언어들은 알고리즘을 설명할 수 있고, 상세한 부분을 구현할 수 있어서 첫 번째 정의를 따르는 엄밀한 의미의 선언형 프로그래밍 언어는 아니다.

마찬가지로, 명령형 프로그래밍 언어로 선언형으로 프로그램을 작성할 수도 있다. 라이브러리나 프레임워크 내부의 비선언형 부분을 캡슐화하여 이렇게 할 수 있다. 이런 형태의 예가 제이유닛 유닛 테스트 프레임워크에 반영되어 쓰이고 있는데, 이것은 정의만 되어 있으면 프레임워크로 등록하여 유닛을 테스트하는 것을 가능하게 한다.

선언형 프로그래밍은 특수 분야 언어(영어: Domain-specific language, DSL)의 형태로 자주 사용된다. 특수 분야 언어의 한 가지 결점은 튜링 완전성이 없다는 것이다. 그 말은 할 수 없는 일이 있다는 것이다. 스프레드시트에서는 전자메일을 보낼 수 없고 전자메일을 이용하여 은행 계좌를 계산할 수 없다는 것이다. 이러한 이유로 특수 분야 언어들은 때로 범용 언어에 내장된다. 이렇게 하면 프로그래머가 특수 분야 언어가 힘을 발휘하는 분야에서 이것을 이용할 수 있고, 특수 분야 언어로 하기 어렵거나 불가능한 문제는 범용 언어를 이용할 수 있다.

여기서 소개한 Validation Framework는 바로 이 선언형 프로그래밍 패러다임을 사용한것이다. 이 밖에도 잘 알려진 선언형 프로그래밍을 포함한 프레임워크 루비 온 레일와 jUnit등의 Unit 시리즈가 있다.



메시지 전달 프로그래밍과 명령행 프로그래밍

명령행 프로그래밍은 위에...

메시지 전달 프로그래밍은 주로 분산환경에서 독자적인 메모리 공간을 가지는 각각의 프로세스가 하나의 문제를 해결하기 위하여 프로세서들은 정보(메시지)들을 교환하는 방식을 말한다. 메시지 전달의 간단한 형태는 Point to Point 통신이다. 이는 한 프로세서가 다른 프로세서로 메시지를 보내는 것인데, 보내는 방식에 따라 동기와 비동기로 나눌 수 있다. 동기 방식은 메시지의 완료를 다른 프로세서에 보내주는 것이며, 비동기 방식은 단지 언제 메시지를 보냈는지만 알 수 있다. 

개념적으로 본다면 DB 프로세스는 프로그래밍 프로세스와 독립된 메모리 공간을 가지는 분산환경이며 프로그래밍 언어와 데이타베이스는 각각이 독립적인 서비스 이기 때문에 둘 중 어느 누구도 상대방에 의존하지 않는 중립적인 메시지를 교환하는게 맞다고 생각했기 때문에 이 블로그에서 소개하는 DB Framework는 이 메시지 전달 프로그래밍 패러다임을 사용한 것이다. 



절차적 프로그래밍과 함수형 프로그래밍

절차적 프로그래밍(procedural programming)은 절차지향 프로그래밍 혹은 절차지향적 프로그래밍이라고도 불리는 프로그래밍 패러다임의 일종으로서, 때때로 명령형 프로그래밍과 동의어로 쓰이기도 하지만, 프로시저 호출의 개념을 바탕으로 하고 있는 프로그래밍 패러다임을 의미하기도 한다. 프로시저는 루틴, 하위프로그램, 서브루틴, 메서드, 함수(수학적 함수와는 다르고 함수형 프로그래밍에 있는 함수와는 비슷한 의미이다.)라고도 하는데, 간단히 말하여 수행되어야 할 연속적인 계산 과정을 포함하고 있다. 프로그램의 아무 위치에서나 프로시저를 호출될 수 있는데, 다른 프로시저에서도 호출 가능하고 심지어는 자기 자신에서도 호출 가능하다.

절차적 프로그래밍 언어들은 절차적 프로그래밍 접근 방식을 따름으로써 프로그래머의 작업을 수월하게 한다. 알골과 같은 언어가 절차적 프로그래밍 언어의 표준적인 예이다. 그 밖에 포트란, PL/I, 모듈라-2, 에이다 등이 있다.

함수형 프로그래밍은 프로그래밍 패러다임의 하나로, 계산을 수학적 함수의 조합으로 생각하는 방식을 말한다. 이것은 일반적인 프로그래밍 언어에서 함수가 특정 동작을 수행하는 역할을 담당하는 것과는 반대되는 개념으로, 함수를 수행해도 함수 외부의 값이 변경될 수 없다.

알론조 처치가 1930년대에 개발한 람다 대수는 함수에 대한 이론적 기반을 세웠다. 이것은 프로그래밍 언어가 아니라 수학적 추상화였지만, 이것은 함수형 프로그래밍의 근간을 이루었다. 처음으로 만들어진 함수형 프로그래밍 언어는 IPL이었다. 존 매카티가 만든 리스프는 훨씬 향상된 함수형 프로그래밍 언어였고, 이것은 현대적 함수형 프로그래밍의 여러 특징을 가지고 있었다. 리스프를 발전시키고 간단하게 만든 언어로 스킴이라는 것도 나왔고1980년대에는 그동안의 함수형 프로그래밍에 대한 연구를 바탕으로 헤스켈이 만들어졌다.



 

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

중복을 볼 수 있는 눈  (0) 2009.03.13
Here is Dragon  (0) 2009.03.12
아키텍쳐 패턴 - Broker 패턴  (0) 2009.03.12
아키텍쳐 패턴 - Pipes and Filter 패턴  (0) 2009.03.11
아키텍쳐 패턴 - Layer 패턴  (0) 2009.03.11
Posted by bleujin



만약 현재 개발중인 환경이 독립적인 협력 컴포넌트들로 구성된 이질적인 분산시스템이라면 Broker 패턴을 생각해 봐야 한다.

분산은 아래와 같은 장점이 있다.
  - 경제성
  - 성능과 범위성
  - 고유 분산성
  - 신뢰성

그해 비해 단점은 중앙집중적 시스템이 가지고 있는 것과는 근본적으로 다른 소프트웨어가 필요하다는 것이다. 보통의 경우 분산 시스템은 느리고(비효율적) 예외의 종류가 비약적으로 증가하기 때문에 만들기가 훨씬 더 어렵다.

간단히 말하자면 브로커 패턴은 중개인을 통해 원격 컴포넌트의 위치 투명성을 제공하는 것이다. 컴포넌트는 위치 투명한 서비스 호출을 통해서 다른 컴포넌트가 제공하는 서비스에 억세스 할수있고 런타임에 컴포넌트의 추가나 제거가 가능해야 한다. 그리고 아키텍쳐는 특정 시스템에 국한된 세부사항(system-specific details)과 특정 구현에 국한된 세부사항(implementation-specific details)을 숨겨야 한다. 

브로커패턴의 예에는 CORBA(common object request broker architecture)와 DCOM(OLE 2.x)이 있는데 현재는 두 사양 모두 많이 사용되지는 않고 EJB2도 위 패턴을 도입했지만 느린 속도등의 이유로 역시 활성화 되지는 못하였다. 

개인적인 의견이지만 분산환경에서 위치투명성이란 코드적 관점이지 프로그래머적 관점은 아니라고 생각한다. 무슨 말이냐면 브로커 패턴을 이용한 분산 시스템에서 코드는 위치투명한 컴포넌트의 서비스 코드를 사용하지만 그걸 작성하는 사람은 이게 원격호출인지 아닌지를 알고 있어야 한다는 뜻이다. 그렇지 않으면 최소한 지금의 네트워크 환경에서는 Hello World 정도의 프로그램이 아닌 보통의 실제 어플리케이션의 복잡함을 감당할 실행속도가 나오기가 힘들다고 생각한다. (비록 coarse-grained 패턴을 쓴다고 해도 말이다. ) 이 블로그의 AL Framework는 이런 관점의 브로커 패턴을 사용한 프레임워크이다.


Broker 패턴은 분산 어플리케이션을 개발하면서 어쩔 수 없이 야기되는 복잡성을 줄인다. Broker 패턴을 사용할 경우 개발자는 분산 구조를 투명하게, 다시 말해서 개발을 위해 필요한 부분과 필요하지 않은 부분을 명확하게 구별해 파악함으로서 분산의 전체 구조를 바라볼 수 있다. 확장된 이 분산 어플리케이션은 이기종 머신에서 실행될 수 있고 각기 다른 프로그래밍 언어로 작성될 수 있다.



브로커 패턴은 아래와 같은 장단점이 있다.
장점
1. 위치 투명성이 제공된다.
2. 컴포넌트의 가변성과 확장성이 보장된다.
3. 플랫폼간의 이식 가능성이 제공된다.
4. 서로 다른 Broker 시스템들간의 상호 운용성이 지원된다.
5. 재사용성이 확보된다.

 

단점
1. 효율이 낮다.
2. 장애 허용성이 낮다.
3. 테스트와 디버깅이 한편으론 용이하고 다른 한편으론 복잡하다.

Broker 패턴은 거대 규모의 인프라 패러다임으로 단일 어플리케이션을 구축할 때에는 잘 사용되지 않으며 여러 어플리케이션 전체를 위한 플랫폼 역할을 한다.
 

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

Here is Dragon  (0) 2009.03.12
프로그래밍 패러다임  (0) 2009.03.12
아키텍쳐 패턴 - Pipes and Filter 패턴  (0) 2009.03.11
아키텍쳐 패턴 - Layer 패턴  (0) 2009.03.11
Class Design  (0) 2009.03.07
Posted by bleujin
Framework/스팸 필터링2009. 3. 12. 02:21

잘 알려져 있는 문제인
"n개의 도시가 있는데, 집에서 (1번 노드) 출발하여 모든 도시를 단 한번씩 거치며 집으로 돌아오는 최소 거리 경로를 찾아라." 길찾기 문제의 문제가 주어졌을 경우

아마도 깊이 우선이니, 너비우선이나 혹은 이기적 알고리즘(greedy algorithm) 같은 걸 떠올릴지 모른다. 알고리즘에 좀더 익숙하다면 분할 정복(devide and conquer), 동적 프로그래밍(dynamic programming), 한정 분기(branch and bound), 다익스트라 알고리즘 등을 사용할 수 도 있다. 이런 결정론 알고리즘은 정확한 해(전역 최적해)를 보장하지만 문제크기가 커지면 현실적인 시간 내에 풀이가 불가능 하거나 greedy algorithm처럼 지역 최적해로 수렴하게 된다.

개인적이지만 이 문제에 대해 가장 좋아하는 풀이방법은 메타 휴리스틱한 방법중 하나인 개미 군락 최적화(ant colony optimization)이다. 이 알고리즘은 자연에서 얻은 영감이 바탕이 된 개미의 군락생활에서 먹으를 집으로 이동하는 자업을 효율적으로 수행하기 위해
 - 이동 경로에 페로몬을 뿌림
 - 가한 페로몬의 경로를 선호
 - 시간에 따라 페로몬 증발 이라는 조건을 활용한다.

간단한 알고리즘을 구성하면
페로몬 값을 초기화 한다
repeat {
   해를 생성한다.  // 높은 페로몬 경로가 선택될 확률이 높다.
   페로몬을 갱신한다.
} until(멈춤 조건) ;
그동한 발생한 해중에 가장 좋은 것을 출력한다.

이 알고리즘을 좋아하는 이유는 첫번째는 처음에는 좋은 해를 찾지 못한다고 해도 시간이 지날수록 점점 더 좋은 해를 구할 가능성이 높고 두번째로 환경이 변해도(최적의 길이 변해도) 그에 대해 적응력을 발휘한다는 점이다. 여기서 프로그래밍 십계중 제 4계를 말할 수 있다. 만약 한번에 답을 찾는 것이 어려운 문제라면 연속성을 고려해라. 처음부터 옳은 답을 맞춰야 할 필요는 없다.

만약 소프트웨어가 단지 수학에 불과하다면 정확한 답과 순간적인- 측정 당시의 효율이 우선이 되겠지만 소프트웨어에서 일어나는 문제는 대부분 "잘~"에 관여한 문제이고 스팸판단도 이와 같은 문제라고 생각한다. 스팸판단에 있어 단어 사용등에 있어 수학모델을 사용하는 결정론적인 알고리즘보다 스팸일 가능성이 높은 순으로 정렬할 수 있고 변하는 환경에 대응할 수 있는 알고리즘 시스템이 나아보인다.


두번째로 생각할 것은 "여러개를 결합하면 가장 좋은 단일 알고리즘 보다 좋다"라는 Hybrid한 접근방식이다.

생체 인식에 있어 가장 많이 알려진 방법은 지문 인식이지만 역사가 오래되어서 정교한 만큼이나 그만큼 해킹 기술도 발달하였다. 지문인식이 대단한 정밀 기술로 보일지도 모르지만 아파트 현관문에 사용되는 정도의 지문 기술은 영화에 나오는 최신 기술따위를 사용하지 않아도 지문을 얻을 수 있다면 열쇠보다도 더 쉽게 뚫을 수 있다.(비록 열쇠를 가지고 다닐 필요가 없어서 편리는 하겠지만) 최근에는 지문인식은 -종이나 젤라틴 등에 - 복제된 지문데이타를 방지하기 위해 땀샘 인식을 하는 등의 좀더 복잡해졌지만 복잡해진 만큼이나 비싸지며 동일인임에도 불구하고 거부할 가능성도 높아진다.

생체인식의 분야에는 사실 영화등에서 소개된것만 해도 홍채, 열적외선, 목소리 등이 있고 발걸음이나 손등의 정맥등을 이용한 생체인식 방법도 사용되고 있다. 먼 미래에 언젠가 기술이 충분하게 성숙이 되면 가격도 싸고 인식의 오류 가능성도 극히 낮은 어떤 방법이 나올지도 모르겠지만 현재로서 프로그래밍 관점에서 생각해보면 가장 합리적인 방법은 이 여러가지 방법을 모두 적용한 하이브리드 모델이 나아보인다. 간단한 지문인식 시스템 정도는 뚫을 수 있겠지만 목소리도 비슷하고 홍채도 비슷하고 열적외선의 분포도 비슷하고 손등의 정맥위치도 비슷하고 발걸음도 비슷하게 할 수 있는 확률은 어느 한 분야의 최고의 기슬을 사용한 것보다 낮을 것이다.

단순히 계산을 빨리 해야 하는등의 문제가 아니라 좀더 "잘" 판단해야 하는 문제에 있어서는 베이지언 필터링 같은 하나의 알고리즘에 의존하기 보다는 이처럼 하이브리드 알고리즘이 더 많은 장점을 가질 수 있다.


'Framework > 스팸 필터링' 카테고리의 다른 글

판단에 있어서..  (0) 2009.03.16
구현  (0) 2009.01.12
판단의 기본 아이디어  (0) 2009.01.12
개요  (0) 2009.01.09
Posted by bleujin
IT 이야기2009. 3. 11. 06:42



(Dogbert의 대통령 선거 연설)
저에게 투표하지 않으면 테러리스트가 여러분의 해골을 샐러드 접시로 사용할겁니다.

제가 당선되면 저에게 투표하지 않은 사람들의 돈을 뺐어서 저를 지지한 사람들에게 나누어 주겠습니다.

공해는 비타민을 함유하고 있습니다. 
 - 감성적인것이 좋군요..



시니컬한 딜버트로 유명한 스콧 아담스의 만화중 하나인데 그 뛰어난 통찰력이 놀랍다.

연설의 기본적인 3가지 범주인데
첫번째는 일단 자신이 아니면 안된다는 공포심을 조장해야 하고
두번째는 편을 갈라서 자기편을 들면 이만큼의 돈을 얻을 것이다라고 말하고(그걸 꼭 지켜야 할 필요는 없다)
세번째로 근거없는 황당한 소리를 지지해줄 몇명의 박수부대를 숨겨놓는 것이다. (이 의미가 맞는지는 불확실)


재미있어서 퍼오긴 했지만 IT 하고 별로 상관은 없어서 다른 유머를 추가한다.


한 여자가, 기구로 비행을 하던 도중 바람에 지도를 날려버렸다. 아무래도 어떤 방향으로 가야할지 몰랐기에
그녀는 어쩔 수 없이 저 아래를 걷고있던 남자에게 소리를 질렀다.

「저, 실례합니다. 여기가 어디인지 가르쳐 주실 수 없습니까? 원래는 1시간 안에 돌아갈 계획이었지만 여전히
   이렇게 하늘만 맴도는 중입니다···」

남자는 이렇게 대답했다.

「당신이 있는 장소는, 대략 지상 30미터 정도의 상공입니다. 위치는 북위 36도 30분과 35분의 사이, 동경
   39도 45부와 50분 사이지요」

그 말을 듣고 여자가 물었다.

「실례지만, 직업이 엔지니어 아니신가요?」
「그렇습니다. 어떻게 아셨죠?」
「지금 받은 정보는 분명 이론적으로는 틀림없을 것입니다. 그러나 숫자는 해석 방법을 모르면 전혀 도움이
   되지않습니다. 실제로 저는 변함없이 계속 헤매고 있을 뿐이고 문제는 아무 것도 해결되지 않았습니다」

그러자 남자는 이렇게 말했다.

「당신은 프로젝트 매니저 아닙니까?」
「네, 그렇습니다만, 어떻게 아셨죠?」
「우선 당신은 자신이 지금 있는 위치나 자신이 향해야 할 목표조차 모릅니다. 게다가 지킬 수 있을지 어떨지
   모를 약속을 혼자 스스로 한 주제에 저에게 문제 해결을 요구하고 있기 때문입니다. 쉽게말해, 상황 자체는
   아무런 변화가 없는데도 당신은 자연스럽게 전부 제 탓이라고 말하고 있기 때문입니다」

'IT 이야기' 카테고리의 다른 글

How vs Who  (0) 2009.03.16
프로젝트의 예측  (0) 2009.03.12
사람이 읽는 코드  (0) 2009.03.11
멘탈 ?  (0) 2009.03.01
소프트웨어의 변증법  (0) 2009.02.23
Posted by bleujin


앞글의 예외처리의 예외에서 소개한 코드는 아키텍쳐 레벨에서 보면 Pipes and Filter 패턴의 한 예이며 가장 많이 알려진 PAF 패턴의 예는 유닉스의 명령어이다. 유닉스의 모든 명령어는 char*가 입출력의 표준 메시지 포맷이기 때문에 ls -al | grep 'abc' | vi 식의 사용이 가능하다. 그리고 티스토리의 플러그인 방식도 PAF의 일례라고 볼 수 있다. 앞의 두 예 모두 순서에 상관없지만 PAF가 항상 순서에 관계없이 동작하는 것은 아니다.


PAF 패턴은 아래와 같은 특징이 있다.

1. 미래에 시스템의 기능을 향상시키기 위해서는 (심지어 사용자도) 프로세싱 단계를 변경하거나 혹은 재조합할 수 있어야 한다.
2. 작은 프로세싱 단계들은 서로 다른 상황에서 거대한 컴포넌트보다 쉽게 재사용된다.
3. 인접하지 않은 프로세싱 단계들 간에는 정보를 공유하지 않는다.
4. 입력데이타는 각기 다른 소스에서 얻는다.
5. 여러가지 방법으로 결과를 표시하거나 저장 할수 있어야 한다.
6. 단계를 멀티프로세싱 방식으로 처리하는 것도 수용할 수 있다. 예를 들어 병렬 혹은 준 병렬 방식을 체택할 수 있다.


절차는 아래의 단계를 밟는다.
1. 시스템의 테스크를 일련의 프로세싱 단계에 따라 구분한다.
2. 각 파이프 사이로 전달할 수 있도록 데이터 포맷을 정의한다.
3. 각 파이프를 어떻게 연결할지 결정한다. (Push? Pull?)
4. 필터를 설계하고 구현한다.
5. 오류 핸들링을 설계한다.
필요할 경우 단계의 중간에 파일에 추가적인 정보를 남기는 것도 고려할 수 있다.


PAF는 아래와 같은 장단점이 있다.

장점

1. 필터 교환에 유연하다.
2. 재조합에 유연하다.
3. 필터 컴포넌트를 재 사용할 수 있다.
4. 파이프라인의 프로토 타입을 빠르게 만들 수 있다.
5. 병렬 프로세싱의 효율성

단점

1. 상태정보를 공유하게 되면 비용이 많이 들며 유연성을 저하시키는 요인이 된다.
2. 병렬 프로세싱을 사용해 효율성을 얻을 것이라는 기대는 그저 망상일 뿐인 경우가 종종있다.
3. 데이타 변환에 과부하가 발생한다.
4. 오류 핸들링을 구현하기 힘들다.


 

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

프로그래밍 패러다임  (0) 2009.03.12
아키텍쳐 패턴 - Broker 패턴  (0) 2009.03.12
아키텍쳐 패턴 - Layer 패턴  (0) 2009.03.11
Class Design  (0) 2009.03.07
나쁜 디자인의 징후  (0) 2009.02.22
Posted by bleujin


GOF의 객체 패턴을 확대하면 아키텍쳐도 몇가지의 패턴을 가진다는 걸 알수 있다.

가장 대표적으로 Layer 패턴을 들 수 있는데 시스템의 규모가 커서 분해(decomposition) 할 필요가 있고 하위 레벨과 상위 레벨의 이슈가 서로 혼재해 있을때 나온다. 앞글의 실험실과 비실험실은 결국 레이어 패턴과 비슷하지만 굳이 실험실과 비실험실이라고 부른 것은 기계적인 효율성이 필요한 곳과 아닌곳을 구분하려고 실험실이라는 용어를 사용하였다. 레이어 패턴의 대표적인 예는 많이 알려진 OSI 7 Layer가 있고 Java의 VM자체가 하나의 Layer이다.

레이어 패턴은 아래와 같은 특징이 있다.

1. 나중에 소스 코드가 변경하다고 하더라도 그것이 시스템 전체에 파문을 일으켜서는 안된다. 그런 변경이 있더라도 오직 하나의 컴포넌트 내에만 국한되어야 하며 다른 컴포넌트에는 영향을 미쳐선 안된다.

2. 인터페이스는 안정성(stability)을 갖추어야 하며, 표준 구현부에 미리 지정되어 있는 것이 좋다.

3. 시스템의 각 부분들은 교환가능해야 한다. 일반적으로 변경에 대비해 설계하면, 시스템을 발전시키도록 촉진할 수 있다.

4. 현재 설계하고 있는 시스템과 동일한 하위 레별의 이슈 때문에 추후에 다른 시스템을 구축해야 할 필요가 있을지 모른다.

5. 각 컴포넌트는 이슈 하나만을 담당하도록 구현되어야 한다. 만약 한 컴포넌트가 다른 이슈를 구현하면 일관성을 잃게 된다.

6. 컴포넌트 경계를 넘나드는 처리가 발생할수록 성능을 하락시킬 가능성이 커진다.


"전산학의 모든 문제는 또 다른 수준의 간접층으로 해결할 수 있다."
라는 유명한 말을 통해 레이어의 개념을 알린 이는 램슨이라고 알려져 있는데 램슨은 사실 서브루틴의 개념을 만든 휠러에게서 따왔다고 밝힌바 있다. 휠러는 그 말에 이어서
 "그러나 그러면 또 다른 문제가 생기는 것이 일반적이다."
라며 간접과 계층화는 공간과 시간의 부담을 추가하고 코드의 가독성을 해칠수 있는 문제점도 같이 언급하였다.

보통의 경우에는 공간과 공간상의 추가 부담은 그리 크지 않기 때문에 일반적으로 큰 관심사가 되지 못한다. 대부분의 경우 추가적인 포인터 참조나 서브루틴 호출에 의한 시간 지연은 전반적인 구조 개선에 비할때 사소한 수준에 그친다. 사실 요즘의 현대적인 프로그래밍 언어들은 추가적인 유연성을 얻기 위한 목적으로 일부 연산들의 경우 항상 하나의 간접층을 거치도록 하는 경향을 보이고 있다. 예를 들어 java나 C#의 경우 객체에 대한 모든 접근이 하나의 포인터 간접을 거치게 하는데, 이는 쓰레기 수거를 위한 것이다. 또한 Java에서는 인스턴스 메서드에 대한 거의 모든 호출이 하나의 조회 테이블을 통해서 분배되는데, 이는 다른 클래스를 상속하는 클래스들이 실행시점에서 메서드를 재 정의 할 수 있도록 하기 위한 것이다.

모든 객체 접근과 메서드 호출에 부가되는 이러한 추가부담에도 불구하고 두 플랫폼은 시장에 선전을 펼치고 있다. 어떤 경우에서는 개발자가 코드에 집어넣은 간접을 컴파일러가 최적화를 통해서 제거하기도 한다. 대부분의 컴파일러들은 함수를 호출하는 것이 함수의 코드를 호출지점에 직접 삽입하는 것(소위 인라인 함수_보다 비싼 경우 자동적으로 그러한 인라인 처리를 수행한다.

반면 코드의 가독성에 대한 간접의 영향은 아주 중요한 문제이다. 지난 50년간 CPU의 속도는 엄청나게 빨라진 반면 코드를 이해하는 사람의 능력은 별로 발전하지 않았다는 점을 감안한다면 충분히 이해할 수 잇을 것이다. 그래서 애자일 프로세스 옹호자들은 오늘이 구체적인 요구가 아니라 미래에 생길 수도 있는 애매하고 명시되지 않는 요구사항들을 처리하기 위해 계층들을 도입할 때에는 아주 신중해야 한다고 조언한다. 스몰더스는 이에 대해 성능 안티패턴을 논의하면서 계층은 케이크를 위한 것이지 소프트웨어를 위한것이 아니다 라고 비꼰 바 있다.


레이어 패턴은 다은과 같은 단계를 거친다.

# 테스크를 레이러로 묶는 추상기준을 정의해야 한다. 이 추상기준을 플랫폼으로부터 개념적 거리(conceptual distance)로 삼는 경우가 많은데 실제 소프트웨어 개발에서는 하드웨어로부터의 거리와 개념적 복잡성의 추상적 기준을 혼합하여 사용한다.

1. 사용자를 위한 시각적 요소
2. 특수하게 지정된 애플리케이션 모듈
3. 공통 서비스 레벨
4. 운영체제 인터페이스 레별
5. 운영체제
6. 하드웨어

# 추상 기준에 따라 얼마나 많은 추상 레별로 나눌지를 결정하고 레이어마다 역할을 부여하고 테스크를 할당한다. ( 상당한 경험과 통찰력이 필요하다.)

# 서비스를 상세히 정의한다.
  레이어 패턴을 구현할때 가장 중요한 원칙은 레이어들을 서로 엄밀히 구분해야 한다는 점이다.  기반레이어는 가볍게 유지하는 대신 상위 레이어는 광범위한 적용석을 확보하도록 확장하는 것이 좋다. 이런 현상을 재사용의 역 피라미드 구조라고 한다.

# 레이어에 대한 정의를 개선해 간다.

# 각 레이어마다 인터페이스 하나씩을 정의한다.

# 개별 레이어를 구조화한다.

# 인접한 레이어들 간의 통신 방식을 정의한다.

# 인접한 레이어들을 서로 분리시키고 오류 핸들링 전략을 설계한다.

레이어 패턴을 사용하지만 부분적으로 터널을 사용하는 경우가 있는 일종의 변형코드를 사용하는 완화된 레이어 시스템을 사용하기도 하는데 유지보수성을 포기하는 대가로 유연성과 성능을 확보하게 된다. 어플리케이션보다 인프라 시스템에서 종종 이런 편법을 찾아볼수 있다. 이러한 편법이 종종 허용되는 이유는 인프라 시스템이 어플리케이션 시스템보다 변경이 빈번하지 않으며 대체로 성능을 유지보수성보다 중요하게 여기기 때문이다. 그리고 앞서 소개한 DB Framework에도 일종의 터널링이 존재하지만 일종의 예외이기 때문에 주의해서 사용해야 한다.


레이어 패턴은 아래와 같은 장단점이 있다.

장점
1. 레이어를 재사용할 수 있다.
2. 표준을 지원한다.
3. 종속성을 국지적으로 최소화한다.
4. 교환가능성이 확보된다.


단점
1. 동작이 변경될 경우 단계별로 재작업이 필요하다.
2. 효율이 낮다.
3. 불필요한 작업이 수행될 수 있다.
4. 레이어의 적절한 개수나 규모를 결정하는 것이 어렵다.



 

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

아키텍쳐 패턴 - Broker 패턴  (0) 2009.03.12
아키텍쳐 패턴 - Pipes and Filter 패턴  (0) 2009.03.11
Class Design  (0) 2009.03.07
나쁜 디자인의 징후  (0) 2009.02.22
Design Principle - SRP  (0) 2009.02.22
Posted by bleujin
IT 이야기2009. 3. 11. 02:35


대부분의 프로그램은 한번에 작성되지 않는다. 프로그램들은 그 수명주기 동안 계속해서 개정되고 재 작성된다. 요구사항의 변화와 기능성의 개선 필요성 때문에 프로그램은 계속해서 수정되어야 한다. 이러한 과정에서 반드시 인간이 원래의 코드를 읽고 이해할 수 있어야 한다. 따라서 컴퓨터가 프로그램을 이해할 수 잇는가 보다 인간이 프로그램을 이해 할수 있는가가 훨씬 중요하다.

물론 컴퓨터는 복잡성을 불평없이 다룰 수 있다. 그러나 사람은 그렇지 못하다. 무어의 법칙에 따르면 컴퓨터 칩의 집적도는 18개월마다 2배씩 향상되어 2배의 복잡함을 처리할 수 있지만(보통 알려져 있는 것과 달리 무어는 클럭 스피드가 아니라 칩의 집적도와 복잡함에 대해 말한 것이다. ) 인간은 18년이 지나도 2배의 복잡도를 가진 문제에 적응할 수 없다. 그리고 이러한 읽기 어려운 코드는 사람의 생산성을 크게 훼손시키기 마련이다. 반면 쉽게 이해할 수 있는 코드는 생산성을 높여준다. 그리고 우리는 그러한 코드에서 미를 발견한다.

컴퓨터 프로그램을 읽기 쉽게 만드는 요인은 무엇인가? 다시 말해서, 무엇이 아름다운 코드인가? 아름다운 프로그램에 대한 기준은 사람마다 다르겠지만, 컴퓨터 코드의 특성을 판정한다는 것이 단순히 미학의 문제는 아니다. 컴퓨터 프로그램은 그것이 주어진 과제를 얼마나 잘 수행하는지에 따라 판정된다. 다른 말로 하면 아름다운 코드는 프로그래머의 노력과는 무관하게 존재하는 추상적인 미덕이 아니다. 아름다운 코드라면 프로그래머를 행복하게, 그리고 생산성을 높이는데 도움을 줄 수 있어야 한다.

아름다운 코드를 논할 때 다소 논쟁의 여지를 제공하는 측면으로는 익숙함이 있다. 사람들은 자신이 생각하는 것보다 보수적이다. 대부분의 사람들은 새로운 개념을 받아들이거나 사고방식을 바꾸기를 꺼려하며, 변화보다는 참고 견디는 것을 더 좋아하는 듯 하다. 익숙한 도구를 다른것으로 교체하거나 새로운 언어를 배우는 일을 특별한 이유없이 꺼리는 사람들도 많다. 사람들은 가능하면 자신이 항상 상식으로 가눚하던 것을 잣대로 해서 새로운 방법을 평가하는 경향이 있다. 그러다보니 새로운 방법을 부당하게 저평가 해버린다.

사고방식을 바꾸는 데 드는 비용은 흔히 생각하는 것보다 훨씬 높다. 완전히 다른 개념으로 전환(이를테면 절차적 프로그래밍에서 논리적 혹은 함수적 프로그래밍으로의 전환)을 용이하게 하려면 다양한 개념들에 정통할 필요가 있다. 가파른 학습곡선은 인간의 두뇌를 고통스럽게 만들고 결국 프로그래머의 생산성을 낮춘다.

아름다운 코드란 결국 간결하며 유연함과 효율성을 가짐과 동시에 보수성을 가진다. 그런데 그것들 자체로 아름다운 코드가 보장되지는 않는다. 그런 요소들 모두의 균형을 처음부터 염두에 두어야 각 요소가 다른 요소와 조화를 이루어서 아름다운 코드가 만들어 지는 것이다. 


컴퓨터가 아니라 사람에게 쉬운 코드를 작성해야 하는 걸 알았다면 이제 사람 누구에게 쉽다는 건지를 파악해보자? 쉽다라는 말 자체가 상대적인 판단을 의미하고 판단을 내리는 주체는 코드를 작성한 사람이 아니라 코드를 읽는 사람이다. (얼마의 시간이 지난후의 작성자도 한명의 독자가 될 수 있다. )

좀더 얘기를 하기전에 이쯤에서 잠깐 퀴즈를 하나 내보자 "어떤일을 하는데 있어서 가장 빠른 방법은 무엇일까?" 정답은 어떤 일을 하지 않는 것이다. 소프트웨어에서 가장 아름다운 코드, 가장 아름다운 함수, 그리고 가장 아름다운 프로그램은 바로 전혀 존재하지 않는 코드(또는 함수, 프로그램)라는 말이 있는 것처럼 가장 쉬운 코드는 존재하지 않는 코드이고 두번째로 쉬운 코드는 읽을 필요가 없는 코드이다.

단순히 스터디를 위해서 참조하는 경우를 제외하고 프로그래머가 다른 사람의 소스를 보는 이유는 
 - 사용방법을 몰라서
 - 해봤는데 기대하단 결과가 나오지 않아서(잘못 사용했거나 버그)
 - 인수 인계 등으로 프로그램을 이해하기 위해서
이다. 그렇다는 얘기는 프로그램이 잘 설계되어 있어서 사용방법이 쉽고 예측대로 작동된다면 굳이 함수나 클래스 내용을 뒤져볼 필요가 없다는 것을 뜻한다. 그래서 사실 읽기 쉬운 코드란 코드 그 자체의 대한 이야기라기 보단 좋은 설계에 대한 이야기이다.


"어려운 코드보다 단순한 코드가 더 좋다" 라는 말을 한번쯤 들어보았을 것이다. 번역되는 책마다 용어 선택의 차이 일수도 있고 아니면 우리 언어가 가지는 추상성의 문제일 수도 있지만 이 글에서는 쉽다와 간결하다를 구분하기로 하자. 쉽다는 어렵다(difficult)의 반대 용어로 사용하고 간결하다는 복잡하다(complicate)의 반대 용어이다. 그래서 쉬우면서 복잡하거나 쉬우면서 간결한 코드가 있을 수 있다.

물론 베스트는 쉬우면서 간결한 코드이고 최악은 어려우면서 복잡한 코드이다. 문제가 되는건 쉽지만 복잡한 코드일때와 어렵지만 간결한 코드일때 어느게 더 나을까 하는 점이다.

예컨데 프로그래밍은 상상이다등의 책에서 자주 언급되는

Case1 ) if else를 사용
if (conditon)
   one line code
else
   one line code

Case 2) 삼항연산자 사용
condition ? one line code : one line code

Case1은 쉽지만 복잡한 코드이고 Case2는 상대적으로 어렵지만 간결한 코드이다. Case 1의 지지자들은 삼항연산자가 잘 쓰이지 않는 연산자이기 때문에 앞에서 말한대로 코드를 읽는 사람을 배려해야 한다고 주장한다. 이 블로그 자체가 원칙을 논하는 블로그는 아니므로 옳다라고 하는 건 아니지만 개인적으로는 Case 2) 상대적으로 어렵지만 간결한 코드를 선호한다.

물론 코드는 읽는 사람을 배려해야 하는 사실을 인정하지 못하는 것은 아니다. 이유는 두가지가 있다.

첫째 어려운 것은 시간이 감에 따라 나아지지만 복잡한것은 그리 나아지지 않기 때문이다. 인간의 지적인 순수 능력은 수십년 전에 비하면 훨신 향상되었지만 여전히 7자리 넘는 숫자를 외우는 데에는 버거워 한다. 설사 읽는 사람이 삼항연산자를 몰랐더라도 아마도 시간이 지남에 따라 곧 적응이 될 테지만 읽는 사람이 몇년차 프로그래머인지에 상관없이 복잡한 프로그램은 여전히 복잡한 프로그램이다.


두번째 이유를 설명하기 전에 위 케이스는 익숙하긴 하지만 난이도 자체가 너무 쉬위므로 아래의 소스를 보자

Case A)
(function(){
  var alertFun = window.alert, c=0;
  window.alert=function(q){
   
    if (++c%4==0) { 
      if (!confirm(q+'\nThere have been '+c+' alert boxes, continue displaying them?')) window.alert=function(){};
    } else alertFun (q);
  }
})();

위 코드를 보고 한눈에 이해했다면 자바스크립트에 비교적 능숙한 프로그래머 일것이다. 위 코드는 웹 페이지의 무한 alert 핵을 막기위해 연속으로 alert 메소드가 호출될 경우 4번마다 한번씩 확인창을 띄어서 원하지 않을 경우 중단시킬 수 있도록 window.alert을 재정의한 코드이다. 자바스크립트의 함수형 언어의 특징과 클로저가 사용되서 어려워진 위의 코드를 쉽게 사용한다면

Case B)
var alertFun = window.alert,c = 0 ;
window.alert = function() {
    if (++c%4==0) { 
      if (!confirm(q+'\nThere have been '+c+' alert boxes, continue displaying them?')) window.alert=function(){};
    } else alertFun (q);

와 같이 바꿀수도 있다. 자바스크립트를 거의 모른다 하더래도 어쨌거나 첫번째 코드에 비해 훨씬 더 이해하기 쉽다. 

이러한 선택이 주어진다면 나는 고민없이 CaseA를 택한다. 단순히 코드 자체만을 본다면 Case A의 코드가 더 어렵다. 하지만 나중에 문제가 생길확률은 B가 월등이 높다. Case B의 코드는 쉽기는 하지만 전역변수인 alertFun의 존재를 기억하고 있어야 하고 혹시 그것을 잊어버려서 다른 곳에서 동일 이름의 변수를 사용한다면 버그의 이유를 찾는데 더 오랜 시간이 걸리기 때문이다. CaseA의 코드는 어렵기는 하지만 다른 코드에 의존하는 것이 거의 없고 매우 간결하고 독립적이기 때문에 문제를 일으킬 소지가 낮아서 애초에 Case A의 소스를 봐야 할 확률이 낮다. (가장 좋은 코드는 존재하지 않는 코드라는 것을 기억하자.)

사실 코드를 읽는 사람의 수준을 고려해 쉽지만 복잡한 코드를 작성하는 것보다 - 쉬운 코드라는 것은 작성자의 입장에서는 아무런 상관이 없는 말이고 - 쉬운 코드를 버리는 대신 복잡한걸 간결하게만 만들 수만 있다면  어렵더라도 애초에 코드를 읽을 필요가 없게 작성해주는 것이 더 좋다. 


정말 아이슈타인이 만들었는지는 모르지만

1. 다섯채의 각각 색깔이 다른 집이 있다.
2. 각 집에는 각각 다른 국적의 사람이 산다.
3. 주인들은 각각 다른 음료수를 마신다.
4. 다른 종류의 담배를 피고, 다른 종류의 동물을 기른다.
5. 영국인은 빨강색 집에 산다.
6. 스웨덴인은 개를 기른다.
7. 덴마크인은 홍차를 마신다.
8. 녹색 집은 흰색 집 왼쪽에 위치해 있다.
9. 녹색 집 사람은 커피를 마신다.
10. 풀몰 담배를 피우는 사람은 새를 기른다.
11. 노란색 집에 사는 사람은 던힐 담배를 피운다.
12. 한가운데 사는 사람은 우유를 마신다.
13. 노르웨이인은 첫번째 집에 산다.
14. 블랜드 담배를 피우는 사람은 고양이를 기르는 사람 옆집에 산다.
15. 말을 기르는 사람은 던힐 담배를 피우는 사람 옆집에 산다.
16. 블루매스터 담배를 피우는 사람은 맥주를 마신다.
17. 독일인은 프린스 담배를 피운다.
18. 노르웨이인은 파란색 집 옆집에 산다.
19. 블랜드 담배를 피우는 사람은 물을 마시는 사람 옆집에 산다.

그럼 여기서 금붕어를키우는사람은 누구일까요~
.. 라는 널리 알려진 퀴즈가 있다.

보통의 프로그래머라면 3-5분 이내에 풀수 있지만 중요한건 결코 프로그램을 이딴식으로 만들어서는 안된다는 것이다.

위의 명제들은 모두 단순하지만 서로 얽혀서 이를 이해하는데 종이와 연필이 필요하다.(만약 위 문제를 머리속으로만 풀었다면 멋지다.) 위 문제는 쉽지만 복잡하다. 만약 위의 각 명제가 하나의 함수라고 가정한다면 복잡한 구조때문에 함수 하나하나는 쉽지만 코드는 무척 이해하기 어려운 구조가 된다. 달리 말해서 코드나 알고리즘의 쉽다 어렵다 보다는 구조에 있어서의 간결한 구조가 훨씬 더 중요하고 만약 구조가 간결하다면 애초에 코드 레벨의 소스를 살펴봐야할 필요는 없을 것이다.





보통의 책에서 쉽다(가독성이 좋다)는 말에는 쉽다는 말과 간결하다는 중의적 의미를 내포하고 있고 단순히 쉽다=좋다는 것이 아니므로 좀더 구분해서 이해해야 한다. 





간결함에 대해 선배들은 다음과 같이 충고하였다. 펄리스의 말은 특히 훌룡하다.  



코드를 삭제함으로써 기능을 추가하도록 노력하라

설계자는 더 추가할 것이 없을때가 아니라 더 뺄것이 없을때 완벽함을 이루었음을 알게 된다. (생떽쥐페리)


강건한 문장은 간결하다, 필요없는 단어는 생략하라(스트렁크와 화이트)

가장 싸고 빠르고 신뢰할 수 있는 요소는 바로 존재하지 않는 요소이다. (벨)

더 적은 것으로 더 많은 것을 얻도록 노력하라

만일 시간이 더 주어졌다면 편지를 더 짧게 썼을 것입니다. (파스칼)

간결함은 복잡함보다 앞서지 않는다. 그 뒤에 오는 것이다(펄리스)

적을수록 많다(브라우닝)

모든 것을 최대한 간결하게, 그러나 너무 단순하지는 않게 만들라(아이슈타인)

종종 소프트웨어는 하나의 비누 거품으로 보아야 한다(펄리스)

간결함을 통해서 아름다움을 추구해라





# 무어의 법칙
Moore의 법칙은 잘못 인용되고 있는 것들 중 하나이다. 대부분의 전문가들은 컴퓨터는 18 개월마다 속도가 두 배로 증가한다고 주장하고 싶어한다. 사실 인텔 창립자 Gordon Moore는 1965년 컴포넌트 당 가장 싼 칩의 복잡함은 매년 두배로 증가했다는 것을 관찰했고, 이러한 트랜드는 약 10년간 지속될 것이라고 예견했다. 그런데 이것은 퍼포먼스가 두 배로 증가한다는 것을 의미하는 것은 아니다. 컴포넌트의 수가 그렇다는 것을 의미한다. Moore의 예견은 데이터에 의해 합리적으로 입증되었다. 1975년 그는 18 개월로 조정했으며 이것이 바로 대부분의 사람들이 요즘 인용하는 것이다. 재미있는 것은 Moore의 법칙은 시스템 디자인의 복잡함에 대해 언급하는 것 과는 달리 클럭(clock) 스피드에 대해서 많이 언급하지 않는다.

무어의 정확한 말을 보려면 http://ko.wikipedia.org/wiki/%EB%AC%B4%EC%96%B4%EC%9D%98_%EB%B2%95%EC%B9%99로 가면된다.

소크라테스는 ‘악법도 법이다’라는 소리를 지껄인 적이 없다…는 주장이 점점 힘을 받고 있는데, ‘아 다르고 어 다르다’ 소크라테스도 공자도 자기가 하고 싶은 바를 후세에 그대로 전달하지는 못했나보다. 무엇이든 자기가 바라는대로 맞춰서 전파하려던 선글래스 독재자까지는 아니더라도 이 시대를 살아가는 사람들은 옛 사람이 남긴 말을 그대로 전달하려고 하지를 않는다.

'IT 이야기' 카테고리의 다른 글

프로젝트의 예측  (0) 2009.03.12
Scott Adams  (0) 2009.03.11
멘탈 ?  (0) 2009.03.01
소프트웨어의 변증법  (0) 2009.02.23
추상화  (0) 2009.02.22
Posted by bleujin