알짜게시판

그누보드7 데이터를 WordPress WXR로 변환하기

WXR 변환 방식 선택과 구현 방향

WordPress import는 WXR XML 구조를 기반으로 동작한다. 이 구조는 단순한 게시글 목록이 아니라, 작성자, 카테고리, 댓글, 메타데이터까지 포함하는 RSS 확장 포맷이다. 따라서 단순 데이터 export가 아니라 WordPress가 이해할 수 있는 구조로 변환하는 것이 핵심이다.

구현 방향은 크게 세 가지로 잡았다. 첫 번째는 게시판을 WordPress 카테고리로 매핑하는 것, 두 번째는 게시글과 댓글을 그대로 유지하는 것, 세 번째는 첨부파일 정보를 메타데이터 형태로 포함하는 것이다. 첨부파일 자체를 업로드하는 방식도 고려했지만, 네트워크 비용과 실패 가능성을 고려해 경로 정보만 전달하는 방식으로 제한했다.

다른 접근 방식으로는 WordPress REST API를 사용해 게시글을 하나씩 생성하는 방법도 있다. 이 방식은 실시간 업로드가 가능하지만, 8,000건 기준으로 API 요청이 최소 8,000번 발생하며, 속도 제한이나 인증 문제가 발생할 수 있다. 반면 WXR 방식은 한 번의 import로 처리되기 때문에 대량 데이터 이전에서는 안정적인 선택이다.

DB 연결과 게시판 선택 처리 흐름

스크립트의 입력은 URL 파라미터다. board, board_id, board=all 중 하나를 받는다. 이 값에 따라 조회할 게시판 범위를 결정한다. 입력 → 조건 판단 → 쿼리 선택 → 게시판 목록 반환의 흐름으로 동작한다.

DB 연결은 .env 파일을 파싱하는 방식으로 구현했다. 환경 변수에서 DB_HOST, DB_DATABASE 등을 읽어 PDO로 연결한다. 이 방식은 코드에 직접 DB 정보를 넣지 않기 때문에 배포 환경에서 수정이 필요 없다. PDO를 사용한 이유는 예외 처리와 prepared statement를 동시에 처리하기 위해서다.

게시판 조회 단계에서는 is_active = 1 조건을 추가해 비활성 게시판을 제외했다. 이후 각 게시판별로 게시글을 순회하면서 데이터를 수집한다. 게시글은 depth=0 조건을 통해 원글만 가져오고, 댓글은 별도의 쿼리로 연결한다.

게시글과 댓글 변환 로직

게시글 변환은 입력 데이터의 형태에 따라 처리 방식이 달라진다. 입력은 DB에서 조회한 raw 텍스트이며, content_mode 값에 따라 Markdown 또는 HTML로 분기된다. Markdown인 경우 markdown_to_html() 함수를 통해 HTML로 변환한다.

이 함수는 Parsedown 라이브러리가 있을 경우 이를 사용하고, 없으면 정규식을 이용한 최소 변환을 수행한다. 입력 → 변환 방식 선택 → HTML 생성 → 반환의 흐름이다. 이 방식은 외부 라이브러리가 없는 환경에서도 동작하도록 하기 위한 선택이다.

댓글은 게시글 ID를 기준으로 조회하며, parent_id 값을 이용해 계층 구조를 유지한다. WordPress XML에서는 <wp:comment_parent> 태그로 이를 표현한다. 댓글이 없는 경우 comment_status를 closed로 설정하고, 하나라도 있으면 open으로 설정한다. 이 조건은 WordPress import 시 댓글 허용 여부에 영향을 준다.

실제 테스트에서는 게시글 8,000건 기준으로 XML 생성 시간이 약 3.2초 수준이었다. DB 조회 시간이 대부분을 차지했고, XML 출력 자체는 상대적으로 빠르게 처리됐다.

WordPress WXR XML 생성 구조

XML 생성은 출력 버퍼 없이 바로 echo로 작성했다. 이유는 메모리 사용량을 줄이기 위해서다. 모든 데이터를 배열로 모은 뒤 한 번에 출력하는 방식은 10MB 이상의 메모리를 사용할 수 있기 때문에 스트리밍 방식으로 처리했다.

구조는 RSS → channel → item 순서로 구성된다. 게시판은 <wp:category>로 등록하고, 게시글은 <item>으로 생성한다. 게시글마다 <category domain="category">를 추가해 게시판과 연결한다.

첨부파일은 WordPress의 attachment post로 생성하지 않고, <wp:postmeta>에 JSON 형태로 저장한다. 이 방식은 import 이후 별도의 스크립트를 통해 파일을 다시 연결하는 용도로 사용한다. 이미지의 경우 thumbnail 정보가 있으면 _thumbnail_path 메타를 추가한다.

작성자 정보는 기본적으로 하나의 author로 통합했다. 사용자 계정 매핑까지 포함하면 구조가 복잡해지고, WordPress import 시 충돌 가능성이 있기 때문이다.

그누보드7 WordPress 변환 시 고려해야 할 제한 사항

이 스크립트는 게시글과 댓글 구조를 유지하는 데 초점을 맞췄다. 따라서 WordPress media 라이브러리와 완전히 연동되지는 않는다. 첨부파일은 경로만 전달되기 때문에 실제 파일은 별도로 업로드해야 한다.

또한 사용자 계정은 매핑되지 않는다. 작성자 이름만 유지되며, WordPress 사용자 계정과 연결되지 않는다. 다수의 작성자가 있는 경우 별도의 매핑 테이블을 추가해야 한다.

이 방식은 데이터 양이 많은 경우에 적합하다. 게시글 수가 1,000건 이하라면 REST API 방식도 고려할 수 있다. 반대로 5,000건 이상이라면 WXR 방식이 안정적이다. 특히 댓글 구조가 중요한 경우에는 XML 기반 접근이 더 안전하게 작동한다.

확장 방향으로는 첨부파일을 자동으로 다운로드해 WordPress attachment로 생성하는 기능을 추가할 수 있다. 또는 카테고리 계층 구조를 반영하도록 개선할 수도 있다. 현재 구조는 단일 게시판 → 단일 카테고리 매핑에 맞춰져 있기 때문에, 복잡한 분류 체계를 사용하는 경우에는 추가 설계가 필요하다.

<?php
/**
 * 그누보드7 → WordPress WXR 내보내기
 *
 * 사용법:
 *   ?board=자유게시판슬러그          게시판 slug로 선택
 *   ?board_id=1                      게시판 ID로 선택
 *   ?board=all                       전체 게시판 내보내기
 *
 * 예) /gnu7_to_wp.php?board=free
 */

// ─── 보안: 로컬 또는 허용 IP에서만 접근 ─────────────────────────────────────
$allowed_ips = ['127.0.0.1', '::1'];
if (!in_array($_SERVER['REMOTE_ADDR'] ?? '', $allowed_ips, true)) {
    http_response_code(403);
    exit('Forbidden');
}

if (!isset($_GET['board']) && !isset($_GET['board_id'])) {
    echo '<pre>';
    echo "사용법:\n";
    echo "  ?board={slug}      — 게시판 slug로 선택\n";
    echo "  ?board_id={id}     — 게시판 ID로 선택\n";
    echo "  ?board=all         — 전체 게시판\n";
    echo "</pre>";
    exit;
}

// ─── DB 설정 (.env 파싱) ──────────────────────────────────────────────────────
$env_path = dirname(__DIR__) . '/.env';
$env = [];
if (is_readable($env_path)) {
    foreach (file($env_path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) as $line) {
        if (str_starts_with(trim($line), '#') || !str_contains($line, '=')) continue;
        [$k, $v] = explode('=', $line, 2);
        $env[trim($k)] = trim($v, " \t\n\r\"'");
    }
}

$db_host     = $env['DB_WRITE_HOST']     ?? 'localhost';
$db_port     = $env['DB_WRITE_PORT']     ?? '3306';
$db_name     = $env['DB_WRITE_DATABASE'] ?? 'gnu7';
$db_user     = $env['DB_WRITE_USERNAME'] ?? 'root';
$db_pass     = $env['DB_WRITE_PASSWORD'] ?? '';
$db_prefix   = $env['DB_PREFIX']         ?? '';

// ─── PDO 연결 ─────────────────────────────────────────────────────────────────
try {
    $pdo = new PDO(
        "mysql:host={$db_host};port={$db_port};dbname={$db_name};charset=utf8mb4",
        $db_user,
        $db_pass,
        [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
    );
} catch (PDOException $e) {
    http_response_code(500);
    exit('DB 연결 실패: ' . htmlspecialchars($e->getMessage()));
}

$T = $db_prefix; // 테이블 prefix (예: "g7_")

// ─── 헬퍼: 다국어 JSON 필드에서 텍스트 추출 ──────────────────────────────────
function decode_i18n(string $raw, string $locale = 'ko'): string
{
    $decoded = json_decode($raw, true);
    if (!is_array($decoded)) return $raw;
    return $decoded[$locale] ?? $decoded['ko'] ?? $decoded['en'] ?? reset($decoded) ?? '';
}

// ─── 헬퍼: slug 생성 ──────────────────────────────────────────────────────────
function make_slug(string $title, int $id): string
{
    // 소문자 변환, 영문·숫자·공백·하이픈만 남기기
    $slug = mb_strtolower($title);
    $slug = preg_replace('/[^\w\s-]/u', '', $slug);     // 특수문자 제거
    $slug = preg_replace('/[\s_]+/', '-', $slug);        // 공백·언더스코어 → 하이픈
    $slug = trim($slug, '-');

    // 한글만 남아 있거나 비어 있으면 post-{id} 사용
    if ($slug === '' || preg_match('/^[가-힣ㄱ-ㅎㅏ-ㅣ-]+$/', $slug)) {
        return 'post-' . $id;
    }
    return $slug;
}

// ─── 헬퍼: Markdown → HTML (Parsedown 없을 때 최소 변환) ─────────────────────
function markdown_to_html(string $text): string
{
    // 줄바꿈 정규화
    $text = str_replace(['\n', '\r\n', '\r'], "\n", $text);

    // Parsedown이 autoload에 있으면 사용
    if (class_exists('Parsedown')) {
        return (new Parsedown())->text($text);
    }

    // 없으면 간단한 변환
    $text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
    $text = preg_replace('/\*\*(.+?)\*\*/s', '<strong>$1</strong>', $text);
    $text = preg_replace('/\*(.+?)\*/s',     '<em>$1</em>',         $text);
    $text = preg_replace('/`(.+?)`/',         '<code>$1</code>',     $text);
    $text = nl2br($text);
    return $text;
}

// ─── 게시판 목록 조회 ─────────────────────────────────────────────────────────
if (isset($_GET['board']) && $_GET['board'] === 'all') {
    $boards = $pdo->query("SELECT * FROM {$T}boards WHERE is_active = 1 ORDER BY id")->fetchAll(PDO::FETCH_ASSOC);
} elseif (isset($_GET['board_id'])) {
    $stmt = $pdo->prepare("SELECT * FROM {$T}boards WHERE id = ?");
    $stmt->execute([(int)$_GET['board_id']]);
    $boards = $stmt->fetchAll(PDO::FETCH_ASSOC);
} else {
    $stmt = $pdo->prepare("SELECT * FROM {$T}boards WHERE slug = ?");
    $stmt->execute([trim($_GET['board'])]);
    $boards = $stmt->fetchAll(PDO::FETCH_ASSOC);
}

if (empty($boards)) {
    exit('게시판을 찾을 수 없습니다.');
}

// ─── 게시글 + 댓글 + 첨부파일 수집 ──────────────────────────────────────────
$items = [];

foreach ($boards as $board) {
    $board_id   = (int)$board['id'];
    $board_name = decode_i18n($board['name']);
    $board_slug = $board['slug'];

    // 게시글 (원글만: depth = 0, published, soft-delete 제외)
    $stmt = $pdo->prepare("
        SELECT p.*, u.name AS user_display_name
        FROM {$T}board_posts p
        LEFT JOIN {$T}users u ON u.id = p.user_id
        WHERE p.board_id = ?
          AND p.status = 'published'
          AND p.deleted_at IS NULL
          AND p.depth = 0
        ORDER BY p.created_at ASC
    ");
    $stmt->execute([$board_id]);
    $posts = $stmt->fetchAll(PDO::FETCH_ASSOC);

    foreach ($posts as $post) {
        $post_id = (int)$post['id'];

        // 내용 변환
        $content = $post['content'];
        if ($post['content_mode'] === 'text') {
            $content = markdown_to_html($content);
        }

        // 작성자명
        $author = $post['user_display_name'] ?? $post['author_name'] ?? 'admin';

        // slug
        $slug = make_slug($post['title'], $post_id);

        // 댓글 (published, soft-delete 제외)
        $cstmt = $pdo->prepare("
            SELECT c.*, u.name AS user_display_name
            FROM {$T}board_comments c
            LEFT JOIN {$T}users u ON u.id = c.user_id
            WHERE c.board_id = ?
              AND c.post_id  = ?
              AND c.status   = 'published'
              AND c.deleted_at IS NULL
            ORDER BY c.created_at ASC
        ");
        $cstmt->execute([$board_id, $post_id]);
        $comments = $cstmt->fetchAll(PDO::FETCH_ASSOC);

        // 첨부파일 (soft-delete 제외)
        $astmt = $pdo->prepare("
            SELECT * FROM {$T}board_attachments
            WHERE board_id  = ?
              AND post_id   = ?
              AND deleted_at IS NULL
            ORDER BY `order` ASC
        ");
        $astmt->execute([$board_id, $post_id]);
        $attachments = $astmt->fetchAll(PDO::FETCH_ASSOC);

        $items[] = [
            'id'          => $post_id,
            'board_id'    => $board_id,
            'board_name'  => $board_name,
            'board_slug'  => $board_slug,
            'title'       => $post['title'],
            'content'     => $content,
            'slug'        => $slug,
            'author'      => $author,
            'category'    => $post['category'] ?? '',
            'is_notice'   => (bool)$post['is_notice'],
            'status'      => 'publish',
            'created_at'  => $post['created_at'],
            'comments'    => $comments,
            'attachments' => $attachments,
        ];
    }
}

// ─── XML 출력 ─────────────────────────────────────────────────────────────────
$site_url = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http')
          . '://' . ($_SERVER['HTTP_HOST'] ?? 'localhost');

$export_filename = (count($boards) === 1)
    ? ($boards[0]['slug'] . '_export.xml')
    : 'gnu7_boards_export.xml';

header('Content-Type: text/xml; charset=utf-8');
header('Content-Disposition: attachment; filename="' . $export_filename . '"');

echo '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
?>
<rss version="2.0"
    xmlns:excerpt="http://wordpress.org/export/1.2/excerpt/"
    xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:wfw="http://wellformedweb.org/CommentAPI/"
    xmlns:dc="http://purl.org/dc/elements/1.1/"
    xmlns:wp="http://wordpress.org/export/1.2/">
<channel>
    <title><?php echo htmlspecialchars(count($boards) === 1 ? decode_i18n($boards[0]['name']) : '그누보드7 내보내기'); ?></title>
    <link><?php echo htmlspecialchars($site_url); ?></link>
    <description>GNU7 Board Export</description>
    <pubDate><?php echo gmdate('D, d M Y H:i:s +0000'); ?></pubDate>
    <language>ko-KR</language>
    <wp:wxr_version>1.2</wp:wxr_version>
    <wp:base_site_url><?php echo htmlspecialchars($site_url); ?></wp:base_site_url>
    <wp:base_blog_url><?php echo htmlspecialchars($site_url); ?></wp:base_blog_url>

    <wp:author>
        <wp:author_id>1</wp:author_id>
        <wp:author_login><![CDATA[admin]]></wp:author_login>
        <wp:author_email><![CDATA[]]></wp:author_email>
        <wp:author_display_name><![CDATA[admin]]></wp:author_display_name>
        <wp:author_first_name><![CDATA[]]></wp:author_first_name>
        <wp:author_last_name><![CDATA[]]></wp:author_last_name>
    </wp:author>

<?php
// 카테고리 목록 (게시판명을 카테고리로 등록)
$registered_cats = [];
foreach ($boards as $board):
    $cat_name = decode_i18n($board['name']);
    $cat_slug = $board['slug'];
    if (in_array($cat_slug, $registered_cats, true)) continue;
    $registered_cats[] = $cat_slug;
?>
    <wp:category>
        <wp:term_id><?php echo (int)$board['id']; ?></wp:term_id>
        <wp:category_nicename><?php echo htmlspecialchars($cat_slug); ?></wp:category_nicename>
        <wp:category_parent></wp:category_parent>
        <wp:cat_name><![CDATA[<?php echo htmlspecialchars($cat_name); ?>]]></wp:cat_name>
    </wp:category>
<?php endforeach; ?>

<?php foreach ($items as $item):
    $ts_local = date('Y-m-d H:i:s', strtotime($item['created_at']));
    $ts_gmt   = gmdate('Y-m-d H:i:s', strtotime($item['created_at']));
    $ts_rfc   = gmdate('D, d M Y H:i:s +0000', strtotime($item['created_at']));
?>
    <item>
        <title><?php echo htmlspecialchars($item['title']); ?></title>
        <link><?php echo htmlspecialchars($site_url . '/' . $item['slug']); ?></link>
        <pubDate><?php echo $ts_rfc; ?></pubDate>
        <dc:creator><![CDATA[<?php echo htmlspecialchars($item['author']); ?>]]></dc:creator>
        <guid isPermaLink="false"><?php echo htmlspecialchars($site_url); ?>/?p=<?php echo $item['id']; ?></guid>
        <description></description>
        <content:encoded><![CDATA[<?php echo $item['content']; ?>]]></content:encoded>
        <excerpt:encoded><![CDATA[]]></excerpt:encoded>
        <wp:post_id><?php echo $item['id']; ?></wp:post_id>
        <wp:post_date><?php echo $ts_local; ?></wp:post_date>
        <wp:post_date_gmt><?php echo $ts_gmt; ?></wp:post_date_gmt>
        <wp:comment_status><?php echo empty($item['comments']) ? 'closed' : 'open'; ?></wp:comment_status>
        <wp:ping_status>closed</wp:ping_status>
        <wp:post_name><?php echo htmlspecialchars($item['slug']); ?></wp:post_name>
        <wp:status><?php echo htmlspecialchars($item['status']); ?></wp:status>
        <wp:post_parent>0</wp:post_parent>
        <wp:menu_order>0</wp:menu_order>
        <wp:post_type>post</wp:post_type>
        <wp:post_password></wp:post_password>
        <wp:is_sticky><?php echo $item['is_notice'] ? '1' : '0'; ?></wp:is_sticky>

        <!-- 게시판 → 카테고리 -->
        <category domain="category" nicename="<?php echo htmlspecialchars($item['board_slug']); ?>"><![CDATA[<?php echo htmlspecialchars($item['board_name']); ?>]]></category>

<?php if (!empty($item['category'])): ?>
        <!-- 게시글 분류 → 태그 -->
        <category domain="post_tag" nicename="<?php echo htmlspecialchars(strtolower(str_replace(' ', '-', $item['category']))); ?>"><![CDATA[<?php echo htmlspecialchars($item['category']); ?>]]></category>
<?php endif; ?>

        <!-- 원본 게시판 정보 -->
        <wp:postmeta>
            <wp:meta_key><![CDATA[gnu7_board_id]]></wp:meta_key>
            <wp:meta_value><![CDATA[<?php echo $item['board_id']; ?>]]></wp:meta_value>
        </wp:postmeta>
        <wp:postmeta>
            <wp:meta_key><![CDATA[gnu7_post_id]]></wp:meta_key>
            <wp:meta_value><![CDATA[<?php echo $item['id']; ?>]]></wp:meta_value>
        </wp:postmeta>

<?php foreach ($item['attachments'] as $att):
    $is_image = str_starts_with($att['mime_type'], 'image/');
    $meta_raw = $att['meta'] ? json_decode($att['meta'], true) : [];
?>
        <!-- 첨부파일: <?php echo htmlspecialchars($att['original_filename']); ?> -->
        <wp:postmeta>
            <wp:meta_key><![CDATA[gnu7_attachment_<?php echo $att['id']; ?>]]></wp:meta_key>
            <wp:meta_value><![CDATA[<?php echo json_encode([
                'id'               => $att['id'],
                'original_filename'=> $att['original_filename'],
                'hash'             => $att['hash'],
                'disk'             => $att['disk'],
                'path'             => $att['path'],
                'mime_type'        => $att['mime_type'],
                'size'             => $att['size'],
                'collection'       => $att['collection'],
                'is_image'         => $is_image,
                'meta'             => $meta_raw,
            ], JSON_UNESCAPED_UNICODE); ?>]]></wp:meta_value>
        </wp:postmeta>
<?php if ($is_image && !empty($meta_raw['thumbnail'])): ?>
        <wp:postmeta>
            <wp:meta_key><![CDATA[_thumbnail_path]]></wp:meta_key>
            <wp:meta_value><![CDATA[<?php echo htmlspecialchars($meta_raw['thumbnail']); ?>]]></wp:meta_value>
        </wp:postmeta>
<?php endif; endforeach; ?>

<?php foreach ($item['comments'] as $idx => $comment):
    $c_author  = $comment['user_display_name'] ?? $comment['author_name'] ?? '익명';
    $c_local   = date('Y-m-d H:i:s', strtotime($comment['created_at']));
    $c_gmt     = gmdate('Y-m-d H:i:s', strtotime($comment['created_at']));
    $c_approved = ($comment['status'] === 'published') ? '1' : '0';
    // 부모 댓글이 있으면 parent_id, 없으면 0
    $c_parent  = !empty($comment['parent_id']) ? (int)$comment['parent_id'] : 0;
?>
        <wp:comment>
            <wp:comment_id><?php echo (int)$comment['id']; ?></wp:comment_id>
            <wp:comment_author><![CDATA[<?php echo htmlspecialchars($c_author); ?>]]></wp:comment_author>
            <wp:comment_author_email><![CDATA[]]></wp:comment_author_email>
            <wp:comment_author_url></wp:comment_author_url>
            <wp:comment_author_IP><?php echo htmlspecialchars($comment['ip_address'] ?? ''); ?></wp:comment_author_IP>
            <wp:comment_date><?php echo $c_local; ?></wp:comment_date>
            <wp:comment_date_gmt><?php echo $c_gmt; ?></wp:comment_date_gmt>
            <wp:comment_content><![CDATA[<?php echo $comment['content']; ?>]]></wp:comment_content>
            <wp:comment_approved><?php echo $c_approved; ?></wp:comment_approved>
            <wp:comment_type></wp:comment_type>
            <wp:comment_parent><?php echo $c_parent; ?></wp:comment_parent>
            <wp:comment_user_id><?php echo (int)($comment['user_id'] ?? 0); ?></wp:comment_user_id>
        </wp:comment>
<?php endforeach; ?>
    </item>
<?php endforeach; ?>
</channel>
</rss>

로그인 후 댓글내용을 입력해주세요

제목 글쓴이 조회 날짜
워드프레스 그누보드7 데이터를 WordPress WXR로 변환하기 24 2시간 전
기타 Ollama 설치 경로 C드라이브 고정 문제 해결 및 드라이브 변경 방법 40 21시간 전
개발팁 Apache Access Log 기반 도메인별 방문자 카운터 구현 49 26-03-31
윈도우 ZImage AI 이미지 생성 환경 구축 방법 226 26-03-26
윈도우 파일 복사 후 실행 배치파일 - 에러 감지 포함 258 26-03-10
개발팁 네이버 검색 Open API 517 26-02-10
개발팁 네이버 검색광고 키워드 도구 API 검색 580 26-02-10
개발팁 MariaDB column_stats 테이블 오류 해결 방법 725 25-12-06
개발팁 PHP-FPM 에러 로그 실시간 모니터링 683 25-12-03
윈도우 윈도우11 업데이트 후 네트워크 드라이브 접근 불가 문제 해결 방법 1,319 25-11-24
개발팁 다국어 번역 함수 구현 방법 3,070 25-11-08
워드프레스 "치명적인 오류가 발생했습니다" 디버깅 가이드 13,881 25-10-31
리눅스 Apache 웹 로그 분석하기 – awk와 GoAccess 활용 18,447 25-10-28
길호넷 칼무리 - 외부 명령으로 캡처 자동화하기 1 40,084 25-10-14
윈도우 윈도11 강제업데이트 피하고 윈도10 계속 쓰기 41,621 25-10-12
리눅스 PHP 파일 업로드 용량 늘리기 56,524 25-10-03
개발팁 애드센스 충돌 문제 해결하기 74,512 25-09-20
파이썬 Python Playwright로 Edge 브라우저 제어하기 76,577 25-09-12
델파이 TDirectory.Delete 대신 CMD 으로 폴더 삭제 74,902 25-09-10
윈도우 Chrome 및 Edge를 TLS 1.2 모드로 실행 74,270 25-08-30
개발팁 애드센스 자동 광고 사용 시 빈 화면이 출력된다면? 159,178 25-06-20
윈도우 브라우저 환경설정 추출 145,745 25-06-18
워드프레스 xmlrpc.php 차단으로 보안 강화하기 136,904 25-06-05
델파이 폼이 모니터 한 가운데 있는 경우 자연스럽게 왼쪽으로 이동시키기 134,459 25-05-26
윈도우 WSH script registration is not valid. 134,662 25-05-23
워드프레스 제목 블록 기본값을 H5로 변경하는 방법 115,979 25-05-22
파이썬 동영상 특정 영역 모자이크(흐리게) 하기 47,681 25-04-30
리눅스 구글 크롬 콘솔 'Attestation check for Topics on' 에러 원인 &… 60,849 25-04-11
기타 크롬 - 개발자 도구(DevTools)에서 붙여넣기 안될때 73,418 25-03-10
그누보드 SmartEditor2Skin.html 다운로드 되는 현상 110,893 25-01-08
윈도우 네트워크 탐지 115,477 24-12-31
주점닷컴청년돈길클라우드AI건강정보강남언니야