Journey to Security/DBMS

CentOS 7 환경에서 PHP로 데이터베이스 연동하기

Cordilog 2026. 4. 7. 18:36

1. PHP의 데이터베이스 연동 방식

 

PHP에서 데이터베이스(DBMS)에 접속하기 위해 사용하는 함수군은 크게 세 가지로 발전해 왔다.

1️⃣ mysql_???() 함수 (구형 방식)

과거에 사용되던 방식으로, 현재는 보안과 성능 문제로 인해 PHP 7.0 이상부터는 완전히 삭제되었다.

  • mysql_connect(): DBMS 접속
  • mysql_query(): 쿼리 실행
  • mysql_num_rows(): 결과 행의 개수 확인

2️⃣ mysqli_???() 함수 (개선된 방식)

MySQL Improved의 약자로, MySQL 4.1.13 이상의 기능을 지원하기 위해 만들어졌다.

객체지향 방식과 절차지향 방식을 모두 지원한다.

  • 객체지향 스타일: $mysqli = new mysqli(...) 형태로 사용.
  • 절차지향 스타일: mysqli_connect(...) 형태로 사용하며, 기존 mysql 함수와 구조가 유사하여 전환이 쉽다.

3️⃣ PDO (PHP Data Objects)

여러 종류의 데이터베이스(MySQL, PostgreSQL, Oracle 등)를 동일한 인터페이스로 다룰 수 있게 해주는 추상화 레이어다.

보안상 가장 권장되며, 특히 'Prepared Statement'를 통한 SQL 인젝션 방어에 탁월하다.

 

2. MariaDB 접속을 위한 함수 

이번 실습에서는 mysqli의 절차지향 방식을 사용하여 접속 프로세스를 익힌다.

1️⃣ mysqli_connect 함수의 구조

PHP 내부에서 정의된 함수의 원형은 다음과 같다.

mysqli_connect(
    ?string $hostname = null,
    ?string $username = null,
    #[\SensitiveParameter] ?string $password = null,
    ?string $database = null,
    ?int $port = null,
    ?string $socket = null
): mysqli|false

 

  • ?string: 해당 인자값은 문자열(string)이거나 null일 수 있음을 의미한다.
  • #[\SensitiveParameter]: PHP 8.2부터 도입된 속성(Attribute)이다. 에러가 발생하여 스택 트레이스(Stack Trace)가 출력될 때, 비밀번호와 같은 민감 정보가 화면에 노출되지 않도록 가려주는 역할을 한다.
  • 반환형 (mysqli|false): 접속에 성공하면 mysqli 객체를 반환하고, 실패하면 false를 반환한다.

2️⃣ 주요 연동 함수

함수명 설명
mysqli_connect($host, $user, $pw, $db) DB 서버에 접속 및 세션 수립
mysqli_query($conn, $query) 연결된 세션을 통해 SQL 질의문 실행
mysqli_fetch_row($result) 실행 결과(Result set)에서 한 행씩 데이터를 가져옴
mysqli_free_result($result) 사용이 끝난 결과 집합 메모리 해제
mysqli_close($conn) DB 연결 종료

 

 

 

3️⃣ 데이터베이스 연동 프로세스

 

 

 

3. 실습 환경 설정: PHP 에러 출력 활성화

개발 단계에서는 문법 오류나 DB 접속 오류를 즉시 확인해야 하므로 php.ini 설정을 변경해야 한다.

  1. 설정 파일 수정: vi /etc/php.ini를 열어 478라인 근처의 display_errors 항목을 On으로 변경한다.
  2. 서비스 재시작: 설정 적용을 위해 아파치 서버를 재시작한다.

[root@victim php]# vi /etc/php.ini
display_errors = On

[root@victim php]# systemctl restart httpd.service

 

📌아파치와 PHP의 결합 방식 (mod_php)

CentOS 7에서 기본적으로 APM을 구축하면 아파치가 PHP를 모듈(mod_php) 형태로 포함하는 경우가 많다.

  • 이 방식에서 PHP는 독립적인 프로그램이 아니라 아파치의 일부분처럼 동작한다.
  • 즉, 아파치 프로세스가 뜰 때 PHP 환경 설정도 같이 불러와지기 때문에 아파치를 재시작해야 PHP 설정도 갱신되는 구조다.

 

4. 실습 1: MariaDB 기본 접속 및 데이터 조회

1️⃣ 코드 작성 (dbconnect1.php)

이 코드는 MariaDB에 접속하여 현재 존재하는 데이터베이스 목록 중 상위 2개를 출력하는 예제다.

<?php
// dbconnect1.php
$dbhost = "localhost";
$dbuser = "root";   
$dbpass = "1234"; // 실제 패스워드 입력
$dbname = "test";

// 1. DB 접속
$conn = mysqli_connect($dbhost, $dbuser, $dbpass, $dbname);

// 2. 쿼리 준비 및 실행
$query  = "SHOW DATABASES";
$result = mysqli_query($conn, $query);

// 3. 데이터 가져오기 (첫 번째 행)
$row    = mysqli_fetch_row($result);
print($row[0] . "<br>");  // information_schema 출력 예상

// 4. 데이터 가져오기 (두 번째 행)
$row    = mysqli_fetch_row($result);
print($row[0]);  // mysql 출력 예상

// 5. DB 연결 종료
mysqli_close($conn);
?>

 

2️⃣ 시퀀스 다이어그램

3️⃣ 예상 결과

  • 비밀번호가 틀렸을 경우: 웹 페이지에 Access denied for user 'root'@'localhost' 경고 메시지가 출력된다. 이는 콘솔에서 mysql -u root -p 접속 시 실패하는 것과 동일한 원리다.
  • 성공 시: 브라우저 화면에 information_schema와 mysql이 줄바꿈되어 나타난다.

 

5. 실습 2: 웹과 DB 연동 (회원 목록 출력)

데이터베이스에 실제 테이블을 생성하고, PHP에서 이를 조회하여 웹 화면에 뿌려주는 실습이다.

1️⃣ MariaDB 데이터 준비

터미널에서 test 데이터베이스에 접속하여 member 테이블을 생성하고 샘플 데이터를 입력한다.

mysql test

CREATE TABLE member (
    no int,
    userid varchar(15),
    userpass varchar(20)
);

INSERT INTO member VALUES(1, 'admin', '1234');
INSERT INTO member VALUES(2, 'whitehat', '2026');
INSERT INTO member VALUES(3, 'blackhat', '1111');

2️⃣ PHP 코드 작성 (dbmemberlist.php)

<?php
// dbmemberlist.php
$conn = mysqli_connect("localhost", "root", "1234", "test");

$query  = "SELECT * FROM member";
$result = mysqli_query($conn, $query);

// 반복적으로 행을 읽어와 출력
while($row = mysqli_fetch_row($result)) {
    print("$row[0] $row[1] $row[2] <br>");
}

mysqli_close($conn);
?>

 

3️⃣ 시퀀스 다이어그램

4️⃣ 예상 결과

URL http://192.168.100.10/php/dbmemberlist.php 접속 시 다음과 같이 출력된다.

1 admin 1234
2 whitehat 2026
3 blackhat 1111

 

6. 실습 3: 게시판 페이징(Pagination) 로직 구현

방대한 데이터를 한 화면에 모두 표시하면 가독성이 떨어지고 부하가 발생한다.

이를 해결하기 위해 데이터를 일정 단위로 나누어 보여주는 페이징 처리가 필수적이다.

 

1️⃣ 변수 정의

  1. 총 게시글 수($total\_post$): DB에 저장된 전체 레코드 개수.
  2. 페이지당 글 수($limit$): 한 화면에 보여줄 게시글 개수.
  3. 총 페이지 수: ceil(총 게시글 수 / 페이지당 글 수)
  4. 시작 위치($start$): 현재 페이지에 따라 가져올 데이터의 시작 인덱스. (현재 페이지 - 1) * 페이지당 글 수

2️⃣ PHP 코드 작성

🟢 [page_link.php] - 링크 생성 함수

이 파일은 하단에 [이전] [1] [2] [3] [다음] 형태의 HTML을 생성하는 함수를 담고 있다.

<?php
function paging($total_post, $post_per_page, $current_page) {
    $total_page = ceil($total_post / $post_per_page);
    $html = "<div class='paging'>";

    // [이전] 버튼: 1페이지가 아닐 때만 링크 활성
    if ($current_page > 1) {
        $prev_page = $current_page - 1;
        $html .= "<a href='list.php?page={$prev_page}'>[이전]</a> ";
    } else {
        $html .= "[이전] ";
    }

    // 페이지 번호 출력
    for ($i = 1; $i <= $total_page; $i++) {
        if ($i == $current_page) {
            $html .= "<strong>$i</strong> "; // 현재 페이지 강조
        } else {
            $html .= "<a href='list.php?page={$i}'>[{$i}]</a> ";
        }
    }

    // [다음] 버튼
    if ($current_page < $total_page) {
        $next_page = $current_page + 1;
        $html .= "<a href='list.php?page={$next_page}'>[다음]</a>";
    } else {
        $html .= "[다음]";
    }

    $html .= "</div>";
    return $html;
}
?>

 

 

🟢 [list.php] - 메인 목록 페이지

배열 데이터를 활용하여 페이징 로직을 재현한다.

실제 서비스에서는 array_slice 대신 SQL의 LIMIT $start, $limit 구문을 사용하게 된다.

 

<?php
include "page_link.php";

// 1. 샘플 데이터 (실제로는 DB에서 가져옴)
$board = array(
    array("num" => 1,  "subject" => "게시글 1",  "name" => "홍길동"),
    // ... (중략) ...
    array("num" => 15, "subject" => "게시글 15", "name" => "이병헌")
);

// 최신글이 위로 오도록 역순 정렬
$board = array_reverse($board);

// 2. 페이징 변수 설정
$limit = 5; // 한 페이지에 5개씩
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
if ($page < 1) $page = 1;

$total_post = count($board);
$total_page = ceil($total_post / $limit);
if ($page > $total_page) $page = $total_page;

// 3. 데이터 추출 범위 계산
$start = ($page - 1) * $limit;
$page_data = array_slice($board, $start, $limit);
?>
<table>
    <tr><th>번호</th><th>제목</th><th>작성자</th></tr>
    <?php foreach ($page_data as $row) { ?>
    <tr>
        <td><?php echo $row["num"]; ?></td>
        <td><?php echo $row["subject"]; ?></td>
        <td><?php echo $row["name"]; ?></td>
    </tr>
    <?php } ?>
</table>
<?php echo paging($total_post, $limit, $page); ?>

 

3️⃣ 시퀀스 다이어그램

 

4️⃣ 예상 결과

  • list.php 최초 접속 시: 15번부터 11번까지 게시글이 보이고 하단에 [1] [2] [3] [다음] 링크가 생성된다.
  • [2] 클릭 시: URL이 list.php?page=2로 변경되며 10번부터 6번 게시글이 출력된다.