PHP & Others

Mysqli 확장 사용하기 1장

컨텐츠 정보

본문

------------------Mysqli 확장 사용하기 1장 : 개괄과 미리준비된 문장

번역자
김영진(cogolda@hanmail.net)

--------------------------알아 두기
이 문서는 http://www.zend.com/php5/articles/php5-mysqli.php 를 제가 번역 수정한 내용입니다. 혹시 질문이나 의견 있으신 분은 코멘트나 이메일을 이용해 주시면 감사하겠습니다. 

대상독자
소개
* 주요 목적
* 주요 특징
* 왜 바꾸는가?
* 경고와 놀라움
소스 코드 보기
* 기본 사용
* 객체지향 인터페이스 사용하기
미리준비된(prepared) 문장
* 바운드(bound) 파라미터
* 바운드 결과(results)
* 바운드 파라미터와 바운드 결과를 같이 사용하기
요약

-------------------------대상 독자

이 기사는 php와 mysql의 사용 경험이 있는 독자를 위해 작성하였다. 그것은 데이터베이스와 프로그래밍의 기본적인 개념을 알고있고, php 스크립트 사용법과, mysql 서버 쿼리문을 보내는 법을 알고 있다고 가정했다.

Php와 mysql 설치법은 이 기사의 범위를 넘어선다

Php 5 설치에 관한 정보는, http://www.php.net/installation
이용 가능한 mysqli 확장에 관한 정보는,  http://www.php.net/mysqli
MySQL 4.1.2 이나 그 이상 버전의 설치에 관한 정보는,  http://www.mysql.com/doc/en/Installing.html

------------------------용어 설명
(원문에는 용어 설명이 맨 마지막에 있는데, 제가 위로 옮겼습니다.)

* Mysql/확장(ext/mysql) :: php의 구형 MySQL 확장(extension). MySQL version 4.1의 모든 기능을 지원하지 않는다.
* ext/mysqli :: php5의 새로운 MySQL 확장. MySQL 3.22 에서 5.0까지 모든 기능을 지원한다.
* MySQL 클라이언트 라이브러리 :: 프로그램이 RDBMS로 통신하기위하게 만드는 MySQL RDBMS의 컴퍼넌트
* MySQL 서버 :: 데이터베이스 안에서 디스크 수준의 표현을 관리하고, 처리작업과 요구를 하는 MySQL RDBMS의 컴포넌트.
(쉽게 말하자면 이 문서는 php5의 새로운 확장인 mysqli에 관한 내용입니다.)

----------------------------소개

90년대 중반 이후, mysql확장은 php와 mysql 사이에 주요 연결로서 서비스되었다. 비록 수년에 걸쳐서, 증가하는 불편함과 약간의 고생이 있었지만, 일반적으로 mysql확장은 좋은 성능을 가지고 있었고, php와 mysql 둘다 버전업을 하면서 보조를 맞추었다.

그렇지만, php5와 mysql 4.1의 소개 이후, 이런 변화는 다소 문제가 시작되고 있다는 것 보여준다. (아마도 mysql의 라이센스 문제때문이라고 보입니다.)

mysql확장에 있는 고생스러움은, 대부분 뚜렷하게도 mysql_pconnet()과 자동적이고 디폴트 커넥션이다. 추가적으로, mysql확장이 지원하는 특징과 mysql 클라이언트 라이브러리가 지원하는 특징 사이의 비호환성은 존재했었다.

이런 문제를 해결하는 노력으로서, 조지 리처는 mysql 4.1과 그 이상에서 새로운 특징을 지원 할 php5를 위한 새로운 mysql 확장을 만들었다.

이 확장은 mysqli확장(ext/mysqli)라고 부른다. I의 의미는 향상(improved), 연결(interface), 독창적인(ingenious), 비호환적인(incompatible), 미완성(incomplete) 이다.
(미완성이란 의미는 지금 개발중이라는 뜻으로 겸손한 입장에서 쓴걸로 보입니다.)

------------------------------주요 목표

새로운 확장의 주요 디자인 목표이다.

* 더 쉬운 관리 == mysql확장 소스코드는 다소복잡하고 조잡한했다. 다양한 특징을 요구하는 mysql의 기능에서 주요 개선은 클라이언트 라이브러리의 버전에 기반하여 사용가능하거나 사용불가능하게 되는 것이다.
* 보다 높은 호환성 == mysql 클라이언트 라이브러리에 보다 가깝게 편하기위해 필요했다. 그래서 그 앞으로 개선될 라이브러리에 대해 php 보다 쉽게 지원될 수 있다.
* 이전 버전과의 호환성 == 비록 mysql확장과 mysqli 사이에 호환성은 완벽하지 않다. 노력은 mysql확장에서 mysqli확장으로 간단히 포팅하는 쪽으로 할 것이다.

추가적으로, 이 확장은 추가적인 추적(additional tracing)와 디버깅, 로드 발란싱(load balancing), 복제기능을 지원한다.

---------------------------왜 바꾸는가?

Mysql 4.1+의 새로운 접근을 넘어서, 모든 사람이 mysqli확장을 사용하기를 원하는 이유가 무엇인가?

위에 언급한 기능에 추가로, mysqli 확장의  다음과 같은 장점이 있다.

* 놀라운 속도 == Ext/mysql과 비교하여 특정 조건에서는 40 가량 빠르다.
* 안전한 보안 == mysql RDBMS의 이전 버전에서 네트워크로부터 약간 패스워드 해쉬를 뽑아내기 위한 공격의 가능성이 존재했고, 그 다음에 사용자 패스워드를 재생성했다. 새로운 인증 절차는 보다 강하고 SSH같은 안전한 인증 절차이다.

--------------------------경고와 의외성

mysqli의 어떤 관점은 이전 확장과 아주 다르다.

* 디폴트 테이터베이스 커넥션 == 만약 여러분이 명시된 케녁션을 서버에 하지 않으면, mysqli는 여러분을 위해 접속하지 않을 것이다.
* 디폴트 링크 == 여러분이 사용하기 원하는 데이터베이스 서버 커넥션은 여러분이 절차 문맥을 통해 mysqli 확장을 사용할 때 명시적으로 참조될 것이다.

이 기사는 ‘world’ 테이터베이스를 사용하며 www.mysql.com에서 다운받을 수 있다.

-------------------기본적인 사용

여기 mysql 서버에 접속하고, 연결된 접속을 사용하여 서버에 쿼리문을 보내고 쿼리 결과를 보여주고 커넥션을 닫는 간단한 예제가 있다.

<?php

/* MySQL server에 접속 */
$link = mysqli_connect(
            'localhost',  /* The host to connect to */
            'user',      /* The user to connect as */
            'password',  /* The password to use */
            'world');    /* The default table to query */

if (!$link) {
  printf("Can't connect to MySQL Server. Errorcode: %s\\n", mysqli_connect_error());
  exit;
}

/* 쿼리문을 서버에 보낸다 */
if ($result = mysqli_query($link, 'SELECT Name, Population FROM City ORDER BY Population DESC LIMIT 5')) {

    print("매우 큰 도시:\\n");

    /* 쿼리문을 불러들이기 */
    while( $row = mysqli_fetch_assoc($result) ){
        printf("%s (%s)\\n", $row['Name'], $row['Population']);
    }

    /* 결과를 파괴하고 그것에 사용된 메모리를 풀어준다 */
    mysqli_free_result($result);
}

/* 접속 닫기 */
mysqli_close($link);
?>

위의 스크립트는 다음과 같이 보여준다.

Very large cities are:

Mumbai (Bombay) (10500000)
Seoul (9981619)
S&atilde;o Paulo (9968485)
Shanghai (9696300)
Jakarta (9604900)

코드가 보여주듯이, mysqli확장과 mysql확장은 아주 비슷하다. 다만 주요 차이점은 절차적인 방식을 사용할 때, 약간 장황하다.

에러 체크를 없이, 위의 스크립트는 모든 핵심을 실패할 수 있고 에러 메시지를 보여줄 수 있다.

-----------------------객체 지향 인터 페이스 사용하기

* 우리는 명령을 위해 명시적 접속이 필요 치 않다. 접속 정보는 우리의 $mysqli와 $result 객체제 저장되고, 메소드를 호출할 때, 필요로서 접근된다
* fetch_assoc()를 사용하여 결과로부터 쿼리 테이터의 열(row)을 불러(fetching)들일 때, 우리는 그것을 사용하기 위해 명시적인 결과 처리를 하지 않아도 된다. 접속 정보로서, 결과 핸들링은 $result 객체에 저장된다.

<?php

/* MySQL 서버에 접속 */
$mysqli = new mysqli('localhost', 'user', 'password', 'world');

if (mysqli_connect_errno()) {
  printf("MySQL 서버에 접속할 수 없습니다. Errorcode: %s\\n", mysqli_connect_error());
  exit;
}

/* 쿼리문을 서버에 보내기 */
if ($result = $mysqli->query('SELECT Name, Population FROM City ORDER BY Population DESC LIMIT 5')) {

    print("매우 큰 도시:\\n");

    /* Fetch the results of the query */
    while( $row = $result->fetch_assoc() ){
        printf("%s (%s)\\n", $row['Name'], $row['Population']);
    }

    /* 결과를 파괴하고 사용된 메모리를 풀어준다 */
    $result->close();
}

/* 접속 닫기 */
$mysqli->close();
?>


-------------------미리준비된 문장(Prepared Statements)

이제 우리는 이 확장의 기본 사용법을 봤다. 새로운 특징을 시험해 보자.

미리준비된 문장은 개발자에게 보다 안전하게 쿼리문을 작성할 수 있고 보다 성능이 높이 작서아기 보다 쉽게 만들어 준다.

그들은 두 양념이 있다.
바운드 파라미터 미리준비된 문장과 바운드 결과 미리준비된 문장.
Bound parameter parepared statements, and bound result prepared statements
(이걸 어떻게 번역해야 할 지, 도저히 감이 않오네요. 이상해도 이해바랍니다.)

--------------------------바운드 파라미터

바운드 파라미터 미리준비된 문장은 쿼리 템플릿을 만들고나서 MySQL 서버에 저장하게 한다. 쿼리문을 만들어야 하고, 템플릿에서 채우기 위한 데이터에서 mysql 서버에 보낸다. 그리고 완전한 쿼리는 형성되고나서 실행된다.

바운드 파라미터 미리 준비된 문장을 사용하고 만드는 기본 과정은 간단하다.

쿼리 템플릿은 만들어지고 mysql 서버에 보내진다. Mysql 서버는 쿼리 템플릿을 받는다. 잘 정의되었는지 검사하고 의미있는지 구분분석하고 특정 버퍼에 저장한다. 그것은 미리준비된 문장을 참고하기 위해 사용될 수 있게 특정 핸들을 반환한다.

만들어야 하는 쿼리가 필요할 때, 템플릿에서 채우기 위한 데이터는 MySQL 서버에 보내지고 완전한 쿼리는 형성되고 나서 실행된다.

이런 과정은 그것을 감싸는 대단히 중요한 동작이다.

쿼리문의 몸체(body)는 단지 MySQL 서버에 보내진다. 쿼리의 실행하기 위한 요청이 있으면, 단지 템플릿에서 채우기위한 대이테가 MySQL 서버에 보내게 되는 것이 필요하다.

쿼리가 실행되는 각각의 시간 대신에, 그런 쿼리문을 검사하고 분석하기 위해 필요한 작업의 대부분은 한번에 되는 것이 필요하다.

추가적으로, 작은 데이터를 포함하는 쿼리를 위해, 쿼리 전송 오버해드는 대단히 줄어든다. 예를 들면, 다음과 같은 쿼리가 있다면..

INSERT INTO City (ID, Name) VALUES (NULL, 'Calgary');

여러분이 쿼리를 실행하고 나서, 여러분은 60이나 그 이상 바이트 대신에, 단지 쿼리 데이터 16 바이트정보 보내는 것이 필요할 것이다.

쿼리를 위한 데이터는 SQL 인젝션 공격(SQL injection attcacks) 존재하지 않기 위해 mysql_real_string()같은 함수를 통하여 넘기는 것이 필요치 않다.

!!!!!!!!!!!!!!용어 설명시작
SQL 인젝션 공격은 데이터가 악의적인 방법으로 기대되지 않은 and/or를 동작하기 위해 쿼리문을 입력할 때 존재한다. 예를 들면 “DELETE FROM grades WHERE class_name=’test_$class’” 처럼 php 스크립트에서 간단한 쿼리를 주면, $class의 변수에 대해 제어 할 수 있는 공격자가, “oops’ or class_name LIKE ‘%’” 처럼 어떤 것을 $class의 변수를 바꿈으로서 발생을 위해 강제로 지울 수 있다.
!!!!!!!!!!!!!!!용어 설명끝

대신에, MySQL 클라이언트와 서버는 미리준비된 문장으로 결속되었을 때, 보낸 데이터가 안전하게 처리될수 있도록 한다.

INSERT INTO City (ID, Name) VALUES (?, ?);

'?' 위치는 literal 데이터를 가진 대부분의 위치에서 사용될 수 있고, 예를 들면from에서 변형 될 수 있다

SELECT Name FROM City WHERE Name = 'Calgary';

위 문장이 아래로 바뀔 수 있다.

SELECT Name FROM City WHERE name = ?;

여기 전체 과정을 설명하기 위한 보다 복잡한 예제가 있다.

<?php
$mysqli = new mysqli('localhost', 'user', 'password', 'world');

/* 접속 체크 */
if (mysqli_connect_errno()) {
    printf("Connect failed: %s\\n", mysqli_connect_error());
    exit();
}

$stmt = $mysqli->prepare("INSERT INTO CountryLanguage VALUES (?, ?, ?, ?)");
$stmt->bind_param('sssd', $code, $language, $official, $percent);

$code = 'DEU';
$language = 'Bavarian';
$official = "F";
$percent = 11.2;

/* 미리준비된 문장 실행하기 */
$stmt->execute();

printf("%d Row inserted.\\n", $stmt->affected_rows);

/* 문장과 접속 닫기 */
$stmt->close();

/* CountryLanguage 테이블을 지운다 */
$mysqli->query("DELETE FROM CountryLanguage WHERE Language='Bavarian'");
printf("%d Row deleted.\\n", $mysqli->affected_rows);

/* 접속 닫기 */
$mysqli->close();
?>

bind_param() 이 처음 파라미터로서 짧은문장이 가지고 있는걸 주목하여라. 이것은 바운드 변수에서 데이터가 어떻게 처리되는지 알려주기 위해 사용되는 형식 문자열이다.

위의 스크립트 경우에서 ‘sssd’는 네번째 파라미터 $percent가 double 또는 float 변수를 포함하는 반면에, 처음 세개의 파라미터 $code, $language, $official가 문자열로서 보내질 것을 지적한다.

!!!!!!!!!용어 설명 시작
계가 bound parameter를 계속 바운드 파라미터라고 번역 했는데, 한글로 번역하자면, 결속 인자라고 할 수 있습니다. Bound는 bind의 과거분사로서 결합된, 결속된, 묶인 그런 의미가 있고, 우리가 포장할 때 상자를 끈으로 묶는(bound)는 의미가 있습니다.
!!!!!!!!!용어설명 끝

bind_param()에서, 각각의 바운드 변수는, 형식 문자열에서, 변수가 어떻게 핸들링되는 명세하는 다른 문자가 될 것이다.

예를 들면

$stmt->bind_param('s', $foo);
$stmt->bind_param('si', $foo, $bar);
$stmt->bind_param('sid', $foo, $bar, $baz);

바인트 타입은 mysqli 확장이 효육성을 위해 그것이 보내는 데이터를 어떻게 인코드(encode)하는지 알게 해준다.

결속(bind) 타입은 매우 간결하다. 데이터는 바운드 변수에서 정수(integer) 변수나 double 변수나 문자열(string)으로서 처리 될것이다.

MySQL 서버에 보내지는 long blobs 같은 특별한 타입도 있다
다음과 같은 데이블은 그 사용을 보여준다.

----------------------------
BIND    |  TYPE COLUMN TYPE
i        |  All INT types
d        |  DOUBLE and FLOAT
b        |  BLOBs
s        | All other types
--------------------------------

---------------------------바운드 결과

바운드 결과 미리준비된 문장은 php에서 가변 변수가 쿼리 결과들에서 데이터 필드 변수에 묶이게 되게 해준다.

이런 결속설정하는 과정이다.

* 쿼리 생성
* 쿼리를 미리준비하게 위해 MySQL 서버에 요청
* 미리준비된 쿼리에서 PHP 변수를 컬럼(columns)에 결속(bind)한다
* 새로운 데이터의 열이 바운드 변수로 적재되도록 요청한다.

여기  이 과정을 상세설명하는 짧은 코드가 있다.

<?php
$mysqli = new mysqli("localhost", "user", "password", "world");

if (mysqli_connect_errno()) {
    printf("Connect failed: %s\\n", mysqli_connect_error());
    exit();
}

/* 미리 준비된 문장 */
if ($stmt = $mysqli->prepare("SELECT Code, Name FROM Country ORDER BY Name LIMIT 5")) {
    $stmt->execute();

    /* 변수를 미리준비된 문장에 결속한다 */
    $stmt->bind_result($col1, $col2);

    /* 값을 불러들인다 */
    while ($stmt->fetch()) {
        printf("%s %s\\n", $col1, $col2);
    }

    /* 문장을 닫는다 */
    $stmt->close();
}
/* 접속을 닫는다 */
$mysqli->close();

?>


-----------------------바운드 파라미터와 바운드 결과를 같이 사용하기

이것은 바운드 파라미터와 바운드 결과를 함께 쓰는 법을 설명하는 보다 복잡한 예제이다.


<?php
$mysqli = new mysqli("localhost", "user", "password", "world");

if (mysqli_connect_errno()) {
    printf("Connect failed: %s\\n", mysqli_connect_error());
    exit();
}

/* 미리 준비된 문장 */
if ($stmt = $mysqli->prepare("SELECT Code, Name FROM Country WHERE Code LIKE ? LIMIT 5")) {

    $stmt->bind_param("s", $code);
    $code = "C%";

    $stmt->execute();

    /* 변수를 미리 준비된 문장에 결합, bind variables to prepared statement */
    $stmt->bind_result($col1, $col2);

    /* fetch values */
    while ($stmt->fetch()) {
        printf("%s %s\\n", $col1, $col2);
    }

    /* 문장 닫기 */
    $stmt->close();
}
/* 접속 닫기 */
$mysqli->close();

?>
/* 제가 보니 잘만 사용하면 대단히 효율적일 것 같다는 생각이 듭니다. 일종의 템플릿 같은 거라고 생각하시면 될 듯 싶습니다. */

---------------------------요약

이 기사는, 우리가 그 개발 과정의 빠른 요약과 함께 mysqli확장의 특징과 설계의 개요를 제공했다. 여러분은 MySQL의 미리준비된 문장과 그것의 장점을 이해했고 어떻게 사용하는지 알았고 Mysql 확장에 객체지향 인터페이스를 편하게 사용할 수 있을 것이다.

---------------------------번역자에 관하여

집에서 놀고 있는 백수


관련자료

댓글 0
등록된 댓글이 없습니다.
Today's proverb
유쾌한 사람은 자기 일에만 몰두하는 사람이 아니다. 때론 자신의 일을 전부 제쳐놓고 타인의 문제에 전력을 쏟는 열정이 있는 사람이다. 타인에게 자신의 힘을 나누어주고 마음을 열어주는 것은 자신의 삶을 행복하게 만드는 방법이다.