개발/DB_MYSQL | Posted by 은우 아빠 2009. 2. 25. 09:41

MySQL - Explain 정보보는법


원본 : http://database.sarang.net/?inc=read&aid=24199&criteria=mysql&subcrit=&id=&limit=20&keyword=explain&page=1


# 이글은 mysql document 의 7.2.1 Explain Syntax 를 대~충 번역한 것입니다.

# 틈틈이 번역하고 있으나 언제 완료될지 모릅니다..


EXPLAIN 을 사용함으로써 인덱스가 적절히 사용되고 있는지 검토할 수 있다. 인덱스가 잘못 사용되고 있다면 ANALYZE TABLE 을 사용하여 테이블을 점검하라.
이것은 테이블의 상태를 갱신하며 옵티마이저의 동작에 영향을 준다.


옵티마이저가 SELECT 에 기록된 순서대로 조인을 행하게 강제하려면 SELECT 대신에 SELECT STRAIGHT_JOIN 을 사용하라.


EXPLAIN 은 SELECT 문에 사용된 각 테이블당 하나의 행을 리턴한다. 나열된 순서는 MYSQL 이 쿼리처리에 사용하는 순서대로 출력된다.


MYSQL 은 모든 조인을 single-sweep multi-join 방식을 사용하여 해결한다. 이것은 MYSQL 이 첫번째 테이블에서 한행을 읽고, 두번째 테이블에서 매치되는 행을 찾고, 세번째 테이블에서 매치되는 행을 찾고.. 그러한 방식이다. 모든 테이블들이 처리된 후 추출된 컬럼을 출력하고 다시 처음 테이블로 돌아가서 조인을 계속한다. 이런식으로 첫번째 테이블에 더이상 남는행이 없을때까지 실행한다.

(어느것이 첫번째 테이블이 될지는 mysql 옵티마이저가 결정할 문제이다. STRAIGHT_JOIN 을 명시하지 않았다면 유저가 입력한 순서와는 관련이 없다.)


MYSQL 4.1 버전에서 EXPLAIN 의 출력포멧이 UNION 과 subquery, derived table 을 다루기에 더 효과적으로 변경되었다. 무엇보다 중요한 것은 id , select_type 의 두 컬럼이 추가된 것이다.


EXPLAIN 의 각 행은 하나의 테이블에 대한 정보를 보여주며 다음과 같은 컬럼들로 구성된다.

  • id

    SELECT 번호, 쿼리내의 SELECT 의 구분번호이다.

  • select_type

    SELECT 의 타입, 다음과 같다.

    • SIMPLE

      단순 SELECT (UNION 이나 서브쿼리를 사용하지 않음)

    • PRIMARY

      가장 외곽의 SELECT

    • UNION

      UNION 에서의 두번째 혹은 나중에 따라오는 SELECT

    • DEPENDENT UNION

      UNION 에서의 두번째 혹은 나중에 따라오는 SELECT, 외곽쿼리에 의존적이다.

    • UNION RESULT

      UNION 의 결과물.

    • SUBQUERY

      서브쿼리의 첫번째 SELECT

    • DEPENDENT SUBQUERY

      서브쿼리의 첫번째 SELECT, 외곽쿼리에 의존적이다.

    • DERIVED

      SELECT 로 추출된 테이블 (FROM 절 내부의 서브쿼리)

  • table

    나타난 결과가 참조하는 테이블명.

  • type

    조인타입, 아래와 같다. 우수한 순서대로 뒤로갈수록 나쁜 조인형태이다.

    • system

      테이블에 단 하나의 행만 존재(시스템 테이블). const join 의 특수한 경우이다.

    • const

      많아야 하나의 매치되는 행만 존재하는 경우. 하나의 행이기 때문에 각 컬럼값은 나머지 연산에서 상수로 간주되며, 처음 한번만 읽어들이면 되기 때문에 무지 빠르다.
      PRIMARY KEY 나 UNIQUE index 를 상수와 비교하는 경우.
      아래의 경우에서 tbl_name 은 const table 로 조인된다.

      SELECT * FROM tbl_name WHERE primary_key=1;
      SELECT * FROM tbl_name
      WHERE primary_key_part1=1 AND primary_key_part2=2;


    • eq_ref

      조인수행을 위해 각 테이블에서 하나씩의 행만이 읽혀지는 형태. const 타입이외에 가장 훌륭한 조인타입니다.
      조인연산에 PRIMARY KEY 나 UNIQUE index 인덱스가 사용되는 경우.
      인덱스된 컬럼이 = 연산에 사용되는 경우. 비교되는 값은 상수이거나 이전조인결과의 컬럼값일수 있다.
      다음 예에서 MySQL 은 ref_table 을 처리하는데 eq_ref 조인을 사용한다.

      SELECT * FROM ref_table,other_table
      WHERE ref_table.key_column=other_table.column;
      SELECT * FROM ref_table,other_table
      WHERE ref_table.key_column_part1other_table.column
      AND ref_table.key_column_part2=1;


    • ref

      이전 테이블과의 조인에 사용될 매치되는 인덱스의 모든행이 이 테이블에서 읽혀진다. leftmost prefix 키만을 사용하거나 사용된 키가 PRIMARY KEY 나 UNIQUE 가 아닐때(즉 키값으로 단일행을 추출할수 없을때) 사용되는 조인.
      만약 사용된 키가 적은수의 행과 매치될때 이것은 적절한 조인 타입니다.
      ref 는 인덱스된 컬럼과 = 연산에서 사용된다.
      아래 예에서 MySQL 은 ref_table 처리에 ref 조인 타입을 사용한다.

      SELECT * FROM ref_table WHERE key_column=expr;
      SELECT * FROM ref_table,other_table
      WHERE ref_table.key_column=other_table.column;
      SELECT * FROM ref_table,other_table
      WHERE ref_table.key_column_part1other_table.column
      AND ref_table.key_column_part2=1;


    • ref_or_null

      ref 와 같지만 NULL 값을 포함하는 행에대한 검색이 수반된다.
      4.1.1 에서 새롭게 도입된 조인타입이며 서브쿼리 처리에서 대개 사용된다.
      아래 예에서 MySQL 은 ref_table 처리에 ref_or_null 조인타입을 사용한다.

      SELECT * FROM ref_table
      WHERE key_column=expr OR key_column IS NULL; 
      

      See Section 7.2.7, “How MySQL Optimizes IS NULL.

    • index_merge

      인덱스 병합 최적화가 적용되는 조인 타입.
      이 경우, key 컬럼은 사용된 인덱스의 리스트를 나타내며 key_len 컬럼은 사용된 인덱스중 가장 긴 key 명을 나타낸다.
      For more information, see
       Section 7.2.6, “Index Merge Optimization”.

    • unique_subquery

      이것은 아래와 같은 몇몇 IN 서브쿼리 처리에서 ref 타입대신 사용된다.

      value
       IN (SELECT primary_key FROM single_table WHERE some_expr)

      unique_subquery 는 성능향상을 위해 서브쿼리를 단순 index 검색 함수로 대체한다.

    • index_subquery

      unique_subquery 와 마찬가지로 IN 서브쿼리를 대체한다. 그러나 이것은 아래와 같이 서브쿼리에서 non-unique 인덱스가 사용될때 동작한다.

      value
       IN (SELECT key_column FROM single_table WHERE some_expr)

    • range

      인덱스를 사용하여 주어진 범위 내의 행들만 추출된다. key 컬럼은 사용된 인덱스를 나타내고 key_len 는 사용된 가장 긴 key 부분을 나타낸다.
      ref 컬럼은 이 타입의 조인에서 NULL 이다.
      range 타입은 키 컬럼이 상수와 =<>>>=<<=IS NULL<=>BETWEEN 또는 IN 연산에 사용될때 적용된다.


      SELECT * FROM tbl_name
      WHERE key_column = 10;
      SELECT * FROM tbl_name
      WHERE key_column BETWEEN 10 and 20;
      SELECT * FROM tbl_name
      WHERE key_column IN (10,20,30);
      SELECT * FROM tbl_name
      WHERE key_part1= 10 AND key_part2 IN (10,20,30);
      
      

    • index

      이 타입은 인덱스가 스캔된다는걸 제외하면 ALL 과 같다. 일반적으로 인덱스 파일이 데이타파일보다 작기 때문에 ALL 보다는 빠르다.
      MySQL 은 쿼리에서 단일 인덱스의 일부분인 컬럼을 사용할때 이 조인타입을 적용한다.


    • ALL

      이전 테이블과의 조인을 위해 풀스캔이 된다. 만약 (조인에 쓰인) 첫번째 테이블이 고정이 아니라면 비효율적이다, 그리고 대부분의 경우에 아주 느린 성능을 보인다. 보통 상수값이나 상수인 컬럼값으로 row를 추출하도록 인덱스를 추가함으로써 ALL 타입을 피할 수 있다.


  • possible_keys

    이 컬럼값은 MySQL 이 해당 테이블의 검색에 사용할수 있는 인덱스들을 나타낸다.
    주의할것은 explain 결과에서 나타난 테이블들의 순서와는 무관하다는 것이다.
    이것은 possible_keys 에 나타난 인덱스들이 결과에 나타난 테이블 순서에서 실제 사용할 수 없을수도 있다는 것을 의미한다.
    이값이 NULL 이라면 사용가능한 인덱스가 없다는 것이다. 이러한 경우에는 인덱스를 where 절을 고려하여 사용됨직한 적절한 컬럼에 인덱스를 추가함으로써 성능을 개선할 수 있다. 인덱스를 수정하였다면 다시한번 EXPLAIN 을 실행하여 체크하라.
    See Section 13.2.2, “ALTER TABLE Syntax”.

    현재 테이블의 인덱스를 보기 위해서는 SHOW INDEX FROM tbl_name.을 사용하라.

  • key

    이 컬럼은 MySQL 이 실제 사용한 key(index) 를 나타낸다.
    만약 사용한 인덱스가 없다면 NULL 값일 것이다. MySQL 이 possible_keys 에 나타난 인덱스를 사용하거나 사용하지 않도록 강제하려면 FORCE INDEXUSE INDEX, 혹은 IGNORE INDEX 를 함께 사용하라.
    See
     Section 13.1.7, “SELECT Syntax”.

    MyISAM 과 BDB 테이블에서는 ANALYZE TABLE 이 옵티마이저가 더나은 인덱스를 선택할 수 있도록 테이블의 정보를 갱신한다.
    MyISAM 에서는 myisamchk --analyze 가 같은 기능을 한다.
    See
     Section 13.5.2.1, “ANALYZE TABLE Syntax” and Section 5.7.2, “Table Maintenance and Crash Recovery”.

  • key_len

    이 컬럼은 MySQL 이 사용한 인덱스의 길이를 나타낸다. key 컬럼값이 NULL 이면 이값도 NULL 이다.
    key_len 값으로 MySQL 이 실제 복수컬럼 키중 얼마나 많은 부분을 사용할 것인지 알 수 있다.

  • ref

    이 컬럼은 행을 추출하는데 키와 함께 사용된 컬럼이나 상수값을 나타낸다.


  • rows

    이 값은 쿼리 수행에서 MySQL 이 예상하는 검색해야할 행수를 나타낸다.

  • Extra

    이 컬럼은 MySQL 이 쿼리를 해석한 추가적인 정보를 나타낸다.
    아래와 같은 값들이 나타날 수 있다.

    • Distinct

      MySQL 이 매치되는 첫행을 찾는 즉시 검색을 중단할 것이다.

    • Not exists

      MySQL 이 LEFT JOIN 을 수행함에 매치되는 한 행을 찾으면 더이상 매치되는 행을 검색하지 않는다.
      아래와 같은 경우에 해당한다.


      SELECT * FROM t1 LEFT JOIN t2 ON t1.id=t2.id
      WHERE t2.id IS NULL;
      
      

      여기서 t2.id 는 NOT NULL 이고, 이경우 MySQL 은 t1 을 스캔한 후 t1.id 값을 사용해 t2 를 검색한다. MySQL 이 t2 에서 매치되는 행을 찾으면 t2.id 는 NULL 이 될 수 없으므로 더이상 진행하지 않는다. 즉, t1 의 각 행에 대해 t2 에서 매치되는 행이 몇개가 있던지 한개만 찾으면 된다.


    • range checked for each record (index map: #)

      MySQL 이 사용할 좋은 인덱스가 없다. 그러나 선행된 테이블의 컬럼값에 따라 몇몇 인덱스를 사용할 수 있다. 선행된 테이블의 개개 행에 대해 MySQL 이 range 나 index_merge 접근법을 사용할 수 있는지 체크할 것이다.
      적용가능성의 핵심은 Section 7.2.5, “Range Optimization” and Section 7.2.6, “Index Merge Optimization” 에 모든 선행된 테이블의 값이 명확하거나 상수인 때를 예외로 하여 기술되어 있다.
      이것은 그리 빠르진 않으나 인덱스가 없는 조인의 경우보다는 빠르다.


    • Using filesort

      MySQL 이 정렬을 위해 추가적인 과정을 필요로한다. 정렬과정은 조인타입에 따라 모든 행을 검색하고 WHERE 절에 의해 매치된 모든 행들의 키값을 저장한다. 그리고 저장된 키값을 정렬하여 재정렬된 순서로 행들을 추출한다.
      See
       Section 7.2.10, “How MySQL Optimizes ORDER BY.


    • Using index

      컬럼정보가 실제 테이블이 아닌 인덱스트리에서 추출된다. 쿼리에서 단일 인덱스된 컬럼들만을 사용하는 경우이다.


    • Using temporary
      MySQL 이 결과의 재사용을 위해 임시테이블을 사용한다. 쿼리 내에 GROUP BY 와 ORDER BY 절이 각기 다른 컬럼을 사용할때 발생한다.


    • Using where
      WHERE 절이 다음 조인에 사용될 행이나 클라이언트에게 돌려질 행을 제한하는 경우이다. 테이블의 모든 행을 검사할 의도가 아니라면 Extra 값이 Using where 가 아니고 조인타입이 ALL 이나 index 라면 쿼리사용이 잘못되었다.

    • 쿼리를 가능한 한 빠르게 하려면, Extra 값의 Using filesort 나 Using temporary 에 주의해야 한다.

    • Using sort_union(...) , Using union(...) , Using intersect(...)
      이들은 인덱스 병합 조인타입에서 인덱스 스캔이 병합되는 형태를 말한다.

      See Section 7.2.6, “Index Merge Optimization” for more information.


    • Using index for group-by

      테이블 접근방식은 Using index 와 같다. MySQL 이 실제 테이블에 대한 어떠한 추가적인 디스크 접근 없이 GROUP BY 나 DICTINCT 쿼리에 사용된 모든 컬럼에 대한 인덱스를 찾았음을 말한다. 추가적으로 각각의 group 에 단지 몇개의 인덱스 항목만이 읽혀지도록 가장 효율적인 방식으로 인덱스가 사용될 것이다.
      For details, see
       Section 7.2.11, “How MySQL Optimizes GROUP BY.

EXPLAIN 의 출력내용중 rows 컬럼값들을 곱해봄으로써 얼마나 효과적인 join 을 실행하고 있는지 알 수 있다. 이 값은 MySQL 이 쿼리수행중 검사해야할 행수를 대략적으로 알려준다. 만약 max_join_size 시스템 변수값을 설정하였다면 이 값은 또한 여러테이블을 사용하는 select 중 어느것을 먼저 실행할지 판단하는데 사용된다.
See
 Section 7.5.2, “Tuning Server Parameters”.


다음 예는 다중테이블 조인이 EXPLAIN 정보를 통해 점차적으로 개선되는 과정을 보여준다. 만약 아래와 같은 select 문을 EXPLAIN 으로 개선한다면 :

EXPLAIN SELECT tt.TicketNumber, tt.TimeIn,
            tt.ProjectReference, tt.EstimatedShipDate,
            tt.ActualShipDate, tt.ClientID,
            tt.ServiceCodes, tt.RepetitiveID,
            tt.CurrentProcess, tt.CurrentDPPerson,
            tt.RecordVolume, tt.DPPrinted, et.COUNTRY,
            et_1.COUNTRY, do.CUSTNAME
        FROM tt, et, et AS et_1, do
        WHERE tt.SubmitTime IS NULL
            AND tt.ActualPC = et.EMPLOYID
            AND tt.AssignedPC = et_1.EMPLOYID
            AND tt.ClientID = do.CUSTNMBR;

이 예에서 아래와 같은 가정이 사용되었다.:

  • The columns being compared have been declared as follows:

    Table Column Column Type
    tt ActualPC CHAR(10)
    tt AssignedPC CHAR(10)
    tt ClientID CHAR(10)
    et EMPLOYID CHAR(15)
    do CUSTNMBR CHAR(15)
  • The tables have the following indexes:

    Table Index
    tt ActualPC
    tt AssignedPC
    tt ClientID
    et EMPLOYID (primary key)
    do CUSTNMBR (primary key)
  • The tt.ActualPC values are not evenly distributed.

먼저, 개선되기 전의 EXPLAIN 은 다음과 같은 정보를 보여준다.:

table type possible_keys key  key_len ref  rows  Extra
et    ALL  PRIMARY       NULL NULL    NULL 74
do    ALL  PRIMARY       NULL NULL    NULL 2135
et_1  ALL  PRIMARY       NULL NULL    NULL 74
tt    ALL  AssignedPC,   NULL NULL    NULL 3872
           ClientID,
           ActualPC
      range checked for each record (key map: 35)


각 테이블의 type 이 ALL 을 나타내므로, MySQL 이 모든 테이블의 카티션곱(Cartesian product) 를 생성한다는 것을 나타낸다.
각 테이블의 행의 조합이 모두 검사되어야 하기 때문에 이것은 아주 오랜 시간이 소요될 것이다.

실제로 이 결과는 74 * 2135 * 74 * 3872 = 45,268,558,720 행에 달한다.
만약 테이블이 더 크다면 얼마나 소요될지 상상할 수도 없을 것이다.

여기서 우선적인 문제는 MySQL 은 같은 타입으로 선언된 컬럼의 인덱스를 더 효과적으로 사용할 수 있다는 것이다. (ISAM 테이블에서는 같은 타입으로 선언되지 않은 인덱스는 사용할 수 없다.) 여기에서 VARCHAR 과 CHAR 은 길이가 다르지 않다면 같은 타입이다.
tt.ActualPC 는 CHAR(10) 이고 et.EMPLOYID 는 CHAR(15) 로 선언되어 있으므로 길이의 불일치가 발생한다.

이러한 컬럼 길이의 불일치 문제의 해결을 위해 ALTER TABLE 을 사용하여 ActualPC 컬럼을 10 글자에서 15 글자로 변경하자 (길이를 늘리는것은 데이타 손실이 없다.)


mysql> ALTER TABLE tt MODIFY ActualPC VARCHAR(15);

이제 tt.ActualPC 와 et.EMPLYID 는 모두 VARCHAR(15) 이다. 다시 EXPLAIN 을 실행해보면 다음 결과와 같다.


table type   possible_keys key     key_len ref         rows    Extra
tt    ALL    AssignedPC,   NULL    NULL    NULL        3872    Using
             ClientID,                                         where
             ActualPC
do    ALL    PRIMARY       NULL    NULL    NULL        2135
      range checked for each record (key map: 1)
et_1  ALL    PRIMARY       NULL    NULL    NULL        74
      range checked for each record (key map: 1)
et    eq_ref PRIMARY       PRIMARY 15      tt.ActualPC 1

훨씬 좋아졌지만 아직 완벽하지 않다. 행의 곱은 이제 74 만큼 줄었다.

이 쿼리는 이제 몇초만에 실행될 것이다.

두번째 작업은 tt.AssignedPC = et_1.EMPLYID 와 tt.ClientID = do.CUSTNMBR 에서의 컬럼길이의 불일치를 수정하는 것이다.


mysql> ALTER TABLE tt MODIFY AssignedPC VARCHAR(15),
    ->                MODIFY ClientID   VARCHAR(15);

이제 EXPLAIN 은 다음과 같은 결과를 보여준다.

table type   possible_keys key      key_len ref           rows Extra
et    ALL    PRIMARY       NULL     NULL    NULL          74
tt    ref    AssignedPC,   ActualPC 15      et.EMPLOYID   52   Using
             ClientID,                                         where
             ActualPC
et_1  eq_ref PRIMARY       PRIMARY  15      tt.AssignedPC 1
do    eq_ref PRIMARY       PRIMARY  15      tt.ClientID   1

이것은 이제 거의 최적의 결과가 된 것 같다.

남아있는 문제는 MySQL 이 기본으로 tt.ActualPC 컬럼의 값이 균등하게 분포되어 있다고 가정한다는 것이다. 하지만 tt 테이블은 실제로 그렇지 않다.

다행히도 MySQL 이 키 분포를 검사하도록 하는것은 매우 쉽다.

mysql> ANALYZE TABLE tt;

이제 완벽한 조인이 되었다. EXPLAIN 결과는 다음과 같다.

table type   possible_keys key     key_len ref           rows Extra
tt    ALL    AssignedPC    NULL    NULL    NULL          3872 Using
             ClientID,                                        where
             ActualPC
et    eq_ref PRIMARY       PRIMARY 15      tt.ActualPC   1
et_1  eq_ref PRIMARY       PRIMARY 15      tt.AssignedPC 1
do    eq_ref PRIMARY       PRIMARY 15      tt.ClientID   1

EXPLAIN 결과의 rows 컬럼값이 나타내는 MySQL 최적화에 의해 예측된 행수에 주목하라.

나타난 숫자가 실제 행수에 근접한지 체크해야 한다. 그렇지 않다면 STRAIGHT_JOIN 를 사용고 FROM 절에서 테이블의 순서를 변경함으로써 더 나은 성능을 얻을 수 있다.

개발/DB_MYSQL | Posted by 은우 아빠 2009. 2. 25. 09:34

MySQL 쓰면서 하지 말아야 할 것 17가지


http://www.slideshare.net/techdude/how-to-kill-mysql-performance

작게 생각하기
- 조만간 규모가 커질거라면 MySQL ecosystem을 봐야된다.
- 그리고 캐싱 빡시게 안 하는 메이저 웹사이트는 없다.
- develooper.com의 Hansen PT랑 Ilia 튜토리얼 볼 것
- 처음부터 확장 가능하게 아키텍처 잘 쪼개놔야된다.
- 복제랑 파티셔닝 어떻게 할지 미리 계획 세워놔라.
- 파일 기반 세션 좀 쓰지마 -_-
- 그렇다고 너무 쓸데없이 크게 생각하지도 말 것
- 특히 성능하고 확장성 구분 못 하면 난감함

EXPLAIN 안 써보기
- SELECT 앞에 EXPLAIN 이라고 붙이기만 하면 되는 것을 (..)
- 실행 계획 확인
- 타입 컬럼에 index 써있는거랑 Extra 컬럼에 index 써있는거랑 "매우 큰" 차이 있음
 * 타입에 있으면 Full 인덱스 스캔 (안 좋다.)
 * Extra 컬럼에 있으면 Covering 인덱스 찾았다는 의미임 (좋다!)
- 5.0 이후부터는 index_merge 최적화도 한다.

잘못된 데이터 타입 선택
- 한 메모리 블럭 단위에 인덱스 레코드가 많이 들어갈수록 쿼리가 빨리 실행될 것이다. (중요)
아.. 정규화 좀 해 -_-... (이거 정말 충격과 공포인 듯)
- 가장 작은 데이터 타입을 써.. (진짜 BIGINT가 필요하냐고..)
- 인덱스 걸리는 필드는 정말 최소한으로 데이터 크기를 써야된다고.
- IP는 INT UNSIGNED로 저장해!! (아주 공감)
 * 이럴 때 쓰라고 INET_ATON 함수가 아예 내장되어 있음.

PHP에서 pconnect 쓰는 짓
- 아파치에서 좀비 프로세스라도 생기면 그 커넥션은 그냥 증발하는거야..
- 어차피 MySQL 접속 속도는 Oracle이나 PostgreSQL 보다 10~100배 빠르다고.

너무 과도한 DB 추상화 계층을 두는 것
- 어디 포팅 열심히 할 거 아니면 추상화 계층 쓰지마 (ADODB, MDB2, PearDB 등)
- scale out 가능한걸 쓰라고.

스토리지 엔진 이해 못 하는 것
- 단일 엔진만으로 전체 아키텍처를 결정했다면 대부분 최적이 아님
- 엔진 별 장단점을 공부할 것
- ARCHIVE : zlib으로 압축해주고 UPDATE 안 되고 로그 Bulk Insert에 유용함.
- MEMORY : 서버 재시작하면 증발. 인덱스가 HASH나 BTREE로 가능함. 임시, 요약 데이터에 사용.
 * 주간 top X 테이블 같은 것.
 * 하여튼 메모리에 박아넣고 싶은 데이터 있으면..

인덱스 레이아웃 이해 못 하는 것
- 제대로 인덱스랑 스토리지 엔진 선택하려면 공부 좀 해
- 엔진은 데이터와 인덱스 레코드를 메모리나 디스크에 레이아웃하는 걸 구현한 것
- clustered 구성은 데이터를 PK 순서에 따라 저장함.
- non-clustered 구성은 인덱스만 순서대로 저장하고 데이터는 순서 가정하지 않음.
- clustered에서는 인덱스만 타면 추가적인 조회 없이 바로 데이터 가져오는 것임.
- 그래서 clustered PK는 작은 놈으로 할 필요가 있다는거
 * 다른 인덱스는 각 레코드마다 PK를 앞에 더 붙이게 되니까.
 * PK 지정 안 하면 아무렇게나 해버림

쿼리 캐시 이해 못 하는 것
- 어플리케이션 read/write 비율은 알고 있어야지
- 쿼리 캐시 설계는 CPU 사용과 읽기 성능 간의 타협
- 쿼리 캐시 크기를 늘린다고 읽기 성능이 좋아지는게 아님. heavy read라도 마찬가지.
- 과도한 CPU 사용을 막기 위해 무효화 할 때는 캐시 항목들을 뭉텅이로 날려버림
- 한마디로 SELECT가 참조하는 테이블 데이터 하나라도 변경되면 그 테이블 캐시는 다 날라간다는 얘기임
- 수직 테이블 파티셔닝으로 처방 
 * Product와 ProductCount를 쪼갠다든지.. 
 * 자주 변하는 것과 변하지 않는 것을 쪼개는게 중요하다 이 말임.

Stored Procedure를 쓰는 것
- 무조건 쓰면 안 된다는게 아니고..
- 컴파일 할 때 무슨 일이 일어나는지 이해 못 하고 쓰면 재앙이 된다 이 말.
- 다른 RDBMS랑 다르게 connection thread에서 실행 계획이 세워짐.
- 이게 뭔 얘기냐 하면 데이터 한 번 가져오고 연결 끊으면 그냥 CPU 낭비 (7~8% 정도)하는 꼴이라는 것.
- 웬만하면 Prepared 구문과 Dynamic SQL을 써라.. 아래 경우를 제외하고
 * ETL 타입 프로시저
 * 아주아주 복잡하지만 자주 실행되지는 않는 것
 * 한 번 요청할 때마다 여러번 실행되는 간단한 것 (연결한 상태로 여러번 써야 된다니까)

인덱스 컬럼에 함수 쓰는 것
- 함수에 인덱스 컬럼 넣어 호출하면 당연히 인덱스 못 탄다
- 함수를 먼저 계산해서 상수로 만든 다음에 = 로 연결해야 인덱스 탈 수 있다.
 * 여기 실행 계획 보면 LIKE도 range type 인덱스 타는 것 보임

인덱스 빼먹거나 쓸모없는 인덱스 만들어 놓는 것
- 인덱스 분포도(selectivity)가 허접하면 안 쓴다. 
- S = d/n 
 * d = 서로 다른 값의 수 (# of distinct values)
 * n = 테이블의 전체 레코드 수
- 쓸모없는 인덱스는 INSERT/UPDATE/DELETE를 느리게 할 뿐..
- FK는 무조건 인덱스 걸어라. (물론 FK 제약 걸면 인덱스 자동으로 생긴다.)
- WHERE나 GROUP BY 표현식에서 쓰이는 컬럼은 인덱스 추가를 고려할 것
- covering index 사용을 고려할 것
- 인덱스 컬럼 순서에 유의할 것!

join 안 쓰는 짓
- 서브쿼리는 join으로 재작성해라
- 커서 제거해라
- 좋은 Mysql 성능을 내려면 기본
- 집합 기반으로 생각해야지 루프 돌리는거 생각하면 안 된다.

Deep Scan 고려하지 않는 것
- 검색엔진 크러울러가 쓸고 지나갈 수 있다.
- 이 경우 계속해서 전체 집합을 정렬한 다음 LIMIT로 가져와야 하니 무진장 느려진다.
- 어떻게든 집합을 작게 줄인 다음 거기서 LIMIT 걸어 가져올 것

InnoDB 테이블에서 WHERE 조건절 없이 SELECT COUNT(*) 하는 짓
- InnoDB 테이블에서는 조건절 없이 COUNT(*) 하는게 느리다.
- 각 레코드의 transaction isolation을 유지하는 MVCC 구현이 복잡해서 그렇다는..
- 트리거 걸어서 메모리 스토리지 엔진 쓰는 테이블에 통계를 별도로 유지하면 된다.

프로파일링이나 벤치마킹 안 하는 것
- 프로파일링 : 병목 찾아내기
- 벤치마킹 : 시간에 따른 성능 변화 추이 평가, 부하 견딜 수 있는지 테스트
- 프로파일링 할 때는 실제 데이터를 옮겨와서 할 것
- 어디가 병목이냐~ Memory? Disk I/O? CPU? Network I/O? OS?
- 느린 쿼리 로그로 남기기
 * log_slow_queries=/path/to/log
 * log_queries_not_using_indexes
- 벤치마킹 시에는 다 고정시키고 변수 하나만 바꿔가면서 해야 함. (쿼리 캐시는 끌 것.)
- 도구를 써라~~
 * EXPLAIN
 * SHOW PROFILE
 * MyTop/innotop
 * mysqlslap
 * MyBench
 * ApacheBench (ab)
 * super-smack
 * SysBench
 * JMeter/Ant
 * Slow Query Log

AUTO_INCREMENT 안 쓰는 것
- PK를 AUTO_INCREMENT로 쓰는건 무진장 최적화 되어 있음
 * 고속 병행 INSERT 가능
  * 잠금 안 걸리고 읽으면서 계속 할 수 있다는!
- 새 레코드를 근처에 놓음으로써 디스크와 페이지 단편화를 줄임
- 메모리와 디스크에 핫 스팟을 생성하고 스와핑을 줄임

ON DUPLICATE KEY UPDATE를 안 쓰는 것
- 레코드가 있으면 업데이트하고 없으면 인서트하고 이런 코드 필요없다!! 다 날려버려라!!
- 서버에 불필요하게 왔다갔다 할 필요가 없어짐
- 5-6% 정도 빠름
- 데이터 입력이 많다면 더 커질 수 있음

하지 말아야 할 것 총정리
  1. Thinking too small
  2. Not using EXPLAIN
  3. Choosing the wrong data types
  4. Using persistent connections in PHP
  5. Using a heavy DB abstraction layer
  6. Not understanding storage engines
  7. Not understanding index layouts
  8. Not understanding how the query cache works
  9. Using stored procedures improperly
  10. Operating on an indexed column with a function
  11. Having missing or useless indexes
  12. Not being a join-fu master
  13. Not accounting for deep scans
  14. Doing SELECT COUNT(*) without WHERE on an InnoDB table
  15. Not profiling or benchmarking
  16. Not using AUTO_INCREMENT
  17. Not using ON DUPLICATE KEY UPDATEK
개발/소프트웨어공학 | Posted by 은우 아빠 2009. 2. 11. 19:51

[펌] CBD방법론...


출 처 : http://blog.naver.com/amuck?Redirect=Log&logNo=80002465378]

1. CBD? 지금이 적기인가?
 
만약 여러분이 CBD(Component Based Development)에 대해 아직 들어보지 못했다면 곧 알게 되겠지만, CBD는 개발 지연과 믿을 수 없는 품질 등 사시사철 지속되는 문제들을 돌파할 수 있는 최신의 개발방법으로서 최근에 관심을 끌고 있다.관계형 데이터베이스, 4GL, CASE와 같은 기술 파도를 연속해서 뚫고 나온 많은 IT 관리자들은 CBD에 대해 회의를 품고 있지만 나름대로 일리도 있다.그들은 이미 생산성이 대폭 증가를 이룰 수 있다는 열광적인 예측들로부터, 적은 투자만으로도 큰 효과를 얻어낼 수 있다는 것 까지 포함해서, 그 동안의 기술 결과들을 익히 보아 왔기 때문이다.
그렇다면 CBD는 지금까지 것과는 다른 것인가? 그것은 CIO들이 무시해도 될 만한 일시적인 기술 유행에 그칠 것인가? 아니면 IT분야에서 포용하고 인정해야 할 새로운 패러다임인가? 비용은 실제로 얼마나 들 것이며 거기서 얻는 이익은 무엇인가?

이 글에서는 CBD전망과 문제들에 대해서 조사하고자 한다. 여러분은 왜 CBD가 IT를 위한 진정한 주요 패러다임 변동인지, 그리고 왜 그것이 결국 널리 적용되어야 하는지 알게 될 것이다. 주요 회사들은 이미 CBD로 이동중이다. 그것이 새로운 개념이기 때문이 아니라 전략적 규범이 될 것이기 때문이다. 가트너 그룹은 2004년 까지 CBD 와 비즈니스 컴포넌트의 대규모 상품을 소유한 IS 조직들은 그렇지 않은 조직들보다, 5배에서 10배 까지 더 많은 생산성과 대응력을 가질 것으로 보고 있다.

많은 회사들은 그들이 계속 전통적인 방법으로 일하는 한, 경쟁자들의 앞서가는 생산성을 도저히 따라잡을 수 없게 될 것이다. 새로운 인터넷 경제에서, IT 솔루션과 비즈니스 성공 사이에 연관이 매우 밀접해 지면서, 많은 CIO들은 시스템 평가 방법을 재정립하고 있다. 기업의 어플리케이션 개발 능력을 향상시키는 일은 단지 효율성 개선 수준이 아닌 훨씬 더 큰 의미를 가지는 것으로 - 전략적 필요성으로 인식되고 있다.

CBD가 가져올 효과는, 열심히 뛰어서 회사의 응용프로그램을 인도해 주는 수준으로부터 더 나아가서, 그 자체가 경쟁력 있는 무기가 되는 것이다. CBD를 사용한 회사들이 새로 정교하게 만든 e비즈니스 어플리케이션을 배치하고 주도해 나가고 있을 때, 다른 회사들은 설계, 개발, 테스트, 디버깅 하느라 고생하게 될 것이다.


사용자 삽입 이미지


만약 CBD의 이점들이 그렇게도 극적인 것이라면, 왜 더 널리 보급되지 않았을까? 거기에는 산업 표준 미성숙, 기술 장벽, CBD기반 도구 부족, 좋은 조언자 부족, 실용 방법론 부족과 같은 몇가지 요인들이 있다. CBD의 전략적인 이점을 정확히 알고 있는 일부 기업들은 야심적인 CBD 프로젝트를 곧 진수할 차비를 차리고 있다. 위에 열거한 요인들 때문에 그 동안의 노력들은 실패를 거듭하였고, 그런 실패들은 IT조직들이 CBD에 대한 편견을 갖는데 기여했다. 그러나 지난 몇 년 동안의 기술 진보에 힘입어 위의 실패 요인들을 해결하게 되었고, CBD를 이용하여 성공을 경험하고 있는 조직들도 이제는 나타나고 있다. 이런 성공들은 CBD의 근본 전망을 확인시켜 주었다. 이렇게 중요한 추세인 CBD를 CIO들이 주시해야 되는 이유는 그에 내포된 전략성 때문이다.

 2. CBD? 진정 새로운 것인가?

 모듈화modular 프로그래밍에 관해 들어본 사람이라면 컴포넌트의 기본적인 가치 명제도 이와 같다고 보면 된다.
큰 시스템을 관리하기 편한 조각으로 나누고, 식별되는 명세서 별로 인터페이스를 명확히 갖는 모듈로 구현하고, 문서로 정확히 나타낸 후, 소프트웨어 빌딩블록의 집합을 만들 수 있다. 개발자들은 빌딩 블록을 조립하여 어플리케이션을 마무리 짓는다.

오늘날까지 경험으로 증명된 바에 따르면, 개발자들이 외부 파라미터 위주로 컴포넌트를 호출하면, 컴포넌트의 내부 코드를 조사하여 그것을 프로그램과 연동시키는 것 보다, 작업을 완료하고 시험하는데 있어, 수십, 수백 배 더 빠르게 일을 마칠 수 있다.


사용자 삽입 이미지


적절히 구현된 모듈화의 주요 이점중의 하나는, 개발자들이 결과를 내는 방법에 관해서는 신경 쓸 필요가 없다는 것이다. 단지 주어진 기능을 사용하면 되는 것이다. 예를 들면, 고정금리 대출이자 계산 모듈이 있다면 새로운 대출금 지불 시스템을 개발할 때 “재사용”할 수 있다. IT조직은 “바퀴를 재 발명” 하는 일과 같은 경우를 피할 수 있을 뿐만 아니라, 전체 개발 스텝에서 널리 사용될 수 있는 복잡한 로직을 가진 모듈을 미리 만들고 테스트하는 등 모자라는 IT 기술자들을 지렛대 효과로 활용할 수 있다.

모듈화 개념은 오랫동안 IT 조직에서 이용되어 왔다. 만약 그것이 컴포넌트 중심 개발의 전부라면 왜 CBD를 IT의 새로운 파도라고 귀찮게 권유하는가?

CBD가 힘을 얻고 있는 이유는 모듈화 이점을 대규모로 확장할 수 있게 되었기 때문이다. e비즈니스 세계는 궁극적으로 이기종 분산 컴퓨팅 환경이다. 미들웨어 수준에서 컴포넌트들은 이기종 플랫폼 마다 서로 다른 아키텍처와 언어, 표준화 장벽들을 극복할 수 있는 접착제 역할을 흘륭히 증명했다. CBD는 전자 상거래와 같은 다방면의 e비즈니스 어플리케이션 개발에서 선진적 방법을 지렛대로 쓸 수 있다.

다시 대출금 계산 루틴 예제로 돌아가서, 만약 이 모듈이 20년 전에 개발되었다면 그것은 관계형 DB가 출현하기 이전 환경인 메인 프레임에 배치되었을 것이다. 그 후에 조직이 클라이언트-서버 시스템으로 전환될 때 이 모듈은 호환성이 없었기 때문에 재사용할 수 없었고 따라서 재작성할 수 밖에 없었다. 더욱이 데스크 탑과, 브라우저 중심 어플리케이션 환경으로 구현되면서 호환성은 더욱 멀어졌고 당연히 재사용은 되지 않았다.

새로운 운영 시스템으로 진화되고 난 후, 새로운 플랫폼, 새로운 DBMS, 새로운 네트워킹 프로토콜, 새로운 프로그래밍 언어들도 진화되면서, 이때 호환성 문제들이 얼마나 복합적으로 얽히는지 우리는 알고 있다. 이것이 바로 관리를 잘하는 IT 조직들이 "모듈화 프로그래밍”을 오랫동안 가장 좋은 규칙으로 선택한 이유이다. 그러나 그들은 지속적으로 진화된 기술 아키텍처들을 지렛대로 이용하여 대규모 개발 방법론으로 발전시키지 못한 아쉬움이 있다.

그러나 이제, 모든 것이 변했다.

오늘날 모든 타입의 컴퓨팅 플랫폼들은 인터넷을 통해 상호 연동된다. 표준들이 등장하여 이기종 시스템들 사이의 비 호환성을 중화 시키고 있다. 사용자 인터페이스용 공통 프로토콜들도 태어났다. 벽이 허물어진 것이다.

오늘날 SQL을 사용하는 관계형 모델은 기업 데이터 관리 분야에서 사실상de facto 표준으로 부상하고 있다. 또한 이기종 분산 어플리케이션 미들웨어 표준으로서 COM+, CORBA, EJB(Enterprise Java Beans) 가 빠르게 성숙되는 것을 볼 수 있고, Java, Visual Basic과 같은 컴포넌트 기반 언어들이 e비즈니스 개발 표준이 되어가는 것도 볼 수 있다.

혹자는 각각의 기술 간에 상대적인 장점들을 논하려 들지도 모른다. 그러나 언제나 얻을 수 있는 이점으로는 어플리케이션과 애플릿, 컴포넌트들이 개발되면 이기종 플랫폼에도 보급되어 수행될 수 있다. 이와 같은 상호연동은 CBD 패러다임으로 이동되면 나타날 수 있는 현실이다. 그렇게 되면 한 환경에서 개발된 소프트웨어 모듈은 다른 환경에서도 실행될 수 있게 된다.

패러다임이 이동되고 있는 증거로는 기술 컴포넌트들이 시장에서 번성하는 것을 보면 알 수 있다. 웹을 통해 컴포넌트들을 이용할 수 있을 뿐 아니라, 어떤 종류의 컴포넌트들이든지 다운로드할 수 있고 구매도 할 수 있다. 때때로 규격품widget이라 불리는 기술 컴포넌트들은 공통 기능들을 자동화하여 GUI 디스플레이 관리나 grid 컨트롤, 프린트 유틸리티 형태로 나오고 있다.

컴포넌트 시장은 더 높은 수준의 기능 분야로 확장되기 시작하였다. e비즈니스 어플리케이션 구축자들은 쇼핑 cart나 주문 관리와 같은 공통 웹사이트 작업을 다룰 수 있는 컴포넌트들을 획득해서 사용할 수 있다.

요약하면, 모듈화 개념은 새로운 것은 분명 아니지만, 이기종 환경에서 구현할 수 있는 능력은 새로운 것이다. CBD는 중요한 개발의 새로운 파도로서, 그것이 아주 새로운 아이디어라서 가 아니라, 품질과 생산성을 함께 이루고자 했던 이전의 생각들을 최근까지는 이루지 못하다가, 이제서야 실현할 수 있는 수단이 되었기 때문이다.
 

3. 컴포넌트?

 컴포넌트 : 특정한 기능을  수행하기 위해  독립적으로 개발되고,  잘 정의된  인터페이스를 가지며, 다른  부품과  조립되어  응용 시스템을  구축하기 위해 사용되는 소프트웨어 부품(단위).   

  A Component  is a software building block,  used to  construct large component or  application for end- users.    -John Dodd(Computer Associates) -    


위에서 말했던 것처럼, 컴포넌트는 그 본질이 개별 특성을 가진 소프트웨어 모듈이다. 그것은 패키지로서, 사용자에게 서비스를 제공할 수 있도록 잘 정의되어야 한다. 컴포넌트는 객체지향 기술 분야의 용어를 자주 빌려 쓰지만, 컴포넌트와 객체지향이 결코 같은 것은 아니다. 메인 프레임에 있는 COBOL 루틴도 자바 프로시저 만큼 쉽게 컴포넌트로 쓸 수 있다.


사용자 삽입 이미지

 

컴포넌트는 각각 명세서를 한 개씩 동반하면서 거기서 자신이 제공하는 서비스를 식별하도록 돕는다. 서비스는 기능을 구체화시켜 이를 통해 밖으로부터 접근할 수 있게 한다. 서비스에는 이름과 파라미터 목록이 있다. 캡슐화encapsulation 개념은 CBD에서 근본을 이루고 있다. 컴포넌트에는 데이터와 그들이 제공할 필요가 있는 프로세스들을 포함시킨다. 그리고 컴포넌트에 접근하여 기능을 수정할 수 있는 방법은 정의된 인터페이스를 통하는 길 뿐이다. 이 “폐쇄 경계”closed boundary 개념은 CBD가 객체지향 기술과는 다른 길을 가도록 한다.

객체지향기술에서, 상속은 한 객체가 다른 객체를 효과적으로 수정할 수 있는 강력한 원리이다. 객체 특성이 변경되면, 해당 하위 객체는 모두 자동으로 변경을 상속 받는다. 상속은 강력하기는 하지만 기술을 습득하기가 매우 어렵고, 시스템 개발에서 큰 도전 정신이 필요하다. 왜냐하면, 설계가 바뀌면 파급효과는 객체 경계를 넘나들어 통제하기가 힘든 때문이다.

CBD 캡슐화로 시스템 설계에서 관리 체계를 세울 수 있게 되었다. 그것은 변경이 일어나도 컴포넌트 내로 국한시킬 수 있고, 캡슐 밖으로는 어떤 영향도 주지 않기 때문이다. 상속기능을 모두 사용하여 객체지향 언어를 쓰면 컴포넌트에서 지렛대로 사용될 수 있으나, CBD 기술로 안정된 어플리케이션 빌딩 블록을 인도할 수 있도록 컴포넌트 경계이내로 캡슐화 사용을 제한했다.

컴포넌트를 구현할 때는 서비스가 어떻게 실행되는지를 정의한다. 구현된 결과로 나오는 것은 소스 코드가 한 부분이고, 다른 한 부분은 설계 모델 자체이다. 고급 툴 집합들 중에는 소스 코드를 모델에 연관시켜, 본래의 비즈니스 요구사항과 프로세스들 까지 역 추적 할 수 있는 것도 있다. 이렇게 연결하면 전통적인 방법과 툴을 사용하였을 때 보다 컴포넌트의 설계 의도를 더욱 깊이 이해 할 수 있게 된다. 컴포넌트 명세서는 서로 다른 몇 개 환경으로 구현될 수 있다. 예를 들면, 어떤 컴포넌트를 COM+ 나 CORBA 두 가지로 구현할 수 있다.

컴포넌트 실행물executable(즉, 오브젝트 코드)은 구현으로부터 나온다. Princeton Softech 제품인 Select Component ManagerTM 와 같은 몇몇 CBD 지원 툴들은 실행물들을 저장할 때 컴포넌트의 구현물implementation과 명세서들도 함께 붙여 놓을 수 있다. 그리고 사이트 관리자가 지정하는 대로 분산시킬 수도 있다.

대체로, 컴포넌트들은 테스팅과 QA 프로세스들을 강도 높게 거쳤기 때문에, 품질이 높은 소프트웨어 모듈들이다. 다시 말하면 미리 시험한 단단한 소프트웨어 루틴들이므로 새로운 어플리케이션에 신뢰를 가지고 포함(재사용) 시킬 수 있다는 것이다. 이렇게 일할 때, 전체적인 어플리케이션의 인도 시기는 더 앞당길 수 있다. CBD 환경을 사용하지 않는 것보다 미리 시험된 컴포넌트는 테스팅과 디버깅 중 많은 부분을 생략할 수 있기 때문이다.

최근에, 표준 인터페이스 정의 언어들이 부상하고 있다 (CORBA에서 IDL, COM에서 ODL 등). 이제는 개발자들이 목표로 하는 미들웨어 환경에서 사용할 최종 상품에 상호연동해서 쓸 수 있도록 컴포넌트를 생산할 수 있게 되었다.

오늘날 컴포넌트는 기술 컴포넌트와 비즈니스 컴포넌트 두 종류로 나누는 것이 보편적이다. 기술 컴포넌트는 위에서 말했던 “규격품”widgets처럼 어플리케이션의 기술적 경향에 초점을 맞추고 다양한 비즈니스에서 재사용 할 수 있다. 왜냐하면, 그것들은 GUI 행동처럼 계통generic 기능들을 가지고 있기 때문이다.

비즈니스 컴포넌트는 초점이 고 수준이다. 그것은 해당 어플리케이션의 요구사항에 제한되어 설계하는 경향을 항상 띠기 때문에 만들기가 더욱 어렵다. 그러나 조그만 더 앞서 생각하면 다른 어플리케이션에서도 재사용 될 수 있는 비즈니스 컴포넌트들을 설계할 수 있다.

예를 들면, 새로운 e비즈니스 어플리케이션에서 화면에 카탈로그 품목들을 표시하고, 선택품목을 쇼핑 바구니에 추가 하는 기능이 필요할 수도 있다. 쇼핑 바구니 관리자를 일반화로 설계하여 여러 서비스를 제공할 수 있는 컴포넌트로 만들면, 현재 개발중인 어플리케이션에서 가치 있게 쓸 뿐만 아니라, 아직 밑그림 단계에 있는 미래의 e비즈니스 어플리케이션에서도 쓸 수 있는 것이다. (이 예는 비즈니스 컴포넌트라기보다는 기술 컴포넌트를 범용으로 만든 정도라고 주장할 수도 있겠는데, 여기서는 단지 컴포넌트 설계 방법을 설명하려는 취지이다.)

기술 컴포넌트들은 개발 작업 속도를 획기적으로 높였다. 그러나 CBD가 IT 조직에 - 수십, 수백 배 생산성 향상으로 실질적으로 기여하려면 - 중요 비즈니스 컴포넌트 라이브러리를 개발하고 활발히 재사용해야 한다.


4. 컴포넌트에도 전략이 필요하다

 CBD로 소프트웨어를 개발할 때 전통적 개발 방법도 함께 써서 혼성 조립 프로세스가 되고 이것은 “소프트웨어 개발의 산업 혁명”으로 부르고 있다. CBD에 내재된 개념들은 다른 산업에서 성공한 품질 개선, 예측 가능한 생산들과 일맥 상통한 것이기 때문이다.
즉, 미리 생산한 표준 컴포넌트들을 획득하고 포함시켜 최종 제품을 구축하는 것이다


 

사용자 삽입 이미지


컴포넌트를 이용해 조립한 어플리케이션 부분이 “재사용” 성취 비율이다. 재사용이 많을 수록 생산성은 커지기 마련이다. 경쟁력이 높으므로 어플리케이션은 더 빨리 인도되고, 전통적 시스템에 비해 품질은 돋보이게 높아진다.

몇 년이 지나면, IT 조직들은 경제적 필요성 때문에라도 CBD로 넘어갈 수 밖에 없다. CBD가 비즈니스 컴포넌트 라이브러리를 이용하면서 충분한 시간을 두고 개발될 때, 큰 생산성 이득을 가져올 수 있다. 그에 더해, 실용 방법론을 채용하여 CBD를 구현한다면, IT는 “대규모 병렬 개발”을 이루어, 주요 어플리케이션의 큰 부분들이 순차로 생산되는 대신에, 동시에 생산할 수 있게 된다. 효과가 전체에 파급되어 생산성이 더욱 증가되면 가트너 그룹이 예측한 대로 다섯 배에서 열 배까지 제품 인도가 빨라질 것이다.

내부 IT 요원을 유지하면서, 고객 요구에 맞추어, 경쟁력 있게 솔루션을 생산하고자 하는 회사들은, 외주 개발비 이하로 생산하면서 경쟁사 보다 신속히 어플리케이션을 인도하려면 CBD가 필요할 것이다. 서비스 제공자들은 경쟁사보다 싸게 입찰에 응하기 위해 CBD가 필요할 것이고, CBD를 사용하지 않는 내부 요원들에게 더 저렴한 비용을 요구하는 목적으로도 CBD가 필요하다. 이런 시나리오로 나가면, 비즈니스 컴포넌트 라이브러리가 있는 서비스 회사는 전투 초기에 어플리케이션 완료 비율이 어느 정도 있으므로, 그 컴포넌트들을 지렛대로 요긴하게 이용할 수 있다. 즉, 출시는 더 신속히, 제품 품질은 더 높게, 비용은 더 낮게 제안 할 수 있게 된다. CBD 구현에 실패한 IT 조직들로서는 작업을 직접하지 못하고 다른 곳에 양도하지 않을 수 없게 될 것이다.

CBD는 또한 부족한 IT 기술자들을 지렛대로 이용할 수 있는 수단이기도 하다. 특별히 전문적 기술을 가지고 있거나 해박한 비즈니스 분야 지식을 가진 최고의 개발자들은 재사용이 가능한 컴포넌트들을 만들고, 다른 많은 개발자들은 그들의 작업 결과를 이용한다. 회사는 이런 재능 있는 사람들로부터 이익 최대화를 실현할 수 있다.

미들웨어 표준 출현과 함께, 컴포넌트 기반 언어와 기술이 도처에 증가하고 있고, 컴포넌트 산업은 초기 상태에 있으며, ERP 벤더와 선진 IT 회사들의 CBD 포용 움직임들 ? 이들 징후들을 볼 때 지나가는 유행으로서가 아닌, CBD에 대한 진지한 장기 투자가 필요하다. CIO들은 CBD의 본질에 친숙해질 필요가 있다. 첨단 회사들로서는 “미래는 바로 지금”the future is now” 이기 때문에 CBD기술의 파도를 결코 놓쳐서는 안 될 것이기 때문이다.


5. CBD는 장애를 극복해야 한다

 어떤 이들은 인센티브 프로그램 필요성에 대해 이의를 제기하기도 하는데, CBD 채널이 성공하려면 주요 요인을 식별할 수 있어야 한다.

컴포넌트 명세서를 문서화하고 회람하는 절차가 있어야 한다. 컴포넌트 개발자들은 무엇을 만들어야 좋은지 알 필요가 있고, 컴포넌트 관리자들로서는 어떤 컴포넌트가 현재 있는지 알 수 있어야만 그들이 이용 방법을 생각할 수 있다
 
 IT 조직은 리파지토리를 만들어 놓고 컴포넌트가 완료될 때마다 그 곳에 저장해야 한다. 컴포넌트는 키워드로 설명해 놓아야만 정교한 검색 방법을 쓸 수 있다. 문화 장벽과 비용 장벽이 극복된다 하더라도, 개발자들로서 필요로 하는 컴포넌트를 찾을 방법이 없다면, 그들은 계속 바퀴를 재 발명할 것이고, 중요한 재사용은 결코 이루어지지 않을 것이다. IT 조직이 라이브러리에 컴포넌트들을 많이 축적해 갈수록, 버전 관리 설비 못 지 않게 연구를 잘 하는 것도 매우 긴요해진다. 원격 개발 팀들이 협의할 수 있는 설비들도, 웹을 통해 원격 리파지터리에 질의할 수 있는 기능과 함께 중요하다.
 
컴포넌트들이 파생된 설계 모델들을 통합하는 방법을 강구해야 한다. 점점 더 가치 있는 자산은 코드가 아니라 설계임을 알게 되는데 이것은 자동화 대상인 비즈니스 프로세스와 역으로 맵핑이 가능하기 때문이다. 컴포넌트 명세서와 설계 모델을 통합시킬 수 있는 컴포넌트 도구들이 있다면 가치를 매우 높여주고, 시간 절감도 가져온다. 왜냐하면 기술 구현을 획득하는 것은 물론 그 뒤에 가려서 안 보이는 기업 비즈니스로 연결된 지적 자산도 획득할 수 있기 때문이다.
 
공지 시스템을 자동화할 필요가 있다 - 개발자들은 컴포넌트에 관한 관심사를 등록할 수 있고 새로운 버전이 이용 가능하게 될 때 이메일을 통해 즉시 알릴 수 있다
 
컴포넌트 관리 소프트웨어는 기술 지원을 독립적으로 해야 한다. CORBA, COM+, EJB와 같은 컴포넌트들은 모두 단일 컴포넌트 리파지토리 안에서 문서화나 관리할 수 있다.
 
많은 조직들은 이미 컴포넌트로 만든 프로그램들을 많이 가지고 있다. COM 기반의 Visual Basic 프로그램도 그 예다. 컴포넌트 리파지토리는 “끌어 놓기drag and drop” 를 지원해야 한다. 거기서 이들 컴포넌트들을 분석하고, 카탈로그 만들고, 라이브러리에 넣어, CBD 가 진행될 수 있도록 한다.
 
IT 조직들은 방법론과 조언자들 도움이 필요하다. 그들은 어떻게 컴포넌트 유산을 찾고, 어떻게 올바른 컴포넌트 설계를 하고, 어떻게 전체 어플리케이션 개발 공정에 CBD를 삽입할지 알 필요가 있다. 개발도구만 가지고는 이런 지식들을 전달할 수 없다. 그것은 사람만이 할 수 있는 일이다.
위의 열거한 리스트가 다는 아니다.
예를 들면, CBD 환경을 확장할 수 있어야만 매우 큰 모델과 큰 개발팀을 지원할 수 있다.
IT 조직들은 다양한 도구와 서비스로 이루어진 채널을 구축할 수 있게 되어서, 사용자들에게 CBD공정을 보여 주면서 접근할 수 있도록 해야한다.
 

6. CBD방법론? 실용적인 경로 채택으로 생존력을 길러야 한다

 인터넷 세상은 이미 컴포넌트 세상이다. IT 조직이 공식적인 CBD 프로그램을 가지고 있든 아니든 상관없이 명백히 소프트웨어 컴포넌트를 이용하여 일하고 있다. 자바나 비쥬얼베이직 언어들과, COM+와 EJB 표준들은 모두 컴포넌트 기반이다. 그러므로 컴포넌트는 더 이상 새로운 이론이 아니다. 이미 널리 보급되어 많은 곳에서 활발히 사용되고 있기 때문이다.

그러나 CBD 개념을 단순히 이해하는 것만으로는 IT 회사들로서 CBD 프로그램 구현에 성공할 수 없다. CBD의 성공 스토리는 아직 찾기 어려우므로 필요한 것은 점차 CBD로 안전하게 이동할 수 있는 방법이다.

CBD는 RAD, 객체지향 개발, 구조적 개발 등, 초기 기술들을 대체하는 것이 아니라, 이런 기술들의 장점을 이용하고 확장 시킨다. 조직들은 그들이 알고 있는 원칙들은 그대로 쓰면서, RAD에서 부족했던 엄격함과 CASE에서 성취할 수 없었던 신속히 돌아 오기(turnaround)와, OO와 UML 이론 중에서 실용적인 것들을 추가하면 된다.

프로젝트에 따라 일부만 CBD로 이동해 갈 수는 있다. 그러는 동안 컴포넌트 구조를 설계하는 일과 CBD 공정에서 개발하는 일들에 관한 본질 원리들을 배우고 또 가치를 평가할 수 있을 때, 그 다음 단계에서 본격적인 “도구 개조”에 착수하면 된다.


개발 공정은 세 가지 주 요소로 구성된다.


정렬 (Align)
설계 (Architect)
조립 (Assemble)


정렬 단계는 “올바른” 시스템이 보장되도록 IT 와 비즈니스를 동기 시켜 개발한다.
설계 활동의 최종 결과는 비즈니스 요구를 반영 해야 한다. 단지 방법론자들만 즐겁게 해 주는 예쁜 그림이어서는 안 된다.

설계 단계에서는 알맞게 세그먼트로 나누어 설계와 개발이 CBD로 잘 진행될 수 있게 한다.
미래 재사용에 쓰려고 핵심 씨앗을 생산적으로 뿌리는 시기는 바로 이 단계다.  이 단계에서 시간과 비용을 획기적으로 절약한다.

조립 단계에서는 UML, 다른 모델링 규율, 코드 생성, CBD 관리 기술을 적용하여 어플리케이션 증분을 최종 완료 시킨다.
요점은 실질 결과를 인도하여 비즈니스에 실질 가치를 더해주는 것이다. 기업 수준의 CBD라면, 조립 공정은 “공급”과 ”관리” 기능을 통합한다. 즉, 도구들을 이용하여 가용 컴포넌트를 찾고 통합하여 최종 비즈니스 솔루션을 만들어간다.
 


컴포넌트 기반 개발 방법은 이제 전성기에 접어들었다고 해도 과언은 아니다.
미들웨어 표준(CORBA, COM+, EJB) 는 이미 쓰이고 있고, CBD에 적합한 객체 지향 언어들은(Java, Visual Basic) 지금 첨단 e비즈니스 어플리케이션 개발에 널리 이용되는 도구이다. 기술 수준에서 본다면 컴포넌트를 사용하는 일은 표준 실무가 되었다.

조직 문화와 비용 문제들은 현실적인 것들이지만, 그것들은 또한 예상 할 수 있는 수준이다. CBD로 이동은 현재 진행중이다. CBD 적응에 실패한 회사들은 경쟁에서 치명적인 손실을 감수해야 될 것이다.

전성기에 접어들었다고는 하나 이는 어디까지나 이론상의 전성기일뿐 실제 프로젝트에 CBD개발방법론에 기초하여 완벽하게 진행되는 경우는 아주 드물다고 할 수 있다. (이는 여타의 다른 개발프로젝트에서도 마찬가지결과를 보인다)

CBD개발방법론 자체로서는 이론에 지나지 않는다.
이론을 구체화 하고, 실제 업무와 프로젝트에 손쉽고 빠르게 적용할 수 있는 도구를 사용하는 것이 좋다.

컴포넌트 기반 개발방법론은 몇 명의 프로젝트 책임자나 설계자에게만 필요한 것이 아니다.  전체 프로젝트에 참가하는 모든 개발자와 프로그래머가 참여할 수 있어야 하며 CBD개발방법론의 분석 및 설계단계 그리고 조립단계를 통털어 관리할 수 있는 특별히 개발된 별도의 도구가 필요하다.


Ø

기술과 응용 측면을 고려한 SOA 도입 방안

관심에 비해 도입 사례 적어… 컴포넌트 기술적 완성 필요

 



임 철 홍 | SK C&C SW공학센터 과장,
전자계산 조직응용 기술사

 

SOA(Service Oriented Architecture)에 대한 기업의 관심이 매우 높아지고 있지만 실제로 도입하기에는 조심스러운 모습을 보이고 있다. 계속 발전하고 검증되고 있으며 SOA를 위한 프로젝트들도 진행이 되고 있기에 머지 않아 SOA가 보편화 될 것으로 예상된다. 하지만 현재 SOA 실현을 위한 장벽이 여전히 존재하고 있다. SOA의 기술(Technology Architecture)과 응용(Application Architecture) 측면을 고려한 도입 방안을 기술했다.

 

기업 환경의 정보 시스템이나 PC에서 활용되는 주요 SW들은 개별적인 단위로 이루어진 프로그램들에 의해서 구성되고 있다. 이들 프로그램의 C/S환경은 업무 단위로, Web 환경은 페이지 단위로 주로 구성이 된다. 또한 개발 단위의 재사용 모듈(DLL)이나 공통 컴포넌트를 활용하며 유틸리티 또는 기본 API 성격을 연계하여 활용하고 있다.

SW 재사용성을 높이기 위한 시도는 현재까지 계속 되고 있으나 크게 성과를 얻지는 못하고 있다. 가장 큰 원인은 개발자 뷰(View)로 제작된 재사용 컴포넌트를 활용하기가 어렵다는 점과 컴포넌트 자체적으로 데이터베이스와 밀접한 연관성을 가지고 있기 때문에 컴포넌트 단위로 다른 정보 시스템에서 활용되기가 어렵다는 점이다. 따라서 사용자 뷰에서도 활용하기가 쉬우며, 데이터베이스에 대한 연관성을 최소화하기 위해서는 기존의 컴포넌트 단위를 비즈니스 단위로 확장한 서비스(비즈니스) 컴포넌트 구축과 이를 연계하는 형태로 구성이 돼야 한다. 이러한 비즈니스 단위의 컴포넌트를 메시지 형태로 연계하는 형태가 SOA이다.

Get Customer, Get Product와 같은 형태가 여러 레거시(Legacy) 시스템의 기능을 조합한 서비스 컴포넌트이며, 이들 서비스 컴포넌트는 특정한 기능을 수행하는 재사용성이 높은 기능 단위로 구성된다. SOA기반 애플리케이션은 이러한 서비스 컴포넌트를 표준 메시지(SOAP)로 연결하여 전체 애플리케이션이 구성된다.

 

SOA기반 애플리케이션의 구성

초기의 SOA 애플리케이션은 웹서비스를 포인트 투 포인트(Point-to-point) 형태로 연결 하는 구조의 형태(Primitive SO A)였다. 모든 연결이 노드(Node)가 증가 할 때마다 증가되어 복잡한 형태를 가졌으며, 기존의 서비스를 재사용 하는 관점에서는 발전적이었지만 이러한 서비스에 대한 유연성은 부족한 형태였다. Networked SOA의 경우는 초기의 형태에서 부족한 유연성과 연결의 복잡함을 해결하기 위해서 ‘Intermediary’를 두고 공통된 버스(Bus) 형태의 통합을 지원하는 형태로 발전하였다. Process-Enabled SOA의 경우는 공통된 버스 기반 위의 서비스들을 체계적으로 모델링 하고 실행하도록 지원하는 프로세스 오케스트레이션(Process Orches-tration) 기반 위에서 동작하는 형태이며, SOA-BPM이 결합된 환경이다.

 

TA(Technical Architecture)측면의 SOA

SOA-BPM기반의 SOA 애플리케이션 구축을 위해서는 레이어(Layer) 구조를 가지는 아키텍처의 구성이 필요하다. 표준화된 통합을 위해서는 웹서비스 기술이 활용되며, ESB와 BPM을 위해서는 프로세스 오케스트레이션 및 워크플로우 기술이 활용된다. 이외에 사용자 UI처리를 위한 X인터넷 및 엔터프라이즈 포털 솔루션이 활용된다.

 

AA(Application Architecture)측면의 SOA

TA측면의 SOA에서 살펴 본 것처럼, SOA 아키텍처는 개별의 서비스가 ESB와 같은 공통적인 Bus에 연결된 모습을 가지고 있다. 이러한 형태의 애플리케이션을 ‘Composite Component’라고 하며, SOA 애플리케이션은 Composite Component가 연동하여 작동하는 형태이다. 각각의 Composite Component는 여러 Service Component로 구성되어 있다. CBD방법론에서 중심적인 컴포넌트가 확장되어 서비스 컴포넌트로 구성된다. 서비스 컴포넌트의 단위는 기술적인 단위보다는 여러 컴포넌트의 조합을 통한 단위 비즈니스 수행이 가능한 형태이다.

SOA 애플리케이션 구축을 위해서는 현재의 업무를 프로세스로 구현하고 이를 다시 서비스 단위의 연결로 표현하는 ‘Ser-vice Decomposition’이 필요하다.

Composite Component 구현을 위해서 프로세스의 분석과 각 Service Compo-nent를 식별하는 작업이 필요하다. 이러한 설계 방법론은 기존의 CBD방법론을 근간으로 하여 확장 발전되었으며, 컨설팅에서 활용 되던 프로세스 모델링 기법과 연결되어 SOA 방법론의 형태로 발전하고 있다.

기업의 업무는 Value Chain으로부터 Mega Process, Process, Sub Process의 형태로 계층 구조를 가지고 있다. Service Component는 Sub Process단위와 같거나 Sub Process의 일부가 될 수 있다. 범 정부 EA에서 정의 하고 있는 서비스 레퍼런스 모델(Service Reference Model)에서도 비슷한 계층 구조를 가지고 있다. 아래 그림은 이러한 계층 구조간의 관계를 기술하고 있다.



 

SOA 도입 방안

SOA 도입을 위해 처음부터 빅뱅 방식에 의한 접근은 다소 위험성이 존재한다. 그 이유로는 베스트 프랙틱스(Best Practice)가 부족한 점과 내부적인 업무를 서비스 컴포넌트화 하는 부분이 단기간 내에 가능한 부분이 아니기 때문이다. 따라서 SOA 도입을 위해서는 단계적인 접근이 필요하다. 초기에는 인터페이스 표준화를 통해 내 외부적인 통합을 중심으로 SOA를 도입하는 것이 바람직하며, 점차적으로 공통 서비스 컴포넌트 구축으로 적용해 나가는 것이 필요하다.

 

- 인터페이스 표준화 (Technical Architecture 중심)

기업 내 외부적인 통합을 위한 환경은 수많은 프로토콜과 표준에 의해서 수행되고 있으며, 실시간적인 처리가 되고 있지 않다. 또한 중앙 집중적인 관리가 되고 있지 않으며, 포인트 투 포인트 연결에 따른 시스템 구성상의 복잡함이 존재하게 된다. SOA를 활용한 인터페이스 표준화는 기존의 EAI와 같은 역할을 하면서 표준화된 메시지(SOAP)에 의한 통합을 수행하는 방식이다. 현재 많은 정보 시스템에서 활용되고 있는 것은 다음과 같은 방법이다. 대부분 DB를 활용하여 Batch에 의존하여 연동하고 있으며, Socket을 활용한 전문 형태의 인터페이스도 많이 활용되고 있다. 이 경우 각 연동 방식에 대한 표준이 없으므로 Case-By-Case로 구축 및 운영되어야 하는 문제점이 발생된다.

SOA 기반 인터페이스 통합을 수행하기 위해 중요한 요소로써 ESB가 있다. ESB는 EAI와 비슷한 역할을 수행하며, 웹서비스 표준을 활용하여 SOA의 관점에서 EAI/B2Bi를 수행하는 미들웨어이다. ESB는 웹서비스 및 어뎁터(JCA 등)를 활용해 내외부 서비스의 연계가 가능하다. <그림6>은 EAI/ESB를 활용하여 다양한 프로토콜 기반의 인터페이스를 표준화 하여 내외부 시스템간의 연동을 지원하는 사례이다. 일반적으로 내부적인 통합을 지원하는 EAI와 대외 채널을 통합하는 MCI(Multi Channel Integration)을 활용하여 인터페이스를 이원화 하는 방안도 많이 활용 되고 있다.



 

- 공통 서비스 컴포넌트 활용(Application Architecture 중심)

인터페이스 표준화는 SOA를 지원하는 기술 아키텍처를 구축하고 내외부적인 통합을 수행하는 방식이다. 개별적으로 존재하는 레거시나 외부의 시스템들과 연계하는 측면에 관점을 두고 있다. 서비스 컴포넌트 구축은 기업의 애플리케이션을 유연하고 재사용성을 높일 수 있도록 컴포넌트 형태로 구축하고 BPM(BPEL)을 통해 프로세스 형태로 구축하고 활용하는 형태다. 기본적인 목적은 CBD와 비슷하지만 이를 비즈니스적인 관점으로 확장한 점이 다르다.

공통 서비스 컴포넌트는 ESB나 Pro-cess Orchestration에 의해서 Composite Component의 형태로 구축되게 되며, 업무 변경이나 환경 변화에 유연하게 대처 가능하다. 또한 기존의 애플리케이션과 유사한 애플리케이션을 구축할 때 중복적인 개발을 막을 수 있고, 공통 컴포넌트에 대한 집중적인 관리를 통해서 효율성을 높일 수 있다.

서비스 컴포넌트들은 프로세스에 따라 조합이 되어 애플리케이션이 완성(앞에서 Composite Component로 설명)되게 된다. 이들은 서로 Loosely Coupling으로 연결되어 있어 유연성이 높다.

다음 그림은 일반 기업에서 활용되는 계약 관리 프로세스를 서비스 컴포넌트를 활용하여 구축한 예이다. CRM, ERP등 개별 애플리케이션에서 활용되는 컴포넌트를 조합하여 서비스 컴포넌트를 구축하고 활용하고 있다. ESB를 활용하여 개별 컴포넌트를 관리하고 연계하여 Composite Component를 구축하게 된다.

Composite Component를 구축하기 위하여 SOA 방법론에서는 서브 프로세스를 구성하는 요소에서 유스케이스를 식별하고 이러한 유스케이스 단위를 서비스 컴포넌트 단위로 식별 하게 된다. 식별된 서비스 컴포넌트는 내부적인 컴포넌트를 식별하여 구별하게 되며 CBD에서 활용되던 방식을 활용하여 설계 및 구축을 수행한다. SOA에서의 애플리케이션 아키텍처는 기존의 방법론과는 달리 기업의 비즈니스 프로세스 및 목표와 직접적인 연관을 가지게 된다. SOA가 비즈니스와 기술의 교차점이 되고 두 가지 요소를 모두 포함하고 있는 모습이 여기에 있다.

 

SOA 이슈 사항

SOA를 구현하기 위한 기술적인 관점에서의 완성도는 높지만, 실제로 구성되는 컴포넌트의 내용을 만들기 위한 기술은 상대적으로 완성도가 낮다. 많은 기업들이 SOA에 관심을 가지고 있으나 실제적인 도입에는 조심스러운 모습이다.

 

SOA 활용을 위한 10가지 제언

1. Divide & Conquer - 가장 재사용도가 높은 비즈니스를 중심으로 순차적으로 컴포넌트를 구축하고 활용 하는 것이 중요하다.
2. Not Solution - 솔루션에 의해서 구축되는 부분보다는 현재의 업무에 기반하여 모델링 되는 부분이 중요하며, SOA를 지원하는 전용 솔루션은 존재 하지 않으며, 여러 솔루션들이 유기적으로 통합되고 동작하도록 하는 것이 SOA 이다.
3. Business & Technology - SOA전문가는 기술 전문가의 관점과 비즈니스 전문가의 관점을 모두 가져야 하며 서로 협력해야 한다.
4. Standardization - 인터페이스 및 구축을 위한 기술을 표준 기술을 수용하여 사용 한다.
5. Invest - SOA를 위한 초기 투자 비용이 많이 들지만, 향후의 재사용을 통해 효과를 보장.
6. Keep Up - 단기간에 구축하여 효과를 보기 어려우며, 지속적인 추진과 노력을 필요로 한다.
7. Not New, Not Magic - SOA가 이전의 기술과 상관없이 갑작스럽게 등장한 개념이 아니며, 이전의 기술을 바탕으로 발전시킨 개념이다.
또한 SOA를 통해 현재의 문제점이 그냥 해결되는 마법이 아니며, 표준화와 체계화를 지향하는 아키텍처 이다.
8. Business Goal - 비즈니스의 Goal과 정보 시스템을 연계하여 비즈니스 목표에 맞도록 Revision하고 성과를 측정, 평가 한다.
9. SOA governance - SOA추진을 위한 별도의 통합 조직과 역할을 필요로 한다.
10. Catch Up - SOA 기술과 솔루션이 빠르게 변화 하고 있으므로, 변화에 유연하게 대응하고 수용해야 한다.


제공 : DB포탈사이트 DBguide.net

출처명: 경영과컴퓨터 [2006년 8월호] 

 


2.SDLC(생명주기)
3.SW 개발 모델
4.개발방법론
5.객체지향방법론
6.CBD
7.SW 테스트 단계
8.SW 테스트 방법
9.유지보수
10.위험관리
11.아웃소싱 
q

개발 방법론의 이해와 배경

계속해서 새로운 비즈니스 영역이 창출되고 새로운 비지니스가 산업 전반에 적용되면서 사용자(Stakeholder)들에게 보여지는 물리적인 시스템인 정보 시스템의 중요성이 강조되고 있다이에 비지니스 패러다임은 계속해서 복잡해 지고 이에 맞는 환경에 적응(Time to Market) 해야 할 필요가 있어 조금씩 진보된 개념들의 방법론들이 계속해서 만들어지고 있다또한 최근에 인터넷 비지니스에 대한 부분이 강화되면서 초기 임기응변적으로 구축되거나 과정보다는 결과를 우선하여 진행하는 시스템 구축되었던 시스템들이 대형화되면서 잘못된 결과들을 야기하면서 막대한 원가 손실과 고객 불만들이 가중되고 있어 방법론에 대한 관심은 더더욱 높아가고 있는 상황이다.


어떻게 하면 비용을 적게 들이면서 빠르게 좋은 소프트웨어를 만들까에 대한 고민에 대한 이야기 

방법론이란?
사용자(Stakeholder:이해당사자요구 사항을 정확히 파악하여 체계적이고 논리적으로 수행하는 처리 절차작업 방법산출물적용 기법,적용 도구를 기술하는 것이 방법론이다방법론을 잘 활용하는 것은 시스템 구축시 개발자들이 방법론을 이해하고 참조하며 시스템의 계획분석설계구현운영의Life Cycle을 따라 정보 시스템을 수행하는 것이며 이는 시스템 개발의 이론적 기반이 된다

처리 절차프로젝트 수행의 작업 단계의 쳬계

작업 방법각 단계별 작업의 구체적인 설명

산출물    단계별 산출물 목록

적용 기법 - 단계별 작업 수행시 소요 기술 또는 기법

적용 도구 - 단계별 필요한 Tool방법론을 표준화하고 개발 주기(Life Cycle)에 적용함으로써 개발 시스템의 품질 및 생산성을 높이는 수단       

방법론은 축척된 경험, KnowHow , 문서화 및 정리된 작업들로 구성이 된다.


방법론의 필요성

시스템 구축과 개발은 Software, Hardware, System Software, Network, DBMS등 모든 시스템의 기술 요소가 결합된 형태로 이루어지는 복잡한 작업이다다음과 같은 이유에서 방법론이 필요하다.


빠른 변화와 다양한 요구로 인해 비지니스 환경에 대응되는 유연한 Sytem 구축하기 위하여

최신의 IT기술을 시스템 구성 전략에 반영하기 위하여

비지니스 시스템 구축시 구성원간의 의사 소통의 표준화하기 위하여

개인의 경험이나 능력에의 의존도를 줄이기 위하여

다양한 능력과 위치의 사람을 효과적으로 시스템 구축에 참여시키기 위하여

 


방법론들의 변화의 동향 및 특성

구조적 방법론 (Structured Methodology)

1950년대 구조적이지 못한 무원칙의 개발 방식은 개발자의 사고능력에 따라 개발되었으며 Logic 구성 또한 개발자 자기 중심적이다이는 개발 생산성의 저하와 유지 보수의 어려움 등의 문제점을 발생시켰으며 결국 Software 위기를 불러 왔다이에 1960년대 GOTO문을 쓰지 말고 구조적으로 프로그래밍하자는 주장이 대두됨으로써 분석과 설계도 구조적으로 하자는 의견이 확대되었고 구조적 방법론의 틀이 완성되었다구조적 방법론은 다음과 같은 특징을 가진다.

특징

한계

기능과 프로세스 중심의 분석과 설계

모듈의 분할정복에 의한 설계 방법

개발 목적이 프로세스와 산출물의 구성

자료 흐름도,자료사전,단위명세서의 자료 중심

데이터 모델링 방법이 미흡

설계와 코딩을 강조자동화 도구 부족 
문제 해결단위가 소규모 ,자체적인 프로세스 변화가 너무 많음(데이터는 변화가 적은 반면)


정보 공학 방법론(Information Engineering Methodology)
80
년대를 거치면서 경영 정보 시스템(MIS),의사 결정 지원 시스템(DSS),임원 정보 시스템(EIS),전략정보시스템(SIS)등의 다양한 형태로 발전하게 되었고 이는 비지니스 시스템의 IT적 관점에서 보면 데이터베이스네트워크 등 다양한 시스템 구성요소의 결합을 의미하게 되었다.시스템의 규모화 복잡도 증가에 따라 체계적인 진행관리가 필요하게 되었고 이에 80년대 중반 이후 James Martin에 의해 정보공학(Information Engineering)의 체계가 정리된 정보공학 방법론이 본격적으로 활용되었다정보 공학 방법론은 다음과 같은 특징을 가진다

특징

한계

데이터와 프로세스의 상관 분석(CRM)

목표시스템은 개별 소프트웨어가 아닌 기업에서 사용되는 비지니스 시스템

정보전략 계획(ISP)작업이 포함되어 있음

데이터 중심의 분석과 설계 및 사용자 참여

CASE (Computer Aided Software Engineering)

분석과 설계의 연계 부족

요구 분석의 리스크는 여전히 존재

효과적인 개발을 위해서 시간이 많이 투자됨

소규모 독립된 시스템에는 부적합함

1980년대 중반 이후 정보공학 방법론은 대규모 비지니스 시스템의 개발에 가장 적합한 방법론으로 인정


객체지향 방법론 (Object Oriented Methodology) 

추상화캡슐화상속정보은닉 등 객체를 중심으로 프로그래밍 구조를 단순화하고재사용성을 강화하는 객체지향 프로그래밍 
방식이러한 원칙이 분석과 설계에 확대되면서 탄생했다.

이전 방법론에서 프로세스와 데이터가 분리되어 처리됨으로써 분석과 설계단계에서 개발단계로 연계 시키는데 많은 문제가 있었다면 객체지향 방법론은 이러한 부분에서 많이 진화된 방법론이다객체지향 방법론은 다음과 같은 특징을 가진다.

특징

한계

재사용성이 높고

안정성 객체단위의 자연스러운 설계

클래스를 통한 시스템의 복잡성 극복

신뢰도가 높고 무결성

새로운 소프트웨어 시장 형성 신속한 설계

신속한 설계 높은 품질의 설계도출

프로그래밍 개발 용이성유지보수 용이성 동적인 소프트웨어 생명 주기 보다 실제적 실감적 모델링 개발자와 관리자 간의 효율적이 의사 소통의 진보된 기법 모델

분석과 설계의 연계 부족

요구 분석의 리스크는 여전히 존재

효과적인 개발을 위해서 시간이 많이 투자됨

소규모 독립된 시스템에는 부적합함

 

- CBD 방법론 (Component Based Development or Design)

e-Business 와 더불어 소프트웨어의 수요 폭주하고 소프트웨어의 대형화복잡도 증가하고 객체지향의 개발 방법의 한계성 극복과 재사용성의 극대화하기 위해 탄생했다.S/W 컴포넌트의 재사용성과 독립성을 보장하여 S/W 복잡성과 생산성을 향상시키기 위해 컴포넌트의 생산선택평가 및 통합으로 구성되는 새로운 개발 패러다임이다. ( 소프트웨어도 하드웨어처럼 조립 해보자. )

          

특징

한계

실행코드를 기반으로 재사용
용도역할기술 표준인터페이스에 대한 정보 준비

교체 가능한 Component를 개발하기 위해 표준 분수

배포 시 관련 문서와 코드들이 독립적인 단위로 패키지화 되어 제공

비지니스 컴포넌트가 사실상 재사용 어려움

컴포넌트 크기아키텍처 스타일인터페이스 명세 등 표준화 미흡

J2EE, .NET, COM 등 주요 표준 컴포넌트 환경의 벤터 종속적

컴포넌트 Repository/Registry 등 공유체계 및 COST 등의 유통 구조가 추상적

비지니스 프로세스 지원에 적용하기 어려움

경영층 및 개발자의 지식 및 경험 부족에 의한 거부감

 


그래서 방법론은 시스템 구축의 표준화 도구이다

오프라인 과의 연계사업의 다각화콘텐트 연계시스템의 다양화 및 대형화도 동시에 진행되었다. E-biz 시대에서는 시스템 구축의 속도가 중요한 요인최초 시스템 구축 시에 시스템 확장에 대한 고려를 하지 않음시스템 구축단계에서 계획대로 실행을 한다면 문제점들을 많이 줄일 수 있을 것표준화된 규칙을 제공하는 것이 방법론이다

 

정리하자면

위와 같이 근 40년간 진보된 개념의 방법론들이 만들어져 왔다앞으로 계속해서 다양한 비지니스 환경의 복잡성은 지금보다 더 심해질 것이고 사용자들의 요구 사항도 지금보다 더 다양해 질 것이다어떠한 방법론이 맞느냐어떠한 방법론이 좋은 것이냐에 대한 논란은 끊이지 않겠지만 지금 내가 처한 환경에서의 가장 알맞은 것들을 편취해서 가장 빠른 시간에 질 좋은 소프트웨어를 사용자들의 요구사항에 맞게 만들 수 있다면 그것이 가장 좋은 방법론 이라고 생각한다.

개발/자바 | Posted by 은우 아빠 2009. 2. 5. 09:47

주민등록 검사의 원리


출생 년도 (YY = 00 - 99 사이의 값) 
      +--+ 
      |   |    출생월 (MM = 01 - 12 사이의 값) 
      |   |    +--+ 
      |   |    |   |   출생일 (DD = 01 - 31 사이의 값) 
      |   |    |   |   +--+ 
      |   |    |   |   |   |       남,여 구분 (1, 3 = 남자, 2, 4 = 여자 )  
      |   |    |   |   |   |           | 
      Y   Y   M   M   D   D   -   X                                    C 
  +------------------------------------------------------------------+ 
  |   0   1   2    3    4   5         6   7   8   9   10  11         12    | index 
  |   6   6   0    5    1   0   -    1   3   2    1   6    1            1    | data 
  |   2   3   4    5    6   7         8   9   2    3   4    5          KEY  | mask 
  +------------------------------------------------------------------+ 
(11- {12+ 18+ 0+  25+ 6+  0+      8+  27+ 4+  3+  24+ 5}%11)% 10 == 1 

* 주민등록 번호 검사 비트 검사 방법 

     1) 주민등록 번호에 앞에서부터 2,3,4,5,6,7,8,9,2,3,4,5를 차례로 곱한다.  {0..11}%8+2 
     2) 각각의 값을 모두 더한다.                                                                  sum 
     3) 이 합을 11로 나눈다. 그러면 나머지는 0,1,2,3,4,5,6,7,8,9,10 중의 하나이다.    
     4) 11에서 나머지를 뺀다.                                               
     5) 이때 나머지 값이 10을 넘으면 10를 빼고,                                           (11-sum%11)%10 

          그렇지 않으면 나머지 값이 주민등록 검사 비트이다. 

  

예) 660510-1321611 

     6 * 2 + 6 * 3 + 0 * 4 + 5 * 5 + 1 * 6 + 0 * 7 + 1 * 8 + 3 * 9 + 2 * 2 + 1 * 3 + 6 * 4 + 1 * 5 = 132  : 합계 
     132 % 11        = 0   : 나머지값 
     (11 - 0) % 10  = 1  : 결과값 
     따라서 키 플래그는 1이다.      그리고 마지막 자리의 체크플래그는 1이다. 
      
     키플래그 == 체크플래그  
개발/자바 | Posted by 은우 아빠 2009. 2. 4. 15:27

jaxp를 이용한 xmlutil




jaxp를 이용하여 xml에 element 추가, attribute 추가, 제거등에 관련한 util를 제공한다.
그리고 작성한 document는 makeFile 함수를 사용하여 파일로 생성이 가능하다.
XmlUtil.java xml을 관리하는 util 파일이고
DDFileUtil.java는 XmlUtil.java를 이용하여 실제 xml을 컨트롤한 예제이다.

package com.gamizi.util;

import java.io.File;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * Xml 파싱관련 클래스
 * @author 큐앤솔브 : 강성현
 *
 */
public class XmlUtil {
public XmlUtil(){
}
/**
* xml 파읽을 읽어들여 document 를 생성한다.
* @param filePath 파일 경로
* @param fileName 파일명
* @return
* @throws Exception
*/
public Document parse(String filePath, String fileName) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
factory.setNamespaceAware( true );
factory.setValidating( true );
Document doc = builder.parse(new File(filePath+fileName));
return doc;
}
/**
* 소스의 Element로 부터 key에 해당하는 자식node를 Element로 형전환하여 반환한다.
* @param srcElement 조회를 원하는 element
* @param key 자식 로드 element
* @return
*/
public Element getElement(Element srcElement, String key){
Element retEle = null;
NodeList ddNodeList = srcElement.getChildNodes();
for(int i = 0 ; i < ddNodeList.getLength() ; i++){
Node node =srcElement.getChildNodes().item(i);
String nodeName = node.getNodeName();
if(key.equals(nodeName)){
retEle = (Element)node;
}
}
return retEle;
}

/**
* Element를 추가한다.
* @param srcElement 추가를 원하는 소스의 element
* @param document //document 
* @param elementName //추가를 원하는 element 이름
* @param removeFlag //기존에 elementName 유무에 따라 Y이면 삭제, N이면 element 계속 추가.
*/
public void addElement(Element srcElement, Document document, String elementName, 
boolean removeFlag){

if(removeFlag){
//삭제할 element를 찾는다.
//조회할 element에 element가 없다면 null을 리턴
Element removeElement = getElement(srcElement,elementName);
if(removeElement!=null){
removeElement(srcElement,removeElement);
}
}
Element addElement = document.createElement(elementName);
srcElement.appendChild(addElement);
//
}

/**
* parentsElements 에 elementName으로 element를 추가 후
* key와 value를 이용하여 attribute를 추가한다.
* removeFlag를 이용하여 Y이면 기존에 elementName 있으면 해당 element를 삭제한다.
* @param parentsElements
* @param document
* @param elementName
* @param key
* @param value
* @param removeFlag
*/
public void addElementAttribute(Element parentsElements, Document document, String elementName, 
String key, String value, boolean removeFlag){
addElement(parentsElements,document,elementName,removeFlag);
addAttribute(getElement(parentsElements,elementName),key, value);
}
/**
* 소스의 요소중 key값에 해당하는 요소가 있는지 조회
* @param srcElement
* @param key
* @return
*/
public boolean isChildElement(Element srcElement, String key){
boolean retFlag = false;
NodeList ddNodeList = srcElement.getChildNodes();
for(int i = 0 ; i < ddNodeList.getLength() ; i++){
Node node =srcElement.getChildNodes().item(i);
String nodeName = node.getNodeName();
if(key.equals(nodeName)){
retFlag = true;
}
}
return retFlag;
}
/**
* @param srcElement
* @param removeElement
*/
public void removeElement(Element srcElement, Element removeElement){
if(isChildElement(srcElement,removeElement.getTagName()))
srcElement.removeChild(removeElement);
}
public void addAttribute(Element srcElement, String key, String value){
srcElement.setAttribute(key, value);
}
public void makeFile(Document document, String fileName, String encoding, String docTypeSystem) throws Exception{
//..변환기 생성
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
//..출력 포맷 설정
transformer.setOutputProperty(OutputKeys.ENCODING, encoding);
transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, docTypeSystem);
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
//..DOMSource 객체 생성
DOMSource source = new DOMSource(document);
//..StreamResult 객체 생성
StreamResult result = new StreamResult(new File(fileName));
//..파일로 저장하기
transformer.transform(source, result);
}
}



명령행 매개변수로 전달된 상품 정보를 가지고 XML 문서(product.xml)를 생성하는 Java Application 과 만들어
진 XML 문서를 DB 테이블에 CLOB 형으로 저장하는 Java Application 을 작성하세요. XML 문서를 만드는 과정
은 비어 있는 Document 객체에 노드들을 추가하는 방식을 활용하십시오.

[ 명령행 매개변수로 입력하는 상품 정보 ]
상품ID, 상품명, 가격, 

   다음과 같은 구조로 XML 파일이 생성되도록 구현한다.
<PRODUCTLIST>      
    <PRODUCT> 
          <ID>...........</ID> 
          <NAME>...........</NAME> 
          <PRICE>...........</PRICE> 
    </PRODUCT> 
   </PRODUCTLIST>


- 저장되는 DB 테이블 스펙
    테이블명 : xmltable
    필드 스펙 : productid   char(5),   xmlcontent clob

  - DB 필드의 CLOB 형 필드에 product.xml 을 저장하는 기능은 PreparedStatement 의 setCharacterStream() 
이라는 메서드를 사용한다.




com.noaats.xml000.CreatXML.java

===================================================

package com.noaats.xml000;

import java.io.StringReader;
import java.io.StringWriter;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.util.Properties;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Text;


public class CreateXML {

 private Document xml;

 private String jdbcDriver = "oracle.jdbc.driver.OracleDriver";

 private String jdbcUrl = "jdbc:oracle:thin:@localhost:1521:XE";

 private String userName = "scott";

 private String userPwd = "tiger";
 
 private String insertSql = "insert into xmltable values ( ?,?) ";

 /**
  * @param args
  *            상품정보를 입력 받는다. args[0] id args[1] name args[2] price
  */
 public static void main(String[] args) {
  CreateXML cxml = new CreateXML(args);
 }

 public CreateXML(String[] args) {
  process(args);
 }

 protected void process(String[] args) {
  if (args.length == 0) {
   System.out.println("Usage : CreateXML ");
  } else {
   Element product = createXMLDocument();
   createID(product, args[0]);
   createName(product, args[1]);
   createPrice(product, args[2]);
   saveXML(args[0], product);
  }
 }

 private void saveXML(String id, Element product) {
  Connection conn = null;
  PreparedStatement ps = null;
  try {
   Driver driver = (Driver)Class.forName(jdbcDriver).newInstance();
   conn = DriverManager.getConnection(jdbcUrl, userName, userPwd);
   
  } catch (Exception sqle) {
   sqle.printStackTrace();
  }

  TransformerFactory factory = TransformerFactory.newInstance();
  try {
   StringWriter sw = new StringWriter();
   Properties output = new Properties();
   output.setProperty(OutputKeys.INDENT, "yes");
   Transformer transformer = factory.newTransformer();
   transformer.setOutputProperties(output);
   transformer.transform(new DOMSource(xml), new StreamResult(sw));
   System.out.println(sw.getBuffer().toString());
   ps = conn.prepareStatement(insertSql);
   ps.setString(1,id);
   ps.setCharacterStream(2,new StringReader(sw.getBuffer().toString()),sw.getBuffer().toString().length());
   ps.execute();
   ps.close();
   conn.close();
  } catch (Exception e) {
   e.printStackTrace();
  } finally {
   try {
    if (ps != null) ps.close();
    if (conn != null ) conn.close();
   } catch (Exception ee) {
    ee.printStackTrace();
   }
  }
    
 }

 private void createPrice(Element product, String price) {
  Element priceElement = createElementWithValue("PRICE", price);
  product.appendChild(priceElement);

 }

 private void createName(Element product, String name) {
  Element productElement = createElementWithValue("PRODUCT", name);
  product.appendChild(productElement);
 }

 private void createID(Element product, String id) {
  Element idElement = createElementWithValue("ID", id);
  // Text idTest = xml.createTextNode(id);
  // idElement.appendChild(idTest);
  product.appendChild(idElement);
 }

 private Element createElementWithValue(String elementName,
   String elementValue) {
  Element element = xml.createElement(elementName);
  Text textNode = xml.createTextNode(elementValue);
  element.appendChild(textNode);
  return element;
 }

 protected Element createXMLDocument() {
  Element product = null;
  DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
  try {
   DocumentBuilder builder = factory.newDocumentBuilder();
   xml = builder.newDocument();
   Element productlist = xml.createElement("PRODUCTLIST");
   xml.appendChild(productlist);

   product = xml.createElement("PRODUCT");
   productlist.appendChild(product);

  } catch (Exception e) {
   e.printStackTrace();
  }
  return product;
 }

}

출처 : http://blog.bagesoft.com/794

개발/자바 | Posted by 은우 아빠 2009. 2. 3. 19:26

JAXP


XML의 소개

Extensible Markup Language (XML)은 웹 브라우져를 통해 한 페이지의 내용 요소들을 구분하는 방법입니다. XML의 문법은 HTML과 비슷합니다. 사실, XML은 HTML이 쓰이는 여러 곳에서 사용될 수 있습니다. 아래에 예가 있습니다. JDC Tech Tip의 목차가 HTML이 아니라 XML로 저장되어 있다고 가정합시다. 아래와 같은 HTML 코드 대신

<html>
<body>
<h1>JDC Tech Tip Index</h1>
<ol><li>
<a
href="http://developer.java.sun.com/developer/TechTips/2000/tt0509.html#tip1">
Random Access for Files
</a>
</li></ol>
</body>
</html>

아래와 같이 나타납니다.

<?xml version="1.0" encoding="UTF-8"?>
<tips>
<author id="glen" fullName="Glen McCluskey"/>
<tip title="Random Access for Files"
     author="glen"
     htmlURL="http://developer.java.sun.com/developer/TechTips/2000/tt0509.html#tip1"
     textURL="http://developer.java.sun.com/developer/TechTips/txtarchive/May00_GlenM.txt">
</tip>
</tips>

XML과 HTML의 코드 사이의 유사성을 보십시오. 두가지 모두 문서가 계층적 엘리먼트로 구성되어 있으며, 얼레먼트는 꺾음 괄호로 구분되어 있습니다.HTML 엘리먼트가 거의 그렇듯이, XML 엘리먼트는 시작 태그와 그 뒤에 나타나는 데이터, 그리고 끝 태그로 구성되어 있습니다.

<element>element data</element>

역시 HTML과 마찬가지로, XML 엘리먼트는 속성을 지정할 수 있습니다. 위의 XML의 예에서, 각 <tip> 엘리먼트는 몇개의 속성을 갖고 있습니다. 'title' 속성은 팁의 이름을 나타내며, 'author' 속성은 저자의 이름을 나타냅니다. 그리고 'htmlURL'과 'textURL' 속성은 두가지의 다른 형식으로 저장된 팁의 링크를 나타냅니다.

두개의 마크업 언어 사이의 유사점은 HTML에서 XML로 옮겨가는데 아주 중요한 장점입니다. 어렵게 습득한 HTML 기술들을 그대로 이용할 수 있기 때문입니다. 그러나, "왜 XML로 바꾸라고 해서 고생하게 만들지?"라는 의문이 들것입니다. 이 의문에 대한 대답으로, 위의 XML 예를 다시 한번 보십시오. 그리고 문법적인 측면이 아니라 의미적인 측면에서 살펴보십시오. HTML은 문서를 어떻게 형식화하는지를 말해주지만, XML은 문서의 내용에 대해 말해줍니다. 이 능력은 매우 강력한 것입니다. XML에서, 클라이언트는 그 데이터를 자신에게 가장 적합한 형식으로 재구성할 수 있습니다. 서버가 제공하는 출력 양식에 구애받지 않습니다. 중요한것은, XML 형식은 가독성을 희생시키지 않으면서도 파서의 편의를 위해 설계되었다는 것입니다. XML은 문서의 구조에 대해 반드시 지켜야 할 약속을 가지고 있습니다. 몇가지를 보자면, 시작 태그는 언제나 끝 태그를 가지고 있어야 하며, 엘리먼트는 정확히 내포관계를 가져야 하고, 모든 속성은 값을 가져야 합니다. 이런 엄격함은 XML 문서의 파싱과 변환을 HTML의 변환에 비해서 높은 신뢰성을 갖게 합니다.

XML과 HTML의 유사성은 그 시작이 갖기 때문에 생깁니다. HTML은 강력한 마크업 언어인 SGML을 간략화한 것입니다. SGML은 개발자가 자신만의 어휘, 즉 문법/태그 등을 만들어낼 수도 있는, 거의 무엇이든지 할 수 있는 "주방의 싱크대"같은 마크업 언어입니다. HTML은 미리 정의된 어휘를 가진, SGML에 대한 아주 작은 부분입니다. 따라서 HTML은 대략 1992년 경에나 쓸만했던, 표현을 위한 요소들입니다. SGML과 HTML은 모두 문제가 있습니다. SGML은 모든것을 합니다. 그래서 아주 복잡합니다. HTML은 간단합니다. 그러나 파싱 규칙이 엉성하고, 어휘는 확장을 할 수 없습니다. 이에 비해 XML은 SGML을 합리적으로 간략화했습니다. 이것의 목적은 복잡하지 않으면서도 SGML의 중요한 목적을 모두 지원합니다. SGML이 "주방의 싱크대"라면 XML은 "스위스 아미 나이프-맥가이버칼... -_-" 입니다.

이런 장점 때문에, XML은 몇가지 응용 분야에서는 HTML을 대체하는것 이상의 일을 할 수 있습니다. 이것은 SGML을 대체할 수도 있으며, SGML의 복잡성 때문에 적용이 불가능했던 분야에 대한 새로운 방법이 될 수 있습니다. XML을 어디에 쓸 것인지에 관계없이, 프로그래밍 언어는 자바를 이용할 것입니다. 자바 언어는 Simple API for XML (SAX)와 Document Object Model (DOM) 인터페이스를 이용하여 XML 문서를 파싱하기 위한 고수준의 도구를 제공하기 때문에, XML을 직접 파싱하기 위한 자신만의 코드를 작성할 수 있습니다. SAX와 DOM 파서는 다른 몇가지 언어에서도 구현한 표준입니다. 자바 프로그래밍 언어에서, Java(tm) API for XML Parsing (JAXP)를 이용해서 이런 파서들을 인스턴스화 할 수 있습니다.

이 팁의 코드를 실행하려면, http://java.sun.com/xml/download.html에서 JAXP와 SAX/DOM 파서를 다운로드 받아야 합니다. 그리고 http://www.megginson.com/SAX/Java에서 SAX 2.0도 다운로드 받아야 합니다. 그리고 classpath를 jaxp, 파서, 그리고 sax2의 JAR 파일을 포함하도록 고치는 것을 잊지 마십시오.

 


 

 SAX API의 이용

SAX API는 XML 문서를 다루기 위한 직력 메커니즘을 제공합니다. 이것은 XML-DEV 메일링 리스트의 멤버들에 의해 서로 다른 벤더들이 구현할 수 있는 표준 인터페이스의 집합으로 개발되었습니다. SAX 모델은 파서가 문서를 읽어나가면서 마크업을 만나면 이벤트 핸들러를 호출하는 형식으로 파싱하도록 합니다. SAX의 첫번째 구현은 1998년 5월에 나왔습니다. 그리고 SAX 2.0은 2000년 5월에 나왔습니다. (이 팁에서 사용된 코드들은 SAX2용입니다.)

마크업 발생에 대한 신호를 위해 SAX2를 사용할 때 할 일은, 몇개의 메소드와 인터페이스를 작성하는 것입니다. 이 인터페이스들에서 ContentHandler 인터페이스가 가장 중요합니다. 이것은 XML 문서를 파싱하기 위한 각각의 단계들을 위한 몇개의 메소드를 선언합니다. 많은 경우, 이런 메소드 중에서 몇개만 이용하게 됩니다. 예를 들어, 아래의 코드는 하나의 ContentHandler 메소드 (startElement)만을 이용하며, 이것을 이용하여 XML Tech Tip 목록에서 HTML 페이지를 생성합니다.

    import java.io.*;
    import java.net.*;
    import java.util.*;
    import javax.xml.parsers.*;
    import org.xml.sax.*;
    import org.xml.sax.helpers.*;
    /**
     * 팁의 제목들의 목록을 보여주고 이에 대한 HTML과 text 버젼의 문서에
     * 연결하는 간단한 HTML 페이지를 생성한다.
     */
    public class UseSAX2 extends DefaultHandler
    {
        StringBuffer htmlOut;

        public String toString()
        {
            if (htmlOut != null)
                return htmlOut.toString();
            return super.toString();    
        }
    
        public void startElement(String namespace, String localName, String qName, Attributes atts)
        {
            if (localName.equals("tip")) 
            {
                String title = atts.getValue("title");
                String html = atts.getValue("htmlURL");
                String text = atts.getValue("textURL");
                htmlOut.append("<br>");
                htmlOut.append("<A HREF=");
                htmlOut.append(html);
                htmlOut.append(">HTML</A> <A HREF=");
                htmlOut.append(text);
                htmlOut.append(">TEXT</A> ");
                htmlOut.append(title);
            }
        }

        public void processWithSAX(String urlString) throws Exception
        {
            System.out.println("Processing URL " + urlString);
            htmlOut = new StringBuffer("<HTML><BODY><H1>JDC Tech Tips Archive</H1>");
            SAXParserFactory spf = SAXParserFactory.newInstance();
            SAXParser sp = spf.newSAXParser();
            ParserAdapter pa = new ParserAdapter(sp.getParser());
            pa.setContentHandler(this);
            pa.parse(urlString);
            htmlOut.append("</BODY></HTML>");
        }

        public static void main(String[] args)
        {
            try
            {
                UseSAX2 us = new UseSAX2();
                us.processWithSAX(args[0]);
                String output = us.toString();
                System.out.println("Saving result to " + args[1]);
                FileWriter fw = new FileWriter(args[1]);
                fw.write(output, 0, output.length());
                fw.flush();
            }
            catch (Throwable t)
            {
                t.printStackTrace();
            }
        }
    }

이 프로그램을 테스트하기 위해, 이 팁의 앞부분의 XML의 소개에 있는 XML 문서를 이용하거나, http://staff.develop.com/halloway/TechTips/TechTipArchive.xml에 있는 조금 더 긴 문서를 이용할 수 있습니다. 이렇게 받은 XML 문서를 컴퓨터의 로컬 디렉토리에 TechtipArchive.xml로 저장하십시오. 아래와 같은 명령어로 HTML 문서를 생성할 수 있습니다.

    java UseSAX2 file:TechTipArchive.xml SimpleList.html

그리고 SimpleList.html을 웹브라우져를 통해서 보고, 최근 팁의 텍스트 버젼과 HTML 버젼의 링크를 따라가 보십시오. (실제 시나리오는 위의 코드를 클라이언트의 브라우져나 서버의 서블릿/jsp 안에 합쳐 넣어야 할것입니다.)

위 코드에는 몇가지 흥미로운 부분이 있습니다. 파서를 생성하는 부분을 보십시오.

    SAXParserFactory spf = SAXParserFactory.newInstance();
    SAXParser sp = spf.newSAXParser();

JAXP에서, SAXParser 클래스는 직접 생성하지 않고, factory 메소드인 newSAXParser()를 이용합니다. 이것은 서로 다르게 구현된 코드들이 소스코드의 변경 없이 끼워넣기가 가능하도록 하기 위한 것입니다. factory는 이 외에도 namespace의 지원, 인증 등의 향상된 파싱 요소들을 지원합니다.JAXP 파서의 인스턴스를 가진 뒤에라도, 아직은 파싱할 준비가 되지 않았습니다. 현재의 JAXP 파서는 SAX 1.0만 지원합니다. SAX 2.0을 지원하도록 하기 위해서, 파서를 ParserAdapter로 감싸야 합니다.

    ParserAdapter pa = new ParserAdapter(sp.getParser());

ParserAdapter 클래스는 SAX2 다운로드의 일부분이며, 지금 있는 SAX1 파서에 SAX2의 기능을 추가합니다.

ContentHandler 인터페이스를 구현하는 대신, UseSAX는 DefaultHandler 클래스를 상속받습니다. DefaultHandler는 모든 ContentHandler의 메소드에 대해 아무것도 하지 않는 비어있는 구현을 제공하는 어댑터 클래스입니다. 따라서 오버라이드 하고싶은 메소드만 구현하면 됩니다.

startElement() 메소드가 실제 작업을 수행합니다. 프로그램은 팁을 제목 목록을 보여주면 되기 때문에, <tip> 엘리먼트만 있으면 되며, <tips>, <author> 엘리먼트는 무시합니다. startElement 메소드는 엘리먼트의 이름을 체크하고 현재 엘리먼트가 <tip>일 때에만 계속해서 작업을 수행합니다. 또 이 메소드는 엘리먼트의 속성을 Attributes 레퍼런스를 통해 접근하도록 합니다. 따라서 팁의 이름과 name, htmlURL, textURL을 추출하는 것이 쉽습니다.

이 예의 결과는 최근의 Tech Tips의 목록을 보여주는 HTML 문서입니다. HTML을 직접 만들어도 됩니다. 하지만 이것을 XML로 하고, SAX 코드를 작성하면 추가적인 유연성을 제공할 수 있습니다. 다른 누군가 Tech Tip을 날짜별로, 저자별로 정렬하거나 몇가지 제약사항을 가지고 필터링을 하고자 한다면, 각각에 대한 파싱 코드를 이용하여 하나의 XML 문서에서 해당 문서를 생성할 수 있습니다.

불행히도, XML 데이터가 복잡해짐에 따라, 위의 샘플도 점점 코딩하고 수정하기가 어려워집니다. 위의 예는 두가지 문제를 가지고 있습니다. 첫번째는, HTML 문서를 생성하기 위한 코드가 단지 문자열을 조작하는 것 뿐이고, 따라서 어딘가에 있을 '>', '/'를 놓지게 된다는 것입니다. 두번째로, SAX API는 많은 것을 기억하지 못합니다. 즉, 앞의 어딘가에 있었던 엘리먼트를 참조하고자 한다면, 앞에서 파싱한 엘리먼트를 저장하기 위한 자신만의 state machine을 만들어야 한다는 뜻입니다.

Document Object Model (DOM) API가 이 두가지 문제점을 해결합니다.

 


 

DOM API의 이용

DOM API는 SAX API와는 완전히 다른 문서 처리 모델을 기반으로 하고 있습니다. (SAX와 같이) 한번에 문서의 한 부분을 읽는 대신, DOM 파서는 문서 전체를 읽습니다. 그리고 읽고 수정할 수 있는 프로그램 코드를 만들 수 있도록 문서 전체를 트리 구조로 만듭니다. SAX와 DOM의 차이점을 간단히 말하자면, 순차적/읽기 전용 접근과 임의/읽기/쓰기 접근의 차이입니다.

DOM API의 핵심은 Document와 Node 인터페이스입니다. Document는 XML 문서를 나타내는 최상위 객체입니다. Document는 데이터를 Node의 트리 형태로 저장하고 있으며, Node는 element, attribute, 또는 다른 형태의 내용물을 저장하는 기본 타입입니다. 또, Document는 새로운 Node를 생성하기 위한 factory로도 동작합니다. Node는 트리에서의 하나의 데이터를 표현하며, 트리에 있어서의 필요한 동작을 제공합니다. 노드에 대해 그 부모/형제/자식 노드를 찾을 수 있습니다. 또 Node를 추가/삭제함으로써 문서를 수정할 수 있습니다.

DOM API의 예제를 보기 위해, 위의 SAX에서 보여졌던 XML 문서를 처리해 봅시다. 이번에는 저자별로 묶어서 출력합니다. 이것은 몇가지의 작업을 더 해야 합니다. 아래에 코드가 있습니다.

    //UseDOM.java
    import java.io.*;
    import java.net.*;
    import java.util.*;
    import javax.xml.parsers.*;
    import org.w3c.dom.*;

    public class UseDOM
    {
        private Document outputDoc;
        private Element body;
        private Element html;

        private HashMap authors = new HashMap ();
    
        public String toString ()
        {
            if (html != null)
            {
                return html.toString ();
            }
            return super.toString();
        }

        public void processWithDOM (String urlString)
            throws Exception
        {
            System.out.println ("Processing URL " + urlString);
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance ();
            DocumentBuilder db = dbf.newDocumentBuilder ();
            Document doc = db.parse (urlString);
            Element elem = doc.getDocumentElement ();
            NodeList nl = elem.getElementsByTagName ("author");
            for (int n=0; n<nl.getLength (); n++)
            {
                Element author = (Element)nl.item (n);
                String id = author.getAttribute ("id");
                String fullName = author.getAttribute ("fullName");
                Element h2 = outputDoc.createElement ("H2");
                body.appendChild (h2);
                h2.appendChild (outputDoc.createTextNode ("by " + fullName));
                Element list = outputDoc.createElement ("OL");
                body.appendChild (list);
                authors.put (id, list);
            }
            NodeList nlTips = elem.getElementsByTagName ("tip");
            for (int i=0; <nlTips.getLength (); i++)
            {
                Element tip = (Element)nlTips.item (i);
                String title = tip.getAttribute ("title");
                String htmlURL = tip.getAttribute ("htmlURL");
                String author = tip.getAttribute ("author");
                Node list = (Node) authors.get (author);
                Node item = list.appendChild (outputDoc.createElement ("LI"));
                Element a = outputDoc.createElement ("A");
                item.appendChild (a);
                a.appendChild(outputDoc.createTextNode (title));
                a.setAttribute ("HREF", htmlURL);
            }
        }

        public void createHTMLDoc (String heading)
            throws ParserConfigurationException  
        {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance ();
            DocumentBuilder db = dbf.newDocumentBuilder ();
            outputDoc = db.newDocument ();
            html = outputDoc.createElement ("HTML");
            outputDoc.appendChild (html);
            body = outputDoc.createElement ("BODY"); 
            html.appendChild (body);
            Element h1 = outputDoc.createElement ("H1");
            body.appendChild (h1);
            h1.appendChild (outputDoc.createTextNode (heading));
        }

        public static void main (String[] args)
        {
            try
            {
                UseDOM ud = new UseDOM ();
                ud.createHTMLDoc ("JDC Tech Tips Archive");
                ud.processWithDOM (args[0]);
                String htmlOut = ud.toString ();
                System.out.println ("Saving result to " + args[1]);
                FileWriter fw = new FileWriter (args[1]);
                fw.write (htmlOut, 0, htmlOut.length ());
                fw.flush ();
            }
            catch (Throwable t)
            {
                t.printStackTrace ();
            }
        }
    }

XML을 TechTipArchive.xml로 저장했다고 가정합시다. 아래와 같은 명령어로 코드를 실행할 수 있습니다.

    java UseDOM file:TechTipArchive.xml ListByAuthor.html

그리고 웹브라우져로 ListByAuthor.html을 열어서 저자별로 구성된 팁의 목록을 봅니다.

코드가 동작하는 방법을 알기 위해, createHTMLDoc 메소드부터 살펴봅시다. 이 메소드는 outputDoc Document를 생성합니다. 이것은 나중에 HTML 출력을 만드는데 이용됩니다. SAX와 마찬가지로, 파서는 factory 메소드를 통해 생성됩니다. 그러나 여기서는 factory 메소드가 DocumentBuilderFactory 클래스에 있습니다. createHTMLDoc의 아래쪽 반은 HTML의 기본적인 요소들을 생성합니다.

    outputDoc.appendChild (html);
    body = outputDoc.createElement ("BODY"); 
    html.appendChild (body);
    Element h1 = outputDoc.createElement ("H1");
    body.appendChild (h1);
    h1.appendChild (outputDoc.createTextNode (heading));

이 코드를 SAX 예제에 있는 HTML의 요소를 생성하는 코드와 비교해 보십시오.

    //direct string manipulation from SAX example
    htmlOut = new StringBuffer ("<HTML><BODY><H1>JDC Tech Tips Archive</H1>");

DOM API를 이용하여 문서를 생성하는 것은 문자열을 직접 다루는 것만큼 간단하거나 빠르지 않습니다. 그러나 (특히 큰 문서에 대해서) 에러의 발생이 적습니다.

useDOM 예제에서 가장 중요한 부분은 processWithDOM 메소드입니다. 이 메소드는 두가지 일을 합니다. (1) 저자 엘리먼트를 찾고 이것을 출력으로 제공합니다. (2) 팁을 찾고 저자별로 구분하여 출력으로 제공합니다. 이 단계들은 각각 문서의 최상위 엘리먼트에 접근할 수 있어야 합니다. 이것은 getDocumentElement () 메소드를 통해 수행됩니다. 저자 정보는 <author> 엘리먼트에 들어있습니다. 이런 엘리먼트는 최상위 레벨에서 getElementsByTagName ("author") 메소드를 호출하면 찾을 수 있습니다. getElementsByTagName 메소드는 NodeList를 리턴합니다. 이것은 간단한 Node의 컬렉션입니다. 그리고 각각의 Node는 getAttribute 메소드를 호출하기 위해 Element로 형변환됩니다. getAttribute 메소드는 저자의 아이디와 이름을 입력으로 받습니다. 각각의 저자는 2수준의 머리말로 목록이 만들어집니다. 출력 문서는 저자의 이름을 담고 있는 <H2> 요소를 생성해 냅니다. Node를 추가하기 위해서는 2개의 단계가 필요합니다. 첫번째로 출력 문서에서 createElement와 같은 factory 메소드를 통해 Node를 생성합니다. 다음으로 노드가 appendChild 메소드를 통해 추가됩니다. 노드는 자신을 생성한 문서에만 추가될 수 있습니다.

저자 머리말이 제 위치에 놓이게 되면, 각각의 팁에 링크를 생성할 차례입니다. <tip> 요소를 <author> 엘리먼트를 찾을때와 같은 방법으로 getElementsByTagName을 이용해 찾습니다. 팁 속성을 추출하는 방법도 비슷합니다. 한가지 차이점은 노드를 어디에 추가할 것인가를 결정하는 것입니다. 저자가 다르면 다른 목록에 추가되어야 합니다. 이 작업을 위한 기초 작업으로 저자 엘리먼트를 처리할 때에 <OL> 노드를 추가하고 저자의 아이디로 HashMap 인덱싱을 했습니다. 이제, 팁의 저자 아이디 속성을 팁이 추가될 적당한 <OL> 노드를 찾는데 이용할 수 있습니다.

XML에 대해 더 깊이 다룬 내용을 보시려면, 2000년에 Addison-Wesley에서 출판한 Neil Bradley의 The XML Companion을 참고하십시오. JAXP에 대한 더 자세한 정보는http://java.sun.com/xml/index.html의 Java(tm) Technology와 XML 페이지를 참고하십시오. SAX2에 대한 더 자세한 정보는 http://www.megginson.com/SAX/index.html을 참고하십시오. DOM 표준은 http://www.w3.org/TR/REC-DOM-Level-1에 있습니다. 

출처 http://www.javastudy.co.kr/docs/techtips/000627.html