RSS구독하기:SUBSCRIBE TO RSS FEED
즐겨찾기추가:ADD FAVORITE
글쓰기:POST
관리자:ADMINISTRATOR
'Web_developing'에 해당되는 글 102

1. 테이블 생성
CREATE TABLE `TranTest` (
`num` int(11) NOT NULL auto_increment,
`col01` varchar(32) default NULL,
PRIMARY KEY (`num`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `TranTest2` (
`num` int(11) NOT NULL auto_increment,
`col01` varchar(32) default NULL,
PRIMARY KEY (`num`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

2. 입력 용 프로시저 생성
CREATE PROCEDURE `Prc_TranTest_Input`
(
in in_col01 varchar(32)
)
BEGIN
INSERT INTO TranTest SET col01 = in_col01;
END;

3. SELECT 용 프로시저 생성
CREATE PROCEDURE `Prc_TranTest_Select`
(
in in_col01 varchar(32)
)
BEGIN
SELECT num, col01 FROM TranTest WHERE col01 like concat(in_col01, ‘%’) ;
END;

4. Transaction 용 프로시저 생성
CREATE PROCEDURE `Prc_TranTest_InError`
(
)
NOT DETERMINISTIC
SQL SECURITY DEFINER
COMMENT ”
BEGIN
DECLARE dbErr int default 0;
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET dbErr = -1;
START TRANSACTION;
INSERT INTO TranTest SET col01 = ‘1234′;
INSERT INTO TranTest2 SET col012 = ‘1234′; //일부러 에러값을 내기위해 칼럼명을 다르게 적습니다.
IF dbErr < 0 THEN
ROLLBACK;
ELSE
COMMIT;
END IF;
END;

5. PHP 를 이용하여 SELECT 및 입력하기
$mysqli = new mysqli(”localhost”, “USERID”, “USERPW”, “USERDB”);
if (mysqli_connect_error()) {
printf(”Connect Failed : %s\n”, mysqli_connect_error());
exit;
}
$mysqli->query(”set names utf8″);

//SELECT
if($qry = $mysqli->query(”Call Prc_TranTest_Select(’123′)”)) {
$rs = $qry->fetch_object();
echo $rs->col01;
}

//INSERT TranTest;
$mysqli->query(”Call Prc_TranTest_InError()”);

2008/02/18 16:17 2008/02/18 16:17
이 글에는 트랙백을 보낼 수 없습니다
저 렴한 광랜의 등장과 대용량 하드디스크 가격의 하락, 그리고 Adobe 사의 플래시 플레이어(Flash Player), 플래시 비디오(Flash Video)의 등장으로 인터넷을 통한 비디오 공유가 정말 많이 보편화 되었습니다. 구글 비디오(http://video.google.com)와 YouTube(http://www.youtube.com)가 이미 이런 비디오 사업을 시작한지 오래됐지만, 여전히 공략할 수 있는 틈새 시장이 굉장히 많이 있다고 할 수 있는데, 그럼 우리도 한 번 틈새 시장을 공략해 보면 어떨까? PHP, 플래시, Adobe의 Flex를 이용해서 어떻게 하면 비디오 공유 사이트를 만들 수 있을까? 사실 알고보면 정말 쉽게 만들 수 있습니다.

여기서는 PHP를 이용해서 웹 사이트를 만드는 방법 그리고 Flex를 이용해서 Flash로 된 비디오 뷰어를 만드는 방법에 대해서 설명을 하겠습니다. 그럼 먼저 더 진행하기 전에 준비해야 하는 소프트웨어 몇 가지를 살펴보도록 하죠.

우선 서버쪽에서는 PHP와 MySQL이 필요합니다. MySQL은 비디오에 대한 정보(예를 들면, 비디오의 파일 이름, 미리보기 파일, 미리보기 파일의 너비와 높이, 제목, 그리고 기타 정보 등)를 저장하는데 사용되겠습니다. PHP는 전체적인 페이지를 구성하는데 사용되게 됩니다.

그리고 ffmpeg(http://ffmpeg.mplayerhq.hu/) 이라고 하는 유틸리티 프로그램도 필요한데요, 이 프로그램은 유저가 올리는 비디오 파일을 플래시 비디오 파일(FLV)로 변환하는 역할을 수행하게 됩니다. ffmpeg은 비디오의 변환외에도 비디오의 스냅샷을 찍어서 미리보기 파일을 만들어 낼 수도 있습니다. 이 ffmpeg을 이용하게 되면 사실상 어려운 작업을 손쉽게 처리할 수가 있어서 우리가 MyTube를 구현하는데 상당히 유용하게 활용이 됩니다.

클라이언트쪽에서는 두 가지 방법이 있는데요. 하나는 YouTube에서와 같이 HTML과 Flash를 같이 이용해서 인터페이스를 구성하는 방법이 있고, 두 번째로 플래시만으로 인터페이스를 구성하는 방법이 있습니다. 이 글에서는 Flex 프레임워크를 이용해서 비디오를 보고 등록된 비디오를 탐색하고 찾기 위해서 리스트를 생성하도록 할 예정입니다.

PHP로 서버쪽 코드 구성하기

서버쪽 코드 작성을 하기에 앞서, MySQL에 데이터 스키마를 생성해야 합니다. 우선 사용할 데이터베이스를 생성하기 위해서, 다음과 같이 mysqladmin 명령어를 사용합니다.
mysqladmin create movies 
스키마가 생성되었으면 데이터베이스를 로드합니다. 스키마 파일은 아래 [리스트 1]과 같습니다.

[리스트 1] movies.sql
DROP TABLE IF EXISTS movies; 
CREATE TABLE movies (
movieId INTEGER NOT NULL AUTO_INCREMENT,
title VARCHAR( 255 ),
source VARCHAR( 255 ),
thumb VARCHAR( 255 ),
width INTEGER,
height INTEGER,
PRIMARY KEY( movieId )
);
이제 위의 movies.sql 스키마 파일을 다음과 같은 명령어를 이용해서 데이터베이스에 테이블을 생성할 수 있습니다.
mysql movies < movies.sql 
데이터베이스에 무비 데이터를 넣기 위해서는 HTML을 이용해서 업로드 기능을 만들어야 합니다. 즉 비디오 파일을 받아서 플래시 비디오 파일로 변환하고 미리보기 파일을 만든후에 데이터베이스에 넣으면 되겠습니다.

업로드 페이지의 제작

비디오 파일을 업로드 하기 위한 HTML 파일은 다음 [리스트 2]에서 보는 것과 같이 아주 간단합니다.

[리스트 2] addmovie.html
<html>
<body>
<form enctype="multipart/form-data" method="post" action="upload.php">
<input type="hidden" name="MAX_FILE_SIZE" value="300000" />
<table>
<tr><td>Title</td><td><input type="text" name="title"></td></tr>
<tr><td>Movie</td><td><input type="file" name="movie"></td></tr>
</table>
<input type="submit" value="Upload" />
</form>
</body>
</html>
위 HTML은 입력받은 정보를 upload.php 페이지로 전송하게 되는데, 이 페이지에서 비디오를 변환하고 미리보기를 만들어 내고 데이터베이스에 저장하게 되는 것입니다. 다음의 upload.php 코드를 보도록 하겠습니다.

[리스트 3] upload.php
<html><body>
<?php
require "DB.php";

function converttoflv( $in, $out )
{
unlink( $out );
$cmd = "ffmpeg -v 0 -i $in -ar 11025 $out 2>&1";
$fh = popen( $cmd, "r" );
while( fgets( $fh ) ) { }
pclose( $fh );
}

function getthumbnail( $in, $out )
{
unlink( $out );
$cmd = "ffmpeg -i $in -pix_fmt rgb24 -vframes 1 -s 300x200 $out 2>&1";
$fh = popen( $cmd, "r" );
while( fgets( $fh ) ) { }
pclose( $fh );
}

function flv_import( $upfile, $fname, $title )
{
$fname = preg_replace( '/..*$/', '', basename( $fname ) );
$flvpath = "$fname.flv";
$thumbpath = "$fname.gif";

converttoflv( $upfile, "movies\$flvpath" );
getthumbnail( $upfile, "movies\$thumbpath" );

$dsn = 'mysql://root@localhost/movies';
$db =& DB::connect( $dsn );
if ( PEAR::isError( $db ) ) { die($db->getMessage()); }

$sth = $db->prepare( 'INSERT INTO movies VALUES ( 0, ?, ?, ?, ?, ? )' );
$db->execute( $sth, array( $title, $flvpath, $thumbpath, 300, 200 ) );
}

flv_import( $_FILES['movie']['tmp_name'], $_FILES['movie']['name'], $_POST['title'] );
?>
File sucessfully uploaded
</body></html>
위 코드에서 flv_import 함수를 중점적으로 봐야 합니다. flv_import 함수내에서 converttoflv 함수로 비디오 파일을 플래시 비디오 파일로 변경하고 있고, getthumbnail 함수로 미리보기 파일을 생성하고 있습니다. 그리고 다음으로 업로드가 완료된 무비에 대한 정보를 데이터베이스에 저장합니다. 위 리스트 3에 있는 FLV 플래시 비디오 파일과 미리보기 파일을 생성하는 함수는 ffmpeg 라는 명령어와 여러가지 명령어를 이용해서 변환을 수행하게 됩니다.

그럼 다음 [그림 1]처럼 addmovie.html 페이지를 브라우저로 한 번 보도록 하겠습니다.


[그림 1] 비디오를 업로드하는 페이지

여기서 비디오 파일을 지정하고 Upload 버튼을 눌러서 비디오 파일을 처리하기 위해 서버에 보낼 수가 있습니다.

사 실 upload.php 파일은 아주 기본적인 부분만 작성이 되어 있기때문에, 실제 서비스 용도로 사용하기 위해서는 에러 체크 루틴을 추가 적으로 작성해야 합니다. 그리고 그것보다도 더 큰 문제는 크기가 큰 비디오 파일을 업로드 하려고 할 때인데, 이런 경우에는 파일의 크기가 크기 때문에 비디오 변환 과정이 오래 걸리게 되고 유저의 대기시간이 상당히 길어지면서 더 큰 문제가 됩니다.

그럼 이런 문제를 해결하기 위한 방법은 뭐가 있을까요? 한 가지 방법으로 큰 크기의 비디오 파일이 업로드가 되면(10초 이상의 비디오 파일) 유저에게는 잠시 후에 비디오가 게시될 것이라고 말을 해 놓고 서버내의 다른 폴더에 비디오 파일을 저장한 후에 또 다른 스크립트를 이용해서 비디오를 변환하는 것입니다.

더 진행하기 전에 왜 꼭 플래시 비디오 파일로 변환을 해야 하는지에 대해서 생각을 해보도록 합시다. 그냥 원본 그대로 저장해도 되는데 왜 플래시 비디오로 변환을 하려고 하는 걸까요? 그건 원본 상태로 저장을 해 놓게 되면 각 비디오 파일의 타입 별로 비디오 플레이어를 찾아서 수행할 수 있는 코드를 작성을 해야만 하기 때문에, 굉장히 힘든 일이 될 것이라고 생각합니다.

이 과정을 플래시 파일로 변환하여 해결하게 되면 코드도 간단해지고 많은 OS에서 실행될 수 있도록 할 수도 있습니다.

그럼 이제 HTML과 플래시를 이용해서 유투브와 같은 입력양식을 간단하게 만들어 보겠습니다.

HTML과 플래시를 이용한 인터페이스 구성

입 력받은 URL을 가지고 플래시 비디오 파일을 재생하는 프로그램을 만들기 위해서는 Adobe Flex Builder 2를 이용해서 새로운 프로젝트를 만들어야 합니다. 새로운 프로젝트를 생성했다면 simplemovie.mxml 이라는 Flex 애플리케이션을 만들어야 합니다. 이 애플리케이션 파일의 내용은 다음 [리스트 4]와 같습니다.

[리스트 4] simplemovie.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:VBox backgroundColor="white" width="400" height="335">
<mx:VideoDisplay width="400" height="300" id="videoPlayer"
source="{Application.application.parameters.movie}" />
<mx:HBox width="100%" horizontalAlign="center">
<mx:Button label="Play" click="videoPlayer.play()" />
</mx:HBox>
</mx:VBox>
</mx:Application>
위 [리스트 4]에서 두가지 중요한 요소를 볼 수 있습니다 : VideoDisplay 요소는 비디오를 재생하는 역할을 하고 "Play"라는 이름을 가진 버튼은 비디오의 재생이 끝난 후에 유저가 클릭할 경우 비디오를 다시 재생하는 역할을 하게 됩니다.

VideoDisplay 요소는 "source"라는 속성을 가지게 되는데요, source 속성으로 재생해야 하는 플래시 비디오 파일의 URL을 지정하게 됩니다. 위 리스트에서 source 속성이 가지고 있는 값은 HTML 파일에 있는 <object>나 <embed> 태그에 있는 FlashVars 속성의 값이 되게 됩니다.

Flex Builder를 이용하게 되면 위 [리스트 4]의 simplemovie.mxml 프로그램을 simplemovie.swf 파일로 변환할 수가 있고, 변환된 swf 파일을 bin 디렉토리에서 PHP가 있는 곳의 디렉토리로 이동시켜야 합니다. 그리고 PHP 페이지를 하나 만들어서 비디오를 재생시킬 수가 있습니다. 다음 [리스트 5]를 보도록 하죠.

[리스트 5] simpletest.php
<?php
require "DB.php";

$moviebase = 'http://localhost:8080/movies/';

$dsn = 'mysql://root@localhost/movies';
$db =& DB::connect( $dsn );
if ( PEAR::isError( $db ) ) { die($db->getMessage()); }

$source = null;
$movieId = 1;
if ( array_key_exists( 'movie', $_GET ) )
$movieId = $_GET['movie'];

$movies = array();
$res = $db->query( 'SELECT movieId, source, title FROM movies' );
while( $row = $res->fetchrow( ) ) {
$movies []= $row;
if ( $row[0] == $movieId )
$source = $row[1];
}

if ( $source == null )
$source = $movies[0][1];
?>
<html>
<body>
<table>
<tr><td valign="top">
<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="400"
height="335"
codebase="http://fpdownload.macromedia.com/get/flashplayer/current/swflash.cab">
<param name="movie" value="simplemovie.swf" />
<param name="quality" value="high" />
<param name="flashVars" value="movie=<?php echo( $moviebase.$source ) ?>">
<embed src="simplemovie.swf" quality="high"
width="400" height="335" play="true"
loop="false"
quality="high"
flashVars="movie=<?php echo( $moviebase.$source ) ?>"
type="application/x-shockwave-flash"
pluginspage="http://www.adobe.com/go/getflashplayer">
</embed>
</object>
</td><td valign="top">
<?php
foreach( $movies as $movie ) {
?>
<a href="simptest.php?movie=<?php echo( $movie[0] )?>"><?php echo( $movie[2] )?></a><br/>
<?php
}
?>
</td></tr></table>
</body>
</html>
위 PHP 스크립트는 우선 데이터베이스에 접속한 후에 비디오 리스트를 받아오고 있습니다. 그리고 나서 URL을 통해서 넘어온 ID와 읽어온 비디오 리스트를 비교해서 동일한 ID를 가진 비디오가 있는지를 확인하게 됩니다. 동일한 ID를 찾을 경우 이 ID 값을 simplemovie.swf 파일로 넘기기 위해서 flashVars 속성에 지정하고 있습니다.

PHP 스크립트 다음 부분에서는 HTML 태그가 시작되고 simplemovie.swf 파일을 페이지에 표시하기 위한 태그인 <object>, <embed> 태그를 사용하고 있습니다. 물론 여기에 재생해야 하는 비디오에 대한 URL도 지정해 주고 있는 걸 볼 수 있습니다. 그리고 바로 아래에 현재 비디오외에 볼 수 있는 비디오에 대한 링크를 만들어 주고 있는 걸 볼 수 있습니다.

이제 웹 브라우저에서 [리스트 5]에 있는 페이지를 한 번 보도록 하겠습니다. 다음 [그림 2]와 같이 나오네요.


[그림 2] 간단한 비디오 플레이어와 함께 볼 수 있는 비디오에 대한 리스트가 출력되었다.

웹 브라우저를 열자마자 첫번째 비디오가 재생이 되게 되고, 오른쪽에 있는 비디오 리스트중에서 하나를 선택하면 페이지가 다시 로딩되면서 선택된 비디오가 재생되게 됩니다.

정말 간단하죠? Flex 파일 하나와 PHP 파일 하나, 그리고 데이터베이스만 가지고 위와 같이 비디오 공유 사이트를 만들었습니다.




Flex를 이용한 인터페이스 구성 파트 1


Flex 를 이용해서 원하는 비디오를 재생시키기 위해서는 Flex에게 재생시킬 수 있는 비디오의 리스트를 알려줘야만 합니다. 어떻게 해야하냐면 XML파일을 이용해서 Flex에게 비디오 리스트 정보를 넘겨줄 수가 있는데요, 여기서는 PHP로 다시 돌아가서 데이터베이스에서 비디오 리스트를 읽어들인 후에 XML로 출력하는 스크립트를 만들어 보도록 하겠습니다. 다음 [리스트 6]의 movies.php 파일을 보도록 하죠.

[리스트 6] movies.php
<?php
require "DB.php";

$moviebase = 'http://localhost:8080/movies/';

header( 'content-type: text/xml' );

$dsn = 'mysql://root@localhost/movies';
$db =& DB::connect( $dsn );
if ( PEAR::isError( $db ) ) { die($db->getMessage()); }
?>
<movies>
<?php
$res = $db->query( 'SELECT title, source, thumb, width, height FROM movies' );
while( $row = $res->fetchrow( ) ) {
?>
<movie title="<?php echo( $row[0] ) ?>" source="<?php echo( $moviebase.$row[1] ) ?>"
thumb="<?php echo( $moviebase.$row[2] ) ?>" width="<?php echo( $row[3] ) ?>"
height="<?php echo( $row[4] ) ?>" />
<?php
}
?>
</movies>
위 PHP 스크립트를 만든 후에 커맨드 라인에서 실행시키거나 브라우저에서 실행시키게 되면 다음 [그림 3]과 같은 XML 리스트를 볼 수가 있습니다.


[그림 3] XML로 표현된 비디오 리스트

이제 XML로 비디오 리스트 정보를 넘겨줄 수 있으니 전에 만들었던 simplemovie.mxml 파일을 좀 더 개선해 보도록 하겠습니다. 다음 [리스트 7]은 simplemovie.mxml의 업그레이드 된 버전입니다.

[리스트 7] mytube1.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute" creationComplete="movieXmlData.send()">

<mx:HTTPService method="get" url="http://localhost:8080/movies.php"
id="movieXmlData" result="onGetMovies( event )" />

<mx:Script>
import mx.rpc.events.ResultEvent;
import mx.controls.VideoDisplay;
import mx.controls.List;
import mx.rpc.http.HTTPService;
import mx.collections.ArrayCollection;

[Bindable]
private var movies : ArrayCollection = new ArrayCollection();

public function onGetMovies( event : ResultEvent ) : void
{
var firstMovie : String = event.result.movies.movie[0].source.toString();
videoPlayer.source = firstMovie;

movies = event.result.movies.movie;
movieList.selectedIndex = 0;
}

public function onPrevious() : void
{
if ( movieList.selectedIndex == 0 )
movieList.selectedIndex = movies.length - 1;
else
movieList.selectedIndex -= 1;
videoPlayer.source = this.movieList.selectedItem.source.toString();
}

public function onPlay() : void
{
videoPlayer.source = this.movieList.selectedItem.source.toString();
videoPlayer.play();
}

public function onNext() : void
{
if ( movieList.selectedIndex >= ( movies.length - 1 ) )
movieList.selectedIndex = 0;
else
movieList.selectedIndex += 1;
videoPlayer.source = this.movieList.selectedItem.source.toString();
}

public function onChange() : void
{
videoPlayer.source = this.movieList.selectedItem.source.toString();
}
</mx:Script>

<mx:HBox width="100%" paddingLeft="10" paddingTop="10" paddingRight="10">
<mx:VBox>
<mx:VideoDisplay width="400" height="300" id="videoPlayer" complete="onNext()" />
<mx:HBox width="100%" horizontalAlign="center">
<mx:Button label="<<" click="onPrevious()" />
<mx:Button label="Play" click="onPlay()" />
<mx:Button label=">>" click="onNext()" />
</mx:HBox>
</mx:VBox>
<mx:List width="100%" height="340" id="movieList"
dataProvider="{movies}"
change="onChange()"
labelField="title"></mx:List>
</mx:HBox>

</mx:Application>
전과 비교해서 크게 바뀐 부분이 있다면 파일의 상단에 있는 액션 스크립트(ActionScript)인데요, 이 코드로 전체적인 인터페이스를 관리하게 됩니다. 이 액션 스크립트가 하는 일은 먼저 onGetMovies 함수에 있는 HTTPService를 이용해서 movies.php 파일로부터 XML 정보를 읽어들이는 것입니다. HTTPService 클래스는 XML 리스트를 찾게 되면 XML 문서 객체모델(Document Object Model)로 변경하게 되고, 우린 이 문서 객체 모델을 이용해서 첫번째 비디오에 대한 정보를 얻어서 재생할 수가 있습니다. 또 onGetMovies 함수는 movies 라는 변수를 이용해서 페이지에 있는 리스트 박스에 비디오의 이름을 출력하게 됩니다. 나머지 액션 스크립트는 유저가 리스트 박스에 있는 비디오를 선택하거나 "이전", "다음" 버튼을 선택했을 경우를 처리하기 위한 코드입니다.

위 리스트의 제일 아래를 보면 전체 유저 인터페이스를 구성하는 Flex 오브젝트를 볼 수 있습니다. 여기에 이전 비디오와 다음 비디오를 선택할 수 있는 버튼을 배치하고, 오른쪽에는 현재 비디오 리스트를 출력하는 리스트 박스가 배치되어 있습니다.

이제 Flex Builder를 이용해서 위 프로그램을 컴파일하고 실행시키게 되면 [그림 4]와 같은 화면을 볼 수 있습니다.


[그림 4] Flex를 이용해서 구성한 첫 번째 인터페이스

리스트 박스에서 비디오를 선택할 수도 있고, 이전, 다음 버튼을 이용해서 비디오를 선택할 수도 있습니다. 멋있죠? 이제 미리보기 이미지를 제공해 주면 더 멋있어질 것 같네요.

Flex를 이용한 인터페이스 구성 파트 2 – 미리보기 이미지

미 리보기 이미지를 비디오 리스트에 출력하려면 리스트 박스를 비디오의 제목과 미리보기 이미지를 같이 출력할 수 있도록 수정해야 합니다. 다행히도 Flex의 기능을 이용하면 아주 쉽게 수정이 가능합니다. 우선 <List> 태그에 itemRenderer를 추가하도록 하고, 다음 [리스트 8]을 보겠습니다.

[리스트 8] mytube2.mxml
...
<mx:List width="100%" height="340" id="movieList"
dataProvider="{movies}"
change="onChange()"
itemRenderer="MovieItem"></mx:List>
...
여기서 MovieItem이라는 item renderer MXML 컴포넌트를 만들었습니다. 이 컴포넌트를 만들려면 메뉴에서 "New > MXML Component"를 선택하고 [리스트 9]와 같이 컴포넌트를 코드에 넣으면 됩니다.

[리스트 9] MovieItem.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" height="80">
<mx:Image source="{data.thumb}" width="{data.width/3}"
height="{data.height/3}" rotation="5" left="10" top="0" />
<mx:Label text="{data.title}" fontWeight="bold" top="10" left="100" fontSize="18" />
</mx:Canvas>
여기서는 Canvas 라는 컨테이너를 이용했지만, 원한다면 마음에 드는 컨테이너를 선택해서 설정하셔도 괜찮습니다. 그리고 나서 미리보기 이미지 추가를 위해서 <mx:Image> 태그를 사용하고 비디오 제목을 출력하기 위해서 <mx:Label>을 이용하면 됩니다. 좀 더 재밌게 해보기 위해서 미리보기 이미지를 약간 회전시켜 봤습니다. [그림 5]를 보도록 하죠.


[그림 5] 미리보기 이미지를 추가한 리스트 박스

꽤 괜찮은 비디오 플레이어가 됐네요. 여기에 추가적으로 비디오 설명, 재생 시간, 링크, 추천 버튼과 같은 기능을 추가하면 더 좋을 것 같습니다.

저장소와 대역폭

데 이터베이스를 구성하고 비디오 공유 사이트 페이지를 만들어 내는것 자체는 지금까지 잘 해결했는데, 사실 이것말고 한가지 더 고려해야 할 것이 있습니다. 바로 대역폭을 고려해야 합니다. 플래시 비디오 파일 자체가 인코딩이 잘 된다고 해도 역시 비디오 파일이기 때문에 크기가 큰 편에 속하게 됩니다. 그렇기 때문에 중간에 끊김없이 비디오 재생을 얼마나 할 수 있는지 파악하는 것이 무엇보다 중요하게 됩니다.

확실한 방법중 하나는 인터넷 데이터 센타에서 서비스를 하는 것입니다. 다른 방법으로는 비디오 공유 사이트에서는 비디오 자체에 대한 데이터를 가지지 않고 링크만 저장하고 실제 비디오 데이터는 다른 곳에 저장하는 방법입니다. 아마존의 S3 서비스 같은 경우가 좋은예인데요, 저렴한 가격으로 좋은 저장소로 활용할 수가 있고, 어느정도 사이트가 커질때까지는 안정적으로 활용할 수 있습니다.

결론

플래시를 이용한 비디오의 등장과 광대역폭의 보편화로 인해서 이제 적은 예산으로 비디오 공유 사이트를 운영한다는 것이 현실로 다가왔습니다. 바라기는 이 글을 통해서 여러분이 좀 더 멋있는 비디오 공유사이트를 만들어냈으면 하는 것입니다.
2007/12/26 16:16 2007/12/26 16:16
이 글에는 트랙백을 보낼 수 없습니다

1. 사용자 인증의 단일 창구 SSO

 가. SSO(Single Sigh On)의 개념

  - 사용자가 한 개의 ID, PASSWORD로 여러 응용시스템을 접근할 수 있는 기술

 나. SSO가 필요한 이유

  - 기업내 다양한 응용시스템 도입 및 운영에 따른 ID, PASSWORD 관리 복잡

  - 중앙집중적인 ID관리 및 시스템 권한관리를 통한 업무의 순화 및 표준화 실현


2. SSO의 요소기술과 EAM의 비교

 가. SSO의 특징

   1) 특징 : 단일의 ID, PASSWORD를 통한 다양한 시스템 접근 제공

   2) 구성요소 : 클라이언트/서버 Agent, SSO 인증서버, LDAP

   3) 주요기능 : 단일 로그인, 표준화된 인증 접근방법, 중앙집중식 접근관리 가능

   4) 구현방식 : 쿠키방식, 웹방식, PKI인증서 이용방식


 나. EAM(Enterprise Access Management)와 SSO의 비교

비교항목 EAM SSO
목적 ID관리와 권한, 자원정책의 결합 중앙집중식 ID 관리
장점 자원접근시 권한까지 제어
개별응용레벨의 권한제어
단일 ID 로 사용의 편리성
인증정책과 권한설정이 용이
단점 사용자/자원별 권한관리 어려움
고가 및 구현의 복잡성
ID, PASSWORD노출시 전체시스템 위험
자원별 권한관리 미비


3. SSO 적용시 고려사항 및 활용

 가. 단일 ID, PASSWORD 사용으로 노출시 기업전체의 보안위협이 되므로 개인의 철저한 ID,

       PASSWORD관리와 병행하여 ONE-TIME PASSWORD 정책이 필요함

 나. PKI 기반의 SSO 단순성 탈피위해 PMI(Previlege Management Infrastructure) 의 AC(Attribute

       Certificate) 인증서 사용하고 , 향후 EAM 도입이 필요함.

EAM 시스템과 요구 사항

연·재·목·차

제1회 EAM 시스템과 요구 사항
제2회 SSO 모델과 보안 기술
제3회 SSO·RBAC 표준화 동향


기업의 IT 인프라와 자산에 대한 통합화 및 관리 그리고 통제에 대한 관심이 증대되고 있다. 단순한 사내망에 불과하던 기업 IT 인프라가 직원, 고객, 파트너 등으로 사용자가 확대되면서 다양한 서비스를 요구받기 때문이다. 모든 기업들은 회사의 자원을 보호하고 자원에 대한 접근 권한을 관리함으로써 이러한 복잡한 문제들을 해결하길 바라고 있다. 이러한 기업의 요구에 부합하는 기술적 트렌드를 3회에 걸쳐 연재한다.

강신범 | 소프트포럼 기반기술개발실장


EAM(Extranet Access Management) 시스템은 인트라넷·익스트라넷 서비스를 동시에 수행하는 현재의 기업 IT 인프라 시스템을 안전하게 구성하고 효율적인 관리를 수행할 수 있는 환경 제공을 목적으로 한다.

EAM 시스템에 대한 이해를 돕기 위해 먼저 기업 내 IT 인프라를 EAM 시스템으로 통합하는 시나리오를 간단히 기술하고, 이 시나리오에 근거해서 EAM 시스템의 요구사항을 보안 a기술 측면에서 살펴본다.


EAM 시스템

현재 그룹웨어, 인사관리(HR) 시스템 그리고 전사적자원관리(ERP) 시스템을 운영하고 있는 회사의 경우 각 애플리케이션 별로 각각의 사용자 계정과 권한 관리를 위한 자원이 투입돼 운영된다. 이는 부가적인 관리를 필요로 함은 물론이고 획일적이고 통합되지 않은 상태의 시스템 운영으로 인한 다양한 부작용 및 보안적 결함을 드러내게 된다. 이와 같은 고민에 빠진 회사는 규모의 증대와 IT 인프라 확산에 비례해 보안적 결함이 점차 증가하게 된다. 또한 일정 시일 이후에는 EAM 시스템 도입 비용을 상회하는 기존 시스템의 운영비용으로 인한 손실분을 안게 되는 최악의 상황에까지 몰리게 된다.

이같은 회사가 EAM 시스템을 도입하게 되었을 경우에 회사 내부에서 사용되는 그룹웨어, HR, ERP를 EAM 시스템으로 통합하고 각 애플리케이션별로 개별적으로 관리되던 사용자 계정, 권한관리시스템까지도 단일화된 EAM 시스템에서 통합 관리하게 된다. 이 작업으로 인한 회사의 IT 인프라는 다음과 같이 변경된다.

우선 사내 직원은 사용자 인증을 최초로 한번 받고, 각 애플리케이션을 SSO(Single Sign-On) 기술에 의해 추가 인증없이 사용할 수 있다. 또한 각 애플리케이션은 개인화 서비스를 통해 인증된 사용자의 정보를 얻어 동작에 적용한다.

개별 애플리케이션은 통합된 인가·접근제어 정책에 따라 사용자의 접근을 제어한다. 사내 IT 인프라 관리자는 EAM 관리시스템을 통해 각 애플리케이션별로 접근권한을 사용자에게 제어할 수 있다. 사용자가 통합 인증을 받고 각 애플리케이션에 접근하는 모든 감사정보를 EAM 시스템이 관리해 개별적으로 관리되던 감사정보를 통합해 관리할 수 있게 된다.


EAM 시스템의 요구사항

EAM 시스템은 적어도 다음과 같은 6가지 요구사항 항목을 가진다.

△인증(Authentication):

시스템에 접근하는 사용자를 확인한다. 일반적으로 ID/PWD 방식이 가장 널리 사용되며 보안성을 강화하기 위해 암호, PKI 기술들이 이용된다.

△SSO:

통합 인증된 사용자가 개별 애플리케이션에 추가적인 인증 요구 없이 사용할 수 있어야 한다.

△인가·접근제어(Authorization):

개별 애플리케이션의 각 자원 및 서비스에 대한 인가·접근제어 권한을 관리 툴로 설정하고, 설정된 인가·접근제어 권한이 개별 애플리케이션 동작에 적용이 돼야 한다.

△개인화(Personalization):

통합 인증된 사용자가 개별 애플리케이션에 접근할 때, 접근하는 사용자의 아이덴티티(Identity)와 사용자의 정보를 확인할 수 있는 기술이 제공돼야 한다.

△관리(Administration):

통합 인증을 위한 사용자 계정, 개별 애플리케이션의 인가·접근제어, 개인화를 위한 정보제공의 범위, 감사기능 등을 편리하게 관리할 수 있는 기능이 제공돼야 한다.

△감사(Auditing):

전체 시스템에 접근해 통합 인증을 받고 SSO으로 개별 애플리케이션에 접근, 인가·접근제어가 수행되는 모든 과정이 감사 기록으로 남아야 한다.

<그림> Extranet Computing 환경


인증(Authentication)

EAM 시스템의 요구사항에서 사용자 인증에 관련된 보안 기술은 전체 시스템의 보안성에 매우 중요한 부분을 차지한다. 이와 관련해 EAM 시스템에서 사용될 수 있는 다양한 인증방법 기술에 대해서 소개한다.

인증방법은 기본적인 ID/PWD 방식과 보안성이 상대적으로 높은 X.509 인증서, OTP(One Time Password), 생체인식으로 나뉘어 진다. 아래에서 설명하고 있는 인증 방법들은 일반적인 EAM 시스템에서 웹 환경에 적용하기 적합한 기술들이다.

△Basic ID/PWD:

HTTP/HTTPS 프로토콜의 BASIC Authentication 방법을 이용해 ID와 패스워드로 사용자를 인증한다. 이 인증 방법은 HTTP 프로토콜을 사용하는 경우 사용자가 입력한 ID와 패스워드가 네트워크를 통해 암호화되지 않고 전송된다. HTTPS 프로토콜을 이용하는 경우에는 암호화돼 전달된다.

△Digest Authentication:

HTTP/HTTPS 프로토콜의 Digest Authentica-tion 방법을 이용해 ID와 패스워드로 사용자를 인증한다. 이 인증방법은 패스워드가 평문으로 전달되지 않아 Basic ID/PWD 인증보다 향상된 보안 서비스를 제공하지만, 패스워드가 저장될 때 반드시 패스워드 원본 혹은 복호화될 수 있는 정보로 저장돼야 하기 때문에 서버에서의 패스워드 관리에 각별한 주의가 요구된다.

△Form Based Authentication:

Customized Form(HTML/JSP/ASP etc.)을 이용해 사용자를 확인한다. 폼에 입력되는 인증 정보는 사용자 ID/PWD 만으로 구성될 수도 있고 사용자 ID/PWD 이외의 다른 정보, 예를 들어 사용자 주민등록번호 등을 추가적으로 포함해 사용할 수 있다. 전달되는 정보의 암호화를 위해서는 HTTPS 프로토콜이나 기타 암호 제품을 사용할 수 있다.

△X.509 Client Certificate over SSL:

HTTPS 프로토콜의 사용자 인증서 인증 프로토콜을 이용해 인증한다. 인증서의 폐기 여부 확인을 위해 CRL 검증과 OCSP 검증 방법을 사용할 수 있다.

△One Time Password 인증:

한 번만 사용될 수 있는 패스워드를 이용해 사용자를 인증한다. 사용자에게 미리 전달된 OTP Token을 이용하여 사용자를 인증한다.

△생체 인식:

사용자의 지문이나 홍채 등을 이용해 사용자를 인증한다. 아직은 널리 사용되고 있지 않으나 지문 인식의 경우 그 영역이 확대되고 있는 추세이다.

지금까지 기업의 IT 인프라 관리를 위해 효율성과 보안성을 모두 만족시킬 수 있는 EAM 시스템에 대한 개념과 요구사항을 살펴봤다. 기업의 다양한 IT 시스템 운영에 있어서 보다 안전하고 저비용의 운영 방식을 원한다면 각 애플리케이션 별로 분산돼 있는 인증과 권한 관리 부분을 통합시켜 전사적인 관리가 가능한 EAM 시스템 도입을 고려해 봐야 한다.



연재/EAM·SSO 기술과 표준화 동향 ②

SSO 모델과 보안 기술

강신범
소프트포럼 기반기술개발실장

연·재·목·차

제1회 EAM 시스템과 요구 사항
제2회 SSO 모델과 보안 기술
제3회 SSO·RBAC 표준화 동향

SSO 모델

일반적으로 사용되는 SSO 시스템은 두 가지 모델로 구분된다. SSO 대상 애플리케이션에서 사용되는 사용자 인증 방법을 별도의 SSO 에이전트가 대행해주는 Delegation(인증 대행) 방식과 SSO 시스템과 신뢰관계를 토대로 사용자를 인증한 사실을 전달받아 SSO를 구현하는 Propagation(인증정보 전달) 방식으로 구분된다.

<그림 1> SSO Delegation Model

△Delegation 방식: 대상 애플리케이션의 인증 방식을 변경하기 어려울 때 많이 사용된다. 대상 애플리케이션의 인증 방식을 전혀 변경하지 않고, 사용자의 대상 애플리케이션 인증 정보를 에이전트가 관리해 사용자 대신 로그온 해주는 방식이다. 즉 Target Server 1을 로그온 할 때 User1이 alice/alice라는 ID/ PWD가 필요하다면, 에이전트가 이 정보를 가지고 있고, User1이 Target Service 1에 접근할 때 에이전트가 대신 alice/alice ID/PWD 정보를 전달해서 로그온 시켜준다. △Propagation 방식: 통합 인증을 수행하는 곳에서 인증을 받아 대상 애플리케이션으로 전달할 토큰(Token)을 발급 받는다. 대상 애플리케이션에 사용자가 접근할 때 토큰을 자동으로 전달해 대상 애플리케이션이 사용자를 확인할 수 있도록 하는 방식이다. 웹 환경에서는 쿠키(Cookie)라는 기술을 이용해 토큰을 자동으로 대상 애플리케이션에 전달할 수 있다. 이러한 웹 환경의 이점으로 웹 환경에서의 SSO는 대부분 이 모델을 채택하고 있다.

<그림 2> SSO Propagation Model

△Delegation & Propagation 방식: 웹 환경이라고 하더라도 Propagation 방식이 모두 적용될 수는 없다. 특히 웹 애플리케이션의 변경이 전혀 불가능하고 사용자 통합이 어려운 경우 Delegation 방식을 사용하게 된다. 또한 대상 애플리케이션들이 많이 있고 애플리케이션의 특성들이 다양한 경우 각 애플리케이션에 Delegation 방식과 Propagation 방식을 혼용해서 전체 시스템의 SSO을 구성한다.

△Web 기반 One Cookie Domain SSO: SSO 대상 서비스와 응용 애플리케이션들이 하나의 Cookie Domain안에 존재할 때 사용된다. 일반적인 기업 내부의 컴퓨팅 환경이다. 통합인증을 받은 사용자는 토큰을 발급받게 되고, 이 토큰은 Cookie Domain에 Cookie로 설정되어 Cookie Domain 내의 다른 서비스로 접근할 때 자동으로 토큰을 서비스에 제공하게 된다. 서비스에서 동작되는 SSO 에이전트는 토큰으로부터 사용자 신원을 확인하고 요청된 자원에 대한 접근을 허가 해준다.

Web 기반 Multi Cookie Domain SSO

SSO 대상 서비스와 응용 애플리케이션들이 여러 도메인으로 분산돼 있을 경우다. Multi Domain 환경인 경우에는 사용자 인증 및 토큰의 발행을 위한 마스터 에이전트가 존재한다.
마스터 에이전트는 각 서비스 에이전트의 사용자 인증을 위임받아 수행한다. 인증된 사용자에게는 토큰을 발급하고 각 서비스 에이전트에게 안전하게 전달한다. 또한 에이전트가 해당 토큰을 자신의 Domain에서 Cookie로 저장해 사용할 수 있도록 한다. 각 서비스 에이전트의 신뢰도 및 SSO 시스템의 보안 레벨에 따라 다음과 같이 두 가지 방식으로 서비스될 수 있다.

<그림 3> One Token for All Multi Cookie Domain

△One Token for All Multi Cookie Domain: 모든 도메인이 하나의 토큰을 공유한다. 모든 시스템은 서로 신뢰관계를 가져야 한다. 토큰이 하나만 사용되므로, 마스터 에이전트는 사용자 인증 및 각 도메인에 대한 토큰 제공에 대해서만 수행하게 돼 에이전트들의 관리 및 구성이 매우 간단하다. 하지만 모든 도메인들이 서로 신뢰관계를 가져야 한다는 제약사항으로 인해 적용대상이 제한적이다. 일반적으로 하나의 기업에서 운영하는 다중 도메인 서비스들을 SSO로 구성할 때 많이 사용된다.

<그림 4> One Token form each cookie domain & One Token for Master Agent

△One Token for each cookie domain & One Token for Master Agent: 마스터 에이전트와 각 도메인들이 각각 토큰을 가진다. 마스터 에이전트는 각 도메인의 에이전트들과 신뢰관계를 가지며, 각 도메인의 에이전트들 사이는 신뢰관계를 가지지 않는다. 마스터 에이전트는 각 도메인의 에이전트에게 전달할 사용자 토큰을 발행하므로, 에이전트들의 레벨이나 속성에 따라 토큰에 저장되는 사용자 정보의 양을 조절할 수 있다. 토큰이 각 도메인 별로 발행되므로, 도메인별 Replay Attack 등에 대한 취약성이 전체 시스템에 영향을 주지 않는다.

SSO 보안 기술

토큰은 쿠키를 통해 전달되므로 외부에 노출되는 정보이다. 완벽한 보안을 위해서는 토큰이 네트워크에서 노출되어서는 안되지만, 비용 및 관리상의 이유로 허용되고 있다. 하지만 토큰을 통해 토큰이 포함하고 있는 정보까지 외부에 노출하는 것은 심각한 결함을 제공한다. 토큰의 네트워크 구간에서의 정보 노출 및 위·변조를 방지하기 위해 다음과 같은 보안기술이 사용된다.

△Data Confidentiality: 토큰은 주요 암호 알고리즘(AES, SEED)과 128bit 이상의 키로 암호화돼 보호되어야 한다. △Data Integrity: 토큰은 MAC

(Message Authentication Code) 등을 포함해 데이터의 무결성을 보장해야 한다. △Replay Attack Protection: 토큰은 사용자와 대상 애플리케이션 사이에 전달되는 인증 정보이다. 일반적으로 토큰은 네트워크에 노출되며, 노출된 토큰을 사용해 다른 사용자가 인증을 받고 들어올 수 있다(Replay Attack). 특히 웹 환경에서 이러한 문제점이 중요한 이슈로 등장하고 있다. 이러한 문제점을 근본적으로 해결하기 위해서는 토큰을 네트워크에 노출시키지 않아야 한다.
토큰을 네트워크에 노출시키지 않기 위해서는 항상 사용자와 대상 애플리케이션 사이에 암호 채널을 형성해야 하며, 이 채널을 통해 토큰을 전달해야 한다. 그러나 SSL과 같은 채널 암호를 사용하는 데에는 매우 많은 비용이 요구되어 실제로 많이 사용되고 있지는 않다.
SSL과 같은 암호채널을 사용하지 않으면서 Replay Attack이 발생할 수 있는 상황을 줄일 수 있도록 다음과 같은 보안 기술들이 사용된다.

△사용자 주소 제한: 토큰이 발행될 때 접속한 사용자 주소 정보를 토큰 내부나 토큰을 발행한 서버에서 기억함으르써 Token이 제출된 사용자 주소와 최초 발행시 기억된 주소를 비교하여 접속한 곳 이외에서의 접속을 제한할 수 있다. 사용자 주소가 업무진행 중에 자주 변경되지 않는 시스템일 경우 유효하다. 예를 들어 회사내의 인터넷 환경일 경우 사용될 수 있다. 인터넷 환경의 경우에는 사용자 주소가 특정 범위에서 자주 바뀔 수 있는 환경도 있기 때문에 불특정 다수를 위한 일반적인 인터넷 서비스에 사용하기에는 부적합하다.

△유효시간 제한: 토큰의 유효시간을 매우 짧게 줌으로써 Replay Attack에 사용될 수 있는 시간을 제한한다. 유효시간내에 임계시간(예: 유효시간의 1/2)을 넘으면 자동으로 토큰을 재 발행하여 사용자는 의식하지 못하고 서비스를 계속 사용하게 한다. 지금까지 사용자가 각 애플리케이션별로 별도의 인증을 받지 않고, 한번의 통합 인증만으로 각 애플리케이션들을 사용할 수 있도록 하는 SSO 기술에 대해 살펴보았다. 대상 애플리케이션의 수정을 최소화할 수 있는 Delegation 모델과 토큰을 생성해 통합 인증 서비스를 제공하는 Propagation 모델 그리고 양자의 장점을 조합한 Delegation & Propagation 방식을 이해한다면 현재 도입 대상 기업의 IT 인프라와 진행중인 서비스 및 애플리케이션의 특성에 적합한 EAM 시스템 도입을 결정하는데 도움이 될 것이다.

EAM·SSO 기술과 표준화 동향

제3회 SSO·RBAC 표준화 동향

2007/10/11 16:27 2007/10/11 16:27
이 글에는 트랙백을 보낼 수 없습니다

출처 :

http://comeng.andong.ac.kr/%7Echi23/bbs/view.php?id=downdoli_db&page=1&sn1=&divpage=1&sn=off&ss=on&sc=on&select_arrange=hit&desc=desc&no=2

 

보통 my-sql에서는 PK로 잡고 auto-increment를 많이 사용하는데
이를 오라클 DB로 마이그레이션 하면서 다음과 같이 한다.....

원본 my-sql 쿼리:
select no, gno, ono, nested, id, name, title, registerDate, readno ]
from tableName
order by gno desc, ono asc limit 0, 15

오라클로 변환된 쿼리
select *
from (select a.*,rownum rnum from
(select * from table_notice order by gno desc, ono asc) a)
where rnum >  페이지당 리스트 수* ( 원하는페이지 번호 - 1) and rownum <= 페이지당 리스트 수

rownum으로 번호를 매기는 것이 order by보다 우선 순위가 높단다.
그리고 위의 방식 말고도 인덱스를 이용한 방법도 있는데..

뭐 이것도 쓸만하지만 더 쓸만한건 ;;;; 내가 품고 이찌롱 ^_^

2007/09/27 17:41 2007/09/27 17:41
이 글에는 트랙백을 보낼 수 없습니다
출처 블로그 > The Secret To Success Is To Never Give Up
원본 http://blog.naver.com/nkmin80/140032103146

※ 인덱스란?

  인덱스는 테이블이나 클러스트에서 쓰여지는 선택적인 객체로서,
오라클 데이터베이스 테이블내의
원하는 레코드를 빠르게 찾아갈 수 있도록 만들어진 데이터 구조
입니다.  

자동 인덱스 : 프라이머리 키 또는 uinque 제한 규칙에 의해 자동적으로 생성되는 인덱스 입니다.

수동 인덱스 : CREATE INDEX 명령을 실행해서 만드는 인덱스들 입니다.  

※  Index를 생성하는 것이 좋은 Column

WHERE절이나 join조건 안에서 자주 사용되는 컬럼
null 값이 많이 포함되어 있는 컬럼
WHERE절이나 join조건에서 자주 사용되는 두 개이상의 컬럼들


※  다음과 같은 경우에는 index 생성이 불필요 합니다.
table이 작을 때
테이블이 자주 갱신될 때

※  오라클 인덱스는 B-tree(binary search tree)에 대한 원리를 기반으로 하고 있습니다.

  B-tree인덱스는 컬럼안에 독특한 데이터가 많을 때 가장 좋은 효과를 냅니다.

이 알고리즘 원리는

 ① 주어진 값을 리스트의 중간점에 있는 값과 비교합니다.    
     만약 그 값이 더 크면 리스트의 아래쪽 반을 버립니다.
     만약 그 값이 더 작다면 위쪽 반을 버립니다.

 ② 하나의 값이 발견될 때 까지 또는 리스트가 끝날 때까지 그와 같은 작업을 다른 반쪽에도
     반복합니다.



 ※  인덱스는 B-tree 구조를 가지며 크게 다음 네 가지로 분류될수 있습니다.


Bitmap 인덱스

  비트맵 인덱스는 각 컬럼에 대해 적은 개수의 독특한 값이 있을 경우에 가장 잘 작동합니다.
  그러므로 비트맵 인덱스는 B-tree 인덱스가 사용되지 않을 경우에서 성능을 향상 시킵니다.
  테이블이 매우 크거나 수정/변경이 잘 일어나지 않는 경우에 사용할수 있습니다.

SQL>CREATE BITMAP INDEX emp_deptno_indx
        ON emp(deptno);


Unique 인덱스

  Unique 인덱스는 인덱스를 사용한 컬럼의 중복값들을 포함하지 않고 사용할 수 있는 장점이 있습니다.
  프라이머리키 와 Unique 제약 조건시 생성되는 인덱스는 Unique 인덱스입니다.

SQL>CREATE UNIQUE INDEX emp_ename_indx
        ON  emp(ename);


Non-Unique 인덱스

   Non-Unique 인덱스는 인덱스를 사용한 컬럼에 중복 데이터 값을 가질수 있습니다.

SQL>CREATE INDEX  dept_dname_indx
        ON  dept(dname);


결합 (Concatenated(=Composite)) 인덱스

   복수개의 컬럼에 생성할 수 있으며 복수키 인덱스가 가질수 있는 최대 컬럼값은 16개입니다

SQL>CREATE UNIQUE INDEX emp_empno_ename_indx
        ON  emp(empno, ename);


※  인덱스의 삭제

 
 - 인덱스의 구조는 테이블과 독립적이므로 인덱스의 삭제는 테이블의 데이터에는 아무런 영향도 미치지
않습니다.

 - 인덱스를 삭제하려면 INDEX의 소유자이거나 DROP ANY INDEX권한
을 가지고 있어야 합니다.


 - INDEX는 ALTER를 할 수 없습니다.


SQL>DROP INDEX emp_empno_ename_indx ;


※  인덱스에 대한 정보는 USER_INDEXES 뷰 또는 USER_IND_COLUMNS뷰를 통해 검색할 수
      있습니다.

※ 인덱스 생성

 

Oralce 9i에서 인덱스는 Primary Key와 Unique Key와는 별도로 CREATE TABLE의 USING INDEX CREATE INDEX 문법을 이용해서 정의하는 것이 가능해 졌습니다.


아마도 대부분의 사용자들은 다음과 같은 방식을 알고 계실텐데…

예제를 보시면서 어떤 것이 바뀌었는지 확인토록 해보세요~~

SQL> create table test (
 c1 varchar2(4) not null,
 c2 number(10)  not null,
 constrint pk_test primary key(c1) using index );


테이블이 생성되었습니다.


위의 create table문은 pk_test라는 이름을 가지는 primary key 제약 조건을 만들며 아울러 pk_test라는 이름을 가진 인덱스를 만듭니다. 이 두가지는 user_ind_colums 뷰와 user_constraints 뷰에서 table_name = ‘TEST’라는 조건을 주시면 확인이 가능합니다.

그러나 9i이후에서는 인덱스에 대해 명시적으로 이름을 주는 것이 가능해 졌는데…

우선 아래의 예제를 참고 하도록 하죠…


SQL> create table test (

 c1 varchar2(4) not null,
 c2 number(7),
 constraint pk_test primary key(c1)
 using index
 (create index idx_test_c1 on test(c1))
 );


테이블이 생성되었습니다.

이 경우는 테이블을 만들면서 primary key를 만드는데 (원래는 default로 pk를 만들게 되면 그 컬럼으로 인덱스를 만듭니다.) 인덱스의 이름은 primary key 이름과 다르게 주기 위해 using index 구안에 create index문을 이용해서 인덱스를 생성했습니다.


--------------------------------------------------------------------
아래의 SQL문중 하나를 이용해 인덱스는 놔두고 PK만 삭제할 수 있습니다.
--------------------------------------------------------------------

SQL> alter table test drop primary key keep index;


테이블이 변경되었습니다.


또는


SQL> alter table test drop constraint pk_test;


테이블이 변경되었습니다.

2007/09/25 13:59 2007/09/25 13:59
이 글에는 트랙백을 보낼 수 없습니다

Apache 서버가 갑자기 움직이지 않게되거나,

기동이 안되고 다음과 같은 에러를 낼때 대응

 

File size limit exceeded$HTTPD -DSSL

 

이 경우에는 아파치가 만들어내는 로그의 크기가

엄청나게 커져서, ext2시스템이 인식할수 있는

파일크기의 한계인 2G를 넘어선 경우이다..

 

# tail /PATH/TO/logs/error_log
[notice] child pid 10132 exit signal File size limit exceeded (25)
[notice] child pid 10131 exit signal File size limit exceeded (25)

# ls -al /PATH/TO/logs -rw-r--r--   1 root   root   2147483647 Sep 28 11:01 access_log
-rw-r--r--   1 root   root   2147483647 Oct 13 14:03 error_log

이후에  해당 파이을 백업을 하고,  재기동을 하면 정상적으로
기동된다.
 
# mv /PATH/TO/logs/access_log /PATH/TO/logs/access_log.bak
# mv /PATH/TO/logs/error_log /PATH/TO/logs/error_log.bak
# service httpd start
2007/08/08 14:23 2007/08/08 14:23
이 글에는 트랙백을 보낼 수 없습니다
New Object Model

PHP 5 에는 새로운 객체 모델이 존재한다. PHP의 객체 조작사항은 더 나은 성능과 보다 많은 특징을 위해 완벽하게 재작성되었다. PHP이전 버전에서 객체는 원시적인 형태로 조작되었다. ( 실례를 들자면 정수나 문자열 같은 것들을 말이다. ). 이 객체 안의 메소드가 가진 결점은 의미론적으로, 어떤 변수가 할당되었거나 파라미터의 값을 메소드로 전달할 때 모든 객체들이 복제된다는 것이었다. 이제 새로운 접근방식에서는 객체가 핸들에 의해 참조되고 값으로 참조되지 않는다. ( 객체식별자와 같은 핸들로 생각할 수 있을 것이다 ).

   많은 PHP 프로그래머들은 심지어는 과거의 객체모델이 멋대로 복제되는 것을 눈치채지 못했고 그랬기때문에 PHP어플리케이션의 대부분은 아주 좁은 틀을 벗어나 동작했을 것이다. 


Private and Protected Members

PHP 5 는 클래스 프로퍼티의 가시성( 可視性 : visibility )을 정의할 수 있게 하는 private 와 protected 멤버 변수를 도입했다.


Example B-4. Private and Protected Members accesibility

Protected 멤버 변수는 확장된 클래스에서 중복 선언되면 접근할 수 있는데 비해, private 멤버 변수는 단지 멤버 변수를 소유한 클래스에서만 접근이 가능하다.

<?php
class MyClass
{
  
private $Hello = "Hello, World!\n"
;
  
protected $Bar = "Hello, Foo!\n"
;
  
protected $Foo = "Hello, Bar!\n"
;

   function
printHello
() {
       print
"MyClass::printHello() " . $this->Hello
;
       print
"MyClass::printHello() " . $this->Bar
;
       print
"MyClass::printHello() " . $this->Foo
;
   }
}

class
MyClass2 extends MyClass
{
  
protected $Foo
;
            
   function
printHello
() {
      
MyClass::printHello();                         
/* Should print */
      
print "MyClass2::printHello() " . $this->Hello;
/* Shouldn't print out anything */
      
print "MyClass2::printHello() " . $this->Bar
/* Shouldn't print (not declared)*/
      
print "MyClass2::printHello() " . $this->Foo
/* Should print */
  
}
}

$obj = new MyClass
();
print
$obj->Hello
/* Shouldn't print out anything */
print $obj->Bar;   
/* Shouldn't print out anything */
print $obj->Foo;   
/* Shouldn't print out anything */
$obj->printHello();
/* Should print */

$obj = new MyClass2
();
print
$obj->Hello
/* Shouldn't print out anything */
print $obj->Bar;   
/* Shouldn't print out anything */
print $obj->Foo;   
/* Shouldn't print out anything */
$obj->printHello
();
?>
 
 

PHP 5에서는 또한 private 와 protected 메소드가 도입되었다.


Example B-5. Protected methods example

<?php
class Foo
{
  
private function aPrivateMethod
() {
       echo
"Foo::aPrivateMethod() called.\n"
;
   }

  
protected function aProtectedMethod
() {
       echo
"Foo::aProtectedMethod() called.\n"
;
      
$this->aPrivateMethod
();
   }
}

class
Bar extends Foo
{
  
public function aPublicMethod
() {
       echo
"Bar::aPublicMethod() called.\n"
;
      
$this->aProtectedMethod
();
   }
}

$o = new Bar
;
$o->aPublicMethod
();
?>

과거의 코드에서는 사용자 정의된 클래스나 함수가 "public", "protected" 또는 "private"를 명기하지 않고 실행했을 것이다.


Abstract Classes and Methods

PHP 5는 또한 추상클래스라는 것과 추상메소드라는 것이 도입되었다. 추상메소드는 단지 메소드의 특징적인 사항을 선언하고 수행사항을 제공하지는 않는다. 다음 예시에서 클래스는 abstract로 선언하는 것을 필요로 하는 추상 메소드를 포함한다.


Example B-6. Abstract class example

<?php
abstract
class AbstractClass
{
  
abstract public function test
();
}

class
ImplementedClass extends AbstractClass
{
  
public function test
() {
       echo
"ImplementedClass::test() called.\n"
;
   }
}

$o = new ImplementedClass
;
$o->test
();
?>

요약클래스는 명시화될 수 없다. 과거의 코드에서는 사용자 정의된 클래스나 함수에 "abstract"를 명기하지 않고 실행했을 것이다.


Interfaces

PHP 5 에서 interface라는 새로운 객체지향 개념이 도입되었다. 클래스는 인터페이스의 임의의 목록을 수단으로 삼을 것이다.


Example B-7. Interface example

<?php
interface Throwable
{
  
public function getMessage
();
}

class
MyException implements Throwable
{
  
public function getMessage
() {
      
// ...
  
}
}
?>

과거에는 사용자정의된 클래스나 함수에 "interface"나 "implements"를 붙이지 않고 실행했을 것이다.


Class Type Hints

 느슨한 유형이 남아있는 PHP5는 클래스 타입 힌트를 사용하여 파라미터로부터 메소드로 값을 전달하는 개체의 요청된 클래스를 선언하는 능력을 도입하였다.


Example B-8. Class type hinting example

<?php
interface Foo
{
   function
a(Foo $foo
);
}

interface Bar
{
   function
b(Bar $bar
);
}

class
FooBar implements Foo, Bar
{
   function
a(Foo $foo
) {
      
// ...
  
}

   function
b(Bar $bar
) {
      
// ...
  
}
}

$a = new FooBar
;
$b = new FooBar
;

$a->a($b
);
$a->b($b
);
?>

이러한 클래스 타입 힌트는 보통 유형화된 언어에서와 같이 컴파일 할 때에는 체크하지 않는다. 그러나 실행 도중에 체크한다. 이것은 다음을 의미한다:

<?php
function foo(ClassName $object
) {
  
// ...
}
?>


는 다음과 같다 :

<?php
function foo($object
) {
   if (!(
$object instanceof ClassName
)) {
       die(
"Argument 1 must be an instance of ClassName"
);
   }
}
?>
 

PHP 5 는 최종 멤버와 메소드를 선언하기 위해 "final" 키워드를 도입하였다. final을 붙인 메소드와 멤버는 하위클레스에 의해 오버라이드 되지 않는다.


Example B-9. final method

<?php
class Foo
{
  
final function bar
() {
      
// ...
  
}
}
?>

이것은 더군다나 최종적인 클래스를 만드는데 가능하다. 이것은 클래스가 특별화 되는 것을 막는다. (다른 클래스에 의해 '절대' 상속될 수 없다.). final 클래스는 자신들의 메소드를 final 로 선언할 필요가 없다.


Example B-10. final class

<?php
final
class Foo
{
  
// class definition
}

// the next line is impossible
// class Bork extends Foo {}
?>

프로퍼티는 final 속성을 가질 수 없다..

과거에는 사용자정의된 클래스나 함수에 "final"을 붙이지 않고 실행했을 것이다.


Objects Cloning

   PHP 4 에서는 객체가 복제되었을 때 복제된 생성자를 실행하기 위한 결정을 사용자가 할 수 없는 그런 방법을 제공했었다. 복제하는 동안에는 PHP 4 에서는 모든 객체의 프로퍼티에 대한 이상적 복제물을 만들기 위해 비트복사를 하는 단역을 수행하였다.  

   전체적으로 복제된 프로퍼티와 함께 복제된 객체를 만드는 것은 항상 원하는 행동이 아니다. 생성자 복제를 위해 필요한 것에 대한 좋은 예는 만약 GTK 윈도우를 묘사하는 객체를 소유하고 있고 객체는 이 객체를 복제할때의 GTK 윈도우의 리소스로 유지될 때, 아마 같은 프로퍼티를 가진 새로운 윈도우를 생성하길 원할것이고 새 윈도우의 리소스를 새 객체가 포함하길 원할 것이다. 다른 예로는 만약 객체가 다른 객체를 사용하는 참조자를 붙들어매고 있고, 개발자는 부모객체를 복제할때, 이 다른 객체의 새로운 인스턴스를 생성하길 원할 것이고 그래서 복제된 개체는 그 자신의 분산된 복제물을 가지고 있을 것이다.

복제되는 객체는 clone 키워드를 사용하여 생성된다. ( 가능하다면 객체의 __clone() 메소드를 호출한다 ). 객체의 __clone() 메소드는 직접적으로 호출될 수는 없다.

개발자들이 객체의 새로운 복제물을 생성하기 위해 요청할때, PHP 5 는 __clone() 메소드가 정의 되었는지 아닌지의 여부를 체크할 것이다. 만약 정의되지 않았다면 모든 객체의 프로퍼티를 복제할 기본적인 __clone() 을 호출할 것이다. __clone() 메소드가 정의되었다면 생성된 객체 안에 필요한 프로퍼티를 정할 의무가 있을 것이다. 편의를 위해 엔진은 소스객체로부터 모든 프로퍼티를 임포팅하는 함수를 공급해서 그것들이 소스 오브젝트의 값에 의한 복제물 과 함께 시작할 수 있도록 할 것이고 변경할 필요가 있는 프로퍼티를 단지 오버라이드 할 것이다.


Example B-11. Objects cloning

<?php
class MyCloneable
{
   static
$id = 0
;

   function
MyCloneable
() {
      
$this->id = self::$id
++;
   }

   function
__clone
() {
      
$this->address = "New York"
;
      
$this->id = self::$id
++;
   }
}

$obj = new MyCloneable
();

$obj->name = "Hello"
;
$obj->address = "Tel-Aviv"
;

print
$obj->id . "\n"
;

$obj_cloned = clone $obj
;

print
$obj_cloned->id . "\n"
;
print
$obj_cloned->name . "\n"
;
print
$obj_cloned->address . "\n"
;
?>
 

PHP 5 는 개발자로 하여름 클래스를 위해 생성자 메소드를 선언할 수 있게 한다. 생성자 메소드를 가진 클래스들은 새롭게 생성된 객체에서 이 메소드를 초기화 하는데 적당하게 호출해서 객체가 사용되기 전에 필요로 하지 않게 할 것이다.

PHP 4 에서 생성자 메소드는 클래스 자신의 이름과 같은 이름을 가진 클래스 메소드였다. 파생된 클래스로부터 부모 생성자를 호출하는 것이 매우 일반적일 때, PHP 4는 거대한 클래스 계층 주변으로 클래스를 옮기는 성가신 부분을 만들어내는 방법을 택해 작업했다. 만약 클래스가 다른 부모클래스 아래로 귀속하기 위해 옮겨졌을 때, 부모클래스 생성자의 잘 바뀌는 이름과 부모클래스 생성자를 호출하는 파생된 클래스의 코드는 수정될 것이다.

PHP 5 는 __construct()라는 이름으로 불리우는 생성자 메소드를 선언하는 표준방법을 도입하였다. 


Example B-12. using new unified constructors

<?php
class BaseClass
{
   function
__construct
() {
       print
"In BaseClass constructor\n"
;
   }
}

class
SubClass extends BaseClass
{
   function
__construct
() {
      
parent::__construct
();
       print
"In SubClass constructor\n"
;
   }
}

$obj = new BaseClass
();
$obj = new SubClass
();
?>

이전 버전과의 호환성을 위해 만약 PHP 5 가 주어진 클래스를 위한 __construct() 함수를 찾지 못한다면 클래스의 이름으로 이루어진 이전 방식의 생성자 함수를 찾을 것이다. 효과적으로 이것은 클래스가 __construct() 라는 이름을 가진 메소드를 가진 경우 이것이 다른 의미론을 위해 쓰일것이라는 의미를 가지고 있는데 호환성 문제를 가진 경우에 한해서만 해당한다.

Destructors

객체를 위해 파괴자를 정의하는 능력을 소유하는 것은 매우 유용할 수 있다. 파괴자는 디버깅을 위해, 데이터베이스의 연결을 끊을 때,기타 다른 여러가지 일을 마무리할 때 로그 메세지를 남길 수 있다. PHP 4에선느 객체 파괴자를 위한 기술이 존재하지 않았지만, PHP가 셧다운을 요청하는 도중에 실행될 등록함수를 위해 이미 지원했다. 

PHP 5 는 자바와 같은 다른 객체지향언어에서와 유사한 개념을 가진 파괴자를 도입하였다. : 객체의 파괴자를 파괴한 객체가 최종적으로 참조되었을때,  어떤 파라미터 값도 받지 않는 __destruct() 라고 이름 붙여진 클래스 메소드는 메모리에서 객체가 해제되기 전에 호출된다.

Example B-13. Destructor

<?php
class MyDestructableClass
{
   function
__construct
() {
       print
"In constructor\n"
;
      
$this->name = "MyDestructableClass"
;
   }

   function
__destruct
() {
       print
"Destroying " . $this->name . "\n"
;
   }
}

$obj = new MyDestructableClass
();
?>

생성자와 같이 부모클래스의 파괴자는 엔진에 의해 명시적으로 호출되지는 않을 것이다. 부모클래스의 파괴자를 실행하기 위해 파생된 클래스에서는 명확하게 파괴자 본문에 parent::__destruct() 를 적어 호출하여야 한다.

Constants

PHP 5 는 완전한 클래스 상수를 도입하였다:

Example B-14. Class constant example

<?php
class Foo
{
   const
constant = "constant"
;
}

echo
"Foo::constant = " . Foo::constant . "\n"
;
?>

과거에는 사용자정의된 클래스나 함수에 "const"를 붙이지 않고 실행했을 것이다.


Exceptions

PHP 4 는 예외사항 조정에 대한 요소가 없었다. PHP 5 에서는 다른 프로그래밍 언어와 유사한 예외모델을 도입하였다. "catch all"을 위해 지원하지만, "finally"조항을 위한 것이 아님을 주의하여라.

Exception 은 catch 블록에서 다시 제시될 수 있다. 또한 다양한 catch 블록에서 소유하고 있는것도 가능하다. 이러한 경우에서 잡힌 exception 은 꼭대기부터 바닥까지의 각각의 catch 블록의 클래스타입과 비교되고, 'instanceof'를 지닌 첫 블록의 매치는 실행될 것이다. catch 블록이 끝날때 실행사항은 마지막 catch 블록까지 지속된다. 만약 어떤 catch 블록도 'instanceof' 매치를 지니고 있지 않은경우, 그 다음 try/catch block이 다음 try/catch 블록이 존재하지 않고 사용가능하지 않을때까지 검색될 것이다. 이러한 경우 exception 은 exception 절을 발견하지 못하고 프로그램은 예외사항을 보여주면서 중단한다.


Example B-15. Exception creation example

<?php
try
{
  
throw new Exception('Hello'
);
}
catch (Exception $exception
) {
   echo
$exception
;
}
?>

과거에는 사용자정의된 클래스나 함수에 "catch","throw" 또는 "try"를 붙이지 않고 실행했을 것이다.

Dereferencing objects returned from functions

PHP 4 에서는 함수에 의해 반환되는 객체들의 값을 부분참조하기란 불가능했고, 더 이상의 메소드를 만드는 것은 이러한 객체를 호출하였다. PHP 5에서는 다음과 같은 것들이 가능하다 :


Example B-16. Dereferencing example

<?php
class Circle
{
   function
draw
() {
       print
"Circle\n"
;
   }
}
    
class
Square
{
   function
draw
() {
       print
"Square\n"
;
   }
}

function
ShapeFactoryMethod($shape
) {
   switch (
$shape
) {
       case
"Circle"
:
           return new
Circle
();
       case
"Square"
:
           return new
Square
();
   }
}

ShapeFactoryMethod("Circle")->draw
();
ShapeFactoryMethod("Square")->draw
();
?>

Static member variables initialization

정적 클래스의 정적 멤버 변수가 이제 초기화될 수 있다.


Example B-17. Static variable initialization example

<?php
class foo
{
   static
$my_static = 5
;
  
public $my_prop = 'bla'
;
}

print
foo::$my_static
;
$obj = new foo
;
print
$obj->my_prop
;
?>
 

PHP 5 는 메소드를 정적으로 선언하기 위해 'static' 키워드를 도입하였다. 그래서 객체의 외부 전후에서 호출할 수 있다.


Example B-18. Static Methods example

<?php
class Foo
{
  
public static function aStaticMethod
() {
      
// ...
  
}
}

Foo::aStaticMethod
();
?>

가상변수 $this 는 static으로 선언된 메소드 안에서는 사용할 수 없다.


instanceof

PHP 5 는 객체가 클래스의 인스턴스건 아니건간에 그것을 조사하고 클래스를 확장하고 인터페이스조건을 제시하게 하기 위한 instanceof  키워드를 도입하였다.


Example B-19. instanceof example

<?php
class baseClass
{ }

$a = new baseClass
;

if (
$a instanceof baseClass
) {
   echo
"Hello World"
;
}
?>
 

Static은 개발자로 하여금 참조의 방식으로 변수의 값을 상수로 전달하도록 하는 컴파일 도중에 다루어진다. 이러한 변화는 또한 그들의 성능을 엄청나게 향상시켰지만 상수로의 우회적 참조가 더이상의 일을 할 수 없음을 의미한다.


Parameters passed by reference

참조에 의해 함수로 전달된 파라미터값은 아마 이제부터 기본값을 가질 수 있을 것이다.


Example B-20.

<?php
function my_function(&$var = null
) {
   if (
$var === null
) {
       die(
"$var needs to have a value"
);
   }
}
?>
 

__autoload() 요격실행 함수는 선언되지 않은 클래스가 명시화되었을 때, 자동적으로 호출될 것이다. 해당 클래스의 이름은 __autoload() 요격실행 함수로 인자처럼 전달될 것이다.


Example B-21. __autoload() example

<?php
function __autoload($className
) {
   include_once
$className . ".php"
;
}

$object = new ClassName
;
?>
 

메소트 호출과 프로퍼티 접근은 __call(), __get() 그리고 __set() 메소드에 의해 오버로드 될 수 있다.


Example B-22. __get() and __set()

<?php
class Setter
{
  
public $n
;
  
public $x = array("a" => 1, "b" => 2, "c" => 3
);

   function
__get($nm
) {
       print
"Getting [$nm]\n"
;

       if (isset(
$this->x[$nm
])) {
          
$r = $this->x[$nm
];
           print
"Returning: $r\n"
;
           return
$r
;
       } else {
           print
"Nothing!\n"
;
       }
   }

   function
__set($nm, $val
) {
       print
"Setting [$nm] to $val\n"
;

       if (isset(
$this->x[$nm
])) {
          
$this->x[$nm] = $val
;
           print
"OK!\n"
;
       } else {
           print
"Not OK!\n"
;
       }
   }
}


$foo = new Setter
();
$foo->n = 1
;
$foo->a = 100
;
$foo->a
++;
$foo->z
++;
var_dump($foo
);
?>

Example B-23. __get() example

<?php
class Caller
{
  
private $x = array(1, 2, 3
);

   function
__call($m, $a
) {
       print
"Method $m called:\n"
;
      
var_dump($a
);
       return
$this->x
;
   }
}

$foo = new Caller
();
$a = $foo->test(1, "2", 3.4, true
);
var_dump($a
);
?>
 

객체는 아마 foreach와 같은 방식을 사용하였을때의 오버로딩하는 방법으로 반복될 수 있을 것이다. 기본적인 행동은 모든 프로퍼티를 반복한다.


Example B-24. Object iteration example

<?php
class Foo
{
  
public $x = 1
;
  
public $y = 2
;
}

$obj = new Foo
;

foreach (
$obj as $prp_name => $prop_value
) {
  
// using the property
}
?>

인스턴스의 각각의 클래스는 Traversable이라는 빈 인터페이스를 실행할 foreach로 반복될 수 있다. 따가서 적합하게 실행되는 몇몇 객체들은 foreach와 합께 사용될 수 있다.

IteratorAggregate 와 Iterator 인터페이스는 PHP 코드에서 반복사용되는 방법을 상술할 수 있도록 한다. 그것들에 있어 첫번째는 Iterator 인터페이스를 이행하거나 반복가능한 내부 클래스에서 명시회되는 배열이나 객체를 반드시 리턴해야 하는 getIterator() 라는 메소드를 포함한다.


Example B-25. Iterator creation example

<?php
class ObjectIterator implements Iterator
{

  
private $obj
;
  
private $num
;

   function
__construct($obj
) {
      
$this->obj = $obj
;
   }
   function
rewind
() {
      
$this->num = 0
;
   }
   function
valid
() {
       return
$this->num < $this->obj->max
;
   }
   function
key
() {
       return
$this->num
;
   }
   function
current
() {
       switch(
$this->num
) {
           case
0: return "1st"
;
           case
1: return "2nd"
;
           case
2: return "3rd"
;
           default: return
$this->num."th"
;
       }
   }
   function
next
() {
      
$this->num
++;
   }
}

class
Object implements IteratorAggregate
{

  
public $max = 3
;

   function
getIterator
() {
       return new
ObjectIterator($this
);
   }
}

$obj = new Object
;

// this foreach ...
foreach($obj as $key => $val
) {
   echo
"$key = $val\n"
;
}

// matches the following 7 lines with the for directive.
$it = $obj->getIterator
();
for(
$it->rewind(); $it->valid(); $it->next
()) {
  
$key = $it->current
();
      
$val = $it->key
();
         echo
"$key = $val\n"
;
}
unset(
$it
);
?>
 

새로운 __METHOD__ 가상 상수는 현재 클래스와 메소드 내부에서 메소드가 사용될 때의 메소드를 보여주고, 클래스 밖에서 사용될때의 함수를 보여준다.


Example B-26. __METHOD__ use example

<?php
class Foo
{
   function
show
() {
       echo
__METHOD__
;
   }
}

class
Bar extends Foo
{
}

Foo::show();
// outputs Foo::show
Bar::show();
// outputs Foo::show either since __METHOD__ is
             // compile-time evaluated token

function test
() {
   echo
__METHOD__
;
}

test();     
// outputs test
?>
 

새로운 __toString() 기술 메소드는 객체를 문자열 변환객체로 오버로드 하도록 한다.


Example B-27. __toString() example

<?php
class Foo
{
   function
__toString
() {
       return
"What ever"
;
   }
}

$obj = new Foo
;

echo
$obj;
// call __toString()
?>
 

PHP 5 는 재설계한 클래스와 인터페이스, 함수와 메소드를 확장요소들과 같은 기능들을 추가한 완벽한 Reflection API 와 함께 출시되었다. 

Reflection API 는 또한 함수와 클래스 그리고 메소드를 위한 도큐먼트 해설을 얻는 방법을 제공한다.

거의 모든 객체지향코드의 외관모습은 개별적으로 상세히 기록된 Reflection API를 사용함에 의해 반영될 수 있다.


Example B-28. Reflection API use example

<?php
class Foo
{
  
public $prop
;
   function
Func($name
) {
       echo
"Hello $name"
;
   }
}

reflectionClass::export('Foo'
);
reflectionObject::export(new Foo
);
reflectionMethod::export('Foo', 'func'
);
reflectionProperty::export('Foo', 'prop'
);
reflectionExtension::export('standard'
);
?>

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

2007/05/18 19:25 2007/05/18 19:25
이 글에는 트랙백을 보낼 수 없습니다

PHP에서는 실시한 그래픽 이미지 생성을 위해 PNG나 JPG와 같은 이미지를 생성, 조작, 출력할 수 있는

이미지 함수들을 제공한다. 이러한 함수들은 GD 그래픽 라이브러리가 설치되어 있어야 사용 가능하다.


1.ImageCreate(X ,Y)

가로크기가 X 이고, 세로 크기가  Y인 이미지 파일을 만들 수 있는 가상의 작업 공간을 생성한다.

$im = ImageCreate(300,300)


2.ImageCreateFromGIF(파일명)

지정한 위치에 있는 GIF 파일을 가져와 그 파일과 가로 세로 크기가 같은 새 이미지를 생성한다.

$im= ImageCreateFromGIF("apple.gif")


3.ImageCreateFromPNG(파일명)

지정한 위치에 있는 PNG 파일을 가져와 그 파일과 가로 세로 크기가 같은 새 이미지를 생성한다.

$im = ImageCreateFromPNG("orange.png")


4.ImageCreateFromJPEG(파일명)

지정한 위치에 있는 JPEG 파일을 가져와 그 파일과 가로 세로 크기가 같은 새 이미지를 생성한다.

$im= ImageCreateFromJPEG("melon.jpg")


5.ImageGIF(식별자, 파일명)

ImageCreate() 함수나  ImageCreateFromGIF() 함수를 통해 생성되어 메모리에 올려져 있는 이미지를

브라우저나 파일로 출력한다.


식별자 : ImageCreate() 함수나 ImageCreateFromGIF() 함수로부터 반환된 이미지 식별자

파일명 : 생성될 이미지 파일의 이름

           지정하지 안으면 파일이 생성되지 않고 브라우저에 출력만된다.

           단 브라우저에 출력하려면 함수를 호출하기 전에 Header() 함수를 먼저 호출하여야 한다.


//브라우저 출력

$im = ImageCreate(300,300);

Header("content-type: image/gif");

ImageGIF($im);


//orange.gif 파일로 생성

$im = ImageCreate(300,300);

ImageGIF($im, "orange.gif");


6.ImagePNG(식별자, 파일명)

ImageCreate() 함수나  ImageCreateFromPNG() 함수를 통해 생성되어 메모리에 올려져 있는 이미지를

브라우저나 파일로 출력한다.


7.ImageJPEG(식별자 , 파일명)

ImageCreate() 함수나  ImageCreateFromJPEG() 함수를 통해 생성되어 메모리에 올려져 있는 이미지를

브라우저나 파일로 출력한다.


8.ImageDestroy(식별자)

ImageCreate() 함수에 의해 반환된 이미지 식별자가 점유하고 있던 메모리를 해제한다.


9.ImageColorAllocate(식별자, R, G, B )

지정한 RGB 값으로 해당 식별자가 가리키는 이미지의 배경색이나 문자열의 색깔을 바꾼다.


10.ImageColorDeAllocate(식별자)

ImageColorAllocate() 함수에 의해 할당되었던 색상을 제거한다.


11.ImageRectangle(식별자, 좌측상단X , 좌측상단Y, 우측하단 X , 우측하단 Y, 컬러식별자)

인자로 전달된 네 개의 좌표에 따라 사각형을 지정한 컬러식별자의 색상대로 그린다.


$im=imagecreate(300,300);     //가로300 세로300 이미지 공간을 생성
$gray=imagecolorallocate($im, 0 ,255,0);  //가장 먼저 호출되는 Imagecolorallocate()가 이미지 배경색을 채움
$red=imagecolorallocate($im,255,0,0);        //imagerectangle($im, 180,180,200,200,$red);
imagerectangle($im, 180,180,200,200,$red);   //지정한 색의 테두리를 갖는 사각형 생성

imagepng($im);  //출력


12.ImageFilledRectangle(식별자, 좌측상단X , 좌측상단Y, 우측하단 X , 우측하단 Y, 컬러식별자)
ImageRectangle() 함수는 사각형의 테두리만 색을 채우지만 이 함수는 사각형 내부를 색상으로 채운다.

imagefilledrectangle($im, 180,180,200,200,$red);  


13.ImagePolygon(식별자, 꼭지점좌표, 꼭지점수, 컬러식별자)

지정한 위치와 색깔을 테두리로 갖는 다각형을 그린다.

꼭지점좌표 : (첫번째x, 첫번째y, 두번째x, 두번째y, .....)


$im=imagecreate(400,300);
$gray=imagecolorallocate($im, 0 ,255,0);
$red=imagecolorallocate($im,255,0,0);
$point=array(10,10,150,50,120,180,39,108);
Imagepolygon($im, $point, 4, $red);

Imagepng($im);


14.ImageFilledPolygon(식별자, 꼭지점좌표, 꼭지점수, 컬러식별자)

ImagePolygon() 함수처럼 다각형을 생성하지만 내부가 지정된 색상으로 채워진다.


15.ImageLine(식별자, 시작X좌표, 시작Y좌표, 끝X좌표, 끝Y좌표, 칼라식별자)

이미지 내에 선을 긋는다.

ImageLine($im,10,10,300,10, $red)


16.ImageDashedLine(식별자, 시작X좌표, 시작Y좌표, 끝X좌표, 끝Y좌표, 칼라식별자)

선을 그리되 점선으로 그린다.


17.ImageSetPixel(식별자, X좌표, Y좌표, 칼라식별자)

좌표가 가리키는 위치에 점을 하나 찍는다.


18.ImageString(식별자, 폰트, X좌표, Y좌표, 문자열, 칼라식별자)

좌표가 가리키는 위치에 지정한 색깔로 문자열을 출력한다.

Imagestring($im, 1, 200, 200, "welcome PHP", $red);


19.ImageStringUp(식별자, 폰트, X좌표, Y좌표, 문자열, 칼라식별자)

좌표가 가리키는 위치에 지정한 색깔로 문자열을 출력하되 세로로 출력한다.


20.ImageChar(식별자, 폰트, X좌표, Y좌표, 문자열, 칼라식별자)

ImageString() 함수와 동일하나 단 하나의 문자만을 출력한다.


21.ImageCharUp(식별자, 폰트, X좌표, Y좌표, 문자열, 칼라식별자)

ImageString() 함수와 동일하나 단 하나의 문자만을 출력하되 세로로 출력한다.


22.ImageSX(식별자) / ImageSY(식별자)

이미지의 가로와 세로 크기를 반환한다.


23.GetImageSize(파일명)

GIF나 JPEG, PNG, SWF 이미지 파일의 크기 정보를 반환한다.

$size=GetImageSize("apple.jpg")

$size[0]  : 이미지 가로크기           //300
$size[1]  : 이미지 세로크기           //400
$size[2]  : 파일의 확장자( GIF:1, JPG2, PNG:3)      //2
$size[3]  : 태그안에 사용할 수 있는 형식으로 가로세로 크기값 출력      //width=320 height=400


24.ImageArc(식별자, 중심의X좌표, 중심의Y좌표, 타원너비, 높이, 시작점, 끝점, 컬러식별자)

타원을 그린다.


25.ImageFill(식별자, X좌표, Y좌표, 컬러식별자)

지정한 X, Y 좌표로 점을 찍어 그 점이 속하는 영역의 색을 칠한다.


26.ImageColorTransparent(식별자, 컬러식별자)

식별자가 가리키는 이미지에서  지정한 색깔을 제거해 투명하게 처리한다.


27.ImageCopyResized()

원본 이미지의 일부분을 대상 이미지에 복사한다.


28.ImageCopy()

원본 이미지의 일부분을 복사한다.


29.ImageTTFText()

트루타입 폰트를 이용하여 이미지에 문자열을 출력한다.


30.ImageTTFBBox()

인자로 전달한 문자열이 어느 정도의 자리를 차지하는지를 px 단위의 수치를 담은 배열의 형태로 반환한다.


31.ImageColorAt(식별자, X좌표, Y좌표)

지정한 위치의 픽셀이 가지고 있는 색깔의 인덱스를 얻는다.


32.ImageColorForIndex(식별자, 컬러인덱스)

지정한 클러 인덱스의 RGB 값을 연관 배열의 형태로 반환한다.


33.ImageColorExact(식별자, R, G, B)

특정 색깔의 컬러 인덱스를 반환한다.


34.ImageColorClosest(식별자, R, G, B)

지정한 RGB 성분의 각에 가장 근접하는 컬러 인덱스값을 반환한다.


35.ImageColorSet(식별자, 컬러인덱스, R, G, B)

지정한 컬러 인덱스에 해당하는 색깔을 red, green, blue 인자로 지정한 색깔로 바꾼다.


36.ImageColorResolve(식별자, R, G, B)

인자로 지정한 특정 색깔의 컬러 인덱스를 반환한다.


37.ImageColorsTotal(식별자)

해당 이미지 팔레트에 사용된 총 색상의 수를 반환한다.


38.ImageInterlace(식별자)

식별자가 가리키는 이미지의 interlace 옵션을 설정한다.


39.ImageTypes()

현재 설치되어 있는  PHP가 지원하는 이미지 타입을 반환한다.

2007/01/24 12:48 2007/01/24 12:48
이 글에는 트랙백을 보낼 수 없습니다

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

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

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

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

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

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

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

  • id

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

  • select_type

SELECT 타입, 다음과 같다.

    • SIMPLE

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

    • PRIMARY

가장 외곽의 SELECT

    • UNION

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

    • DEPENDENT UNION

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

    • UNION RESULT

UNION 결과물.

    • SUBQUERY

서브쿼리의 첫번째 SELECT

    • DEPENDENT SUBQUERY

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

    • DERIVED

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

  • table

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

  • type

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

    • system

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

    • const

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

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

    • eq_ref

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

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

    • ref

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

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

    • ref_or_null

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

SELECT * FROM ref_tableWHERE key_column=expr OR key_column IS NULL;

See Section 7.2.7, “How MySQL Optimizes IS NULL.

    • index_merge

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

    • unique_subquery

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

value
IN (SELECT primary_key FROM single_table WHERE some_expr)

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

    • index_subquery

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

value
IN (SELECT key_column FROM single_table WHERE some_expr)

    • range

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

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

    • index

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

    • ALL

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

  • possible_keys

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

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

  • key

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

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

  • key_len

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

  • ref

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

  • rows

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

  • Extra

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

    • Distinct

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

    • Not exists

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

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

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

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

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

    • Using filesort

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

    • Using index

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

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

    • Using where
      WHERE 절이 다음 조인에 사용될 행이나 클라이언트에게 돌려질 행을 제한하는 경우이다. 테이블의 모든 행을 검사할 의도가 아니라면 Extra 값이 Using where 아니고 조인타입이 ALL 이나 index 라면 쿼리사용이 잘못되었다.
    • 쿼리를 가능한 빠르게 하려면, Extra 값의 Using filesort Using temporary 주의해야 한다.
    • Using sort_union(...) , Using union(...) , Using intersect(...)
      이들은 인덱스 병합 조인타입에서 인덱스 스캔이 병합되는 형태를 말한다.

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

    • Using index for group-by

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

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

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

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

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

  • The columns being compared have been declared as follows:

Table

Column

Column Type

tt

ActualPC

CHAR(10)

tt

AssignedPC

CHAR(10)

tt

ClientID

CHAR(10)

et

EMPLOYID

CHAR(15)

do

CUSTNMBR

CHAR(15)

  • The tables have the following indexes:

Table

Index

tt

ActualPC

tt

AssignedPC

tt

ClientID

et

EMPLOYID (primary key)

do

CUSTNMBR (primary key)

  • The tt.ActualPC values are not evenly distributed.

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

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


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

실제로 결과는 74 * 2135 * 74 * 3872 = 45,268,558,720 행에 달한다.
만약 테이블이 크다면 얼마나 소요될지 상상할 수도 없을 것이다.
여기서 우선적인 문제는 MySQL 같은 타입으로 선언된 컬럼의 인덱스를 효과적으로 사용할 있다는 것이다. (ISAM 테이블에서는 같은 타입으로 선언되지 않은 인덱스는 사용할 없다.) 여기에서 VARCHAR CHAR 길이가 다르지 않다면 같은 타입이다.
tt.ActualPC
CHAR(10) 이고 et.EMPLOYID CHAR(15) 선언되어 있으므로 길이의 불일치가 발생한다.

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

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

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

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

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

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

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

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

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

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

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

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

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

mysql> ANALYZE TABLE tt;

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

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

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

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

2006/12/11 19:42 2006/12/11 19:42
이 글에는 트랙백을 보낼 수 없습니다
서브쿼리란 무엇인가?

서브쿼리는, 핵심적으로, 다른 SELECT 구문의 부분으로서 자주 사용 되는 SELECT 구문이다. 물론 INSERT, UPDATE, DELETE나 다른 구문과 함께 사용 될 수 있다. 서브쿼리는 다양한 최적화를 위하는 것과 마찬가지로, 매우 복잡한 검색이나 복잡한 보고서를 저장하기 위하여 사용된다. 우리는 GROUP BY 절의 모든 개수 핵심적으로 갖고, 여러분의 결과를 좀더 세밀히 구별하기 위해 SELECT의 FROM 절로 사용할 수 있다.

시작하기

mysql에서 서브쿼리를 사용하기 위해서, 여러분은 MySQL 4.1의 버전이 필요할 것이다. 다만 4.1 alpha 버전만 제외하고, 어떤 버전이든 상관없다(4.1 이상으로). 여러분은 최신 버전을 원할 것이다. 만약 없다면, 이 기사를 읽어보고, 가서 mysql 공식 사이트에 가서 다운받자.

이 기사에 있는, 모든 여제는 world database을 이용할 것이다. 그것은 world.sql.gz으로 mysql 웹 사이트에서 사용할 수 있다.
http://www.mysql.com/get/Downloads/Manual/world.sql.gz/from/pick/

초 간단 예제

음. 처음 볼 것은 가능한한 가장 간결한 서브쿼리이다

SELECT (SELECT 1);

 +------------+
 | (SELECT 1)  |
 +------------+
 |          1     |
 +------------+

이것은 정말 간단하다.

SELECT (SELECT (SELECT 1));

 +---------------------+
 | (SELECT (SELECT 1))  |
 +---------------------+
 |                   1           |
 +---------------------+

어떤가? 그러나 별로 쓸모있진 않다.

보다 쓸모있는 예제
지금부터, 보다 효율적인 방법(JOINs같은)으로 될 수 있는 것들은 무시하자, 그리고 약간의 가능한 서브쿼리과 그 결과를 주목하자. 처음으로, 우리는 다른 방법의 개수가 되게 할수 있는 매우 간결한 쿼리를 볼 것이다.

SELECT name, headofstate
 FROM Country
 WHERE code=(SELECT "SWE");

 +--------+-----------------+
 | name   | headofstate          |
 +--------+-----------------+
 | Sweden | Carl XVI Gustaf     |
 +--------+-----------------+

그래, 나는 인정할 것이다. 그것은 별로 쓸모있지 않다. 가지말고 나와 함께 놀아죠.(Stay with me here!). 만약 여러분이 비슷한 쿼리를 시도한다면, 좀더 유용하게 시간을 써보자. 가장 인구가 많은 도시의 정보를 알아보자.
(population은 인구, head of state는 시장/대통령이라는 뜻)
SELECT name, headofstate, population
 FROM Country
 WHERE population=(SELECT MAX(population) FROM Country);

 +-------+-------------+------------+
 | name  | headofstate | population     |
 +-------+-------------+------------+
 | China | Jiang Zemin | 1277558000    |
 +-------+-------------+------------+

LIMIT를 왜 않쓰냐고?, 그러나 LIMIT는 MySQL-한정적이다, 그래서 다른 데이터베이스에 포터블한 해결책이 아니다. 그리고 항상 아주 정확한 결과가 되지 못한다. (ORDER BY와 LIMIT으로 하면, 만약 정확히 같고, 가장 인구가 높은 두 국가 중 하나만 선택(selete)된다. 위의 서브쿼리는 대부분의 데이터베이스에 호환성이 높다. 보다 복잡한 걸 해보자, 공식언어로 국가에 관한 정보이다.

테이블을 속여보자
FROM 절에서, 서브쿼리는 새로운 서브쿼리 지원하는 가장 유용한 특징중 하나이다. 특히, 그것은 쿼리의 FROM 부분에서 입력을 허락하게 되는 간결한 쿼리문이다. MySQL이 쿼리문을 처리할 때, 그것은 서브쿼리의 결과는 실제로는 다른 테이블이다. 처음에는 간결한 예제로 보자, 내 말을 보여주겠다.

SELECT foo
 FROM (SELECT 1 AS foo) AS tbl;

 +-----+
 | foo   |
 +-----+
 |   1   |
 +-----+

여러분들은 끝 부분을 주목할 것이다. 'FROM (SELECT 1 AS foo) AS tbl' 은 테이블의 목록을 찾기위한 입력이다. 여러분은 또한 내가 AS tbl로 서브쿼리(메인 쿼리 부분) 를 대응하는 것을 주목할 것이다. FROM 절에서, 모든 서브쿼리는 어떤 것이 모든 것이 대응할 것이다. 한편 여러분은 에러를 볼것이다. 모든 테이블은 이름이 있어야 하고 서브쿼리도 예외는 아니다.

만약 우리가 보다 어려운 (그러나 유용한) 예제로 옮긴다면, 우리는 서브쿼리의 힘을 보기시작할 것이다. 모든 국가에서 가장 많은 공식언어가 몇개인지 찾아보자.

/* 1 */
/* AS는 MAX()함수로 나온 값의 임시 필드명을 지정하는 것입니다. */
 SELECT MAX(tbl.nr) AS nr
 FROM
   (
     /* 2 */
     SELECT countrycode, COUNT(*) AS nr
     FROM CountryLanguage
     WHERE isofficial='T'
     GROUP BY countrycode
   ) AS tbl;

 +---------+
 | MAX(nr)   |
 +---------+
 |       4   |
 +---------+

다시, MySQL 전문가인 여러분들 중 몇몇은 ORDER BY 와 LIMIT로 찾았을 같은 결과를 주목할 지도 모른다. 다시, 필자는 서브쿼리 부분을 별명(alias)을 불러야 한다. 그리고 필자는 AS tbl을 선택했다. 서브쿼리 없이 쉽게 할 수 없는 것을 해보자: 공식언어의 최대 개수로 그의 국가에 대한 정보를 얻어보자.


/* 1 */
 SELECT name, population, headofstate, top.nr
 FROM
   Country,
   (
     /* 2 공식언어(official languages)의 개수에 기반한 국가 코드(country codes)를 비교한다.*/
     SELECT countrycode, COUNT(*) AS nr
     FROM CountryLanguage
     WHERE isofficial='T'
     GROUP BY countrycode
     HAVING nr=(
       /* 3 SELECT #4 번으로부터 nr_official_language의 최대크기를 알아낸다. */
       SELECT MAX(summary.nr_official_languages)
       FROM
         (
           /* 4 모든 국가와 각각의 공식언어(official languages)의 개수를 찾는다. */
           SELECT countrycode, COUNT(*) AS nr_official_languages
           /* CountryLanguage 테이블에서 */
           FROM CountryLanguage
           /* 공식언어가 있는 경우에만 */
           WHERE isofficial='T'
           /* countrycode 순서로 */
           GROUP BY countrycode
          /* 원래의 테이블 이름 AS 별칭으로 임시로 쓸 이름 */
         ) AS summary
       )
   ) as top
 WHERE Country.code=top.countrycode

 +--------------+------------+-------------+----+
 | name         | population | headofstate | nr         |
 +--------------+------------+-------------+----+
 | Switzerland  |    7160400 | Adolf Ogi   |  4          |
 | South Africa |   40377000 | Thabo Mbeki |  4       |
 +--------------+------------+-------------+----+

이 쿼리문은 간결하지 않다. 그것은 복잡한 결과를 얻기 위해 네개의 SELECT 구문으로 다른 부분으로 감쌌다. 필자는 /* 1 */ 이런 형식의 주석을 추가했다. 그래서 우리는 논리적으로 쿼리문은 분할할수 있다. 만약 우리는 숫자로 분리된 각 쿼리문을 보고, 역순으로, 이해하면 가장 쉽다.
(복잡한 서브쿼리문을 볼떄는 안에서 바깥으로 보면 쉽다는 얘기입니다)

4. 이 SELECT 문은 모든 국가와 각각의 공식언어(official languages)의 개수를 찾는다.
3. 이 SELECT 문은 SELECT #4 번으로부터 nr_official_language의 최대크기를 알아낸다. 동시에, 최대크기를 위해 countrycode 컬럼을 얻는 방법은 SQL-표준이 아니다.  
2. 이 SELECT에서, 우리는 공식언어(official languages)의 개수에 기반한 국가 코드(country codes)를 비교한다.
1. 마지막으로 우리는 매칭한(matching) 국가에 관해 국가정보(country information)를 찾기 위한 국가(country) 테이블에 기대어 JOIN을 사용할 수 있다.

필자는 여러분은 무언가를 배웠기를 희망한다. 그리고 필자는 서브쿼리에 관해 여러분 스스로 읽기를 바란다, 이 시리즈의 다음 기사는 Correlation, ANY, EXISTS를 논의할 것이다.

역자 한마디..
오류가 많으니 읽으시는 분들이 좀 주의해서 읽어 주시면 감사하겠습니다.

http://www.phpschool.com/ 에서 활동하고 계신 김영진님의 글입니다.
2006/11/29 12:34 2006/11/29 12:34
이 글에는 트랙백을 보낼 수 없습니다
웅쓰:웅자의 상상플러스
웅자의 상상플러스
전체 (379)
게임 (5)
영화 (2)
기타 (23)
맛집 (5)
영어 (2)
대수학 (3)
형태소 (5)
Hacking (9)
Linux (112)
HTML (48)
Application_developing (48)
Web_developing (102)
Window (11)
«   2023/02   »
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28        
  1. 2016/01 (1)
  2. 2015/12 (3)
  3. 2015/10 (3)
  4. 2015/03 (2)
  5. 2015/01 (4)