WordPress 수정사항


블로그 개편에 따라 그동안 미뤄왔던 워드프레스 설정을 정리해 보았습니다.

플러그인

오래되거나 불필요한 플러그인들은 제거하였습니다. 현재 사용중인 플러그인들은 다음과 같습니다.

  • Add MIME Types : zip 등의 파일이 업로드되지 않아 MIME을 추가하기 위해 사용
  • Akismet Anti-spam: 스팸 방지 : 스팸 방지
  • Blocksy 컴패니언 : Blocksy 테마와 호환
  • Code Snippets : 코드 스니펫을 사용하여 functions.php 수정없이 PHP 코드를 삽입
  • Default featured image : 대표 이미지를 설정하지 않을 경우 기본 이미지를 표시
  • External Links : 링크를 새창에서 오픈하고, 링크에 아이콘을 표시
  • Firelight Lightbox : 이미지, 갤러리, 동영상 등을 라이트박스에서 오픈
  • Jetpack : 보안, 성능
  • Site Kit by Google : Google Search Console 연동
  • WP-DBManager : DB 관리
  • Yoast SEO : SEO 최적화
  • 간편한 목차 : 글 상단에 목차 생성
  • 또 다른 관련 포스트 플러그인(YARPP) : 관련 포스트 표시

스니펫

삭제한 플러그인과 클래 편집기로 작성한 글들에 대응하기 위해 스니펫을 작성하였습니다.
스니펫은 Code Snippets로 관리하고 있습니다.

위젯 - 댓글

우측 사이드바 위젯에 사용할 댓글 목록입니다. 핑백과 트랙백은 제외하고 댓글이 작성된 게시물을 10개 표시합니다.

// 예제 : [recent_comment_posts count="10"]

function recent_comment_posts_shortcode($atts) {

    $atts = shortcode_atts(array(
        'count' => 10
    ), $atts);

    $comments = get_comments(array(
        'number' => $atts['count'],
        'status' => 'approve',
        'type'   => 'comment'
    ));

    $output = '<h3 class="widget-title">Comment</h3>';
    $output .= '<ul class="recent-comment-list">';

    foreach ($comments as $comment) {

        $author = esc_html($comment->comment_author);
        $title  = esc_html(get_the_title($comment->comment_post_ID));
        $link   = get_comment_link($comment);

        $text = $author . ' - ' . $title;

        if (mb_strlen($text, 'UTF-8') > 33) {
            $text = mb_substr($text, 0, 33, 'UTF-8') . '...';
        }

        $output .= sprintf(
            '<li>• <a href="%s">%s</a></li>',
            esc_url($link),
            esc_html($text)
        );
    }	
	

    $output .= '</ul>';

    return $output;
}

add_shortcode('recent_comment_posts', 'recent_comment_posts_shortcode');

Comment

위젯 - 카테고리

우측 사이드바 위젯에 사용할 카테고리 목록입니다. Depth 1은 제외하고 표시하며 카테고리내의 게시글 갯수도 함께 표시합니다.

// 예제 : [category_list count="100"]

function category_list_shortcode($atts) {

    $atts = shortcode_atts(array(
        'count' => 20
    ), $atts);

    $categories = get_terms(array(
        'taxonomy'   => 'category',
        'orderby'    => 'name',
        'order'      => 'ASC',
        'hide_empty' => true,
        'number'     => $atts['count'],
        'parent'     => 0, // 일단 상위만 가져오고
        'hierarchical' => false
    ));

    // 최상위 제외를 위해 전체 다시 가져온 후 필터링
    $all_categories = get_terms(array(
        'taxonomy'   => 'category',
        'orderby'    => 'name',
        'order'      => 'ASC',
        'hide_empty' => true,
    ));

	// 최상위(0) 제외
    $categories = array_filter($all_categories, function($cat) {
        return $cat->parent != 0;
    });

    $output = '<h3 class="widget-title">Category</h3>';
    $output .= '<ul class="category-list">';

    foreach ($categories as $category) {

        $output .= sprintf(
            '<li>• <a href="%s">%s <span class="category-count">(%d)</span></a></li>',
            esc_url(get_category_link($category->term_id)),
            esc_html($category->name),
            $category->count
        );
    }

    $output .= '</ul>';

    return $output;
}

add_shortcode('category_list', 'category_list_shortcode');

Category

본문 - 최다 조회 포스트

About 글에 넣기 위한 단축코드입니다. 조회수가 많은 개시글 목록을 표시합니다.

// 예제 : [popular_posts count="20"]

function popular_posts_shortcode( $atts ) {
    $atts = shortcode_atts( [
        'count' => 10,
        'days'  => 365,
    ], $atts );

    $count = max( 1, min( 50, intval( $atts['count'] ) ) );
    $days  = max( 1, min( 730, intval( $atts['days'] ) ) );

    if ( ! function_exists( 'stats_get_csv' ) ) {
        return '<p>Jetpack Stats가 필요합니다.</p>';
    }

    // 필터링으로 누락될 걸 대비해 API로는 넉넉하게 가져옵니다. (요청 갯수의 3배, 최대 100개 제한)
    $api_limit = min( 100, $count * 3 );

    $top_posts = stats_get_csv( 'postviews', [
        'days'  => $days,
        'limit' => $api_limit, 
    ] );

    if ( empty( $top_posts ) ) {
        return '<p>데이터가 없습니다.</p>';
    }

    $html = '<div><ol>';
    $displayed_count = 0; // 실제 화면에 표시된 갯수를 세는 카운터 변수

    foreach ( $top_posts as $post_data ) {
        // 목표한 갯수를 채웠다면 반복문을 완전히 종료합니다.
        if ( $displayed_count >= $count ) {
            break;
        }

        if ( $post_data['post_id'] == 0 ) continue;

        $post = get_post( $post_data['post_id'] );
        if ( ! $post || $post->post_status !== 'publish' ) continue;

        $category = get_the_category( $post->ID );
        $cat_name = $category ? esc_html( $category[0]->name ) : '';

        $html .= '<li>';
        $html .= '<a href="' . esc_url( get_permalink( $post->ID ) ) . '">' . esc_html( $post->post_title ) . '</a>';
        if ( $cat_name ) $html .= ' &bull; <span>' . $cat_name . '</span>';
        $html .= ' &bull; <span>' . number_format( $post_data['views'] ) . '</span>';
        $html .= '</li>';

        $displayed_count++; // [수정 포인트 3] 성공적으로 출력이 추가될 때만 카운트를 올립니다.
    }

    $html .= '</ol></div>';

    // 만약 필터링 때문에 하나도 노출되지 않았다면 빈 태그 대신 안내 문구를 띄웁니다.
    if ( $displayed_count === 0 ) {
        return '<p>데이터가 없습니다.</p>';
    }

    return $html;
}
add_shortcode( 'popular_posts', 'popular_posts_shortcode' );

  1. AMD BC-250Private > Game3,913
  2. Batocera 설치Private > Game2,706
  3. X-Gunner PistolPrivate > Game940
  4. Multi Track bin 파일 병합Private > Game846
  5. PS Vita Retroarch 설치Private > Game779
  6. 이글렛II - 바토세라 설치Private > Game702
  7. 안드로이드 Retroarch 한글깨짐Private > Game694
  8. Y700 2세대IT > Device612
  9. Retrobat & Batocera 롬 공유Private > Game603
  10. RetroArch 번역기능Private > Game577
  11. PlayStation2 - OPLPrivate > Game557
  12. POWKIDDY A12 바탑Private > Game457
  13. ASAP-NX 펌웨어 업데이트Private > Game454
  14. Batocera에 Switch 게임추가Private > Game411
  15. C# - 프로그램 종료하기IT > .Net335
  16. PCSX2 - 레일슈팅 세팅Private > Game328
  17. Neogeo ASPPrivate > Game323
  18. Retrobat - TeknoparrotPrivate > Game319
  19. 뷰3 - RetroArch 설치Private > Game286
  20. 레노버 G9 게임패드Private > Game260

본문 - 간단한 버튼

오래전 사용하던 플러그인에서 사용된 단축코드를 위해 추가한 스니펫입니다. 주로 다운로드 버튼을 위해 사용중입니다.

// 예제 : [symple_button url="https://kimstar.kr/blog/wp-content/uploads/2018/11/Rpi_bootSelector.zip"]Download[/symple_button]

function symple_button_shortcode($atts, $content = null) {

    $atts = shortcode_atts(array(
        'url' => '#',
        'color' => 'black',
        'size' => 'default',
        'border_radius' => '3px',
        'target' => 'self',
        'rel' => '',
    ), $atts);

    $target = ($atts['target'] === 'blank') ? '_blank' : '_self';

    $style = sprintf(
        'display:inline-block;padding:10px 20px;background:%s;color:#fff;text-decoration:none;border-radius:%s;',
        esc_attr($atts['color']),
        esc_attr($atts['border_radius'])
    );

    return sprintf(
        '<a href="%s" target="%s" rel="%s" style="%s">%s</a>',
        esc_url($atts['url']),
        esc_attr($target),
        esc_attr($atts['rel']),
        $style,
        do_shortcode($content)
    );
}
add_shortcode('symple_button', 'symple_button_shortcode');

Download

본문 - 간단한 박스

오래전 사용하던 플러그인에서 사용된 단축코드를 위해 추가한 스니펫입니다. 특정 범위를 박스안에 넣기 위해 사용됩니다.

// 예제 : [symple_box color="gray" fade_in="false" float="center" text_align="left" width=""]동해물과 백두산이[/symple_box]

function symple_box_shortcode($atts, $content = null) {

    $atts = shortcode_atts(array(
        'color' => 'gray',
        'text_align' => 'left',
        'width' => '',
    ), $atts);

    $colors = array(
        'gray'   => '#f5f5f5',
        'blue'   => '#eaf4ff',
        'green'  => '#eef9ee',
        'red'    => '#ffecec',
        'yellow' => '#fff9db'
    );

    $bg = isset($colors[$atts['color']])
        ? $colors[$atts['color']]
        : '#f5f5f5';

    $style = sprintf(
        'background:%s;padding:15px;border:1px solid #ddd;border-radius:5px;text-align:%s;',
        esc_attr($bg),
        esc_attr($atts['text_align'])
    );

    if (!empty($atts['width'])) {
        $style .= 'width:' . esc_attr($atts['width']) . ';';
    }

    return '<div style="' . $style . '">' .
           do_shortcode($content) .
           '</div>';
}
add_shortcode('symple_box', 'symple_box_shortcode');

동해물과
백두산이
마르고
닳도록

본문 - 코드 하이라이트

코드 작성시 prism 을 사용하여 코드 하이라이트를 표시하기 위해 사용됩니다. 과거 클래식 편집기로 작성된 코드 블럭까지 대응합니다.

function enqueue_prism_autohighlight() {
    // 단일 글/페이지가 아니면 리턴
    if (!is_singular()) return;
    
    // 기존 구텐베르크 코드 블록이 있거나, 본문에 'lang:' 또는 'decode:'가 포함되어 있을 때만 로드
	global $post;
    $has_gutenberg_code = has_block('code', $post);
    $has_legacy_code = (strpos($post->post_content, 'lang:') !== false || strpos($post->post_content, 'decode:') !== false);
    if (!$has_gutenberg_code && !$has_legacy_code) return;	
	
	// 프리즘 스타일
    wp_enqueue_style(
        'prism-css',
        'https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css'
    );
	
	// 스타일
    wp_add_inline_style('prism-css', '
        @font-face {
            font-family: "D2Coding";
            src: url("https://cdn.jsdelivr.net/gh/naver/d2codingfont@3.2/D2Coding-Ver1.3.2-20180524.woff2") format("woff2");
            font-weight: normal;
            font-style: normal;
        }
		
        /* 레거시 pre 태그와 내부 코드 폰트도 함께 지정 */
        pre.wp-block-code,
        pre.wp-block-code code,
        pre[class*="lang:"],
        pre[class*="lang:"] code,
        code[class*="language-"],
        pre[class*="language-"] {
            font-family: "D2Coding", monospace !important;
            font-size: 14px !important;
        }
    ');	

    // 코어 먼저 로드
    wp_enqueue_script(
        'prism-js',
        'https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js',
        [],
        null,
        true
    );

    // autoloader는 코어에 의존
    wp_enqueue_script(
        'prism-autoloader',
        'https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/autoloader/prism-autoloader.min.js',
        ['prism-js'],
        null,
        true
    );

    // inline script는 autoloader 이후에 실행
	wp_add_inline_script('prism-autoloader', '
		document.addEventListener("DOMContentLoaded", function() {
            // 구텐베르크 코드 블록과 레거시 pre 블록을 모두 타겟팅합니다.
			var blocks = document.querySelectorAll("pre.wp-block-code code, pre[class*=\'lang:\']");
            
            blocks.forEach(function(el) {
                // 구텐베르크는 pre 안의 code 태그를, 레거시는 pre 태그 자체 혹은 내부를 분석해야 합니다.
                // 안전하게 텍스트를 가져오기 위해 블록 요소를 정의합니다.
                var isLegacyPre = el.tagName.toLowerCase() === "pre";
                var targetBlock = isLegacyPre ? el : el; 

				if (!targetBlock.className.match(/language-/)) {
					var code = targetBlock.textContent.trim();
					var lang = "java"; // 기본값

					// 언어 감지
					if (code.match(/^<\?php/))                                         lang = "php";
					else if (code.match(/^</) && code.match(/>/))                       lang = "markup";
					else if (code.match(/^\s*(using\s+System|namespace\s+\w+|Console\.Write|string\[\]\s+args)/m)) lang = "csharp";
					else if (code.match(/^\s*(import|public\s+class|System\.out|new\s+\w+\s*[<(])/m)) lang = "java";
					else if (code.match(/^\s*(def |import |print\(|class \w+:)/m))      lang = "python";
					else if (code.match(/^\s*(const |let |var |=>|function\s*\(|require\()/m)) lang = "javascript";
					else if (code.match(/^\s*(#include|int main|std::)/m))              lang = "cpp";
					else if (code.match(/^\s*(SELECT|INSERT|UPDATE|DELETE|CREATE|FROM|WHERE)/im)) lang = "sql";
					else if (code.match(/^\s*(\$|echo |function\s+\w+\s*\()/m))        lang = "bash";
					else if (code.match(/[{};]\s*\n|:\s*(#|rgb|var\(|px|em|%)/m))      lang = "css";

                    // Prism.js는 기본적으로 <pre><code class="language-xx">...</code></pre> 구조를 좋아합니다.
                    // 만약 레거시 pre 태그라면 내부에 code 태그를 생성해 감싸주어 Prism이 완벽하게 인식하도록 만듭니다.
                    if (isLegacyPre) {
                        var codeHtml = el.innerHTML;
                        el.innerHTML = "<code class=\'language-" + lang + "\'></code>";
                        el.querySelector("code").innerHTML = codeHtml;
                    } else {
                        targetBlock.classList.add("language-" + lang);
                    }
				}
			});
            
			Prism.highlightAll();
		});
	', 'after');
}

add_action('wp_enqueue_scripts', 'enqueue_prism_autohighlight');

본문 - 펼치기

펼치기/접기 플러그인을 삭제하고 작성한 스니펫입니다.

// 예제 : [expand title="자세한 내용 보기"]타이틀을 적지 않으면 자동으로 ‘펼쳐보기’라는 제목이 붙습니다.[/expand]

function custom_expand_shortcode( $atts, $content = null ) {
    $atts = shortcode_atts(
        array( 'title' => '펼쳐보기' ),
        $atts,
        'expand'
    );

    add_action( 'wp_footer', 'custom_expand_enqueue_assets', 5 );

    $content = wp_kses_post( do_shortcode( $content ) );
    $title   = esc_html( $atts['title'] );

    return '
    <div class="custom-toggle-container">
        <button class="custom-toggle-btn" aria-expanded="false">
            <span class="toggle-arrow"></span>
            <span class="toggle-title">' . $title . '</span>
        </button>
        <div class="custom-toggle-content" hidden>' . $content . '</div>
    </div>';
}
add_shortcode( 'expand', 'custom_expand_shortcode' );


function custom_expand_enqueue_assets() {
    static $done = false;
    if ( $done ) return;
    $done = true;
    ?>
    <style>
    .custom-toggle-container {
        margin: 15px 0;
        border: 1px solid #ddd;
        border-radius: 4px;
        overflow: hidden;
    }
    .custom-toggle-btn {
        display: flex;
        align-items: center;
        width: 100%;
        padding: 12px 15px;
        background-color: #f9f9f9;
        border: none;
        cursor: pointer;
        text-align: left;
        font-size: 16px;
        font-weight: bold;
        transition: background-color 0.2s ease;
    }
    .custom-toggle-btn:hover { background-color: #f1f1f1; }
    .toggle-arrow {
        display: inline-block;
        width: 0; height: 0;
        margin-right: 10px;
        border-left: 6px solid transparent;
        border-right: 6px solid transparent;
        border-top: 6px solid #333;
        transition: transform 0.2s ease;
        flex-shrink: 0;
    }
    .custom-toggle-btn[aria-expanded="true"] .toggle-arrow {
        transform: rotate(180deg);
    }
    .custom-toggle-content {
        padding: 15px;
        border-top: 1px solid #ddd;
        background-color: #fff;
    }
    .custom-toggle-content[hidden] { display: none !important; }
    .custom-toggle-content > p:first-child { margin-top: 0; }
    .custom-toggle-content > p:last-child  { margin-bottom: 0; }
    </style>
    <script>
    document.addEventListener("DOMContentLoaded", function () {
        // 빈 <p> 태그 제거
        document.querySelectorAll(".custom-toggle-content p").forEach(function (p) {
            if (p.innerHTML.trim() === "") p.remove();
        });

        document.body.addEventListener("click", function (e) {
            var btn = e.target.closest(".custom-toggle-btn");
            if (!btn) return;

            var content = btn.nextElementSibling;
            var isOpen  = btn.getAttribute("aria-expanded") === "true";

            if (isOpen) {
                content.setAttribute("hidden", "");
                btn.setAttribute("aria-expanded", "false");
            } else {
                content.removeAttribute("hidden");
                btn.setAttribute("aria-expanded", "true");
            }
        });
    });
    </script>
    <?php
}

본문 - 이미지 링크

LightBox에서 이미지를 보여줄때 resize된 썸네일 이미지가 보이는게 불편하여, 이미지 링크를 자동을 원본 이미지로 연결해 주는 스니펫입니다.

function auto_link_images_to_original_media( $content ) {

    if ( is_admin() || empty( $content ) ) {
        return $content;
    }

    libxml_use_internal_errors( true );

    $dom = new DOMDocument();
    $dom->loadHTML(
        '<?xml encoding="utf-8" ?>' . $content,
        LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD
    );

    $images = $dom->getElementsByTagName( 'img' );

    $imgs = [];
    foreach ( $images as $img ) {
        $imgs[] = $img;
    }

    foreach ( $imgs as $img ) {

        // 이미 링크가 있으면 건너뜀
        $parent = $img->parentNode;
        $has_link = false;

        while ( $parent ) {
            if ( $parent->nodeName === 'a' ) {
                $has_link = true;
                break;
            }
            $parent = $parent->parentNode;
        }

        if ( $has_link ) {
            continue;
        }

        $src = $img->getAttribute( 'src' );

        if ( empty( $src ) ) {
            continue;
        }

        // 첨부파일 ID 찾기
        $attachment_id = attachment_url_to_postid( $src );

        // 리사이즈 이미지인 경우
        if ( ! $attachment_id ) {

            $original_src = preg_replace(
                '/-\d+x\d+(?=\.(jpg|jpeg|png|gif|webp)$)/i',
                '',
                $src
            );

            $attachment_id = attachment_url_to_postid( $original_src );
        }

        // 원본 URL 얻기
        if ( $attachment_id ) {
            $full = wp_get_attachment_image_src( $attachment_id, 'full' );

            if ( ! empty( $full[0] ) ) {
                $link_url = $full[0];
            } else {
                $link_url = $src;
            }
        } else {
            $link_url = $src;
        }

        $a = $dom->createElement( 'a' );
        $a->setAttribute( 'href', $link_url );
        $a->setAttribute( 'target', '_blank' );
        $a->setAttribute( 'rel', 'noopener noreferrer' );

        $img->parentNode->replaceChild( $a, $img );
        $a->appendChild( $img );
    }

    return $dom->saveHTML();
}

add_filter( 'the_content', 'auto_link_images_to_original_media', 20 );

헤더 제거

기존에 코드 하이라이트를 위해 헤더에 다음 코드를 삽입하였습니다. 현재 스니펫으로 대체하여 사용하기 때문에 해당 내용은 삭제하였습니다.

<link rel="stylesheet" href="//cdn.jsdelivr.net/gh/wan2land/d2coding/d2coding.css">
<style type="text/css">
code, pre {
    border-radius: 5px;
    font-family: D2Coding, monospace;
    line-height: 1.5;
    font-size: 14px;
}
</style>

<!-- ref : https://webdir.tistory.com/439 -->
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/10.3.2/styles/dracula.min.css">
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/10.3.2/highlight.min.js"></script>

<script>
//hljs.initHighlightingOnLoad();
jQuery(document).ready(function () {
	jQuery('div pre').each(function(i, block) { 
		hljs.highlightBlock(block); 
	});
	jQuery('pre code').each(function(i, block) { 
		hljs.highlightBlock(block); 
	});
});
</script>

광고

댓글 남기기