블로그 개편에 따라 그동안 미뤄왔던 워드프레스 설정을 정리해 보았습니다.
플러그인
오래되거나 불필요한 플러그인들은 제거하였습니다. 현재 사용중인 플러그인들은 다음과 같습니다.

- 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
- • kimstar - AMD BC-250
- • kws1070 - AMD BC-250
- • kimstar - 무선 레이싱휠 리뷰 및 설치 팁
- • kizeumo - 무선 레이싱휠 리뷰 및 설치 팁
- • 오가 - 스위치용 아케이드 독
- • 프레데터 - 드림캐스트 개조
- • kimstar - 드림캐스트 개조
- • kimstar - 스위치용 아케이드 독
- • 프레데터 - 드림캐스트 개조
- • 오가 - 스위치용 아케이드 독
위젯 - 카테고리
우측 사이드바 위젯에 사용할 카테고리 목록입니다. 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
- • Book > Etc (10)
- • Book > IT (13)
- • IT > .Net (114)
- • IT > Database (14)
- • IT > Develope (23)
- • IT > Device (35)
- • IT > Etc (78)
- • IT > Framework (14)
- • IT > Java (27)
- • IT > Mobile (18)
- • IT > OAM (27)
- • IT > Utility (11)
- • IT > Web (37)
- • Private > 3D Print (39)
- • Private > Culture (25)
- • Private > Etc (97)
- • Private > Game (183)
- • Private > Hobby (31)
- • Private > Pla-Model (21)
- • Private > Vehicle (5)
본문 - 최다 조회 포스트
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 .= ' • <span>' . $cat_name . '</span>';
$html .= ' • <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' );- AMD BC-250 • Private > Game • 3,913
- Batocera 설치 • Private > Game • 2,706
- X-Gunner Pistol • Private > Game • 940
- Multi Track bin 파일 병합 • Private > Game • 846
- PS Vita Retroarch 설치 • Private > Game • 779
- 이글렛II - 바토세라 설치 • Private > Game • 702
- 안드로이드 Retroarch 한글깨짐 • Private > Game • 694
- Y700 2세대 • IT > Device • 612
- Retrobat & Batocera 롬 공유 • Private > Game • 603
- RetroArch 번역기능 • Private > Game • 577
- PlayStation2 - OPL • Private > Game • 557
- POWKIDDY A12 바탑 • Private > Game • 457
- ASAP-NX 펌웨어 업데이트 • Private > Game • 454
- Batocera에 Switch 게임추가 • Private > Game • 411
- C# - 프로그램 종료하기 • IT > .Net • 335
- PCSX2 - 레일슈팅 세팅 • Private > Game • 328
- Neogeo ASP • Private > Game • 323
- Retrobat - Teknoparrot • Private > Game • 319
- 뷰3 - RetroArch 설치 • Private > Game • 286
- 레노버 G9 게임패드 • Private > Game • 260
본문 - 간단한 버튼
오래전 사용하던 플러그인에서 사용된 단축코드를 위해 추가한 스니펫입니다. 주로 다운로드 버튼을 위해 사용중입니다.
// 예제 : [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');본문 - 간단한 박스
오래전 사용하던 플러그인에서 사용된 단축코드를 위해 추가한 스니펫입니다. 특정 범위를 박스안에 넣기 위해 사용됩니다.
// 예제 : [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>




