* 이 게시물은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.
(워드프레스 해부 ㉒) 숏코드
글 본문에 [프로필] 한 줄 넣으면 프로필 카드가 뜨고, [최근글] 넣으면 목록이 나온다. 이런 걸 숏코드(shortcode)라 한다. 정적인 글 안에 동적인 조각을 심는 장치다.
만드는 법은 add_shortcode('이름', 콜백). 핵심 규칙 하나 — 콜백은 결과를 echo가 아니라 return 해야 한다. echo하면 출력 위치가 어긋난다.
직접 해보기
add_shortcode( 'greeting', function( $atts ) {
$a = shortcode_atts( array( 'name' => '방문자' ), $atts ); // 속성 기본값
return '<strong>안녕하세요, ' . esc_html( $a['name'] ) . '님!</strong>';
} );
// 글에서 사용: [greeting name="코즈모"]
shortcode_atts()로 속성의 기본값을 정하고, 출력할 땐 19편에서 배운 esc_html()로 이스케이프하는 걸 잊지 말자.
✅ 확인 — 글에
[greeting name="코즈모"]를 넣고 발행하면, 그 자리에 “안녕하세요, 코즈모님!”이 렌더되면 성공. 대괄호가 그대로 보이면 숏코드가 등록 안 된 것이다.
흔히 막히는 지점 — 숏코드 콜백에서 echo로 출력하면 결과가 글 맨 위로 튀어 올라간다. 숏코드는 반환값(return)이 그 자리에 삽입되는 구조다. 문자열을 만들어 return하고, HTML이 길면 ob_start()로 버퍼링해 반환하자.
속성을 제대로 다루기
실전 숏코드는 속성이 여러 개다. shortcode_atts()로 기본값을 정의하면, 사용자가 실제로 넘긴 값만 그 위에 덮인다. 하나 알아둘 것 — 속성 이름은 전부 소문자로 정규화된다. [button URL="..."]처럼 대문자로 써도 콜백엔 url로 들어온다.
add_shortcode( 'button', function( $atts ) {
$a = shortcode_atts( array(
'text' => '자세히',
'url' => '#',
'color' => '#0d6efd',
), $atts, 'button' );
return sprintf(
'<a href="%s" style="background:%s" class="cz-btn">%s</a>',
esc_url( $a['url'] ), // URL 자리 → esc_url
esc_attr( $a['color'] ), // 태그 속성 자리 → esc_attr
esc_html( $a['text'] ) // 화면 텍스트 자리 → esc_html
);
} );
// 사용: [button text="구매하기" url="https://cozlab.app" color="#ff8000"]
이스케이프 함수가 자리마다 다른 게 핵심이다. URL은 esc_url, 태그 속성 안은 esc_attr, 사람이 읽는 텍스트는 esc_html. 같은 값이라도 어디에 출력되느냐에 따라 방어법이 달라진다.
긴 HTML은 출력 버퍼로 반환한다
return 규칙 때문에 출력할 HTML이 길어지면 문자열을 .로 잇는 코드가 금세 지저분해진다. 이럴 땐 ob_start()로 출력을 버퍼에 모았다가 ob_get_clean()으로 한 번에 문자열로 받아 반환하면 깔끔하다.
add_shortcode( 'recent_posts', function( $atts ) {
$a = shortcode_atts( array( 'count' => 5 ), $atts );
$q = new WP_Query( array(
'posts_per_page' => (int) $a['count'],
'no_found_rows' => true, // 페이지네이션 불필요 → 쿼리 절약
) );
ob_start(); // 여기서부터 출력을 버퍼에 모은다
echo '<ul class="cz-recent">';
while ( $q->have_posts() ) { $q->the_post();
printf( '<li><a href="%s">%s</a></li>',
esc_url( get_permalink() ), esc_html( get_the_title() ) );
}
echo '</ul>';
wp_reset_postdata(); // 전역 $post 원복
return ob_get_clean(); // 버퍼 내용을 문자열로 반환
} );
// 사용: [recent_posts count="3"]
콜백 안에서 echo를 써도 되는 건 ob_start()가 그 출력을 가로채 버퍼에 담기 때문이다. 결국 밖으로 나가는 건 return한 문자열 하나뿐이라 규칙을 지킨다. 커스텀 쿼리를 돌렸으면 14편의 wp_reset_postdata()를 꼭 호출해 전역 상태를 되돌리자.
내용을 감싸는 숏코드 — [box]…[/box]
여는 태그만 있는 숏코드 말고, [box]내용[/box]처럼 감싸는(enclosing) 형태도 만들 수 있다. 이때 콜백의 두 번째 인자 $content로 사이에 낀 내용이 들어온다.
add_shortcode( 'box', function( $atts, $content = '' ) {
$a = shortcode_atts( array( 'type' => 'info' ), $atts );
// 안에 또 다른 숏코드가 있을 수 있으니 do_shortcode로 한 번 더 처리
$inner = do_shortcode( $content );
return '<div class="cz-box cz-' . esc_attr( $a['type'] ) . '">' . $inner . '</div>';
} );
// 사용: [box type="tip"]여기에 [greeting name="코즈모"] 도 중첩 가능[/box]
do_shortcode($content)를 거치면 안쪽에 낀 다른 숏코드까지 재귀적으로 처리된다. 이 $content 인자가 있느냐 없느냐가 여는형과 감싸는형을 가른다.
숏코드가 실행되지 않을 때 — 체크리스트
본문에선 잘 되던 숏코드가 다른 곳에선 대괄호 그대로 보이는 경우가 많다. 원인은 대개 정해져 있다.
- 위젯·템플릿 파일에서는 자동 실행 안 됨 —
the_content를 거치지 않기 때문. 직접echo do_shortcode('[greeting]')로 호출해야 한다. - 발췌(excerpt)·대표 영역에선 숏코드가 실행되지 않고 태그만 남거나 제거된다.
- 대괄호가 그대로 보임 —
add_shortcode등록이 안 됐거나, 로딩 순서상init전에 호출된 경우. - 줄바꿈이 깨짐 —
wpautop이 반환 HTML을<p>로 감싸며 생기는 문제. 반환 문자열을trim()하면 대개 해결된다. - 페이지 캐시 — 고쳤는데 반영이 안 되면 캐시를 비운다(18편).
숏코드 vs 블록 — 언제 무엇을
구텐베르크 시대에도 숏코드는 사라지지 않았다. 둘은 용도가 다르다.
| 기준 | 숏코드 | 블록 |
|---|---|---|
| 편집 경험 | 텍스트 [태그] 입력 | 시각적 UI |
| 쓸 수 있는 곳 | 본문·위젯·이메일·다른 CMS | 주로 에디터 안 |
| 만드는 법 | 함수 하나(add_shortcode) | block.json + JS(edit/save) |
| 성격 | 레거시·범용 | 현대 워드프레스 |
| 추천 | 기존 유지·범용 삽입 | 새로 만들면 이쪽 |
정리하면 새로 만든다면 블록이 방향이다. 다만 이메일 본문, 위젯, 다른 시스템과의 호환처럼 “어디에나 텍스트로 꽂아야 하는” 상황에선 숏코드가 여전히 가장 간단하다. 그래서 실무에선 둘을 상황에 맞게 섞어 쓴다.
고전적인 숏코드를 봤으니, 다음은 현대 워드프레스의 핵심 — 구텐베르크 블록의 구조다.
자주 묻는 질문 (FAQ)
숏코드가 뭔가요?
[shortcode] 형태로 글 안에 동적 콘텐츠를 넣는 기능입니다. add_shortcode로 이름과 처리 함수를 등록합니다.
블록 에디터에서도 숏코드가 되나요?
네, 숏코드 블록을 쓰거나 문단에 직접 넣으면 됩니다. 다만 새로 만든다면 블록으로 만드는 걸 권하기도 합니다.

답글 남기기