워드프레스 – CRUD

wp-crud

 

* CRUD: 데이터의 Create(생성), Read(읽기), Update(갱신), Delete(삭제)의 약자.

워드프레스에 처음 입문하는 개발자라면, 워드프레스가 기본적으로 제공하는 포스트 타입의 데이터 처리가 아닌 사용자정의 데이터를 CRUD하는 방법이 궁금할 것입니다.
본 포스트에서는 간단한 애플리케이션 제작과정을 통해 워드프레스의 일반적인 CRUD를 설명합니다.

자세한 설명에 앞서 제작 과정을 미리 살펴보겠습니다.
1. 클래스파일 생성, 테마 functions.php에 포함/인스턴스 생성
2. 사용자정의 데이터 테이블 생성
3. 원하는 포스트에 애플리케이션이 나타날 수 있도록 숏코드 등록
4. 폼 구성
5. 데이터 삽입
6. 읽기 처리
7. 갱신 처리
8. 삭제 처리

 

1. 클래스파일 생성, 테마 functions.php에 포함/인스턴스 생성

현재 사용중인 테마 디렉토리의 어딘가에 mystatus.php 파일을 생성하여 아래 코드를 저장합니다.

1
2
class MY_Status {
}

현재 사용중인 테마의 functions.php를 엽니다.
저는 현재 사용중인 테마의 inc 디렉토리에 mystatus.php를 생성하였기 때문에, 아래와 같은 경로로 클래스 파일을 포함하고 인스턴스를 생성하였습니다.

1
2
include 'inc/mystatus.php';
$GLOBALS['mystatus'] = new MY_Status();

작업준비가 완료되었습니다. functions.php는 닫고, mystatus.php를 열어 CRUD를 하나씩 구현해봅시다.

 

2. 사용자정의 데이터 테이블 생성

만약 애플리케이션을 플러그인으로 제작하여 배포할 계획이라면, 플러그인이 활성화될 때 테이블이 자동 생성되도록 액션을 걸어주어야 하겠지만, 저는 테마 functions.php에 클래스 파일을 include 하는 방식으로 예제를 만들었기 때문에 데이터 테이블을 직접 미리 만들어 주었습니다.

CREATE TABLE IF NOT EXISTS `wp_z_mystatus` (
  `ID` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` bigint(20) NOT NULL,
  `content` text NOT NULL,
  `post_date` datetime NOT NULL,
  PRIMARY KEY (`ID`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1000 ;

클래스에 생성자함수(__construct)를 만들고, 편의를 위하여 테이블 이름을 변수로 만들어둡니다.

1
2
3
4
5
6
class MY_Status {
    function __construct(){
        global $wpdb;
        $this->table = $wpdb->prefix . 'z_mystatus';
    }
}

 

3. 원하는 포스트에 애플리케이션이 나타날 수 있도록 숏코드 등록

애플리케이션을 화면에 나타내는 방법은 여러가지가 있겠지만, 저는 숏코드를 등록하여 포스트(또는 페이지) 내용의 원하는 위치에 나타나도록 하는 방법을 택하였습니다.
우선 포스트(또는 페이지)를 하나 생성하여 편집기에 [mystatus]를 입력해줍니다. 이 부분이 실제 애플리케이션으로 대체될 것입니다. 숏코드가 생소하시다면 우선 여기를 먼저 살펴보세요.

숏코드는 add_shortcode를 이용하여 등록합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class MY_Status {
    function __construct(){
        global $wpdb;
        $this->table = $wpdb->prefix . 'z_mystatus';
        add_shortcode( 'mystatus', array($this, 'shortcode') );
    }
    function shortcode(){
        ob_start();
        ?>
 
<div id="mystatus">
            <?php $this->the_form(); ?>
            <?php $this->the_list(); ?>
        </div>
 
        <?php
        return ob_get_clean();
    }
    function the_form(){
    }
    function the_list(){
    }
}

라인 7~16. 편집기에 입력한 [mystatus]는 shortcode 함수가 리턴하는 내용으로 대체됩니다. 리턴하지 않고 그냥 출력을 하면 편집기에 입력된 다른 내용들보다 먼저 출력되기 때문에 버퍼를 이용하여 리턴해주는 것입니다.
라인 17~20. the_form은 HTML 폼을, the_list는 등록된 데이터를 출력하게될 것입니다.

 

4. 폼 구성

이제 the_form에 폼을 구성해봅시다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function the_form(){
    if( ! is_user_logged_in() )
        return;
    ?>
 
<form id="mystatus-form" action="<?=admin_url('admin-post.php')?>" method="post">
        <?php wp_nonce_field('mystatus', 'mystatus_nonce'); ?>
        <input type="hidden" name="action" value="mystatus-post">
 
 
            <textarea name="content" rows="5"></textarea>
         
 
 
 
            <input type="submit" value="올리기">
         
 
    </form>
 
    <?php
}

라인 2. 본 애플리케이션은 로그인 유저만 작성할 수 있도록 계획하였기 때문에 로그인되지 않은 경우 폼을 출력하지 않습니다.
라인 5. 유의하여 보아야하는 부분은 폼의 action 속성입니다. 요청 데이터를 처리하는 방식은 개발자마다 스타일이 다르겠지만, 워드프레스가 제공하는 일반적인 방법은 admin-post.php를 활용하는 것입니다.
admin-post.php에서 요청 데이터를 처리하기 위해서는 액션을 걸어주어야 할텐데, 훅네임을 어떻게 만들어주어야하는지는 admin-post.php 파일을 열어보면 알 수 있습니다. 아래와 같이 훅 네임이 조합되는 것을 알 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$action = 'admin_post';
$action .= '_' . $_REQUEST['action'];
do_action( $action );[/php]</pre>
따라서 우리는 아래와 같이 훅 네임을 만들어 걸어주면 되겠습니다.
<pre>class MY_Status {
    function __construct(){
        global $wpdb;
        $this->table = $wpdb->prefix . 'z_mystatus';
        add_shortcode( 'mystatus', array($this, 'shortcode') );
        add_action( 'admin_post_mystatus-post', array($this, 'post_request') );
    }
    function post_request(){
        // 데이터 처리
    }
 
    ...

참고로, 비로그인 유저에 의한 요청인 경우 훅네임이 ‘admin_post_mystatus-post’ 이 아니라, ‘admin_post_nopriv_mystatus-post’ 입니다. 본 애플리케이션은 로그인 유저만 작성할 수 있도록 하였기 때문에 굳이 걸지 않았지만, 비로그인 유저의 요청을 허용하는 애플리케이션은 아래와 같이 추가로 액션을 걸어주어야합니다.

1
add_action( 'admin_post_nopriv_mystatus-post', array($this, 'post_request') );

 

5. 데이터 삽입

이제 ‘admin-post.php’로 ‘action’ 변수가 ‘mystatus-post’ 인 요청이 발생하면 ‘post_request’ 함수가 실행될테니 여기서 데이터 처리를 하면 되겠습니다. 만약 워드프레스의 DB 클래스 사용법에 익숙치 않다면 반드시 여기를 미리 참고하세요.

경축! 아무것도 안하여 에스천사게임즈가 새로운 모습으로 재오픈 하였습니다.
어린이용이며, 설치가 필요없는 브라우저 게임입니다.
https://s1004games.com

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function post_request(){
    global $wpdb, $user_ID;
 
    if( ! is_user_logged_in()
        || ! isset($_REQUEST['mystatus_nonce'])
        || ! wp_verify_nonce($_REQUEST['mystatus_nonce'], 'mystatus') )
        return;
 
    $content = trim($_REQUEST['content']);
    $content = apply_filters('pre_comment_content', $content);
    if( empty($content) ){
        wp_die( '내용을 입력하세요.' );
    }
 
    $data = array(
        'content' => $content,
        'user_id' => $user_ID,
        'post_date' => current_time('mysql'),
    );
    $wpdb->insert($this->table, $data);
 
    $goback = wp_get_referer();
    wp_redirect( $goback );
    exit;
}

라인 4~7. 우선 로그인 여부와 nonce를 체크합니다.
라인 9~10. 내용을 필터링합니다. 필터링은 개발자가 원하는 수준으로 하시면 되겠습니다. 저는 워드프레스 댓글 내용 필터링 수준(사용자 역할에 따라 부분적으로 HTML허용)을 적용하였습니다.
라인 15~20. 데이터를 삽입합니다.
라인 22~24. 이전 페이지로 리다이렉트 합니다.

 

6. 읽기 처리

이제 입력한 데이터를 화면에 출력하기 위해서 the_list 함수 작성하겠습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function the_list(){
    $rows = $this->get_rows();
    if( empty($rows) )
        return;
    ?>
 
<div id="mystatus-list">
        <?php foreach($rows as $row){ ?>
 
<div class="item">
 
<div class="content">
                <?php echo apply_filters('comment_text', $row->content); ?>
            </div>
 
 
<div class="date"><?php echo human_time_diff( strtotime($row->post_date), current_time('timestamp') ); ?>전</div>
 
        </div>
 
        <?php } ?>
    </div>
 
    <?php } function get_rows($limit=10){ global $wpdb, $user_ID; $sql = $wpdb->prepare("SELECT * FROM $this->table ORDER BY post_date DESC LIMIT %d", $limit);
    $rows = $wpdb->get_results( $sql );
    return $rows;
}

라인 2. 작성자 구분없이 최근 10개의 데이터를 가져옵니다.
라인 10. ‘내용’은 댓글 수준으로 필터링하였습니다.
라인 12. ‘날짜’는 ‘1분전’, ‘1일전’ 이런 형식으로 출력되도록 human_time_diff 헬퍼를 이용하였습니다.

 

7. 갱신 처리

갱신 처리를 위해서 앞서 작성한 the_list, the_form, post_request를 모두 보완 해야합니다.
우선 the_list에 수정 링크를 삽입해보겠습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
function the_list(){
    global $user_ID;
    $rows = $this->get_rows();
    if( empty($rows) )
        return;
    ?>
 
<div id="mystatus-list">
        <?php foreach($rows as $row){ ?>
 
<div class="item">
 
<div class="content">
                <?php echo apply_filters('comment_text', $row->content); ?>
            </div>
 
 
<div class="meta">
                <span class="date"><?php echo human_time_diff( strtotime($row->post_date), current_time('timestamp') ); ?>전</span>
                <?php if( $row->user_id == $user_ID ){ ?>
                <span class="sep">|</span>
                <a href="<?php echo add_query_arg('mystatus-ID', $row->ID)?>" class="edit">수정</a>
                <?php } ?>
            </div>
 
        </div>
 
        <?php } ?>
    </div>
 
    <?php
}

라인 15~18. 전역변수인 $user_ID는 현재 로그인 사용자의 아이디입니다. 이를 이용하여 작성자 아이디와 같다면 ‘수정’ 링크를 출력합니다. 수정 링크의 URL은 add_query_arg를 이용해서 현재 페이지 URL에 ‘mystatus-ID’ 변수를 붙여서 생성하였습니다. 예를들어 현재 페이지의 URL이 아래와 같다면
http://example.com/?page_id=2
수정 URL은 아래와 같이 만들어집니다.
http://example.com/?page_id=2&mystatus-ID=1001

이제, 수정 링크를 클릭하면, 데이터를 수정할 수 있도록 해당 데이터를 가져와 폼에 넣어주어야 합니다.
그러기 위해서 the_form 함수를 보완해봅시다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
function the_form(){
    if( ! is_user_logged_in() )
        return;
 
    $ID = isset($_GET['mystatus-ID']) ? $_GET['mystatus-ID'] : '';
    $content = '';
    $submit_label = '올리기';
    $update = false;
    if( $row = $this->get_row( $ID ) ){
        extract($row);
        $update = true;
        $submit_label = '수정';
    }
    ?>
 
<form id="mystatus-form" action="<?=admin_url('admin-post.php')?>" method="post">
        <?php wp_nonce_field('mystatus', 'mystatus_nonce'); ?>
        <input type="hidden" name="action" value="mystatus-post">
        <input type="hidden" name="ID" value="<?php echo $ID;?>">
 
 
            <textarea name="content" rows="5"><?php echo esc_textarea($content); ?></textarea>
         
 
 
 
            <input type="submit" value="<?=$submit_label?>">
            <?php if( $update ){ ?>
            <a href="<?php echo remove_query_arg('mystatus-ID')?>" class="cancel">취소</a>
            <?php } ?>
         
 
    </form>
 
    <?php } function get_row($ID){ global $wpdb, $user_ID; $row = null; $ID = absint($ID); if( $ID > 0 && $user_ID > 0 ){
        $sql = $wpdb->prepare("SELECT * FROM $this->table WHERE user_id=%d AND ID=%d", $user_ID, $ID);
        $row = $wpdb->get_row( $sql, ARRAY_A );
    }
    return $row;
}

라인 5. 요청된 아이디가 있는지 확인합니다.
라인 9~13. 요청된 아이디가 있다면, 데이터를 가져옵니다.
라인 15~29. 가져온 데이터를 넣어주고, ID 필드와 취소 링크를 추가하였습니다.
라인 32~42. 데이터를 가져오는 get_row 라는 함수가 추가되었는데, 요청된 아이디로 데이터를 가져오되, 현재 로그인한 사용자로 제한하고 있습니다. 따라서 팀블로그(혹은 회원제 사이트)인 경우 다른 사용자는 편집을 할 수 없을 것입니다.

이제 post_request 함수에서 갱신 처리도 할 수 있도록 보완해보겠습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
function post_request(){
    global $wpdb, $user_ID;
 
    if( ! is_user_logged_in()
        || ! isset($_REQUEST['mystatus_nonce'])
        || ! wp_verify_nonce($_REQUEST['mystatus_nonce'], 'mystatus') )
        return;
 
    $ID = absint($_REQUEST['ID'])>0 ? $_REQUEST['ID'] : null;
 
    $content = trim($_REQUEST['content']);
    $content = apply_filters('pre_comment_content', $content);
 
    if( empty($content) ){
        wp_die( '내용을 입력하세요.' );
    }
 
    $update = false;
    if( $row = $this->get_row( $ID ) )
        $update = true;
 
    $data = array('content' => $content);
 
    if( $update ){
        $wpdb->update($this->table, $data, compact('ID'));
 
    }else{
        $data['user_id'] = $user_ID;
        $data['post_date'] = current_time('mysql');
        $wpdb->insert($this->table, $data);
    }
 
    $goback = wp_get_referer();
    $goback = remove_query_arg('mystatus-ID', $goback);
    wp_redirect( $goback );
    exit;
}

the_form과 마찬가지로 요청된 ID로 데이터를 가져오고, 이를 통해 현재 상태가 생성인지 갱신인지를 판단하여 데이터 처리를 합니다. 여기서 user_id와 post_date는 생성시에만 처리되는 점을 유의하세요.

 

8. 삭제 처리

우선 삭제처리를 위한 액션을 추가로 걸어줍니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
class MY_Status {
    function __construct(){
        global $wpdb;
        $this->table = $wpdb->prefix . 'z_mystatus';
        add_shortcode( 'mystatus', array($this, 'shortcode') );
        add_action( 'admin_post_mystatus-post', array($this, 'post_request') );
        add_action( 'admin_post_mystatus-delete', array($this, 'delete_request') );
    }
    function delete_request(){
        // 데이터 처리
    }
 
    ...

the_list의 ‘수정’ 링크 옆에 ‘삭제’ 링크를 추가합니다.

1
2
3
4
5
6
7
...
 
<a href="<?php echo add_query_arg('mystatus-ID', $row->ID)?>" class="edit">수정</a>
<span class="sep">|</span>
<a href="<?php echo admin_url('admin-post.php?action=mystatus-delete&ID='.$row->ID.'&mystatus_nonce='.wp_create_nonce('mystatus'))?>" class="delete">삭제</a>
 
...

삭제 링크의 URL의 쿼리 변수들을 잠시 살펴보면,
– admin-post.php에서 처리할 action
– 수정할 데이터의 ID
– 보안 목적의 nonce
로 구성되었습니다.
http://(도메인과 경로)/wp-admin/admin-post.php?action=mystatus-delete&ID=1001&mystatus_nonce=0bdde911f8

위 URL에 접근하면 delete_request 함수가 실행될 것입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function delete_request(){
    global $wpdb, $user_ID;
 
    if( ! is_user_logged_in()
        || ! isset($_REQUEST['mystatus_nonce'])
        || ! wp_verify_nonce($_REQUEST['mystatus_nonce'], 'mystatus') )
        return;
 
    $ID = absint($_REQUEST['ID'])>0 ? $_REQUEST['ID'] : null;
 
    if( $row = $this->get_row( $ID ) )
        $wpdb->query( $wpdb->prepare("DELETE FROM $this->table WHERE ID=%d", $ID) );
 
    $goback = wp_get_referer();
    $goback = remove_query_arg('mystatus-ID', $goback);
    wp_redirect( $goback );
    exit;
}

삭제를 위한 데이터 검증 및 절차는 생성/갱신과 비슷하므로 설명은 생략하겠습니다.

 

마치며

본 애플리케이션은 워드프레스에서의 일반적인 CRUD를 설명하기 위해 제작한 것으로, 실서비스에 적용하기엔 다소 미흡할 것입니다. 하지만 본 애플리케이션을 기본 골격으로하여 the_list의 페이징 혹은 무한스크롤, 비로그인 유저 쓰기 허용, 편집기 부착, 사용자 역할별 권한 제한, AJAX로의 전환 등을 적용하면서 절차적으로 업그레이드 해본다면 게시판 같은 애플리케이션으로 발전시킬 수 있을 것입니다.

 

[출처] http://biscuitpress.kr/503

 

본 웹사이트는 광고를 포함하고 있습니다.
광고 클릭에서 발생하는 수익금은 모두 웹사이트 서버의 유지 및 관리, 그리고 기술 콘텐츠 향상을 위해 쓰여집니다.
번호 제목 글쓴이 날짜 조회 수
194 [JavaScript] How to Write a Simple Interpreter in JavaScript file 졸리운_곰 2021.04.13 519
193 [HTML] HTML로 간단한 웹페이지 만들기 file 졸리운_곰 2021.02.13 641
192 무료 반응형웹 템플릿 사이트 모음 file 졸리운_곰 2021.02.13 548
191 [javascript, 자바스크립트] 자동 실행 함수 (window.onload, $(document).ready()) 졸리운_곰 2021.02.13 449
190 배민찬은 Vue를 어떻게 사용하나요? file 졸리운_곰 2020.12.02 354
189 [속깊은 자바스크립트 강좌] 자바스크립트의 Scope와 Closure 기초 file 졸리운_곰 2020.09.16 447
188 [Javascript] with 명령에 대하여 졸리운_곰 2020.09.16 459
187 Webpack 완전정복하기!! file 졸리운_곰 2020.07.20 726
186 [javascript] 압축(Minify) / 난독화(Uglify) 졸리운_곰 2020.07.01 293
185 HTML div 왼쪽, 오른쪽 배치 졸리운_곰 2020.06.02 470
184 [html] iframe을 사용하지 말아야 할 이유. (단점) 졸리운_곰 2020.05.10 567
183 자바스크립트 자료구조 연결 리스트(Linked List) file 졸리운_곰 2020.03.30 324
182 Javascript) innerText vs innerHTML 졸리운_곰 2020.03.13 425
181 InnerHTML 사용시 알 수 없는 런타임 오류 졸리운_곰 2020.03.13 505
180 How to Create an addChild() Function in JavaScript file 졸리운_곰 2020.03.09 438
179 JavaScript post request like a form submit 졸리운_곰 2020.03.09 474
178 [pure javascript] Vanilla JS equivalents of jQuery methods 졸리운_곰 2020.03.09 374
177 [Javascript] innerHTML 의 함정 졸리운_곰 2020.03.08 374
176 jQuery append() in Pure Javascript 졸리운_곰 2020.03.08 313
175 HTML 테이블 표 가로 및 세로 합치기 (td tr 병합 방법) file 졸리운_곰 2020.02.27 486
대표 김성준 주소 : 경기 용인 분당수지 U타워 등록번호 : 142-07-27414
통신판매업 신고 : 제2012-용인수지-0185호 출판업 신고 : 수지구청 제 123호 개인정보보호최고책임자 : 김성준 sjkim70@stechstar.com
대표전화 : 010-4589-2193 [fax] 02-6280-1294 COPYRIGHT(C) stechstar.com ALL RIGHTS RESERVED