MySQL데이터베이스에 PDO(PHP Data Object) 사용법

프로그래밍/PHP 2018. 4. 1. 17:04
반응형

PDO(PHP Data Object) 는 이기종 데이터베이스에 접근하는 공통 API를 제공하는 것을 목적으로 만들어 졌습니다. mysqli 는 객체 스타일과, 절차적 스타일의 API를 제공하는데 비해  PDO 는 객체 스타일의  API 만을 제공합니다. PDO 는 예전에 사용하던 mysql API  와는 달리 Prepared Statement 를 제공하므로 SQL Injection 방어에 사용될  수 있습니다.

 

Preapred Statement 는 원래 SQL 을 미리 데이터베이스에서 컴파일해 두고, 파라미터 값만 바꿔서 처리해서 쿼리의 성능을 올리는데 사용되었지만, 요즘은 SQL Injection 방어용으로 더욱 많이 예기 되어지는것 같습니다. 그만큼 요즘은 보안이 큰 이슈로 등장 하였습니다.

 

PDO를 사용하기 위해서는 PHP 5.1.0 이상의 버전을 사용해야 합니다.

 

PHP 데이터베이스 API 를 사용하다 보면 어떤 한 작업을 하는데 즉, 동일한 기능을 구현하는데 사용할 수 있는 방법이 여러가지가 있는 경우가 많습니다.

 

데이터베이스에 연결하는데 사용하는 정보를 제공하는 방법이라든가, 스테이트먼트에 파라미터를 제공하는 방법 등 각각 사용하는 적절한 상황이 있어서 많은 방식이 존재할거라고 생각하지만 너무 복잡하고, 매뉴얼 찾아보기도 힘들어지는 경향이 있는 것 같습니다.

 

물론 이건 어디까지나 개인적인 생각입니다.

 

 

1. 데이터베이스에 연결하기


<?php
$dsn = "mysql:host=localhost;port=3306;dbname=testdb;charset=utf8";
try {
    $db = new PDO($dsn, "testuser", "testuser");
    $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
    $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    echo "데이터베이스 연결 성공!!<br/>";
} catch(PDOException $e) {
    echo $e->getMessage();
}
?>


데이터베이스에 연결하기 위해서 PDO 객체의 생성자에 DSN(Data Source Name), 아이디, 비밀번호를 인자로 입력합니다. DSN의 port 는 기본값 3306 이 경우 생략해도 됩니다. 데이터베이스 연결 객체를 생성한 후에 setAttribute 메소드를 사용해서 두 가지 속성을 지정하고 있습니다.

 

- PDO::ATTR_EMULATE_PREPARES: 이 속성은 Preppared Statement 를 데이터베이스가 지원 하지 않을 경우 에뮬레이션 하는 기능으로 false 를  지정해서 데이터베이스의 기능을 사용하도록 합니다.


- PDO::ATTR_ERRMODE : 이 속성은 PDO 객체가 에러를 처리하는 방식을 결정합니다.


PDO::ERRMODE_EXCEPTION 은 에러가 발행했을때 PDOException 을 throw 하도록합니다. 이 경우 try {} catch{} 를 사용하여 에러를 처리하면 됩니다. 다른 방법으로는 PDO::ERRMODE_SILENT(에러 코드만 설정), PDO::ERRMOdE_WARNING(E_WARNING 발생)이 있습니다.



2. 조회하기


<?php
$dsn = "mysql:host=localhost;port=3306;dbname=testdb;charset=utf8";

try {
    $db = new PDO($dsn, "testuser", "testuser");
    $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
    $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    $keyword = "%테스트%";
    $no = 1;

    $query = "SELECT num, name FROM tb_test WHERE name LIKE ? AND num > ?";

    $stmt = $db->prepare($query);
    $stmt->execute(array($keyword, $no));
    $result = $stmt->fetchAll(PDO::FETCH_NUM);

    for($i = 0; $i < count($result); $i++) {
        printf ("%s : %s <br />", $result[$i][0], $result[$i][1]);
    }

} catch(PDOException $e) {
    echo $e->getMessage();
}
?>


- 쿼리에 값을 바꿔 넣을 곳에 ? (placeholder)를 사용합니다.
- $stmt = $db->prepare($query); : 스테이트먼트를 생성합니다.
- $stmt->execute(array($keyword, $no)); : 배열로 값을 입력합니다.
- $result = $stmt->fetchAll(PDO::FETCH_NUM); : 결과를 가져옵니다.


  PDO::FETCH_NUM : 숫자 인덱스 배열 반환
  PDO::FETCH_ASSOC : 컬럼명이 키인 연관배열 반환
  PDO::FETCH_BOTH : 위 두가지 모두
  PDO::FETCH_OBJ : 컬럼명이 프로퍼티인 인명 객체 반환
 


파라미터가 없는 쿼리의 경우, 파라미터 배열 없이 호출하면 됩니다.


<?php
$query = "SELECT num, name FROM tb_test";

$stmt = $db->prepare($query);
$stmt->execute();
?>



Prepared Statement를 사용하는 또 다른 방법이 있습니다. statement 의 bindParam 또는 bindValue 메소드를 사용하는 방법 입니다. 두 가지 메소드를 서로 대체할 수 있습니다.


- Place Holder에 ?를 사용하고 값을 지정하는 메소드에는 숫자 인덱스를 사용해서 값을 바인딩할 수 있습니다.


<?php
$keyword = "%테스트%";
$no = 1;
$query = "SELECT num, name FROM tb_test WHERE name LIKE ? AND num > ?";

$stmt = $db->prepare($query);
$stmt->bindParam(1, $keyword, PDO::PARAM_STR);
$stmt->bindParam(2, $no, PDO::PARAM_INT);

//$stmt->bindValue(1, $keyword, PDO::PARAM_STR);
//$stmt->bindValue(2, $no, PDO::PARAM_INT);

$stmt->execute();
?>



- Place Holder에 이름을 지정해서 그 이름에 따라 값을 지정할 수 있습니다.

<?php
$keyword = "%테스트%";
$no = 1;

$query = "SELECT num, name FROM tb_test WHERE name LIKE :name AND num > :num";

$stmt = $db->prepare($query);

$stmt->bindParam(':name', $keyword, PDO::PARAM_STR);
$stmt->bindParam(':num', $no, PDO::PARAM_INT);

//$stmt->bindValue(':name', $keyword, PDO::PARAM_STR);
//$stmt->bindValue(':num', $no, PDO::PARAM_INT);

$stmt->execute();
?>


- 결과 값을 fetch 하는 다른 방법 입니다.


<?php
while ($row = $stmt->fetch(PDO::FETCH_NUM, PDO::FETCH_ORI_NEXT)) {
    printf ("%s : %s <br />", $row[0], $row[1]);
}
?>



3. 입력, 수정, 삭제 및 트랜잭션 처리


<?php
$dsn = "mysql:host=localhost;port=3306;dbname=testdb;charset=utf8";

try {
    $db = new PDO($dsn, "testuser", "testuser");
    
    $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
    $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    
    $name = "테스트";
    $query = "INSERT INTO tb_test (name) VALUES (?)";
    
    $stmt = $db->prepare($query);
    
    try {
        $db->beginTransaction();
        $stmt->execute(array($name));
        
        $db->commit();
        
        $id = $db->lastInsertId();
    } catch(PDOException $e) {
        $db->rollback();
        echo $e->getMessage();
    }
} catch(PDOException $e) {
    echo $e->getMessage();
}
?>


반응형

댓글을 달아 주세요