솔그루

그누보드 크롤링 및 마이그레이션용 board class

Board.php 클래스 사용 설명서

그누보드5 게시판 게시물 등록 클래스

개요

Board 클래스는 그누보드5 게시판에 프로그래밍 방식으로 게시물을 등록할 수 있는 유틸리티 클래스입니다.
의존성 주입(DI) 패턴을 사용하여 $g5$config를 외부에서 주입받습니다.


파일 위치

/lib/Board.php

기본 사용법

<?php
// common.php 로드 (필수)
include_once('../common.php');

// Board 클래스 로드
include_once(G5_LIB_PATH.'/Board.php');

// 인스턴스 생성 (의존성 주입)
$board = new Board($g5, $config);

// 게시물 등록
$wr_id = $board->write(
    'freeboard',          // 게시판 테이블명
    '게시물 제목',        // 제목
    '게시물 내용입니다.'  // 내용
);

if ($wr_id) {
    echo "게시물 등록 성공! ID: {$wr_id}";
} else {
    echo "게시물 등록 실패";
}

write() 메서드

메서드 시그니처:

write($bo_table, $subject, $content, $files = array(), $options = array())

매개변수:

  • $bo_table (string, 필수) - 게시판 테이블명 (예: freeboard, notice)
  • $subject (string, 필수) - 게시물 제목
  • $content (string, 필수) - 게시물 내용 (HTML 가능)
  • $files (array, 선택) - 첨부파일 경로 배열
  • $options (array, 선택) - 추가 옵션 배열

반환값:

int|false - 성공 시 등록된 게시글 ID(wr_id), 실패 시 false


$options 배열 옵션

작성자 정보:

  • mb_id - 회원 아이디 (기본값: 'admin')
  • wr_name - 작성자명 (기본값: '관리자')
  • wr_email - 이메일 (기본값: '')
  • wr_ip - 작성자 IP (기본값: REMOTE_ADDR)

게시물 속성:

  • ca_name - 분류 (기본값: '')
  • wr_link1, wr_link2 - 링크 URL (기본값: '')
  • html - HTML 사용: 'html1' (태그필터), 'html2' (모든태그), '' (사용안함) (기본값: 'html1')
  • secret - 비밀글: 'secret' 또는 '' (기본값: '')
  • mail - 답변 메일 수신: 'mail' 또는 '' (기본값: '')

날짜/시간:

  • wr_datetime - 등록일시 (기본값: 현재시간, Y-m-d H:i:s 형식)
  • wr_last - 최종 수정일시 (기본값: wr_datetime과 동일)

기타:

  • wr_hit - 조회수 (기본값: 0)
  • wr_num - 글 번호 (기본값: 자동생성)
  • wr_parent - 부모 게시글 ID (기본값: 자동설정)
  • wr_1 ~ wr_10 - 여분 필드 (기본값: '')

파일 첨부 예제

방식 1: 단순 경로 배열

$files = [
    '/home/www/data/uploads/image1.jpg',
    '/home/www/data/uploads/document.pdf'
];

$wr_id = $board->write('freeboard', '제목', '내용', $files);

방식 2: 연관 배열 (원본 파일명 지정)

$files = [
    ['path' => '/tmp/upload_abc123', 'name' => '원본파일명.jpg'],
    ['path' => '/tmp/upload_def456', 'name' => '문서.pdf']
];

$wr_id = $board->write('freeboard', '제목', '내용', $files);

전체 옵션 사용 예제

<?php
include_once('../common.php');
include_once(G5_LIB_PATH.'/Board.php');

$board = new Board($g5, $config);

// 첨부 파일
$files = [
    '/home/www/data/temp/report.pdf'
];

// 옵션 설정
$options = [
    'mb_id'       => 'user123',
    'wr_name'     => '홍길동',
    'wr_email'    => 'user@example.com',
    'ca_name'     => '공지사항',
    'wr_link1'    => 'https://example.com',
    'html'        => 'html2',        // 모든 HTML 태그 허용
    'secret'      => '',             // 공개글
    'wr_datetime' => '2024-01-15 10:30:00',
    'wr_1'        => '커스텀값1',    // 여분 필드
    'wr_2'        => '커스텀값2'
];

$content = '
<h2>안내사항</h2>
<p>본 게시물은 <strong>자동 등록</strong>된 게시물입니다.</p>
<ul>
    <li>첫 번째 항목</li>
    <li>두 번째 항목</li>
</ul>
';

$wr_id = $board->write(
    'notice',         // 공지사항 게시판
    '[공지] 시스템 점검 안내',
    $content,
    $files,
    $options
);

if ($wr_id) {
    echo "등록 완료: wr_id = {$wr_id}";
}

내부 동작 흐름

  1. 1 게시판 테이블 존재 여부 확인
  2. 2 옵션 기본값 설정 및 데이터 이스케이프 처리
  3. 3 g5_write_{bo_table} 테이블에 게시글 INSERT
  4. 4 g5_board_new 테이블에 새글 정보 INSERT
  5. 5 g5_board 테이블의 게시글 수 증가 (bo_count_write)
  6. 6 첨부파일 처리 (data/file/{bo_table}/ 디렉토리에 저장)
  7. 7 g5_board_file 테이블에 파일 정보 INSERT

주의사항

  • common.php 필수: 반드시 common.php를 먼저 include해야 합니다.
  • 게시판 존재 확인: bo_table에 해당하는 게시판이 실제로 존재해야 합니다.
  • 파일 경로: $files에 전달하는 경로는 서버의 물리적 절대 경로여야 합니다.
  • 권한: 파일 업로드 디렉토리에 쓰기 권한이 필요합니다.
  • 실행파일 차단: php, cgi, exe 등 실행 파일은 자동으로 확장자에 -x가 추가됩니다.
  • 이미지 검증: 이미지 확장자 파일은 실제 이미지인지 검증됩니다.

활용 예시

1. 외부 데이터 마이그레이션:

기존 시스템의 게시물을 그누보드5로 이전할 때 사용

2. API 연동:

외부 서비스에서 받은 데이터를 게시판에 자동 등록

3. 크롤링 데이터 저장:

웹 크롤링으로 수집한 데이터를 게시판에 저장

4. 배치 작업:

정기적인 알림이나 공지사항 자동 등록

 

Board.php 클래스 사용 설명서 | 파일 위치: /lib/Board.php

 

[code]
<?php
/**
 * 그누보드5 게시판 관리 클래스
 *
 * 의존성 주입 패턴을 사용하여 $g5를 외부에서 주입받습니다.
 */

if (!defined('_GNUBOARD_')) exit;

class Board {
    private $g5;
    private $config;
    private $db;

    /**
     * 생성자
     *
     * @param array $g5 그누보드5 전역 설정 배열
     * @param array $config 그누보드5 config 배열
     */
    public function __construct($g5, $config) {
        $this->g5 = $g5;
        $this->config = $config;
        $this->db = $g5['connect_db'];
    }

    /**
     * 게시물 등록
     *
     * @param string $bo_table 게시판명 ex) freeboard
     * @param string $subject 게시물 제목
     * @param string $content 게시물 내용
     * @param array $files 업로드할 파일 경로 배열, 물리적 경로입력
     *                     ex) ['/home/account/www/data/uploads/image1.jpg', ...]
     * @param array $options 추가 옵션
     *                     - mb_id: 회원 아이디 (기본값: 'admin')
     *                     - wr_name: 작성자명 (기본값: '관리자')
     *                     - wr_email: 이메일 (기본값: '')
     *                     - ca_name: 분류 (기본값: '')
     *                     - wr_link1, wr_link2: 링크 (기본값: '')
     *                     - html: HTML 사용 여부 (기본값: 'html1')
     *                     - secret: 비밀글 여부 (기본값: '')
     *                     - mail: 답변 메일 수신 (기본값: '')
     *                     - wr_num: 글 번호 (기본값: 자동 생성)
     *                     - wr_datetime: 등록일시 (기본값: 현재 시간)
     *                     - wr_last: 최종 수정일시 (기본값: wr_datetime과 동일)
     *                     - wr_hit: 조회수 (기본값: 0)
     *                     - wr_ip: 작성자 IP (기본값: REMOTE_ADDR)
     *                     - wr_parent: 부모 게시글 ID (기본값: 등록 후 자동 설정)
     *                     - wr_1 ~ wr_10: 여분 필드
     * @return int|false 등록된 게시글 ID(wr_id) 또는 실패 시 false
     */
    public function write($bo_table, $subject, $content, $files = array(), $options = array()) {
        // 게시판명 검증
        if (empty($bo_table)) {
            return false;
        }

        // 제목, 내용 검증
        if (empty($subject) || empty($content)) {
            return false;
        }

        $write_table = "g5_write_{$bo_table}";

        // 테이블 존재 확인
        $table_check = sql_query("SHOW TABLES LIKE '{$write_table}'");
        if (sql_num_rows($table_check) == 0) {
            return false;
        }

        // 옵션 기본값 설정
        $mb_id = isset($options['mb_id']) ? $options['mb_id'] : 'admin';
        $wr_name = isset($options['wr_name']) ? $options['wr_name'] : '관리자';
        $wr_email = isset($options['wr_email']) ? $options['wr_email'] : '';
        $ca_name = isset($options['ca_name']) ? $options['ca_name'] : '';
        $wr_link1 = isset($options['wr_link1']) ? $options['wr_link1'] : '';
        $wr_link2 = isset($options['wr_link2']) ? $options['wr_link2'] : '';
        $html = isset($options['html']) ? $options['html'] : 'html1';
        $secret = isset($options['secret']) ? $options['secret'] : '';
        $mail = isset($options['mail']) ? $options['mail'] : '';

        // wr_option 조합
        $wr_option_arr = array();
        if ($html) $wr_option_arr[] = $html;
        if ($secret) $wr_option_arr[] = $secret;
        if ($mail) $wr_option_arr[] = $mail;
        $wr_option = implode(',', $wr_option_arr);

        // wr_num 생성 (지정되지 않은 경우)
        if (isset($options['wr_num'])) {
            $wr_num = $options['wr_num'];
        } else {
            // common.lib.php의 get_next_num() 함수 사용
            $wr_num = get_next_num($write_table);
        }

        $wr_reply = '';

        // 날짜/시간 옵션 처리
        $wr_datetime = isset($options['wr_datetime']) ? $options['wr_datetime'] : G5_TIME_YMDHIS;
        $wr_last = isset($options['wr_last']) ? $options['wr_last'] : $wr_datetime;
        $wr_hit = isset($options['wr_hit']) ? intval($options['wr_hit']) : 0;
        $wr_ip = isset($options['wr_ip']) ? $options['wr_ip'] : $_SERVER['REMOTE_ADDR'];
        $wr_parent = isset($options['wr_parent']) ? intval($options['wr_parent']) : 0;

        // 데이터 이스케이프 처리
        $wr_subject = sql_real_escape_string($subject);
        $wr_content = sql_real_escape_string($content);
        $ca_name = sql_real_escape_string($ca_name);
        $wr_link1 = sql_real_escape_string($wr_link1);
        $wr_link2 = sql_real_escape_string($wr_link2);
        $mb_id = sql_real_escape_string($mb_id);
        $wr_name = sql_real_escape_string($wr_name);
        $wr_email = sql_real_escape_string($wr_email);
        $wr_option = sql_real_escape_string($wr_option);
        $wr_datetime = sql_real_escape_string($wr_datetime);
        $wr_last = sql_real_escape_string($wr_last);
        $wr_ip = sql_real_escape_string($wr_ip);

        // 여분 필드 처리
        $wr_1 = isset($options['wr_1']) ? sql_real_escape_string($options['wr_1']) : '';
        $wr_2 = isset($options['wr_2']) ? sql_real_escape_string($options['wr_2']) : '';
        $wr_3 = isset($options['wr_3']) ? sql_real_escape_string($options['wr_3']) : '';
        $wr_4 = isset($options['wr_4']) ? sql_real_escape_string($options['wr_4']) : '';
        $wr_5 = isset($options['wr_5']) ? sql_real_escape_string($options['wr_5']) : '';
        $wr_6 = isset($options['wr_6']) ? sql_real_escape_string($options['wr_6']) : '';
        $wr_7 = isset($options['wr_7']) ? sql_real_escape_string($options['wr_7']) : '';
        $wr_8 = isset($options['wr_8']) ? sql_real_escape_string($options['wr_8']) : '';
        $wr_9 = isset($options['wr_9']) ? sql_real_escape_string($options['wr_9']) : '';
        $wr_10 = isset($options['wr_10']) ? sql_real_escape_string($options['wr_10']) : '';

        // 게시글 INSERT
        $sql = " INSERT INTO {$write_table}
                    SET wr_num = '{$wr_num}',
                         wr_reply = '{$wr_reply}',
                         wr_comment = 0,
                         ca_name = '{$ca_name}',
                         wr_option = '{$wr_option}',
                         wr_subject = '{$wr_subject}',
                         wr_content = '{$wr_content}',
                         wr_link1 = '{$wr_link1}',
                         wr_link2 = '{$wr_link2}',
                         wr_link1_hit = 0,
                         wr_link2_hit = 0,
                         wr_hit = {$wr_hit},
                         wr_good = 0,
                         wr_nogood = 0,
                         mb_id = '{$mb_id}',
                         wr_password = '',
                         wr_name = '{$wr_name}',
                         wr_email = '{$wr_email}',
                         wr_homepage = '',
                         wr_datetime = '{$wr_datetime}',
                         wr_last = '{$wr_last}',
                         wr_ip = '{$wr_ip}',
                         wr_1 = '{$wr_1}',
                         wr_2 = '{$wr_2}',
                         wr_3 = '{$wr_3}',
                         wr_4 = '{$wr_4}',
                         wr_5 = '{$wr_5}',
                         wr_6 = '{$wr_6}',
                         wr_7 = '{$wr_7}',
                         wr_8 = '{$wr_8}',
                         wr_9 = '{$wr_9}',
                         wr_10 = '{$wr_10}' ";

        if (!sql_query($sql)) {
            return false;
        }

        $wr_id = sql_insert_id();

        // 부모 아이디에 UPDATE (옵션으로 지정되지 않은 경우 자신의 ID로 설정)
        if ($wr_parent == 0) {
            $wr_parent = $wr_id;
        }
        sql_query("UPDATE {$write_table} SET wr_parent = '{$wr_parent}' WHERE wr_id = '{$wr_id}'");

        // 새글 INSERT
        sql_query("INSERT INTO {$this->g5['board_new_table']}
                   (bo_table, wr_id, wr_parent, bn_datetime, mb_id)
                   VALUES ('{$bo_table}', '{$wr_id}', '{$wr_parent}', '{$wr_datetime}', '{$mb_id}')");

        // 게시글 수 증가
        sql_query("UPDATE {$this->g5['board_table']} SET bo_count_write = bo_count_write + 1 WHERE bo_table = '{$bo_table}'");

        // 파일 처리
        if (!empty($files) && is_array($files)) {
            $file_count = $this->writeFiles($bo_table, $wr_id, $files);

            // 파일 개수 업데이트
            if ($file_count > 0) {
                sql_query("UPDATE {$write_table} SET wr_file = '{$file_count}' WHERE wr_id = '{$wr_id}'");
            }
        }

        return $wr_id;
    }

    /**
     * 게시물 파일 업로드 처리
     *
     * @param string $bo_table 게시판명
     * @param int $wr_id 게시글 ID
     * @param array $files 파일 경로 배열 또는 파일 정보 배열
     *                     - 문자열 배열: ['경로1', '경로2', ...]
     *                     - 연관 배열: [['path' => '경로', 'name' => '원본명'], ...]
     * @return int 업로드된 파일 개수
     */
    private function writeFiles($bo_table, $wr_id, $files) {
        if (empty($files) || !is_array($files)) {
            return 0;
        }

        // 파일 개수 체크
        $file_count = 0;
        $upload_count = count($files);

        for ($i = 0; $i < $upload_count; $i++) {
            $file_path = is_array($files[$i]) ? $files[$i]['path'] : $files[$i];
            if (!empty($file_path) && file_exists($file_path)) {
                $file_count++;
            }
        }

        if ($file_count == 0) {
            return 0;
        }

        // 디렉토리가 없다면 생성
        $upload_dir = G5_DATA_PATH.'/file/'.$bo_table;
        if (!is_dir($upload_dir)) {
            @mkdir($upload_dir, G5_DIR_PERMISSION);
            @chmod($upload_dir, G5_DIR_PERMISSION);
        }

        $chars_array = array_merge(range(0, 9), range('a', 'z'), range('A', 'Z'));

        // 가변 파일 업로드
        $upload = array();
        $success_count = 0;

        for ($i = 0; $i < count($files); $i++) {
            $upload[$i] = array(
                'file'     => '',
                'source'   => '',
                'filesize' => 0,
                'image'    => array('', '', '')
            );

            // 연관 배열 또는 문자열 처리
            if (is_array($files[$i])) {
                $tmp_file = $files[$i]['path'];
                $original_name = isset($files[$i]['name']) ? $files[$i]['name'] : basename($tmp_file);
            } else {
                $tmp_file = $files[$i];
                $original_name = basename($tmp_file);
            }

            if (!file_exists($tmp_file)) {
                continue;
            }

            $filesize = filesize($tmp_file);
            $filename = $original_name;
            $filename = get_safe_filename($filename);

            // 이미지나 플래시 파일 검증
            $timg = @getimagesize($tmp_file);

            if (preg_match("/\.({$this->config['cf_image_extension']})$/i", $filename) ||
                preg_match("/\.({$this->config['cf_flash_extension']})$/i", $filename)) {
                if (!$timg || $timg[2] < 1 || $timg[2] > 16) {
                    continue;
                }
            }

            $upload[$i]['image'] = $timg ? $timg : array('', '', '');
            $upload[$i]['source'] = $filename;
            $upload[$i]['filesize'] = $filesize;

            // 실행 파일 확장자 차단
            $filename = preg_replace("/\.(php|phtm|htm|cgi|pl|exe|jsp|asp|inc)/i", "$0-x", $filename);

            shuffle($chars_array);
            $shuffle = implode('', $chars_array);

            // 파일명 생성
            $upload[$i]['file'] = abs(ip2long($_SERVER['REMOTE_ADDR'])).'_'.substr($shuffle, 0, 8).'_'.replace_filename($filename);

            $dest_file = $upload_dir.'/'.$upload[$i]['file'];

            // 파일 복사
            if (copy($tmp_file, $dest_file)) {
                chmod($dest_file, G5_FILE_PERMISSION);
                $success_count++;
            } else {
                // 복사 실패 시 초기화
                $upload[$i]['file'] = '';
                $upload[$i]['source'] = '';
                $upload[$i]['filesize'] = 0;
            }
        }

        // 파일 정보를 DB에 저장
        $saved_count = 0;
        for ($i = 0; $i < count($upload); $i++) {
            if (empty($upload[$i]['file'])) {
                continue;
            }

            $bf_source = sql_real_escape_string($upload[$i]['source']);
            $bf_file = sql_real_escape_string($upload[$i]['file']);
            $bf_filesize = (int)$upload[$i]['filesize'];
            $bf_width = isset($upload[$i]['image'][0]) ? (int)$upload[$i]['image'][0] : 0;
            $bf_height = isset($upload[$i]['image'][1]) ? (int)$upload[$i]['image'][1] : 0;
            $bf_type = isset($upload[$i]['image'][2]) ? (int)$upload[$i]['image'][2] : 0;

            $sql = "INSERT INTO {$this->g5['board_file_table']}
                    SET bo_table = '{$bo_table}',
                         wr_id = '{$wr_id}',
                         bf_no = '{$saved_count}',
                         bf_source = '{$bf_source}',
                         bf_file = '{$bf_file}',
                         bf_content = '',
                         bf_download = 0,
                         bf_filesize = '{$bf_filesize}',
                         bf_width = '{$bf_width}',
                         bf_height = '{$bf_height}',
                         bf_type = '{$bf_type}',
                         bf_datetime = '".G5_TIME_YMDHIS."'";

            if (sql_query($sql)) {
                $saved_count++;
            }
        }

        // 빈 파일 정보 삭제
        $row = sql_fetch("SELECT MAX(bf_no) as max_bf_no FROM {$this->g5['board_file_table']}
                          WHERE bo_table = '{$bo_table}' AND wr_id = '{$wr_id}'");

        if ($row && isset($row['max_bf_no'])) {
            for ($i = (int)$row['max_bf_no']; $i >= 0; $i--) {
                $row2 = sql_fetch("SELECT bf_file FROM {$this->g5['board_file_table']}
                                  WHERE bo_table = '{$bo_table}' AND wr_id = '{$wr_id}' AND bf_no = '{$i}'");

                if ($row2 && !empty($row2['bf_file'])) {
                    break;
                }

                sql_query("DELETE FROM {$this->g5['board_file_table']}
                          WHERE bo_table = '{$bo_table}' AND wr_id = '{$wr_id}' AND bf_no = '{$i}'");
            }
        }

        // 최종 파일 개수 조회
        $row = sql_fetch("SELECT COUNT(*) as cnt FROM {$this->g5['board_file_table']}
                          WHERE bo_table = '{$bo_table}' AND wr_id = '{$wr_id}'");

        return $row ? (int)$row['cnt'] : 0;
    }
}

[/code]

첨부파일

Board.php.zip (4.2 KB) 6회 2026-01-25 13:15
|

댓글 2개

db 마이그레이션 작업하다가 임시로 만든(ai 가 만들어준) 클래스인데 나름 나쁘지 않아서 공유합니다.
댓글을 작성하시려면 로그인이 필요합니다.

그누보드5 팁자료실

+
제목 글쓴이 날짜 조회
3년 전 조회 4,915
5일 전 조회 109
5일 전 조회 100
6일 전 조회 85
2주 전 조회 403
3주 전 조회 362
3주 전 조회 346
1개월 전 조회 413
1개월 전 조회 468
1개월 전 조회 495
1개월 전 조회 485
1개월 전 조회 457
1개월 전 조회 661
1개월 전 조회 547
1개월 전 조회 543
1개월 전 조회 503
1개월 전 조회 654
1개월 전 조회 623
1개월 전 조회 496
1개월 전 조회 719
2개월 전 조회 689
2개월 전 조회 622
2개월 전 조회 620
2개월 전 조회 723
2개월 전 조회 600
2개월 전 조회 613
2개월 전 조회 781
2개월 전 조회 607
2개월 전 조회 814
2개월 전 조회 698
2개월 전 조회 790