PHP

PHP

    ›데이터베이스

    시작하기

    • PHP 무료 강좌
    • PHP란 무엇인가요?
    • PHP 시작하기

    개발 환경 설정

    • 코드 에디터 준비하기
    • 윈도우(Windows) 환경에 APM 설치하기
    • macOS 환경에 APM 설치하기
    • 리눅스(Linux) 환경에 APM 설치하기
    • APM 환경 테스트 및 첫 코드 실행

    PHP 기초 문법

    • PHP는 어떻게 동작할까?
    • 기초 문법 - 태그, 마침표, 그리고 주석
    • 변수와 데이터 타입 - 프로그래밍의 기본기
    • 배열과 객체, 그리고 NULL
    • 변수와 함수의 이름 짓기 - 식별자(Identifier)
    • 절대 변하지 않는 값 - 상수(Constants)와 열거형(Enum)
    • 데이터를 요리하는 도구 - 연산자(Operators)
    • 연산자 우선순위 - 괄호의 마법
    • 흐름을 제어하는 마법 - 조건문 (if, match)
    • 흐름을 제어하는 마법 - 반복문
    • 재사용의 미학 - 함수(Function)
    • 변수의 생존 범위(Scope)와 수명
    • 흐름을 제어하는 마법 - 파일 포함과 예외 처리
    • 실무에서 숨 쉬듯 쓰이는 문자열과 배열 함수
    • 문자열을 다루는 마법의 주문 - 정규표현식(Regex)
    • 에러를 두려워하지 마라 - 디버깅과 에러 처리

    웹 개발 기초

    • 절대 믿지 마라! - 미리 정의된 변수와 외부 입력 처리
    • 사용자와 소통하는 창구 - HTML 폼(Form) 다루기
    • 나를 기억해 줘! - 쿠키(Cookie)와 세션(Session)
    • 서버의 기록 보관소 - 파일 입출력(File I/O)

    데이터베이스

    • 데이터베이스(DB)와 SQL 기초 - 데이터의 든든한 금고
    • 데이터베이스와의 안전한 대화 - PDO 기초

    실전 프로젝트

    • 첫 번째 실전 프로젝트 - 방명록(Guestbook) 만들기
    • 실전 프로젝트 2단계 - 기본 게시판(CRUD) 만들기
    • 실전 프로젝트 3단계 - 계층형(답변형) 게시판 만들기
    • 실전 프로젝트 4단계 - 게시판 레벨업 (댓글, 보안, 성능)
    • 실전 프로젝트 5단계 - 안전한 회원가입과 로그인 시스템

    데이터베이스와의 안전한 대화 - PDO 기초

    데이터베이스를 다루는 SQL 문법을 익혔으니, 이제 PHP 프로그램에서 그 데이터베이스에 접속하고 명령을 내리는 방법을 배울 차례입니다.

    1. PDO(PHP Data Objects)란?

    과거에는 PHP에서 MySQL에 접속할 때 mysql_connect()라는 아주 직관적인 함수를 사용했습니다. 하지만 이 함수들은 보안에 너무 취약해서 최신 PHP(7.0 이상)에서는 완전히 흔적도 없이 삭제되었습니다.

    그 자리를 완벽하게 대체한 것이 바로 PDO입니다. PDO는 MySQL뿐만 아니라 오라클, PostgreSQL, SQLite 등 어떤 데이터베이스를 쓰더라도 똑같은 문법으로 조종할 수 있게 해주는 아주 강력하고 안전한 만능 리모컨입니다.


    2. 데이터베이스 접속하기

    가장 먼저 데이터베이스에 접속하는 코드부터 작성해 보겠습니다.

    <?php
    declare(strict_types=1);
    
    // DSN (Data Source Name): "어떤 DB의, 어느 주소의, 무슨 데이터베이스에 접속할지"를 적는 주소창입니다.
    $dsn = 'mysql:host=localhost;dbname=mydb;charset=utf8mb4';
    
    try {
        $pdo = new PDO($dsn, '아이디', '비밀번호', [
            // 1. 에러가 나면 화면에 엉뚱한 거 띄우지 말고 바로 예외(Exception)를 던져라
            PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION, 
            // 2. 데이터를 가져올 때는 무조건 다루기 편한 연관 배열(Associative Array)로 가져와라
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,       
            // 3. PHP가 가짜로 방어하지 말고, DB의 진짜 Prepared Statement 기능을 사용해라
            PDO::ATTR_EMULATE_PREPARES   => false,                  
        ]);
    } catch (PDOException $e) {
        // 🚨 절대 에러 메시지($e->getMessage())를 화면에 그대로 echo로 출력하지 마세요! 해커에게 DB 구조를 바치는 꼴입니다.
        error_log("DB 접속 실패: " . $e->getMessage());
        exit('데이터베이스 접속에 일시적인 문제가 발생했습니다.');
    }
    

    3. SQL 명령 내리기 (가장 중요)

    PDO에서 SQL 명령을 내리는 방식은 크게 두 가지로 나뉩니다. 이 두 가지를 완벽하게 구별해서 써야만 해킹을 막을 수 있습니다.

    ① 외부 입력값이 아예 없는 안전한 쿼리: query()

    내가 직접 짠 고정된 쿼리만 날릴 때 사용합니다.

    <?php
    // 사용자 입력이 전혀 섞이지 않은 100% 안전한 쿼리
    $stmt = $pdo->query("SELECT * FROM users ORDER BY id DESC LIMIT 5");
    $rows = $stmt->fetchAll();
    

    ② 외부 입력값이 단 하나라도 섞인 쿼리: Prepared Statement (필수!)

    사용자가 폼에서 입력한 글자나, 주소창($_GET)으로 넘어온 값이 SQL 문장에 단 하나라도 섞여 들어간다면 무조건, 예외 없이 Prepared Statement 방식을 써야 합니다. 이것이 이른바 'SQL 인젝션' 공격을 완벽하게 틀어막는 유일한 정석입니다.

    <?php
    $name = $_POST['name']; // 사용자가 입력한 값
    
    // ❌ 최악의 코드: 입력값을 문자열 결합으로 직접 끼워 넣기 (절대 금지!)
    $pdo->query("SELECT * FROM users WHERE name = '{$name}'"); 
    // 만약 사용자가 name 란에 "' OR '1'='1" 이라고 적으면 모든 회원 정보가 다 뚫립니다.
    
    // 🟢 올바른 코드: ? 나 :이름 형태의 '빈칸(Placeholder)'을 뚫어놓고 나중에 값을 안전하게 채워 넣습니다.
    $stmt = $pdo->prepare("SELECT * FROM users WHERE name = :name");
    $stmt->execute([':name' => $name]); 
    $rows = $stmt->fetchAll();
    

    prepare()와 execute()를 거치면, 사용자가 아무리 악의적인 해킹 코드를 넣어도 PHP가 이를 "아, 이건 명령어가 아니라 단순한 문자열(글자)이구나" 하고 안전하게 캡슐화해 버립니다.


    4. 데이터 꺼내오기 (SELECT 결과 받기)

    조회(SELECT)를 한 뒤에는 결과물을 PHP 배열로 끄집어내야 합니다.

    • fetchAll(): 게시판 목록처럼 여러 개의 행(Row)을 한꺼번에 2차원 배열로 가져올 때 씁니다.
    • fetch(): 게시글 상세 보기처럼 딱 하나의 행만 가져올 때 씁니다. 만약 찾는 데이터가 없으면 false를 뱉습니다.
    • fetchColumn(): "총 회원 수가 몇 명이지?" 처럼 딱 하나의 값(숫자나 글자)만 꺼낼 때 아주 유용합니다.
    <?php
    $count = (int) $pdo->query("SELECT COUNT(*) FROM users")->fetchColumn();
    echo "총 회원 수: {$count}명";
    

    5. 데이터 바꾸기 (INSERT, UPDATE, DELETE)

    <?php
    // [INSERT] 방금 넣은 글의 번호 알아내기
    $stmt = $pdo->prepare("INSERT INTO users (name, age) VALUES (:name, :age)");
    $stmt->execute([':name' => '홍길동', ':age' => 30]);
    
    $newId = (int) $pdo->lastInsertId(); // 방금 추가된 행의 id(Auto Increment 값)를 바로 가져옵니다.
    echo "새 회원 번호: {$newId}";
    
    // [UPDATE / DELETE] 몇 개나 바뀌었는지 알아내기
    $stmt = $pdo->prepare("UPDATE users SET age = :age WHERE name = :name");
    $stmt->execute([':age' => 31, ':name' => '홍길동']);
    
    $affected = $stmt->rowCount(); // 이 명령으로 인해 실제로 수정되거나 지워진 행의 개수를 알려줍니다.
    echo "{$affected}명의 정보가 수정되었습니다.";
    

    6. 묶어서 처리하기: 트랜잭션 (Transaction)

    '계좌 이체'를 생각해 봅시다. A의 통장에서 돈을 빼고(1번 쿼리), B의 통장에 돈을 넣어야(2번 쿼리) 합니다. 그런데 A 통장에서 돈은 빠졌는데, 갑자기 서버가 멈춰서 B 통장에 입금이 안 되면 어떻게 될까요? 큰일 나겠죠.

    이렇게 "여러 개의 쿼리가 모두 완벽하게 성공해야만 진짜로 반영하고, 중간에 하나라도 삑사리가 나면 처음 상태로 싹 다 되돌려라(Rollback)"라고 묶어주는 기능이 트랜잭션입니다.

    <?php
    // 트랜잭션 시작 (이제부터 내리는 명령은 임시 상태가 됩니다)
    $pdo->beginTransaction();
    
    try {
        $pdo->prepare("UPDATE accounts SET balance = balance - 10000 WHERE id = 1")->execute();
        $pdo->prepare("UPDATE accounts SET balance = balance + 10000 WHERE id = 2")->execute();
    
        // 두 명령이 모두 에러 없이 무사히 통과했다면, DB에 진짜로 확정 지어라!
        $pdo->commit(); 
        
    } catch (PDOException $e) {
        // 중간에 에러가 났다면, 여태까지 했던 작업을 전부 무효로 만들고 처음으로 되돌려라!
        $pdo->rollBack(); 
        error_log("계좌 이체 실패: " . $e->getMessage());
        exit('이체 처리 중 오류가 발생했습니다.');
    }
    

    7. 실무 선배의 팁: db.php 파일 똑똑하게 만들기

    게시판을 만들 때 require 'db.php'를 여기저기서 여러 번 부르다 보면, 자칫 똑같은 DB 접속을 두 번 세 번 반복해서 서버 메모리를 낭비할 수 있습니다. 이를 막기 위해 static 변수를 활용한 함수 하나를 만들어 두면 아주 깔끔해집니다.

    <?php
    // db.php
    declare(strict_types=1);
    
    function getPdo(): PDO {
        // static 변수는 함수가 끝나도 값이 사라지지 않고 계속 살아있습니다.
        static $pdo = null;
    
        // 만약 최초 호출이라 $pdo가 비어있다면, 그때만 접속을 맺습니다.
        if ($pdo === null) {
            $dsn = 'mysql:host=localhost;dbname=mydb;charset=utf8mb4';
            $pdo = new PDO($dsn, '아이디', '비번', [
                PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
                PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
                PDO::ATTR_EMULATE_PREPARES   => false,
            ]);
        }
    
        // 두 번째 호출부터는 이미 접속해 둔 $pdo 객체를 재활용해서 돌려줍니다.
        return $pdo;
    }
    

    이제 어떤 파일에서든 $pdo = getPdo(); 한 줄만 쓰면, 수백 번을 호출해도 DB 연결은 딱 한 번만 맺어지므로 서버가 아주 편안해합니다.

    Last updated on 2026-4-19 by Myeongjin Cho
    ← 데이터베이스(DB)와 SQL 기초 - 데이터의 든든한 금고첫 번째 실전 프로젝트 - 방명록(Guestbook) 만들기 →
    • 1. PDO(PHP Data Objects)란?
    • 2. 데이터베이스 접속하기
    • 3. SQL 명령 내리기 (가장 중요)
      • ① 외부 입력값이 아예 없는 안전한 쿼리: query()
      • ② 외부 입력값이 단 하나라도 섞인 쿼리: Prepared Statement (필수!)
    • 4. 데이터 꺼내오기 (SELECT 결과 받기)
    • 5. 데이터 바꾸기 (INSERT, UPDATE, DELETE)
    • 6. 묶어서 처리하기: 트랜잭션 (Transaction)
    • 7. 실무 선배의 팁: db.php 파일 똑똑하게 만들기
    커뮤니티
    PHP 공식 웹사이트한국 PHP 개발자 커뮤니티
    유용한 정보
    책 소스 코드
    Copyright © 2026 EZPHP.NET