본문 바로가기
Language/php

PDO를 활용한 데이터베이스 연동

by 잘먹는 개발자 에단 2024. 9. 11.
// PDO 객체 생성
$pdo = new PDO("mysql:host=localhost;dbname=DB이름", "유저아이디", '유저비번');

// 에러 모드 설정
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

// SQL 쿼리 작성
$sql = "SELECT service_name, memo, userid, pw, updated FROM MyAccounts.accounts;";

// 쿼리 준비

$statements = $pdo->prepare($sql);

// 쿼리 실행
$statements->execute();


while($row = $statements->fetch(PDO::FETCH_ASSOC)) {
    $serviceName = $row['service_name'];
    $memo = $row['memo'];
    $userid = $row['userid'];
    $pw = $row['pw'];
    $updated = $row['updated'];

    echo "\n{$serviceName} - {$memo} - {$userid} - {$pw} - {$updated}";
}

 

 

mysql 데이터베이스와 연결하고 데이터를 가져와서 바인딩하기까지의 과정이다.

 

1. 먼저 데이터베이스 연결 문자열을 작성해서 붙여준다.

2. 에러 처리를 위한 에러모드를 설정한다.

$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

 

 

- 이를 통해서 PDO가 오류를 발생시켰을 때 어떻게 처리할 지를 결정한다.

- PDO::ERRMODE_SILENT(기본값)

아무런 에러 메시지를 출력하지 않고, 단순히 오류가 발생하면 내부적으로만 처리한다.

이 경우에 오류가 발생했을 때, 수동으로 확인해야 한다.

이 방식은 가장 조용하지만, 에러 발생 시에 추가 처리가 필요하기 때문에 디버깅이 어렵다.

 

- PDO::ERRMODE_WARNING

에러가 발생하면 경고 warning 을 출력한다.

스크립트가 중단되지는 않지만, 오류정보를 확인할 수 있다.

이 모드는 디버깅에 도움이 되지만, 여전히 스크립트를 중단하지 않기 때문에 주의해야 한다.

 

- PDO::ERRMODE_EXCEPTION ( 추천 )

이 모드를 사용한 상태인데

에러가 발생하면 예외 exception을 던진다.

이 예외는 try-catch 블록으로 잡을 수 있고, 스크립트가 중단된다.

오류 발생 시에 명확한 처리가 가능하고, 개발 단계에서 디버깅 및 문제 해결에 매우 도움이 된다. 

가장 추천하는 방법이다.

 

3. 다음으로는 쿼리를 작성해서 변수에 넣어준다.

$sql = "SELECT service_name, memo, userid, pw, updated FROM ACCOUNTS.accounts;";

 

4. 다음은 쿼리 준비과정을 거친다.

$statements = $pdo->prepare($sql);

prepare()

- SQL 쿼리를 미리 준비한다.

SQL 쿼리를 작성하지만, 아직 실제로 실행하지는 않고, 나중에 데이터를 입력해 실행할 준비를 한다.

prepare()가 호출되면 PDO는 서버로 쿼리를 보내서 미리 컴파일된 상태로 준비해둔다.

 

- 변수를 바인딩할 수 있는 환경을 제공한다.

쿼리에 변수가 포함된 경우에, 나중에 bindValue()나 bindParam()을 사용해서 안전하게 값을 바인딩할 수 있다. 

이를 통해서 SQL 삽입 공격을 방지한다.

// SQL 쿼리 작성 (변수는 :placeholder 형식으로 사용)
$sql = "SELECT service_name, memo, userid, pw, updated FROM ACCOUNTS.accounts WHERE userid = :userid;";

// 쿼리 준비
$statements = $pdo->prepare($sql);

// 변수 바인딩 (사용자 ID를 안전하게 바인딩)
$statements->bindValue(':userid', $userId);

// 쿼리 실행
$statements->execute();

 

이게 특이하다면 좀 특이할 수 있다.

prepare() 메서드는 PDO를 사용할 때 SQL 쿼리를 미리 준비하는 역할을 한다.

이건 Prepared Statement라고 불리고, 보안과 성능 면에서 많은 장점을 가진다.

 

Prepared Statement 준비된 문 은 SQL쿼리를 미리 컴파일하여 실행할 수 있게 만드는 방식이다.

prepare() 메서드는 SQL 쿼리를 미리 준비하고 이후에 데이터를 바인딩 한 후에 실행하는 방법이다. 

이를 통해서 쿼리 실행 과정에서 SQL 삽입 공격을 방지할 수 있다.

 

정리하자면 나중에 바인딩을 따로 함으로써 SQL삽입 공격을 방지하기 때문에 보안이 강화된다.

쿼리를 한번만 컴파일하고, 여러번 다른 값으로 실행할 수 있기 때문에 성능이 향상된다. 특히 반복적으로 같은 쿼리를 실행해야 할 때 유리하다.

SQL과 데이터 바인딩이 분리되어 코드가 더 명확하고 읽기 쉬워진다.

 

 

그렇다면 만약에 prepared statement 를 거치지 않고 바로 execute()를 하면 어떻게 될까?

애초에 prepare()와 execute() 는 세트이다.

쿼리를 값을 받아서 구성할 때 자바스크립트에서는 종종 문자열 리터럴을 써서 넣곤한다.

하지만 이것은 SQL 삽입공격에 매우 취약하다. 

 

때문에 prepare()에서 쿼리를 받고, 이후에 바인딩을 한 후에 execute()에서 이를 합쳐서 실행하는 것이다.

 

그럼에도 불구하고 execute() 만을 쓴다면 

이는 query() 메서드를 사용한 것과 같은 동작이 된다. 

 

즉 query()는 단일 SQL 쿼리를 실행할 때 사용되는 메서드이다.

$sql = "SELECT * FROM accounts WHERE userid = 'john_doe'";
$stmt = $pdo->query($sql);

query()

- query() 메서드는 쿼리를 준비하고 바로 실행한다.

- 단순한 쿼리에서 사용할 수 있다. 한번만 실행될 SQL에 적합하다.

- 변수를 쿼리 안에 직접 포함 시켜 실행할 경우에 이 방법을 사용할 수는 있지만, 보안 상 위험하다. SQL 삽입 공격이 가능하다.

 

 

prepare()와 execute()

// 쿼리 준비
$sql = "SELECT * FROM accounts WHERE userid = :userid";
$stmt = $pdo->prepare($sql);

// 안전하게 변수를 바인딩한 후 실행
$stmt->execute([':userid' => $userInput]);

- prepare()는 쿼리를 미리 준비하고 execute()에서 변수를 안전하게 바인딩해서 실행하는 방식이다. 

- 변수가 퐇마된 쿼리나 여러 번 실행할 쿼리에 적합하다.

- 특히 사용자가 입력한 데이터를 처리할 때는 반드시 prepare() - execute() 를 사용하는 것이 보안 상 중요하다.

 

 

5. 준비된 문을 실행한다. 우리는 따로 데이터바인딩 과정은 없었다. 

$statements->execute();

 

6. 결과를 변수에 할당해준다.

fetch()는 SQL 쿼리 결과를 가져올 때 사용한다.

그 결과를 다양한 방식으로 반환할 수가 있는데 해당 메서드 인자로 상수를 전달하면 된다.

그 상수들은 데이터를 어떤 형태로 반환할 지 결정하는데에 사용된다.

 

fetch()에서도 fetchAll()에서도 사용할 수 있다.

 

굉장히 많은데 한번 다 살펴보긴하자..

1. PDO::FETCH_ASSOC ( 위의 예시에서는 이 상수를 사용 )

설명: 쿼리 결과를 **연관 배열(associative array)**로 반환합니다. 각 컬럼의 이름을 배열의 키로 사용하여 데이터를 반환합니다.

형태: ['컬럼명' => 값]

예시:

$row = $stmt->fetch(PDO::FETCH_ASSOC);
print_r($row);  // ['userid' => 'john_doe', 'email' => 'john@example.com']

 

2. PDO::FETCH_NUM

설명: 쿼리 결과를 숫자 인덱스 배열로 반환합니다. 컬럼명 대신 숫자 인덱스를 사용합니다.

형태: [0 => 값, 1 => 값]

예시:

$row = $stmt->fetch(PDO::FETCH_NUM);
print_r($row);  // [0 => 'john_doe', 1 => 'john@example.com']

 

3. PDO::FETCH_BOTH

설명: 연관 배열과 숫자 인덱스 배열 모두를 포함하는 방식으로 결과를 반환합니다.

형태: ['컬럼명' => 값, 0 => 값]

예시:

$row = $stmt->fetch(PDO::FETCH_BOTH);
print_r($row);  // ['userid' => 'john_doe', 'email' => 'john@example.com', 0 => 'john_doe', 1 => 'john@example.com']

 

4. PDO::FETCH_OBJ

설명: 쿼리 결과를 객체 형태로 반환합니다. 컬럼명은 객체의 속성으로 접근할 수 있습니다.

형태: $row->컬럼명

예시:

$row = $stmt->fetch(PDO::FETCH_OBJ);
echo $row->userid;  // 'john_doe'
echo $row->email;    // 'john@example.com'

 

5. PDO::FETCH_CLASS

설명: 쿼리 결과를 지정된 클래스 객체로 반환합니다. 클래스의 속성으로 쿼리 결과를 바인딩합니다. 이 옵션을 사용할 때는 미리 정의된 클래스를 사용하거나 클래스를 정의해야 합니다.

형태: 클래스의 인스턴스

예시:

class User {
    public $userid;
    public $email;
}

$stmt->setFetchMode(PDO::FETCH_CLASS, 'User');
$row = $stmt->fetch();
echo $row->userid;  // 'john_doe'
echo $row->email;   // 'john@example.com'

 

6. PDO::FETCH_LAZY

설명: 결과를 연관 배열, 숫자 배열, 그리고 객체로 모두 접근할 수 있게 반환합니다. 데이터에 접근하는 방법을 유연하게 제공하지만 성능 면에서는 다소 비효율적일 수 있습니다.

형태: $row['컬럼명'], $row[0], $row->컬럼명

예시:

$row = $stmt->fetch(PDO::FETCH_LAZY);
echo $row->userid;  // 객체 방식
echo $row['userid'];  // 연관 배열 방식
echo $row[0];  // 숫자 인덱스 방식

 

 

7. PDO::FETCH_BOUND

설명: 쿼리 결과를 바인딩된 변수에 직접 저장합니다. bindColumn() 메서드와 함께 사용되며, 결과를 반환하지 않고, 바인딩된 변수에 값을 저장합니다.

형태: 변수를 통해 값에 접근

예시:

$stmt->bindColumn('userid', $userid);
$stmt->fetch(PDO::FETCH_BOUND);
echo $userid;  // 'john_doe'

 

8. PDO::FETCH_KEY_PAIR

설명: 쿼리 결과의 첫 번째 컬럼을 배열의 **키(key)**로, 두 번째 컬럼을 배열의 **값(value)**으로 사용하는 방식으로 반환합니다.

형태: [key => value]

예시:

$sql = "SELECT userid, email FROM accounts";
$stmt = $pdo->query($sql);
$rows = $stmt->fetchAll(PDO::FETCH_KEY_PAIR);
print_r($rows);  // ['john_doe' => 'john@example.com', 'jane_doe' => 'jane@example.com']

 

9. PDO::FETCH_UNIQUE

설명: 첫 번째 컬럼의 값을 배열의 키로 사용하고, 나머지 컬럼을 값으로 하는 배열을 반환합니다. 첫 번째 컬럼의 값은 고유해야 합니다.

형태: [unique_key => value]

예시:

$sql = "SELECT userid, email FROM accounts";
$stmt = $pdo->query($sql);
$rows = $stmt->fetchAll(PDO::FETCH_UNIQUE);
print_r($rows);  // ['john_doe' => ['email' => 'john@example.com'], 'jane_doe' => ['email' => 'jane@example.com']]

'Language > php' 카테고리의 다른 글

php자체에 대해서 알아보자  (7) 2024.09.12
inc 파일 ... 대신에?  (1) 2024.09.12
간단한 프로파일링 예시  (0) 2024.09.11
캐싱 예시  (0) 2024.09.11
해시 테이블 사용하기  (0) 2024.09.11