[카테고리:] 워드프레스 개발

  • PDF 직접 다운로드 처리

    웹브라우저에서 PDF 링크를 열면 내장 PDF 뷰어가 뜬다.

    이 때 PDF의 주소 도메인이 현재 도메인과 같다면, 아래처럼 간단하게 처리 가능하다.

    <a href="{URL}" download="">PDF Download</a>Code language: HTML, XML (xml)

    이 때 download 속성에 파일 이름을 넣어서 별도의 이름을 줄 수도 있다.

    그런데 이 방법은 외부 URL에는 통하지 않는다. 다른 도메인에 있는 PDF를 굳이 다운로드 처리하고 싶은 변태들을 위해서는 자바스크립트를 사용해 보자.

    (function () {
        function checkDomain(url) {
            if (url.indexOf('//') === 0) {
                url = location.protocol + url;
            }
            return url.toLowerCase().replace(/([a-z])?:\/\//, '$1').split('/')[0];
        }
    
        function isExternal(url) {
            return ((url.indexOf(':') > -1 || url.indexOf('//') > -1) && checkDomain(location.href) !== checkDomain(url));
        }
    
        function downloadFile(url, callback) {
            var filename = url.split('/').pop()
                , req = new XMLHttpRequest()
            ;
    
            req.open('GET', url, true);
            req.responseType = 'blob';
            req.onload = function () {
                var blob = new Blob([req.response], {
                    type: 'application/pdf',
                });
    
                var isIE = !!document.documentMode;
    
                if (isIE) {
                    window.navigator.msSaveBlob(blob, filename);
                } else {
                    var windowUrl = window.URL || window.webkitURL;
                    var href = windowUrl.createObjectURL(blob);
                    var a = document.createElement('a');
                    a.setAttribute('download', filename);
                    a.setAttribute('href', href);
                    document.body.appendChild(a);
                    a.click();
                    document.body.removeChild(a);
                }
    
                if (callback) {
                    callback();
                }
            };
    
            req.send();
        }
    
        window.downloadControl = {
            checkDomain: checkDomain,
            isExternal: isExternal,
            downloadFile: downloadFile
        }
    })();
    Code language: JavaScript (javascript)

    위 스크립트에서 전역 변수 downloadControl 을 지정했다. 이 스크립트는 공통으로 사용하고, 다른 스크립트 파일에서 아래처럼 써 보자.

    $('.link').on('click', function (e) {
        var target = e.currentTarget
            , url = target.href;
    
        if (downloadControl.isExternal(url)) {
            e.preventDefault();
            target.href = '';
            downloadControl.downloadFile(url, function () {
                target.href = url;
            });
        }
    });Code language: JavaScript (javascript)

    대충 이런 식으로 처리한다. IE11에서도 동작하게끔 처리했다.

  • Naran Terms Merger v1.0.0

    Naran Terms Merger v1.0.0

    워드프레스 사이트 내에 정의된 택소노미 텀들을 정리하는 두구입니다. 블로그를 오래 운영하면서 그래도 자잘한 글들을 쓰다 보니 태그가 많이 쌓입니다.

    그런데 태그를 기분대로 만들다 보니, 태그 분류의 응집력이 너무 떨어집니다. 태그가 글들을 상세하게 분류하는 것도 나름 중요하지만, 불필요하게 태그가 중복되기도 하고, 태그 하나에 포스트가 겨우 하나씩만 대응되기도 합니다. 그런 것들은 재고의 여지가 있습니다.

    파편화된 태그를 모아 좀 더 응집력 있는 태그로 만든다… 컨셉은 좋습니다. 의외로 WordPress.org 에 올라온 플러그인도 많지 않습니다. 이게 해 보면 단순한 작업일 것 같지만 파편화된 텀을 일관성있게 묶게 시각화하는 UI 작업이 간단하지가 않은 것 같습니다.

    그래도 일단 초기의 컨셉을 다듬고 수정한 1.0.0 버전을 만들었습니다. 실제 텀들을 삭제하는 작업을 포함하므로 아직은 좀 더 테스트가 필요하겠습니다. 실제로 운영된 블로그에 설치해서 내부 테스트를 하니, 아직은 좀 더 필요한 부분들이 많은 것 같습니다.

    병합 방법

    관리자 > 도구 > Terms Merger 에서 찾을 수 있습니다. 텀을 헤집는 녀석이라 관리자 권한이어야만 접근이 가능하도록 설정햇습니다.

    화면에 두 개의 탭이 나옵니다. 하나는 Terms, 하나는 Slots 입니다.

    Terms 에서는 택소노미별 텀을 늘어놓습니다. Slots 에서는 병합할 텀들을 모으고 점검합니다.

    Terms 에서 아래 그림처럼 텀을 선택하고 슬롯을 지정합니다. 그러면 해당 텀은 그 슬롯에 배당됩니다.

    텀들을 슬롯에 모으면, Slots 탭으로 이동하여 슬롯에 담긴 텀을 확인해 볼 수 있습니다.

    1 Add new slot 버튼을 누르면 슬롯을 더 만들 수 있습니다. 최대 10개까지 생성이 가능합니다. 즉, 텀을 1번부터 10번 슬롯 중 하나에 담아서 분류를 할 수 있게 되는 것입니다.

    이 때 Assigned term(s) 에 내가 쌓아둔 텀들의 목록이 나타납니다. x를 누르면 슬롯에서 삭제할 수 있습니다.

    2 Header term 은 실제 병합을 할 때 어떤 텀을 대상으로 병합할지 기준을 정하는 것입니다. 파란색으로 하이라이트된 텀이 헤더 텀입니다. 위 그림에서는 ‘admin-ajax.php’라는 텀을 기준으로 나머지 텀들이 병합됩니다.

    3 Merge Terms 를 누르면 병합이 진행됩니다. 진행하기 전 경고창이 떠서 재차 확인을 요구합니다.

    4 Remove this slot 은 슬롯을 지우는 명령입니다. 슬롯만 삭제되고 텀에는 아무런 영향이 없습니다.

    많은 개선점이 …

    UI가 그다지 간단하지는 않았습니다. 많은 디테일한 점이 필요한 것으로 보입니다. 개발된 플러그인을 제 블로그에 설치하여, 나름 실사 블로그 사이트에 쌓인 태그 데이터를 대상으로 테스트해 보았습니다. 그래 보니 실제 사용에 있어 사용자가 정말 편리하게 텀을 분석하고 관리하려면 아직 부족한 점이 많다는 것을 느낍니다. 그래도 이 정도 개발을 진행한 것에 보람을 느끼며 포스팅을 올립니다.

    좀 더 완성도를 올리면 wordpress.org 에 올려 볼까 고민하고 있습니다. 혹시 원하는 분이 계시다면 github repository에서 다운로드 받아 테스트해 보세요.

  • 커스텀 포스트의 주소체계 (permastruct) 수정하기

    워드프레스에서 일반적인 포스트는 관리자 > 설정 > 고유주소에서 적절히 주소 체계를 수정할 수 있지만, 커스텀 포스트의 주소 체계는 딱 고정되어 있다. 보통 <커스텀 포스트 타입 이름>/<포스트 이름(postname, 또는 slug)> 으로 되어 있으며, <커스텀 포스 타입 이름> , 즉 “앞부분” 정도만 register_post_type() 함수의 파라미터 조정을 통해 변경할 수 있다.

    앞으로의 내용을 돕기 위해 커스텀 포스트를 작성한다. 아래와 같이 등록하였다.

    add_action( 'init', 'my_cpt_init' );
    function my_cpt_init() {
    	register_post_type(
    		'my-cpt',
    		[
    			'label'        => 'My CPT',
    			'public'       => true,
    			'hierarchical' => false,
    			'rewrite'      => [
    				'slug'    => 'cpt',
    				'pages'   => true,
    				'feeds'   => false,
    				'ep_mask' => EP_NONE,
    			],
    			'has_archive'  => true,
    			'show_in_rest' => true,
    		]
    	);
    }Code language: PHP (php)

    “앞부분” 변경하기

    우선 앞부분을 변경하는 방법부터 설명한다. ‘my-cpt’란 포스트 타입을 위 예제처럼 입력했을 때, ‘rewrite’ 파라미터 ‘slug’ 항목을 ‘cpt’로 변경하였다. 그러므로 이 타입 포스트들의 URL은 이제 /cpt/<포스트 이름> 체계를 따르게 된다. 임의로 cpt라는 문자열을 주었고 이 문자열은 슬래시도 허용한다. 그러므로 ‘my/cpt’ 같이 줘도 문제 없다.

    “뒷부분” 변경하기

    앞부분은 아주 쉽게 변경 가능하다. 문제는 뒷부분이다. 커스텀 포스트는 자유롭게 이 뒷부분을 변경할 수 없으며, 포스트 이름으로 고정되어 있다. 가령 이 부분을 포스트의 ID로 변경하려고 한다. 하지만 프론트엔드에서 만날 수 있는 설정으로는 이것이 불가능하다.

    그럼 별도의 액션/필터를 활용하여 이 부분을 수정해 보자. 우선 목표부터 좀 설명하기 쉽게 정리하자.

    목표: My CPT 타입의 포스트 ID  2476의 포스트 이름은 'aaaa'다.
    현재 주소는 /cpt/aaaa/ 이다. 이것을 /cpt/2476/ 으로 변경한다.

    워드프레스의 URL 매칭과 메인 쿼리 방식을 생각했을 때, 위 목표를 가능하게 하려면 다음과 같은 3가지를 작업해야 한다.

    1. 워드프레스가 커스텀 포스트를 등록할 때, 하드코딩된 permastruct(고유주소 구조)를 포스트 이름 기반에서 포스트 아이디 기반으로 수정할 것.
    2. 커스텀 포스트의 퍼마링크를 가져올 때, 정확한 주소가 나오는지 확인하고 수정할 것.
    3. rewrite_rule 을 생성할 때 포스트 아이디 기반 다시 쓰기가 메인 쿼리에서 정확히 쿼리될 수 있는지 파라미터를 확인하고 수정할 것.

    변경 #1: permastruct 수정

    커스텀 포스트의 permastruct (고유주소 구조)는 커스텀 포스트 등록시, 포스트의 이름으로 고정되어 있다. 구현부는 WP_Post_Type::add_permastruct() 메소드를 참고한다.

    이것을 수정하려면 ‘registered_post_type’ 액션을 사용하여 글로벌 변수 $wp_rewrite에 접근한다. 그리고 여기서 등록된 내역을 수정한다.

    add_action( 'registered_post_type', 'my_cpt_registered_post_type', 10, 2 );
    function my_cpt_registered_post_type( string $post_type, WP_Post_Type $post_type_object ) {
    	global $wp_rewrite;
    
    	if ( 'my-cpt' === $post_type ) {
    		$wp_rewrite->extra_permastructs[ $post_type ]['struct'] =
    			$wp_rewrite->front . $post_type_object->rewrite['slug'] . '/%post_id%';
    	}
    }Code language: PHP (php)

    워드프레스 코어가 일괄적으로 생성하는 다시 쓰기 규칙 중, 커스텀 포스트 단일 페이지에 접근하기 위한 정규식 표현과 다시쓰기는 아래와 같다. 좌측이 URL과 매칭할 정규식, 오른쪽이 실제로 다시 쓰기되는 쿼리 파라미터 표현이다.

    cpt/([^/]+)/page/?([0-9]{1,})/?$ 	index.php?my-cpt=$matches[1]&paged=$matches[2]
    cpt/([^/]+)(?:/([0-9]+))?/?$ 	index.php?my-cpt=$matches[1]&page=$matches[2]

    두 규칙 다 cpt/ 다음에 ‘슬래시가 아닌 아무 문자열’을 매칭하며, 이 매칭은 my-cpt 포스트의 포스트 이름(슬러그)와 매칭되도록 설계되어 있다. 위 코드까지 추가하게 되면 두 쿼리 파라미터 표현은 아래처럼 변경될 것이다.

    cpt/([0-9]+)/page/?([0-9]{1,})/?$ 	index.php?p=$matches[1]&paged=$matches[2]
    cpt/([0-9]+)(?:/([0-9]+))?/?$ 	index.php?p=$matches[1]&page=$matches[2]

    ‘%post_id%’를 넣어준 덕분에 ‘슬래시가 아닌 아무 문자열’ 정규식 부분이 ‘연속된 숫자’로 변경된 것을 볼 수 있다.

    변경 #2: permalink 수정

    다시 쓰기 규칙이 변경되었으니, 이제 “/cpt/2476/”으로 주소가 변경되었을 것 같지만, 아직은 부족하다. 포스트 편집기에서 보면 퍼마링크가 생뚱맞게 “/cpt/%post_id%/”로 나올 것이다. 이것은 get_permalink() 함수가 커스텀 포스트에 대해서는 %post_id%라는 치환 문자열을 고려하지 않기 때문이다.

    생뚱맞은 %post_id% 가 URL 주소로.

    이 문제는 ‘post_type_link’ 필터를 통해 해결이 가능하다. 아래 코드까지 붙여 넣는다.

    add_filter( 'post_type_link', 'my_cpt_post_type_link', 10, 2 );
    function my_cpt_post_type_link( string $post_link, WP_Post $post ): string {
    	if ( 'my-cpt' === $post->post_type ) {
    		$post_link = str_replace( '%post_id%', $post->ID, $post_link );
    	}
    	return $post_link;
    }Code language: PHP (php)

    이제 URL 주소가 그럴듯하게 나온다! 코드를 수정한 후 반드시 관리자 > 설정 > 고유주소를 재방문하여 rewrite_rule을 다시 생성해야 하는 것을 잊지 말자.

    URL이 수정되었다.

    변경 #3: rewrite_rule 수정

    주소는 올바르게 찍히지만, 해당 주소로 들어갔을 때 404 NOT FOUND 페이지가 나온다. 아직 한 가지가 부족하다. 아까 다시 쓰기를 했을 때 규칙을 보자.

    • 정규 표현식: cpt/([0-9]+)(?:/([0-9]+))?/?$
    • 다시 쓰기: index.php?p=$matches[1]&page=$matches[2]

    다시 쓰기에서 p=$matches[1] 부분이 중요하다. 우리가 ‘/cpt/2476/’ 경로로 들어왔을 때, 이것은 ‘index.php?p=2476&page=’로 치환된다. 여기서 포스트 ID 2476번을 쿼리하라는 명령은 올바르게 전달되지만, 포스트 타입 파라미터가 생략되어 있다. 포스트 타입 파라미터가 생략되면 기본값인 ‘post’가 사용된다. 그러므로 실제로 SQL 쿼리문은 다음처럼 입력되어버린다.

    SELECT wp_posts.* FROM wp_posts WHERE 1=1 AND wp_posts.ID = 2476 AND hws_posts.post_type = 'post' ORDER BY wp_posts.post_date DESC Code language: SQL (Structured Query Language) (sql)

    2476번 포스트의 포스트 타입은 ‘my-cat’이니, 결과적으로 포스트가 없는 것으로 인식되는 것이다. 그럼 이 문제까지 수정해 보자. 다시 쓰기 규칙을 생성하고 기록하기 전에 위치한 ‘rewrite_rules_array’ 필터를 활용하면 수정이 가능하다.

    add_filter( 'rewrite_rules_array', 'my_cpt_rewrite_rules_array' );
    function my_cpt_rewrite_rules_array( array $rules ): array {
    	$obj = get_post_type_object( 'my-cpt' );
    
    	if ( $obj ) {
    		$slug = $obj->rewrite['slug'];
    
    		$new_rules = [
    			$slug . '/([0-9]+)/page/?([0-9]{1,})/?$' => 'index.php?post_type=my-cpt&p=$matches[1]&paged=$matches[2]',
    			$slug . '/([0-9]+)(?:/([0-9]+))?/?$'     => 'index.php?post_type=my-cpt&p=$matches[1]&page=$matches[2]',
    		];
    
    		foreach ( $new_rules as $key => $value ) {
    			if ( isset( $rules[ $key ] ) ) {
    				$rules[ $key ] = $value;
    			}
    		}
    	}
    
    	return $rules;
    }Code language: PHP (php)

    post_type을 ‘my-cpt’로 수정하면 올바르게 글이 쿼리된다.

    위 코드를 합친 플러그인 예제는 gist에도 올려둔다. 이러한 커스터마이즈는 주로 게시판 같은 스타일로 커스텀 포스트를 운용할 때 상당히 유용하다. 적절히 사용하자.

  • Naran Disable Heartbeat

    관리자 영역에서 실행되는 하트비트 스크립트를 해제합니다.

    (더 보기…)
  • Naran Screen Width

    Naran Screen Width

    무진장 간단한 플러그인입니다. 관리자 바에 브라우저 길이를 보여줍니다. 그게 다입니다.

    (더 보기…)
  • Naran Check Requires At Least

    Naran Check Requires At Least

    이 플러그인은 wpseek.com 의 Plugin Doctor에서 영감을 받아 작성하였습니다. 내 플러그인을 지원하는 최소 워드프레스 버전은? 포스트에서 말한 대로 플러그인 작성시 필요한 ‘Requires at least’ 헤더에 쓰일 버전을 검출할 때 유용한 툴입니다.

    (더 보기…)
  • 내 플러그인을 지원하는 최소 워드프레스 버전은?

    워드프레스 헤더 중 Requires at least라는 항목이 존재한다. 해당 플러그인, 테마가 정상 동작할 수 있는 최소 워드프레스 버전을 의미한다.

    워드프레스는 계속 변화한다. 새 함수가 소개되기도 하고, 기존의 함수가 폐기 처분되기도 한다. 새 함수를 사용하면 아무래도 좀 더 편하기 코드 작성이 가능하다. 그러나 그만큼 호환성에 문제가 생기기도 한다. 이 때 이 안정성을 보장하기 위해 이 헤더가 존재한다.

    워드프레스 코어 버전이 여기에 적힌 버전보다 낮다면 플러그인은 제대로 활성화되지 않는다. 활성화된 플러그인이 에러를 내서 사이트가 다운되는 것 보다는 애초에 활성화되지 않는 것이 더 낫기 때문이다.

    그러므로 플러그인 개발자는 좀 더 친절하게 이 항목을 적어 두는 것이 좋다.

    그런데 문제는 내가 작성한 프로그램의 수많은 함수를 보면서 어떤 함수가 워드프레스 코어 어떤 버전에서 추가되었는지 (혹은 폐기되었는지) 찾기 어렵다.

    https://wpseek.com/pluginfilecheck/

    이럴 때 이 사이트를 쓰면 편리하다. 코드를 업로드하면 프로그램의 소스를 분석해서 워드프레스의 코어에 함수가 포함된 버전을 알아서 정리해 준다. 그리고 어떤 버전을 최소로 해야 할지를 결정해 준다.

    내가 작성한 코드에 대한 결과 예시. 최소 버전이 5.3.0이라고 알려 준다.
    어떤 함수를 썼고, 그게 어떤 버전에서 추가되었는지 분석한다.

    결과 화면의 일부분에는 그림처럼 사용된 모든 함수의 추가 지점을 정리해 준다. 여기서 내가 wp_date()라는 함수를 사용했고, 이 함수는 5.3.0에서 추가되었다는 사실을 명확히 보여준다.

    이 사이트에서 내 코드를 올리는 게 조금 찜찜하기는 하지만, 설마 코드를 수집해서 뭘 할 거 같지는 않다. 북마크에 저장해두고 유용히 쓰자.

  • 우커머스 상품 ‘처리중’을 자동으로 ‘완료됨’으로 변경

    <?php
    add_filter( 
      'woocommerce_payment_complete_order_status',
      function () {
        if ( 'processing' === $status ) {
          $status = 'completed';
        }
        return $status;
      }, 10, 3
    );
    Code language: PHP (php)
  • 워드프레스 개발은 타협이 절반 #2

    지난 포스트에 이어 계속 타협접을 짚어 본다. 모든 것은 나 개인의 의견이다.

    (더 보기…)
  • 워드프레스 개발은 타협이 절반 #1

    세상 모든 것이 다 독불장군으로 혼자서 설 수 없는 건 자명한 사실. 워드프레스도 마찬가지다. 여러 가지 조건과 상황에 맞춰 잘 타협해야 한다.

    (더 보기…)
  • VSCode에서 PHP Remote Debugging 설정

    VSCode에서 PHP Remote Debugging 설정

    나는 주력으로 PhpStorm IDE를 꽤 오랫동안 사용해 오고 있다. IDE로서 매우 편리한 기능들이 많기 때문에 버릴 수가 없다.

    하지만 요즘은 VSCode가 엄청나게 많은 개발자들의 지지를 얻고 있다. 무료로 사용 가능하고 많은 확장 기능들이 있어 그 나름대로 많이 편리한 것 같다. 가볍기도 하고.

    나는 그동안 PhpStorm에서 최고로 유용한 기능으로 꼽았던 것이 PHP 원격 디버깅 기능이었다. 다만, 이건 PhpStorm에서만 가능한 사항은 아니고 IDE 별로 적절히 지원만 해 주면 가능한 기능이다.

    앞으로 PhpStorm을 애용할 생각이지만, VSCode를 활용한 개발 환경은 대안으로 늘 만들어 두고 싶었다. 그 중 넘어야 할 산(?) 중 하나인 원격 디버깅 기능을 VSCode에서 셋업해 봤다.

    PHP Debug

    Extensions 메뉴를 선택하고, ‘PHP Debug’를 입력해 확장을 설치한다.

    그다음 디버깅을 하려는 소스 코드가 있는 디렉토리를 연다. 만약 워드프레스 테마나 플러그인을 작업하려면 워드프레스 루트 디렉토리를 여는 것을 더 권장한다.

    VSCode의 설정을 저장하는 .vscode 디렉토리가 생성된다 (만약 없으면 만들면 된다). 이 디렉토리에 launch.json 파일을 생성한다 (마찬가지로 없으면 만들면 된다). 이 파일에 다음처럼 셋팅을 준비한다.

    이미지에 표시된 순서대로 클릭.
    드롭다운 목록 상자가 나오는데 여기서 ‘PHP’ 선택.
    {
        "version": "0.2.0",
        "configurations": [
            {
                "name": "Remote Debugging",
                "type": "php",
                "request": "launch",
                "port": 9003,
                "pathMappings": {
                    "/path/to/wp": "${workspaceFolder}/"
                }
            }
        ]
    }Code language: JSON / JSON with Comments (json)
    • 기본 템플릿 코드는 적당히 무시하고 위 코드로 갈음한다.
    • XDebug 3가 나오면서 기본 포트가 9000번에서 9003번으로 변경되었다. 해당 사항을 명시한다.
    • 호스트는 로컬 개발을 가정하므로 기본인 localhost로 생략하였다.
    • type으로 php를 설정한다.
    • request는 launch로 고정된다.
    • pathMappings 부분의 /path/to/wp 부분은 현재 VSCode에서 열어둔 디렉토리 절대경로 설정한다.
    • name은 임의의 이름이다. 마음대로 정하면 된다.

    이렇게 하면 ‘Run and Debug’ 메뉴에서 ‘Remote Debugging’ 실행 항목이 생성된다. ‘Start Debugging’ 아이콘을 눌러 디버깅을 실행하면 된다.

    그림과 같은 항목이 생긴다. 버튼을 눌러 디버깅 시작.

    PHP XDebug 설정

    xdebug 설정 부분은 대략 아래와 같다.

    zend_extension=xdebug.so
    
    xdebug.mode=develop,debug
    xdebug.remote_enable=on
    xdebug.start_with_request=trigger
    xdebug.idekey=phpstorm-xdebug
    • 이 설정은 XDebug 3의 설정이다. 2와 다르다. 버전을 확인하여 3.X인지 확인하자.
    • mode가 develop도 같이 넣어 에러 출력도 같이 더했다. 원하지 않으면 debug만 넣으면 된다.
    • start_with_requst=yes로 하는 방법도 있다. 그런데 이렇게 하면 언제나 디버깅이 실행된다. 브라우저에서 적절히 ON/OFF를 결정할 수 있으려면 trigger를 설정하면 된다.
    • 브라우저에서 ON/OFF를 결정하는 방법은 세션 쿠키를 보내는 방법이다. 이때 사용하는 설정이 idekey. 이 값을 phpstorm-xdebug로 정했다. 디버깅은 VSCode 뿐만 아니라 PhpStorm에서도 번갈항 쓰는 데다, PhpStorm이 더 주력이기 때문이다.

    브라우저 설정

    start_with_requst=trigger로 설정했으면 이 과정이 필요하다. 브라우저에 절절히 세션 쿠키를 넣을 방법이 필요하기 때문이다. 어지간한 브라우저마다 PHP 디버깅을 위한 확장 프로그램을 제공할 것이다. 나는 주로 파이어폭스와 XDebug Session Cookie를 사용한다. 설치한 다음 쿠키 값으로 phpstorm-xdebug를 설정한다.

    IDE Key는 xdebug 설정과 동일하게.
    플러그인을 통해 세션 쿠키 값을 셋팅할 수 있다.

    디버깅 시작

    워드프레스의 프론트 메인 파일인 index.php에 적당히 브레이크 포인트를 넣고 디버깅을 시작해 봤다. 아래처럼 PHP 처리가 중간에 멈추면서, 해당 과정 중의 콜스택, 변수 정보를 모두 속속들이 볼 수 있다. 여기에서 함수 안으로 들어갈지, 다음 코드 라인으로 진행할지 결정할 수 있다.

    브레이크 포인트에 딱 걸림!
    1. 현재 브레이크 포인트에 걸려서 중지된 라인이 하이라이트되었다.
    2. 디버깅 툴이다. 코드 진행을 제어할 수 있다.
    3. 현재 상태의 변수 이름과 값을 확인할 수 있다. 매우 유용하다.
    4. 콜스택이 나타난다.

    배열의 활용이 두드러진 PHP 코드는 런타임 때 대체 연관 배열 변수 값이 어떤 구조인지 알아보기 어려운 경우가 많다. 이 때 사용할 수 있는 가장 강력한 툴이 바로 XDebug를 활용한 원격 디버깅인 것 같다. 이렇게 XDebug의 원격 디버깅 기능을 활용하면 프로그램의 상황을 자세히 파악하면서 개발할 수가 있다. 보다 품질 좋은 코드를 작성하려면 반드시 필요하다.

  • FakerPress: 테스트용 포스트 일괄 생성 플러그인

    지난번 WP CLI를 소개하는 포스팅 때 개발 테스트를 위해 일괄적으로 임의의 포스팅을 대량으로 생성하는 명령어인 wp post generate에 대해 소개하였다.

    그런데 이 명령어는 포스트의 내용까지는 대량 작성하기가 어려운 점이 있어 단순히 포스트 수만 채우는 정도까지만 유용하다. 그러므로 포스트의 내용이 제대로 출력되는지, 스타일이 제대로 반영되는지 등의 디테일한 사항까지 체크할 수 있으려면 조금 역부족이다.

    그럴 때 정말 유용한 플러그인이 바로 FakerPress 이다. 개발 초기 테스트를 위한 포스트를 원하는대로 생성이 가능하다. 오늘은 이 플러그인을 간단히 소개할까 한다.

    FakerPress는 오픈 소스 플러그인이다. WordPress 플러그인 목록에서 간단히 다운로드 받아 설치할 수 있으며 활성화 즉시 사용 가능하다.

    포스트 생성시 갯수는 물론이고 해당 포스트에 카테고리, 태그, 본문 작성시 삽입할 구조까지 얼추 지정 가능하다. 특히 랜덤한 이미지까지 삽입할 수 있어 매우 유용하다.

    포스트, 코멘트, 텀, 유저 생성이 가능하다.
    포스트의 필드를 적절히 조절 가능
    포스트 콘텐츠 구조까지 적당히 조절 가능
    택소노미나 메타 필드까지 조절 가능

    이렇게 랜덤 생성된 포스트는 모두 FakerPress가 작성한 가짜 포스트라는 태깅이 되어 있어 모두 손쉽게 삭제 가능하다. 설정 페이지로 가서 Let It Go!만 입력하면 차후 언제나 깔끔하고 간단하게 생성한 포스트를 제거 가능하다.

    가버려!

    가짜 포스트를 나름 의미 있게 일일이 생성하는 것은 꽤 귀찮은 일인데, 이 플러그인을 쓰면 꽤 유용하다. 테마 개발자라믄 사용을 강력히 추천한다.

  • 워드프레스 자동 설치 스크립트 샘플

    이번 포스팅은 특정 디렉토리를 기준으로 DB 생성, 소스 다운로드, 설치 및 초기 셋팅을 정해진대로 수행하는 스크립트의 예제를 올린다.

    그동안 간단히 만들어 사용한 거고 WP-CLI를 사용하는 좋은 예시인 것 같다. 코드는 gist 에서 확인할 수 있다.

    이 코드에서 처음에 지정해 줘야 할 변수값들이 있다. 확인해 보자.

    WP_ROOT="/path/to/wp/root"
    DB_ROOT_PASS='0000'
    
    ADMIN_USER=user
    ADMIN_EMAIL=user@email.com
    ADMIN_PASS=pass
    
    SMTP_USER=user@gmail.com
    SMTP_PASS=passCode language: JavaScript (javascript)

    WP_ROOT은 워드프레스 설치들을 모아놓은 상위 디렉토리이다. 여기가 만약 /home/changwoo/develop/wordpress였다면 이 디렉토리의 서브디렉토리별로 워드프레스 설치본이 하나씩 생성된다.

    ADMIN_USER, ADMIN_EMAIL, ADMIN_PASS는 워드프레스 최초설치시 생성하는 관리자의 로그인, 이메일, 패스워드가 된다. 적절히 셋팅해두자.

    SMTP_USER, SMTP_PASS는 기본으로 지메일 SMTP 세팅을 정해두고, 사용자와 패스워드를 입력한다. 지메일 계정의 낮은 보안 앱 접근을 허용해 두어야 하는 것을 잊지 말자.

    중간에 내가 사용하는 naran 프로젝트 플러그인 몇가지도 추가되어 있다. 이것을 정상적으로 실행하려면 git이 설치되어 있어야 한다. 또한 WP CLI를 기본적으로 사용하므로 반드시 설치되어 있어야 하고.

    wp_install 스크립트는 다음과 같이 실행될 것이다.

    $ wp_install foo
    ... (설치 메시지)
    Successfully installed. DB USER/PASS: foo / IDLcKVrGJ4Ju

    예를 들어 WP_ROOT/home/changwoo/develop/wordpres였다면 이 아래 foo라는 디렉토리 아래 워드프레스가 설치된 것이 보일 것이다. 그리고 이 워드프레스의 도메인은 http://foo.dev.site로 설정되어 있다. 나머지 서버 설정은 따로 진행하면 된다.

    스크립트는 예시일 뿐이다. 각자의 환경에 따라 적절히 변경해서 사용하도록 하자.

    wp_install gist

  • wp-cli 사용하기 #3

    어제의 포스팅에 이어 wp-cli에 대해 소개하는 포스팅을 작성한다. 오늘은 아마도 마무리하는 글에 걸맞게 실무적으로 유용한 명령들이 될 것이다.

    (더 보기…)
  • wp-cli 사용하기 #2

    지난 포스트에 이어 wp cli 사용에 대해 계속 포스팅한다. 이 멋진 툴을 소개하려니 신난다. 한번에 좌르륵 풀어내기보다는 아껴서 조금씩조금씩 이야기하고 싶다. 자, 우선 무엇부터 시작할까?

    (더 보기…)
  • wp-cli 사용하기 #1

    의식의 흐름대로 흘러가는 1일 1워프 중이다. 적당히, 진짜 적당껏 분량 조절해서 하루에 하나씩 원하는 내용을 적는 중이다.

    오늘은 드디어 wp-cli의 이야기를 하게 된다. 워드프레스 개발자로서 반드시 숙지해야 할 녀석, 그리고 할 이야기가 무지무지 많은 녀석이다.

    (더 보기…)
  • 워드프레스 플러그인/테마 올바르게 번역하기

    잘못된 방법

    예전에 워드프레스 플러그인 만들기라는 문서를 작성한 적이 있다. 꽤 오래된 문서인데 (벌써 햇수로 6년 되었다!), 워드프레스 API는 여기서 그렇게 변경된 건 없는 것 같다.

    이 문서는 귀찮아서 업데이트하지 않고 있는데, 여기서의 텍스트 번역 부분은 그 당시 워드프레스 환경 및 일반적인 PHP 애플리케이션에 대해서는 맞다. 그러나 더이상 이 방법이 올바르다고 보기 어렵다.

    왜냐면 PoEdit을 이용한 소스 검색은 플러그인/테마의 헤더까지 번역문을 생성하지 않기 때문이다.

    올바른 방법

    플러그인 목록에 플러그인 제목과 나머지 헤더 내용까지 정확하게 번역하려면 wp-cli를 사용하는 것이 좋다. 이 앱에 대해서는 따로 소개하는 포스팅을 작성할 생각인데, 오늘은 맛보기로 미리 언급하기로 하자. 🙂

    wp cli 에는 i18n 명령이 있다. i18n은 ‘internationalization’을 간단히 줄여 쓸 때 자주 사용한다. 욕 아니다. i와 n 사이에 정확히 18글자가 있기 때문이다. 굳이 번역하면 ‘국제화’ 정도?

    사족으로, 이에 반대되는 개념이 지역화, 즉 ‘localization’ 인데 이것도 쓰기 어렵다고 ‘l10n’이라고 쓴다.

    i18n 명령의 서브 명령어로 ‘make-pot’과 ‘make-json’ 둘이 있다.

    make-json블록 에디터가 등장한 이후 자바스크립트에서 보다 편리하게 번역문을 처리하기 위한 새 방법을 지원하는 명령어고, 오늘 포스팅의 범위를 살짝 벗어난다.

    그럼 make-pot을 살펴보자. foo라는 플러그인을 예시로 레시피를 만들어 보자. 참고로 도메인은 bar라고 가정하자. 즉 소스 코드에서 __( 'Hello, World!', 'bar' ); 처럼 번역 텍스트를 작성했다는 뜻이다.

    cd <wordpress_path>/wp-content/plugins/foo
    mkdir languages # 디렉토리가 만들어지지 않았다면.
    wp-cli i18n make-pot . ./languages/bar.pot --domain=barCode language: Bash (bash)

    wp-cli로 번역 텍스트를 추출하면 기특하게 헤더 텍스트까지 알아서 잘 고려한다. 게다가 커맨드라인 방식이라 스크립트 처리하기 용이하다.

    이후 PoEdit을 이용해 번역문을 만든다. 만약 한국어로 번역했다면, 번역 파일의 이름은 bar-ko_KR.po 가 된다.

    기타: XDebug 사용시 트러블슈팅

    아마 xdebug를 사용하면 wp-cli로 pot 파일 생성시 ‘Maximum function nesting level of …’ 같은 에러를 만날 것이다. 이것은 xdebug가 디버깅을 위해 함수 호출 레벨을 낮춰 두기 때문이다. 아마도 소스 코드에서 번역문을 스캔할 때 꽤 많은 함수 중첩이 필요한가 보다.

    에러가 발생한다면 아래와 같은 명령어를 사용해 보자.

    php -d xdebug.max_nesting_level=512 $(which wp-cli) i18n [이후 동일]Code language: Bash (bash)

    참고로 아래처럼 번역에 불필요한 모듈까지 제거하면 속도 향상이 있다고 한다.

    php -n -dextension=phar.so \
        -dextension=json.so \
        -dextension=mbstring.so \
        -d xdebug.max_nesting_level=512 \
        $(which wp-cli) i18n (이후 동일)Code language: JavaScript (javascript)

    출처: Invoking wp i18n make-pot fails when Xdebug is enabled

  • 플러그인 헤더 보충 설명

    플러그인에는 플러그인 헤더가 반드시 필요하다. Header Requirement 코덱스에도 잘 나와 있지만, 몇가지 보충 설명을 더하고자 한다.

    플러그인 헤더를 작성하면 몇몇 내용은 플러그인 목록 정보에 반영된다. 한편 몇몇 내용은 플러그인 활성화 때 플러그인이 정상적으로 동작하는 환경인지 점검하기 위한 용도로 사용되기도 한다.

    목록 정보에 반영되는 필드들

    • Plugin Name: 플러그인 필수 헤더. 플러그인의 제목으로 사용.
    • Plugin URI: 기재하면 ‘플러그인 사이트 방문’이라고 나온다.
    • Description: 제목 옆에 플러그인 설명란으로 사용.
    • Version: 버전 정보로 표시.
    • Author: 작성자 정보로 표시.
    • Author URI: 기재하면 작성자 이름이 하이퍼링크로 변경. 클릭하면 해당 주소로 이동.

    플러그인 활성화시 영향을 주는 필드들

    • Requires at least: 플러그인이 요구하는 최소 워드프레스 버전이다. 만약 플러그인에서 사용하는 함수들을 워드프레스가 충분히 지원하는지 안전하게 확인하기 위한 용도로 사용된다. 만약 플러그인이 설치된 워드프레스가 이 버전보다 낮다면 활성화되지 않는다. 잘못하면 사이트 전체가 멈추는 불상사가 생길 수도 있기 때문이다.
    • Requires PHP: 플러그인이 요구하는 최소 PHP 버전이다. 마찬가지로 활성화시 PHP 버전을 체크하여 서버의 PHP 버전이 이 값보다 낮다면 플러그인은 활성화되지 않는다. ‘Requires at least’ 필드와 마찬가지로 오작동하면 사이트 전체를 멈출 수도 있기 때문이다.

    다국어 지원에 영향을 주는 필드들

    • Text Domain: 플러그인이 사용하는 텍스트도메인을 정확히 기재한다. 다국어 파일이 제대로 만들어졌다면 플러그인이 해당 언어로 표시된다. 위 그림에서도 ‘나란 옵션 편집기’나 ‘아키스밋’이나 헤더는 영어로 작성되었지만, 한국어 번역 파일이 존재하기 때문에 해당 헤더 필드가 번역되어 출력된다.
    • Domain Path: 플러그인의 번역 파일을 정확히 찾기 위해 도움을 주는 필드이다.

    다음에는 플러그인 목록 표시에 잘 반영되는 정확히 번역 파일 제작 방법에 대해 포스팅한다.

  • wp-util #2: wp.ajax

    지난 포스트에 이어 이번에는 wp.ajax 에 대해 포스팅한다. wp.ajax에 대해 소개하고 wp.ajax와 wp.template을 활용한 초간단 플러그인을 같이 제작해 본다.

    (더 보기…)
  • wp-util #1: wp.template

    오늘은 wp-util 스크립트를 소개하고자 한다. 경로는 /wp-includes/js/wp-util.js 이고, 이 스크립트 안에는 두가지 도구가 있는데, 하나는 wp.template, 나머지 하나는 wp.ajax이다. 간단하게 포스팅하는 것이 1일 1워프의 주제인데, 오늘 하루에 두가지를 다 포스팅하기는 내가 너무 힘들고 분량도 아까우므로 🙂 오늘은 그중 하나인 wp.template 만 알아보도록 하자.

    (더 보기…)