회원가입 | 고객센터 |
DESIGNONEX
디자인원엑스
About
Service
PR리그N
Q&AN
노하우N
공지사항N
통계
로그인 회원가입
고객센터
14. 데이터베이스

Database 직접 쿼리 개발

A Administrator
2026.05.19 18:15(수정됨) 8 0

1. 시작하기 — DB 인스턴스 얻기

DXCMS의 모든 DB 작업은 Database 싱글턴 클래스를 통해 이루어집니다. 어떤 PHP 파일(페이지, 스킨, 플러그인)에서든 아래 한 줄로 시작합니다.
$db = Database::getInstance();

// 테이블명 생성 — prefix(dx_) 자동 포함
$tbl = $db->table('posts');         // => "dx_posts"
$tbl = $db->table('members');       // => "dx_members"
$tbl = $db->table('studio_posts');  // => "dx_studio_posts"

※ 반드시 $db->table()을 사용하세요. 테이블명을 하드코딩하면 prefix 변경 시 모두 수정해야 합니다


2. BIGINT ID 핵심 주의사항

DXCMS는 posts, comments 등의 PK를 microtime 기반 16자리 BIGINT로 생성합니다. PHP 32bit 환경에서 (int) 캐스팅하면 값이 손상됩니다.
// ❌ 절대 금지 — 32bit PHP에서 값이 잘림
$id = (int)$_p['id'];                    // 1778651007020800 → 손상
$map[(int)$row['post_id']] = $row;       // 키 손상

// ✓ 올바른 방법 — 문자열로 처리
$id = (string)$_p['id'];                 // "1778651007020800" 유지
$map[(string)$row['post_id']] = $row;    // 키 정확히 저장

// 쿼리 바인딩은 문자열 그대로 전달
$db->row("SELECT * FROM `{$tbl}` WHERE id = ?", array($id));

※ PDO 설정에 ATTR_STRINGIFY_FETCHES=true 가 있어 DB에서 받은 값은 이미 문자열입니다. PHP 코드에서 (int) 캐스팅만 하지 않으면 됩니다.


3. SELECT — 데이터 조회


3-1. row() — 단일 행 조회

조건에 맞는 첫 번째 행을 연관 배열로 반환합니다. 결과 없으면 null 반환.
$db  = Database::getInstance();
$tbl = $db->table('members');

$member = $db->row(
    "SELECT * FROM `{$tbl}` WHERE id = ? AND status = 1",
    array($memberId)
);

if ($member) {
    echo $member['name'];
    echo $member['email'];
}


3-2. rows() — 여러 행 조회

조건에 맞는 모든 행을 2차원 배열로 반환합니다. 결과 없으면 빈 배열 반환.
$db  = Database::getInstance();
$tbl = $db->table('posts');

$posts = $db->rows(
    "SELECT id, title, created_at
     FROM `{$tbl}`
     WHERE board_id = ? AND status = 1
     ORDER BY id DESC LIMIT ?",
    array($boardId, 10)
);

foreach ($posts as $p) {
    echo $p['title'];
}


3-3. value() — 단일 값 조회

COUNT, SUM 등 집계 함수나 컬럼 하나의 값만 필요할 때 사용합니다.
$db  = Database::getInstance();
$tbl = $db->table('members');

// 회원 수
$count = $db->value("SELECT COUNT(*) FROM `{$tbl}` WHERE status = 1");

// 특정 회원의 포인트
$point = $db->value(
    "SELECT point FROM `{$tbl}` WHERE id = ?",
    array($memberId)
);


3-4. find() — 편의 메서드 (단일 행)

SQL 없이 조건 배열로 단일 행을 조회합니다.
$db = Database::getInstance();

// 기본
$member = $db->find('members', array('id' => $memberId));

// 반환 컬럼 지정
$member = $db->find(
    'members',
    array('email' => $email, 'status' => 1),
    'id, name, email'
);


3-5. findAll() — 편의 메서드 (여러 행)

SQL 없이 조건 배열로 여러 행을 조회합니다. 정렬과 LIMIT도 지정 가능합니다.
$db = Database::getInstance();

$rows = $db->findAll(
    'posts',                                        // 테이블명
    array('board_id' => $boardId, 'status' => 1),  // WHERE
    'id, title, created_at',                        // 반환 컬럼
    'id DESC',                                      // ORDER BY
    '10'                                            // LIMIT
);


3-6. IN 절 — 여러 ID 한 번에 조회

복수 ID를 한 번의 쿼리로 조회할 때 IN 절을 사용합니다. BIGINT ID는 반드시 문자열로 처리합니다.
$db  = Database::getInstance();
$tbl = $db->table('studio_posts');

// ID 배열 — 문자열 유지
$ids = array('1778651007020800', '1778766294011100');

// IN 절 플레이스홀더 생성
$ph = implode(',', array_fill(0, count($ids), '?'));

// 쿼리
$rows = $db->rows(
    "SELECT post_id, youtube_url FROM `{$tbl}` WHERE post_id IN ({$ph})",
    $ids
);

// string 키로 맵 생성 — (int) 절대 금지
$map = array();
foreach ($rows as $r) {
    $map[(string)$r['post_id']] = $r;
}

// 사용
$info = isset($map[(string)$postId]) ? $map[(string)$postId] : array();


4. INSERT — 데이터 추가


4-1. insertRow() — 편의 메서드

컬럼 => 값 배열을 전달하면 INSERT SQL을 자동 생성합니다. AUTO_INCREMENT PK 테이블에 사용합니다.
$db = Database::getInstance();

$db->insertRow('download_log', array(
    'file_id'    => $fileId,
    'member_id'  => $memberId,
    'ip'         => dx_ip(),
    'point_used' => 0,
    'created_at' => date('Y-m-d H:i:s'),
));

// 반환값: 마지막 AUTO_INCREMENT ID
$newId = $db->insertRow('categories', array(
    'name'       => '공지사항',
    'sort_order' => 1,
    'created_at' => date('Y-m-d H:i:s'),
));


4-2. insertWithMicrotimeId() — BIGINT ID 자동 생성

posts, comments 등 microtime 기반 BIGINT PK 테이블에 삽입합니다. ID를 자동 생성하고 중복 검사 후 삽입합니다.
$db = Database::getInstance();

$postId = $db->insertWithMicrotimeId('posts', array(
    'board_id'   => $boardId,
    'member_id'  => $memberId,
    'title'      => '제목',
    'content'    => '내용',
    'status'     => 1,
    'created_at' => date('Y-m-d H:i:s'),
    'updated_at' => date('Y-m-d H:i:s'),
));

// 반환값은 문자열 — 절대 (int) 캐스팅 금지
echo $postId; // "1778651007020800"


4-3. insert() / query() — 직접 SQL

INSERT … ON DUPLICATE KEY UPDATE 등 복잡한 구문은 직접 SQL을 작성합니다.
$db  = Database::getInstance();
$tbl = $db->table('studio_posts');
$now = date('Y-m-d H:i:s');

// 직접 INSERT
$db->query(
    "INSERT INTO `{$tbl}`
     (post_id, youtube_url, producer, creator, created_at, updated_at)
     VALUES (?, ?, ?, ?, ?, ?)",
    array($postId, $youtubeUrl, $producer, $creator, $now, $now)
);

// ON DUPLICATE KEY UPDATE (UPSERT)
$db->query(
    "INSERT INTO `{$db->table('settings')}` (setting_key, setting_value)
     VALUES (?, ?)
     ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value)",
    array('site_name', 'DXCMS')
);


5. UPDATE — 데이터 수정


5-1. updateRow() — 편의 메서드

수정할 데이터와 WHERE 조건을 각각 배열로 전달합니다. 모든 조건은 AND로 연결됩니다.
$db = Database::getInstance();

// 단일 컬럼 수정
$db->updateRow(
    'members',
    array('point' => $newPoint),   // SET
    array('id'    => $memberId)    // WHERE
);

// 여러 컬럼 수정
$db->updateRow(
    'studio_posts',
    array(
        'youtube_url' => $youtubeUrl,
        'producer'    => $producer,
        'updated_at'  => date('Y-m-d H:i:s'),
    ),
    array('post_id' => $postId)    // BIGINT — 문자열 그대로
);


5-2. query() — 직접 SQL UPDATE

증감 연산(+=, -=), CASE WHEN 등 복잡한 UPDATE는 직접 SQL을 작성합니다.
$db  = Database::getInstance();
$tbl = $db->table('members');

// 포인트 차감 — 조건부 (0 미만 방지)
$db->query(
    "UPDATE `{$tbl}` SET point = point - ? WHERE id = ? AND point >= ?",
    array($dlPoint, $memberId, $dlPoint)
);

// 조회수 +1
$db->query(
    "UPDATE `{$db->table('posts')}` SET view_count = view_count + 1 WHERE id = ?",
    array($postId)
);

// 반환값: 영향받은 행 수
$affected = $db->query(
    "UPDATE `{$tbl}` SET status = 0 WHERE last_login < ?",
    array(date('Y-m-d', strtotime('-1 year')))
);


6. DELETE — 데이터 삭제


6-1. deleteRow() — 편의 메서드

WHERE 조건 배열을 전달하면 DELETE SQL을 자동 생성합니다.
$db = Database::getInstance();

// 단일 행 삭제
$db->deleteRow('studio_posts', array('post_id' => $postId));

// 여러 조건 (AND)
$db->deleteRow(
    'download_log',
    array('member_id' => $memberId, 'file_id' => $fileId)
);


6-2. query() — 직접 SQL DELETE

IN 절이나 복잡한 조건이 필요한 삭제는 직접 SQL을 작성합니다.
$db  = Database::getInstance();
$tbl = $db->table('post_files');

// 게시글 파일 전체 삭제
$db->query(
    "DELETE FROM `{$tbl}` WHERE post_id = ?",
    array($postId)
);

// IN 절 삭제
$ids = array('1778651007020800', '1778766294011100');
$ph  = implode(',', array_fill(0, count($ids), '?'));
$db->query("DELETE FROM `{$tbl}` WHERE id IN ({$ph})", $ids);

// 날짜 조건 삭제
$db->query(
    "DELETE FROM `{$db->table('visit_log')}` WHERE created_at < ?",
    array(date('Y-m-d', strtotime('-30 days')))
);


7. 트랜잭션 — 원자적 처리

여러 쿼리를 하나의 단위로 처리해야 할 때 사용합니다. 오류 발생 시 rollback()으로 모든 변경을 취소합니다.
$db = Database::getInstance();

try {
    $db->begin(); // 트랜잭션 시작

    // 1. 포인트 차감
    $db->query(
        "UPDATE `{$db->table('members')}` SET point = point - ? WHERE id = ?",
        array($price, $memberId)
    );

    // 2. 구매 로그 기록
    $db->insertRow('purchase_log', array(
        'member_id'  => $memberId,
        'item_id'    => $itemId,
        'price'      => $price,
        'created_at' => date('Y-m-d H:i:s'),
    ));

    // 3. 재고 감소
    $db->query(
        "UPDATE `{$db->table('items')}` SET stock = stock - 1 WHERE id = ?",
        array($itemId)
    );

    $db->commit(); // 성공 시 커밋

} catch (Exception $e) {
    $db->rollback(); // 실패 시 전체 롤백
    dx_log('트랜잭션 실패: ' . $e->getMessage(), 'error');
}


8. 기타 편의 메서드


8-1. exists() — 존재 여부

$db = Database::getInstance();

// 이메일 중복 확인
$isDuplicate = $db->exists('members', array('email' => $email));

// 복합 조건
$isOwner = $db->exists('posts', array(
    'id'        => $postId,
    'member_id' => $memberId,
));
8-2. count() — 행 수
$db = Database::getInstance();

$total  = $db->count('members');                         // 전체
$active = $db->count('members', array('status' => 1));  // 조건부
8-3. tableExists() — 테이블 존재 여부
$db = Database::getInstance();

if (!$db->tableExists($db->table('studio_posts'))) {
    $db->query(
        "CREATE TABLE IF NOT EXISTS `{$db->table('studio_posts')}` (
            post_id     BIGINT UNSIGNED NOT NULL,
            youtube_url VARCHAR(500) NOT NULL DEFAULT '',
            PRIMARY KEY (post_id)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"
    );
}


8-4. escape() — 문자열 이스케이프

$db = Database::getInstance();

// MATCH ... AGAINST 등 플레이스홀더 미지원 구문에만 사용
$kw = $db->escape($userInput);
$db->rows("SELECT * FROM `{$tbl}` WHERE MATCH(title) AGAINST('{$kw}')");

// 일반 쿼리는 반드시 플레이스홀더(?) 사용
$db->row("SELECT * FROM `{$tbl}` WHERE id = ?", array($id));


9. 실전 예제 — studio 데이터 조회

board_latest 스킨 파일 안에서 dx_studio_posts 테이블을 직접 조회하는 실제 코드입니다.
// 1. DB 인스턴스 + 테이블명
$db  = Database::getInstance();
$tbl = $db->table('studio_posts'); // "dx_studio_posts"

// 2. posts 배열에서 ID 수집 — 문자열 유지 필수
$ids = array();
foreach ($posts as $p) {
    if (!empty($p['id'])) $ids[] = $p['id'];
}

// 3. IN 절로 한 번에 조회
$map = array();
if (!empty($ids)) {
    $ph   = implode(',', array_fill(0, count($ids), '?'));
    $rows = $db->rows(
        "SELECT post_id, youtube_url, producer, creator
         FROM `{$tbl}` WHERE post_id IN ({$ph})",
        $ids
    );
    // string 키로 맵 생성 — (int) 절대 금지
    foreach ($rows as $r) {
        $map[(string)$r['post_id']] = $r;
    }
}

// 4. 카드 렌더링에서 사용
foreach ($posts as $p) {
    $row   = isset($map[(string)$p['id']]) ? $map[(string)$p['id']] : array();
    $ytUrl = isset($row['youtube_url']) ? $row['youtube_url'] : '';

    // YouTube ID 추출
    $vidId = '';
    if ($ytUrl && preg_match('/[?&]v=([a-zA-Z0-9_-]{11})/', $ytUrl, $m)) {
        $vidId = $m[1];
    }
    $thumb = $vidId
        ? 'https://img.youtube.com/vi/' . $vidId . '/hqdefault.jpg'
        : '';
}
 

댓글0

로그인 후 댓글을 작성할 수 있습니다.
3.2 폴더 구조 core/ — CMS 엔진 2026.04.21 3.1 엔진 개요 실행 구조 개요 2026.04.21 3.1 엔진 개요 DX 엔진 구조 설명 2026.04.21 2. 시작 가이드 설치 시 보안 경로 구조 2026.04.20 2. 시작 가이드 서버별 설정 파일 상세 2026.04.20 2. 시작 가이드 기본 폴더 구조 설명 2026.04.20 2. 시작 가이드 설치 절차 2026.04.20 2. 시작 가이드 설치 환경 (PHP 버전, 서버 환경) 2026.04.20 비전 DXCMS 비전 2026.04.20 라이선스 DXCMS 오픈소스 및 제3자 소프트웨어 저작권 공지 2026.04.20 라이선스 DXCMS 라이선스 (LGPL 3.0) 2026.04.20 1. DX 철학 / 개념 생태계 확장 전략 2026.04.20 1. DX 철학 / 개념 DXCMS가 지향하는 방향 (플랫폼 vs 단순 CMS) 2026.04.20 1. DX 철학 / 개념 프레임워크 + CMS 통합 구조의 의미 2026.04.20 1. DX 철학 / 개념 기존 CMS와의 구조적 한계 2026.04.20 1. DX 철학 / 개념 왜 DXCMS를 만들었는가 2026.04.20 1. DX 철학 / 개념 DXCMS란 무엇인가 2026.04.20 DXCMS 활용 (CMS) DXCMS 날코딩•막코딩 완전 허용 2026.04.12
31
전체 회원
361
전체 게시글
324
전체 댓글
112
오늘 방문
31,096
전체 방문
1
현재 접속
인기글 7일 이내
최신글
최신댓글
목록